Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / synfigstudio_0_61_06 / src / gtkmm / cellrenderer_timetrack.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file cellrenderer_timetrack.cpp
3 **      \brief Template Header
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 <gtkmm/label.h>
33 #include "cellrenderer_timetrack.h"
34 #include <gtk/gtk.h>
35 #include <gtkmm/spinbutton.h>
36 #include <gtkmm/combo.h>
37 #include <ETL/stringf>
38 #include "widget_value.h"
39 #include "app.h"
40 #include <gtkmm/menu.h>
41 #include <gtkmm/optionmenu.h>
42 #include "widget_time.h"
43 #include "widget_timeslider.h"
44
45 #include <synfigapp/canvasinterface.h>
46 #include "instance.h"
47
48 #include <synfig/timepointcollect.h>
49
50 #endif
51
52 using namespace synfig;
53 using namespace std;
54 using namespace etl;
55 using namespace studio;
56
57 /* === M A C R O S ========================================================= */
58
59 /* === G L O B A L S ======================================================= */
60
61 static char stipple_xpm[] = { 2, 0 };
62
63 //mode for modifier keys
64 enum MODMODE
65 {
66         NONE = 0,
67         SELECT_MASK = Gdk::CONTROL_MASK,
68         COPY_MASK = Gdk::SHIFT_MASK,
69         DELETE_MASK = Gdk::MOD1_MASK
70 };
71
72 /* === P R O C E D U R E S ================================================= */
73
74 /* === M E T H O D S ======================================================= */
75
76 CellRenderer_TimeTrack::CellRenderer_TimeTrack():
77         Glib::ObjectBase        (typeid(CellRenderer_TimeTrack)),
78         Gtk::CellRenderer       (),
79         adjustment_                     (10,10,20,0,0,0),
80
81         property_valuedesc_     (*this,"value_desc",synfigapp::ValueDesc()),
82         property_canvas_        (*this,"canvas",synfig::Canvas::Handle()),
83         property_adjustment_(*this,"adjustment",&adjustment_),
84         property_enable_timing_info_(*this,"enable-timing-info", false)
85 {
86                 dragging=false;
87         selection=false;
88 }
89
90 CellRenderer_TimeTrack::~CellRenderer_TimeTrack()
91 {
92         synfig::info("CellRenderer_TimeTrack::~CellRenderer_TimeTrack(): deleted");
93 }
94
95 void
96 CellRenderer_TimeTrack::set_adjustment(Gtk::Adjustment &x)
97 {
98         property_adjustment_=&x;
99 //      x.signal_value_changed().connect(sigc::mem_fun(*this,&Gtk::Widget::queue_draw));
100 }
101
102 synfig::Canvas::Handle
103 CellRenderer_TimeTrack::get_canvas()const
104 {
105         return const_cast<CellRenderer_TimeTrack*>(this)->property_canvas().get_value();
106 }
107
108 Gtk::Adjustment *
109 CellRenderer_TimeTrack::get_adjustment()
110 {
111         return (Gtk::Adjustment*)property_adjustment_;
112 }
113
114 const Gtk::Adjustment *
115 CellRenderer_TimeTrack::get_adjustment()const
116 {
117         return (const Gtk::Adjustment*)property_adjustment_;
118 }
119
120 bool
121 CellRenderer_TimeTrack::is_selected(const Waypoint& waypoint)const
122 {
123         return selected==waypoint;
124 }
125
126 //kind of a hack... pointer is ugly
127 const synfig::Node::time_set *get_times_from_vdesc(const synfigapp::ValueDesc &v)
128 {
129         if(v.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
130         {
131                 synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle());
132
133                 if(canvasparam)
134                 {
135                         return &canvasparam->get_times();
136                 }
137         }
138
139         ValueNode *base_value = v.get_value_node().get();
140
141         ValueNode_DynamicList *parent_value_node =
142                         v.parent_is_value_node() ?
143                                 dynamic_cast<ValueNode_DynamicList *>(v.get_parent_value_node().get()) :
144                                 0;
145
146         //we want a dynamic list entry to override the normal...
147         if(parent_value_node)
148         {
149                 return &parent_value_node->list[v.get_index()].get_times();
150         }else if(base_value) //don't render stuff if it's just animated...
151         {
152                 return &base_value->get_times();
153         }
154         return 0;
155 }
156
157 bool get_closest_time(const synfig::Node::time_set &tset, const Time &t, const Time &range, Time &out)
158 {
159         Node::time_set::const_iterator  i,j,end = tset.end();
160
161         // stop the crash mentioned in bug #1689282
162         // doesn't solve the underlying problem though, I don't think
163         if (tset.size() == 0)
164         {
165                 synfig::error(__FILE__":%d: tset.size() == 0",__LINE__);
166                 return false;
167         }
168
169         //TODO add in RangeGet so it's not so damn hard to click on points
170
171         i = tset.upper_bound(t); //where t is the lower bound, t < [first,i)
172         j = i; --j;
173
174         double dist = Time::end();
175         double closest = 0;
176
177         if(i != end)
178         {
179                 closest = i->get_time();
180                 dist = abs(i->get_time() - t);
181         }
182
183         if(j != end && (abs(j->get_time() - t) < dist) )
184         {
185                 closest = j->get_time();
186                 dist = abs(j->get_time() - t);
187         }
188
189         if( dist <= range/2 )
190         {
191                 out = closest;
192                 return true;
193         }
194
195         return false;
196 }
197
198 void
199 CellRenderer_TimeTrack::render_vfunc(
200                 const Glib::RefPtr<Gdk::Drawable>& window,
201                 Gtk::Widget& widget,
202                 const Gdk::Rectangle& background_area,
203                 const Gdk::Rectangle& area_,
204                 const Gdk::Rectangle& expose_area,
205                 Gtk::CellRendererState flags)
206 {
207         if(!window)
208                 return;
209
210         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
211         Glib::RefPtr<Gdk::GC> inactive_gc(Gdk::GC::create(window));
212         Gtk::Adjustment *adjustment=get_adjustment();
213         // Gtk::StateType state = Gtk::STATE_ACTIVE;
214         // Gtk::ShadowType shadow;
215
216         Gdk::Color
217                 curr_time_color("#0000ff"),
218                 inactive_color("#000000"),
219                 keyframe_color("#a07f7f");
220         Gdk::Color activepoint_color[2];
221
222         activepoint_color[0]=Gdk::Color("#ff0000");
223         activepoint_color[1]=Gdk::Color("#00ff00");
224
225         inactive_gc->set_rgb_fg_color(inactive_color);
226         inactive_gc->set_stipple(Gdk::Bitmap::create(stipple_xpm,2,2));
227         inactive_gc->set_fill(Gdk::STIPPLED);
228
229         synfig::Canvas::Handle canvas(property_canvas().get_value());
230
231         synfigapp::ValueDesc value_desc = property_value_desc().get_value();
232         synfig::ValueNode *base_value = value_desc.get_value_node().get();
233         // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(base_value);
234
235         synfig::ValueNode_DynamicList *parent_value_node(0);
236         if(property_value_desc().get_value().parent_is_value_node())
237                 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
238
239         // If the canvas is defined, then load up the keyframes
240         if(canvas)
241         {
242                 const synfig::KeyframeList& keyframe_list(canvas->keyframe_list());
243                 synfig::KeyframeList::const_iterator iter;
244
245                 for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter)
246                 {
247                         if(!iter->get_time().is_valid())
248                                 continue;
249
250                         const int x((int)((float)area_.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower())));
251                         if(iter->get_time()>=adjustment->get_lower() && iter->get_time()<adjustment->get_upper())
252                         {
253                                 gc->set_rgb_fg_color(keyframe_color);
254                                 window->draw_rectangle(gc, true, area_.get_x()+x, area_.get_y(), 1, area_.get_height()+1);
255                         }
256                 }
257         }
258
259         //render all the time points that exist
260         {
261                 const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc);
262
263                 if(tset)
264                 {
265                         synfig::Node::time_set::const_iterator  i = tset->begin(), end = tset->end();
266
267                         float   lower = adjustment->get_lower(),
268                                         upper = adjustment->get_upper();
269
270                         Glib::RefPtr<Gdk::GC>   gc = Gdk::GC::create(widget.get_window());
271
272                         Gdk::Rectangle area(area_);
273                         gc->set_clip_rectangle(area);
274                         gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
275
276                         bool valselected = sel_value.get_value_node() == base_value && !sel_times.empty();
277
278                         float cfps = get_canvas()->rend_desc().get_frame_rate();
279
280                         vector<Time>    drawredafter;
281
282                         Time diff = actual_time - actual_dragtime;//selected_time-drag_time;
283                         for(; i != end; ++i)
284                         {
285                                 //find the coordinate in the drawable space...
286                                 Time t = i->get_time();
287
288                                 if(!t.is_valid())
289                                         continue;
290
291                                 //if it found it... (might want to change comparison, and optimize
292                                 //                                       sel_times.find to not produce an overall nlogn solution)
293
294                                 bool selected=false;
295                                 //not dragging... just draw as per normal
296                                 //if move dragging draw offset
297                                 //if copy dragging draw both...
298
299                                 if(valselected && sel_times.find(t) != sel_times.end())
300                                 {
301                                         if(dragging) //skip if we're dragging because we'll render it later
302                                         {
303                                                 if(mode & COPY_MASK) // draw both blue and red moved
304                                                 {
305                                                         drawredafter.push_back((t + diff).round(cfps));
306                                                         gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
307                                                 }else if(mode & DELETE_MASK) //it's just red...
308                                                 {
309                                                         gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
310                                                         selected=true;
311                                                 }else //move - draw the red on top of the others...
312                                                 {
313                                                         drawredafter.push_back((t + diff).round(cfps));
314                                                         continue;
315                                                 }
316                                         }else
317                                         {
318                                                 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
319                                                 selected=true;
320                                         }
321                                 }else
322                                 {
323                                         gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
324                                 }
325
326                                 //synfig::info("Displaying time: %.3f s",(float)t);
327                                 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
328
329                                 //should draw me a grey filled circle...
330                                 Gdk::Rectangle area2(
331                                         area.get_x() - area.get_height()/2 + x + 1,
332                                         area.get_y() + 1,
333                                         area.get_height()-2,
334                                         area.get_height()-2
335                                 );
336                                 render_time_point_to_window(window,area2,*i,selected);
337
338                                 /*window->draw_arc(gc,true,
339                                 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
340                                 area.get_height()/2, area.get_height()*3/4,
341                                 0, 64*360);
342
343                                 gc->set_rgb_fg_color(Gdk::Color("#000000"));
344                                 window->draw_arc(gc,false,
345                                 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
346                                 area.get_height()/2, area.get_height()*3/4,
347                                 0, 64*360);
348                                 */
349                         }
350
351                         {
352                                 vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end();
353                                 for(; i != end; ++i)
354                                 {
355                                         //find the coordinate in the drawable space...
356                                         Time t = *i;
357
358                                         if(!t.is_valid())
359                                                 continue;
360
361                                         //synfig::info("Displaying time: %.3f s",(float)t);
362                                         const int x = (int)((t-lower)*area.get_width()/(upper-lower));
363
364                                         //should draw me a grey filled circle...
365
366                                         Gdk::Rectangle area2(
367                                                 area.get_x() - area.get_height()/2 + x + 1,
368                                                 area.get_y() + 1,
369                                                 area.get_height()-2,
370                                                 area.get_height()-2
371                                         );
372                                         render_time_point_to_window(window,area2,*i,true);
373 /*                                      gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
374                                         window->draw_arc(gc,true,
375                                         area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
376                                         area.get_height()/2, area.get_height()*3/4,
377                                         0, 64*360);
378
379                                         gc->set_rgb_fg_color(Gdk::Color("#000000"));
380                                         window->draw_arc(gc,false,
381                                         area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
382                                         area.get_height()/2, area.get_height()*3/4,
383                                         0, 64*360);
384 */
385                                 }
386                         }
387                 }
388         }
389
390         /* THIS IS NOW HANDLED ENTIRELY BY THE TIMEPOINT SYSTEM
391         // This this is an animated value node, then render the waypoints
392         if(value_node)
393         {
394                 //now render the actual waypoints
395                 synfig::ValueNode_Animated::WaypointList::iterator iter;
396                 for(
397                         iter=value_node->waypoint_list().begin();
398                         iter!=value_node->waypoint_list().end();
399                         iter++
400                 )
401                 {
402                         if(!iter->get_time().is_valid())
403                                 continue;
404                         int x;
405                         bool selected=false;
406                         if(is_selected(*iter))
407                         {
408                                 Time t(iter->get_time());
409
410
411                                 if(dragging)
412                                         t=(t+selected_time-drag_time).round(get_canvas()->rend_desc().get_frame_rate());
413
414                                 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(t-adjustment->get_lower()));
415                                 shadow=Gtk::SHADOW_IN;
416                                 selected=true;
417                         }
418                         else
419                         {
420                                 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()));
421                                 shadow=Gtk::SHADOW_OUT;
422                                 selected=false;
423                         }
424
425
426                         widget.get_style()->paint_diamond(
427                                 Glib::RefPtr<Gdk::Window>::cast_static(window),
428                                 state,
429                                 shadow,
430                                 area,
431                                 widget,
432                                 "solid",
433                                 area.get_x()+x-area.get_height()/4,
434                                 area.get_y()+area.get_height()/4,
435                                 area.get_height()/2,
436                                 area.get_height()/2
437                         );
438                 }
439         }
440         */
441                 Gdk::Rectangle area(area_);
442         // If the parent of this value node is a dynamic list, then
443         // render the on and off times
444         if(parent_value_node)
445         {
446                 const int index(property_value_desc().get_value().get_index());
447                 const synfig::ValueNode_DynamicList::ListEntry& list_entry(parent_value_node->list[index]);
448                 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(list_entry.timing_info);
449                 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter,next;
450
451                 bool is_off(false);
452                 if(!activepoint_list.empty())
453                         is_off=!activepoint_list.front().state;
454
455                 int xstart(0);
456
457                 int x=0,prevx=0;
458                 for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++)
459                 {
460                         x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower())));
461                         if(x<0)x=0;
462                         if(x>area.get_width())x=area.get_width();
463
464                         bool status_at_time=0;
465                         if(next!=activepoint_list.end())
466                         {
467                                 status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0);
468                         }
469                         else
470                                 status_at_time=!list_entry.status_at_time(Time::end());
471
472                         if(!is_off && status_at_time)
473                         {
474                                 xstart=x;
475                                 is_off=true;
476                         }
477                         else
478                         if(is_off && !status_at_time)
479                         {
480                                 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
481                                 is_off=false;
482                         }
483
484                         /*
485                         if(!is_off && iter!=activepoint_list.end() && next->state==false && iter->state==false)
486                         {
487                                 xstart=x;
488                                 is_off=true;
489                         }
490                         else if(is_off && next!=activepoint_list.end() && iter->state==false && next->state==true)
491                         {
492                                 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
493                                 is_off=false;
494                         }
495                         else if(is_off && iter!=activepoint_list.end() && iter->state==true)
496                         {
497                                 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), prevx-xstart, area.get_height());
498                                 is_off=false;
499                         }
500                         */
501
502
503
504                         if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper())
505                         {
506                                 int w(1);
507                                 if(selected==*iter)
508                                         w=3;
509                                 gc->set_rgb_fg_color(activepoint_color[iter->state]);
510                                 window->draw_rectangle(gc, true, area.get_x()+x-w/2, area.get_y(), w, area.get_height());
511                         }
512                         prevx=x;
513                 }
514                 if(is_off)
515                 {
516                         window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_height());
517                 }
518         }
519
520         // Render a line that defines the current tick in time
521         {
522                 gc->set_rgb_fg_color(curr_time_color);
523
524                 const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower())));
525
526                 if(adjustment->get_value()>=adjustment->get_lower() && adjustment->get_value()<adjustment->get_upper())
527                         window->draw_rectangle(gc, true, area.get_x()+x, area.get_y(), 1, area.get_height());
528         }
529 }
530
531 synfig::ValueNode_Animated::WaypointList::iterator
532 CellRenderer_TimeTrack::find_waypoint(const synfig::Time& t,const synfig::Time& scope)
533 {
534         synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
535
536     Time nearest(Time::end());
537
538         synfig::ValueNode_Animated::WaypointList::iterator iter,ret;
539
540         if(value_node)
541         {
542                 for(
543                         iter=value_node->waypoint_list().begin();
544                         iter!=value_node->waypoint_list().end();
545                         iter++
546                         )
547                 {
548                         Time val=abs(iter->get_time()-selected_time);
549                         if(val<nearest)
550                         {
551                                 nearest=val;
552                                 ret=iter;
553                         }
554                 }
555
556                 if(nearest!=Time::end() && nearest<scope)
557                 {
558                         return ret;
559                 }
560         }
561         throw int();
562 }
563
564 bool
565 CellRenderer_TimeTrack::activate_vfunc(
566         GdkEvent* event,
567         Gtk::Widget& widget,
568         const Glib::ustring& treepath,
569         const Gdk::Rectangle& background_area,
570         const Gdk::Rectangle& cell_area,
571         Gtk::CellRendererState flags)
572 {
573         path=treepath;
574         synfig::ValueNode_Animated::WaypointList::iterator iter;
575     Time nearest=1000000000;
576         Gtk::Adjustment *adjustment=get_adjustment();
577
578         // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
579
580         synfig::Canvas::Handle canvas(get_canvas());
581
582         synfig::ValueNode_DynamicList *parent_value_node(0);
583         if(property_value_desc().get_value().parent_is_value_node())
584                 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
585
586         Time deltatime = 0;
587         Time curr_time;
588         switch(event->type)
589         {
590         case GDK_MOTION_NOTIFY:
591                 curr_time=((float)event->motion.x-(float)cell_area.get_x())/(float)cell_area.get_width()*(adjustment->get_upper()-adjustment->get_lower())+adjustment->get_lower();
592
593                 mode = NONE;
594                 {
595                         Gdk::ModifierType mod;
596                         Gdk::Event(event).get_state(mod);
597                         mode = mod;
598                 }
599                 break;
600         case GDK_BUTTON_PRESS:
601         case GDK_BUTTON_RELEASE:
602         default:
603                 curr_time=((float)event->button.x-(float)cell_area.get_x())/(float)cell_area.get_width()*(adjustment->get_upper()-adjustment->get_lower())+adjustment->get_lower();
604                 {
605                         Gdk::ModifierType mod;
606                         Gdk::Event(event).get_state(mod);
607                         mode = mod;
608                 }
609                 break;
610         }
611         actual_time = curr_time;
612         if(canvas)
613                 curr_time=curr_time.round(canvas->rend_desc().get_frame_rate());
614         selected_time=curr_time;
615
616     Time pixel_width((adjustment->get_upper()-adjustment->get_lower())/cell_area.get_width());
617
618     switch(event->type)
619     {
620         case GDK_BUTTON_PRESS:
621                 //selected_time=((float)event->button.x-(float)cell_area.get_x())/(float)cell_area.get_width()*(adjustment->get_upper()-adjustment->get_lower())+adjustment->get_lower();
622
623                 //Deal with time point selection, but only if they aren't involved in the insanity...
624                 if(/*!value_node && */event->button.button == 1)
625                 {
626                         Time stime;
627
628                         /*!     UI specification:
629
630                                 When nothing is selected, clicking on a point in either normal mode order
631                                         addative mode will select the time point closest to the click.
632                                         Subtractive click will do nothing
633
634                                 When things are already selected, clicking on a selected point does
635                                         nothing (in both normal and add mode).  Add mode clicking on an unselected
636                                         point adds it to the set.  Normal clicking on an unselected point will
637                                         select only that one time point.  Subtractive clicking on any point
638                                         will remove it from the the set if it is included.
639                         */
640
641                         synfigapp::ValueDesc valdesc = property_value_desc().get_value();
642                         const Node::time_set *tset = get_times_from_vdesc(valdesc);
643
644                         bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
645                         bool selectmode = mode & SELECT_MASK;
646
647                         //NOTE LATER ON WE SHOULD MAKE IT SO MULTIPLE VALUENODES CAN BE SELECTED AT ONCE
648                         //we want to jump to the value desc if we're not currently on it
649                         //      but only if we want to add the point
650                         if(clickfound && !(sel_value == valdesc))
651                         {
652                                 sel_value = valdesc;
653                                 sel_times.clear();
654                         }
655
656                         //now that we've made sure we're selecting the correct value, deal with the already selected points
657                         set<Time>::iterator foundi = clickfound ? sel_times.find(stime) : sel_times.end();
658                         bool found = foundi != sel_times.end();
659
660                         //remove all other points from our list... (only select the one we need)
661                         if(!selectmode && !found)
662                         {
663                                 sel_times.clear();
664                         }
665
666                         if(found && selectmode) //remove a single already selected point
667                         {
668                                 sel_times.erase(foundi);
669                         }else if(clickfound) //otherwise look at adding it
670                         {
671                                 //for replace the list was cleared earlier, and for add it wasn't so it works
672                                 sel_times.insert(stime);
673                         }
674                 }
675
676                 selection=false;
677                 try
678                 {
679                         iter=find_waypoint(selected_time,pixel_width*cell_area.get_height()/2);
680                         selected_waypoint=iter;
681                         selected=*iter;
682
683                         selection=true;
684                 }
685                 catch(int)
686                 {
687                         selection=false;
688                         selected=synfig::UniqueID::nil();
689                 }
690
691                 if((!sel_times.empty() || selection) && event->button.button==1)
692                 {
693                         dragging=true;
694                         drag_time=selected_time;
695                         actual_dragtime=actual_time;
696                 }
697                 //selected_time=iter->time;
698
699                 /*
700                 // Activepoint Selection
701                 if(parent_value_node)
702                 {
703                         const int index(property_value_desc().get_value().get_index());
704                         const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(parent_value_node->list[index].timing_info);
705                         synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter;
706
707                         for(iter=activepoint_list.begin();iter!=activepoint_list.end();++iter)
708                         {
709                                 Time val=abs(iter->time-selected_time);
710                                 if(val<nearest)
711                                 {
712                                         nearest=val;
713                                         selected=*iter;
714                                         selection=true;
715                                 }
716                         }
717                         // Perhaps I sould signal if we selected this activepoint?
718                 }*/
719
720                         if(event->button.button==3)
721                         {
722                                 Time stime;
723                                 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
724                                 const Node::time_set *tset = get_times_from_vdesc(valdesc);
725
726                                 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
727
728                                 etl::handle<synfig::Node> node;
729                                 if(valdesc.get_value(stime).get_type()==ValueBase::TYPE_CANVAS)
730                                 {
731                                         node=Canvas::Handle(valdesc.get_value(stime).get(Canvas::Handle()));
732                                 }
733                                 else //if(valdesc.is_value_node())
734                                 {
735                                         node=valdesc.get_value_node();
736                                 }
737
738                                 if(clickfound && node)
739                                 {
740                                         show_timepoint_menu(node, stime, actual_time<stime?SIDE_LEFT:SIDE_RIGHT);
741                                 }
742                         }
743
744                 break;
745         case GDK_MOTION_NOTIFY:
746                 //DEBUGPOINT();
747                 //if(selection && dragging)
748                 //      selected_time=((float)event->motion.x-(float)cell_area.get_x())/(float)cell_area.get_width()*(adjustment->get_upper()-adjustment->get_lower())+adjustment->get_lower();
749                 return true;
750
751                 break;
752         case GDK_BUTTON_RELEASE:
753                 {
754                         DEBUGPOINT();
755
756                         //selected_time=((float)event->button.x-(float)cell_area.get_x())/(float)cell_area.get_width()*(adjustment->get_upper()-adjustment->get_lower())+adjustment->get_lower();
757                         dragging=false;
758
759                         /*if(event->button.button==3 && selection)
760                         {
761                                 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
762                                 return true;
763                         }
764                         */
765
766                         //Time point stuff...
767                         if(event->button.button == 1)
768                         {
769                                 bool delmode = (mode & DELETE_MASK) && !(mode & COPY_MASK);
770                                 deltatime = actual_time - actual_dragtime;
771                                 if(sel_times.size() != 0 && (delmode || !deltatime.is_equal(Time(0))))
772                                 {
773                                         synfigapp::Action::ParamList param_list;
774                                         param_list.add("canvas",canvas_interface()->get_canvas());
775                                         param_list.add("canvas_interface",canvas_interface());
776
777                                         if(sel_value.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
778                                         {
779                                                 param_list.add("addcanvas",sel_value.get_value().get(Canvas::Handle()));
780                                         }else
781                                         {
782                                                 param_list.add("addvaluedesc",sel_value);
783                                         }
784
785                                         set<Time>       newset;
786                                         std::set<synfig::Time>::iterator i = sel_times.begin(), end = sel_times.end();
787                                         for(; i != end; ++i)
788                                         {
789                                                 param_list.add("addtime",*i);
790
791                                                 newset.insert((*i + deltatime).round(get_canvas()->rend_desc().get_frame_rate()));
792                                         }
793
794                                         if(!delmode)
795                                                 param_list.add("deltatime",deltatime);
796                                 //      param_list.add("time",canvas_interface()->get_time());
797
798                                         if(mode & COPY_MASK) //copy
799                                         {
800                                                 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
801                                                         ->process_action("timepoint_copy", param_list);
802                                         }else if(delmode) //DELETE
803                                         {
804                                                 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
805                                                         ->process_action("timepoint_delete", param_list);
806                                         }else //MOVE
807                                         {
808                                                 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
809                                                         ->process_action("timepoint_move", param_list);
810                                         }
811
812                                         //now replace all the selected with the new selected
813                                         sel_times = newset;
814                                 }
815                         }
816
817
818
819                         /*if(value_node && selection)
820                         {
821                                 if(selected_time==drag_time && event->button.button!=3)
822                                         signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
823                                 else
824                                 if(event->button.button==1)
825                                 {
826                                         synfig::Waypoint waypoint(*selected_waypoint);
827                                         Time newtime((waypoint.get_time()+(selected_time-drag_time)).round(canvas->rend_desc().get_frame_rate()));
828                                         if(waypoint.get_time()!=newtime)
829                                         {
830                                                 waypoint.set_time(newtime);
831                                                 signal_waypoint_changed_(waypoint,value_node);
832                                         }
833                                 }
834                         }*/
835
836                         //if(selection)
837                         //      selected_time=iter->time;
838                         //selected_time=iter->get_time();
839                         return true;
840                 }
841         default:
842                 //std::cerr<<"unknown event type "<<event->type<<std::endl;
843                 return false;
844                 break;
845         }
846
847
848
849         return false;
850 }
851
852
853
854 Glib::PropertyProxy<synfigapp::ValueDesc>
855 CellRenderer_TimeTrack::property_value_desc()
856 {
857         return Glib::PropertyProxy<synfigapp::ValueDesc>(this,"value_desc");
858 }
859
860 Glib::PropertyProxy<synfig::Canvas::Handle>
861 CellRenderer_TimeTrack::property_canvas()
862 {
863         return Glib::PropertyProxy<synfig::Canvas::Handle>(this,"canvas");
864 }
865
866 Glib::PropertyProxy<Gtk::Adjustment* >
867 CellRenderer_TimeTrack::property_adjustment()
868 {
869         return Glib::PropertyProxy<Gtk::Adjustment* >(this,"adjustment");
870 }
871
872 void
873 CellRenderer_TimeTrack::set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface>      h)
874 {
875         canvas_interface_ = h;
876 }
877
878 static void
879 set_waypoint_model(std::set<synfig::Waypoint, std::less<UniqueID> > waypoints, Waypoint::Model model, etl::loose_handle<synfigapp::CanvasInterface> canvas_interface)
880 {
881         // Create the action group
882         synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Change Waypoint Group"));
883
884         std::set<synfig::Waypoint, std::less<UniqueID> >::const_iterator iter;
885         for(iter=waypoints.begin();iter!=waypoints.end();++iter)
886         {
887                 Waypoint waypoint(*iter);
888                 waypoint.apply_model(model);
889
890                 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set"));
891
892                 assert(action);
893
894                 action->set_param("canvas",canvas_interface->get_canvas());
895                 action->set_param("canvas_interface",canvas_interface);
896
897                 action->set_param("waypoint",waypoint);
898                 action->set_param("value_node",waypoint.get_parent_value_node());
899
900                 if(!canvas_interface->get_instance()->perform_action(action))
901                 {
902                         group.cancel();
903                         return;
904                 }
905         }
906 }
907
908 void
909 CellRenderer_TimeTrack::show_timepoint_menu(const etl::handle<synfig::Node>& node, const synfig::Time& time, Side side)
910 {
911         std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
912         int n;
913         n=synfig::waypoint_collect(waypoint_set,time,node);
914
915         Gtk::Menu* menu(manage(new Gtk::Menu()));
916
917         // Create the interpolation method menu
918         if(!waypoint_set.empty())
919         {
920                 Gtk::Menu* interp_menu(manage(new Gtk::Menu()));
921                 Waypoint::Model model;
922
923                 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_TCB);
924                 else model.set_after(INTERPOLATION_TCB);
925                 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("TCB"),
926                         sigc::bind(
927                                 sigc::ptr_fun(set_waypoint_model),
928                                 waypoint_set,
929                                 model,
930                                 canvas_interface()
931                         )
932                 ));
933
934                 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_LINEAR);
935                 else model.set_after(INTERPOLATION_LINEAR);
936                 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Linear"),
937                         sigc::bind(
938                                 sigc::ptr_fun(set_waypoint_model),
939                                 waypoint_set,
940                                 model,
941                                 canvas_interface()
942                         )
943                 ));
944
945                 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_HALT);
946                 else model.set_after(INTERPOLATION_HALT);
947                 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Ease"),
948                         sigc::bind(
949                                 sigc::ptr_fun(set_waypoint_model),
950                                 waypoint_set,
951                                 model,
952                                 canvas_interface()
953                         )
954                 ));
955
956                 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_CONSTANT);
957                 else model.set_after(INTERPOLATION_CONSTANT);
958                 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Constant"),
959                         sigc::bind(
960                                 sigc::ptr_fun(set_waypoint_model),
961                                 waypoint_set,
962                                 model,
963                                 canvas_interface()
964                         )
965                 ));
966
967
968                 menu->items().push_back(
969                         Gtk::Menu_Helpers::MenuElem(
970                                 side==SIDE_LEFT?_("Change \"In\" Interp."):_("Change \"Out\" Interp."),
971                                 *interp_menu
972                         )
973                 );
974         }
975
976         menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-jump-to"),
977                 sigc::bind(
978                         sigc::mem_fun(
979                                 *canvas_interface(),
980                                 &synfigapp::CanvasInterface::set_time
981                         ),
982                         time
983                 )
984         ));
985
986         if(!waypoint_set.empty())
987         {
988                 if(waypoint_set.size()==1)
989                 {
990                         delete menu;
991                         menu=0;
992                         signal_waypoint_clicked_(" ",*waypoint_set.begin(),2);
993                         return;
994                 }
995                 else
996                         synfig::info("Too many waypoints under me");
997         }
998         else
999                 synfig::info("ZERO waypoints under me");
1000
1001         if(menu)menu->popup(3,gtk_get_current_event_time());
1002 }