1 /* === S Y N F I G ========================================================= */
2 /*! \file cellrenderer_timetrack.cpp
3 ** \brief Template Header
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
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.
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.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
32 #include <gtkmm/label.h>
33 #include "cellrenderer_timetrack.h"
35 #include <gtkmm/spinbutton.h>
36 #include <gtkmm/combo.h>
37 #include <ETL/stringf>
38 #include "widget_value.h"
40 #include <gtkmm/menu.h>
41 #include <gtkmm/optionmenu.h>
42 #include "widget_time.h"
43 #include "widget_timeslider.h"
45 #include <synfigapp/canvasinterface.h>
48 #include <synfig/timepointcollect.h>
52 using namespace synfig;
55 using namespace studio;
57 /* === M A C R O S ========================================================= */
59 /* === G L O B A L S ======================================================= */
61 static char stipple_xpm[] = { 2, 0 };
63 //mode for modifier keys
67 SELECT_MASK = Gdk::CONTROL_MASK,
68 COPY_MASK = Gdk::SHIFT_MASK,
69 DELETE_MASK = Gdk::MOD1_MASK
72 /* === P R O C E D U R E S ================================================= */
74 /* === M E T H O D S ======================================================= */
76 CellRenderer_TimeTrack::CellRenderer_TimeTrack():
77 Glib::ObjectBase (typeid(CellRenderer_TimeTrack)),
79 adjustment_ (10,10,20,0,0,0),
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)
90 CellRenderer_TimeTrack::~CellRenderer_TimeTrack()
92 synfig::info("CellRenderer_TimeTrack::~CellRenderer_TimeTrack(): deleted");
96 CellRenderer_TimeTrack::set_adjustment(Gtk::Adjustment &x)
98 property_adjustment_=&x;
99 // x.signal_value_changed().connect(sigc::mem_fun(*this,&Gtk::Widget::queue_draw));
102 synfig::Canvas::Handle
103 CellRenderer_TimeTrack::get_canvas()const
105 return const_cast<CellRenderer_TimeTrack*>(this)->property_canvas().get_value();
109 CellRenderer_TimeTrack::get_adjustment()
111 return (Gtk::Adjustment*)property_adjustment_;
114 const Gtk::Adjustment *
115 CellRenderer_TimeTrack::get_adjustment()const
117 return (const Gtk::Adjustment*)property_adjustment_;
121 CellRenderer_TimeTrack::is_selected(const Waypoint& waypoint)const
123 return selected==waypoint;
126 //kind of a hack... pointer is ugly
127 const synfig::Node::time_set *get_times_from_vdesc(const synfigapp::ValueDesc &v)
129 if(v.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
131 synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle());
135 return &canvasparam->get_times();
139 ValueNode *base_value = v.get_value_node().get();
141 ValueNode_DynamicList *parent_value_node =
142 v.parent_is_value_node() ?
143 dynamic_cast<ValueNode_DynamicList *>(v.get_parent_value_node().get()) :
146 //we want a dynamic list entry to override the normal...
147 if(parent_value_node)
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...
152 return &base_value->get_times();
157 bool get_closest_time(const synfig::Node::time_set &tset, const Time &t, const Time &range, Time &out)
159 Node::time_set::const_iterator i,j,end = tset.end();
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)
165 synfig::error(__FILE__":%d: tset.size() == 0",__LINE__);
169 //TODO add in RangeGet so it's not so damn hard to click on points
171 i = tset.upper_bound(t); //where t is the lower bound, t < [first,i)
174 double dist = Time::end();
179 closest = i->get_time();
180 dist = abs(i->get_time() - t);
183 if(j != end && (abs(j->get_time() - t) < dist) )
185 closest = j->get_time();
186 dist = abs(j->get_time() - t);
189 if( dist <= range/2 )
199 CellRenderer_TimeTrack::render_vfunc(
200 const Glib::RefPtr<Gdk::Drawable>& window,
202 const Gdk::Rectangle& background_area,
203 const Gdk::Rectangle& area_,
204 const Gdk::Rectangle& expose_area,
205 Gtk::CellRendererState flags)
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;
217 curr_time_color("#0000ff"),
218 inactive_color("#000000"),
219 keyframe_color("#a07f7f");
220 Gdk::Color activepoint_color[2];
222 activepoint_color[0]=Gdk::Color("#ff0000");
223 activepoint_color[1]=Gdk::Color("#00ff00");
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);
229 synfig::Canvas::Handle canvas(property_canvas().get_value());
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);
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());
239 // If the canvas is defined, then load up the keyframes
242 const synfig::KeyframeList& keyframe_list(canvas->keyframe_list());
243 synfig::KeyframeList::const_iterator iter;
245 for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter)
247 if(!iter->get_time().is_valid())
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())
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);
259 //render all the time points that exist
261 const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc);
265 synfig::Node::time_set::const_iterator i = tset->begin(), end = tset->end();
267 float lower = adjustment->get_lower(),
268 upper = adjustment->get_upper();
270 Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(widget.get_window());
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);
276 bool valselected = sel_value.get_value_node() == base_value && !sel_times.empty();
278 float cfps = get_canvas()->rend_desc().get_frame_rate();
280 vector<Time> drawredafter;
282 Time diff = actual_time - actual_dragtime;//selected_time-drag_time;
285 //find the coordinate in the drawable space...
286 Time t = i->get_time();
291 //if it found it... (might want to change comparison, and optimize
292 // sel_times.find to not produce an overall nlogn solution)
295 //not dragging... just draw as per normal
296 //if move dragging draw offset
297 //if copy dragging draw both...
299 if(valselected && sel_times.find(t) != sel_times.end())
301 if(dragging) //skip if we're dragging because we'll render it later
303 if(mode & COPY_MASK) // draw both blue and red moved
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...
309 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
311 }else //move - draw the red on top of the others...
313 drawredafter.push_back((t + diff).round(cfps));
318 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
323 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
326 //synfig::info("Displaying time: %.3f s",(float)t);
327 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
329 //should draw me a grey filled circle...
330 Gdk::Rectangle area2(
331 area.get_x() - area.get_height()/2 + x + 1,
336 render_time_point_to_window(window,area2,*i,selected);
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,
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,
352 vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end();
355 //find the coordinate in the drawable space...
361 //synfig::info("Displaying time: %.3f s",(float)t);
362 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
364 //should draw me a grey filled circle...
366 Gdk::Rectangle area2(
367 area.get_x() - area.get_height()/2 + x + 1,
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,
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,
390 /* THIS IS NOW HANDLED ENTIRELY BY THE TIMEPOINT SYSTEM
391 // This this is an animated value node, then render the waypoints
394 //now render the actual waypoints
395 synfig::ValueNode_Animated::WaypointList::iterator iter;
397 iter=value_node->waypoint_list().begin();
398 iter!=value_node->waypoint_list().end();
402 if(!iter->get_time().is_valid())
406 if(is_selected(*iter))
408 Time t(iter->get_time());
412 t=(t+selected_time-drag_time).round(get_canvas()->rend_desc().get_frame_rate());
414 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(t-adjustment->get_lower()));
415 shadow=Gtk::SHADOW_IN;
420 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()));
421 shadow=Gtk::SHADOW_OUT;
426 widget.get_style()->paint_diamond(
427 Glib::RefPtr<Gdk::Window>::cast_static(window),
433 area.get_x()+x-area.get_height()/4,
434 area.get_y()+area.get_height()/4,
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)
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;
452 if(!activepoint_list.empty())
453 is_off=!activepoint_list.front().state;
458 for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++)
460 x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower())));
462 if(x>area.get_width())x=area.get_width();
464 bool status_at_time=0;
465 if(next!=activepoint_list.end())
467 status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0);
470 status_at_time=!list_entry.status_at_time(Time::end());
472 if(!is_off && status_at_time)
478 if(is_off && !status_at_time)
480 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
485 if(!is_off && iter!=activepoint_list.end() && next->state==false && iter->state==false)
490 else if(is_off && next!=activepoint_list.end() && iter->state==false && next->state==true)
492 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
495 else if(is_off && iter!=activepoint_list.end() && iter->state==true)
497 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), prevx-xstart, area.get_height());
504 if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper())
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());
516 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_height());
520 // Render a line that defines the current tick in time
522 gc->set_rgb_fg_color(curr_time_color);
524 const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower())));
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());
531 synfig::ValueNode_Animated::WaypointList::iterator
532 CellRenderer_TimeTrack::find_waypoint(const synfig::Time& t,const synfig::Time& scope)
534 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
536 Time nearest(Time::end());
538 synfig::ValueNode_Animated::WaypointList::iterator iter,ret;
543 iter=value_node->waypoint_list().begin();
544 iter!=value_node->waypoint_list().end();
548 Time val=abs(iter->get_time()-selected_time);
556 if(nearest!=Time::end() && nearest<scope)
565 CellRenderer_TimeTrack::activate_vfunc(
568 const Glib::ustring& treepath,
569 const Gdk::Rectangle& background_area,
570 const Gdk::Rectangle& cell_area,
571 Gtk::CellRendererState flags)
574 synfig::ValueNode_Animated::WaypointList::iterator iter;
575 Time nearest=1000000000;
576 Gtk::Adjustment *adjustment=get_adjustment();
578 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
580 synfig::Canvas::Handle canvas(get_canvas());
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());
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();
595 Gdk::ModifierType mod;
596 Gdk::Event(event).get_state(mod);
600 case GDK_BUTTON_PRESS:
601 case GDK_BUTTON_RELEASE:
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();
605 Gdk::ModifierType mod;
606 Gdk::Event(event).get_state(mod);
611 actual_time = curr_time;
613 curr_time=curr_time.round(canvas->rend_desc().get_frame_rate());
614 selected_time=curr_time;
616 Time pixel_width((adjustment->get_upper()-adjustment->get_lower())/cell_area.get_width());
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();
623 //Deal with time point selection, but only if they aren't involved in the insanity...
624 if(/*!value_node && */event->button.button == 1)
628 /*! UI specification:
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
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.
641 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
642 const Node::time_set *tset = get_times_from_vdesc(valdesc);
644 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
645 bool selectmode = mode & SELECT_MASK;
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))
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();
660 //remove all other points from our list... (only select the one we need)
661 if(!selectmode && !found)
666 if(found && selectmode) //remove a single already selected point
668 sel_times.erase(foundi);
669 }else if(clickfound) //otherwise look at adding it
671 //for replace the list was cleared earlier, and for add it wasn't so it works
672 sel_times.insert(stime);
679 iter=find_waypoint(selected_time,pixel_width*cell_area.get_height()/2);
680 selected_waypoint=iter;
688 selected=synfig::UniqueID::nil();
691 if((!sel_times.empty() || selection) && event->button.button==1)
694 drag_time=selected_time;
695 actual_dragtime=actual_time;
697 //selected_time=iter->time;
700 // Activepoint Selection
701 if(parent_value_node)
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;
707 for(iter=activepoint_list.begin();iter!=activepoint_list.end();++iter)
709 Time val=abs(iter->time-selected_time);
717 // Perhaps I sould signal if we selected this activepoint?
720 if(event->button.button==3)
723 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
724 const Node::time_set *tset = get_times_from_vdesc(valdesc);
726 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
728 etl::handle<synfig::Node> node;
729 if(valdesc.get_value(stime).get_type()==ValueBase::TYPE_CANVAS)
731 node=Canvas::Handle(valdesc.get_value(stime).get(Canvas::Handle()));
733 else //if(valdesc.is_value_node())
735 node=valdesc.get_value_node();
738 if(clickfound && node)
740 show_timepoint_menu(node, stime, actual_time<stime?SIDE_LEFT:SIDE_RIGHT);
745 case GDK_MOTION_NOTIFY:
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();
752 case GDK_BUTTON_RELEASE:
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();
759 /*if(event->button.button==3 && selection)
761 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
766 //Time point stuff...
767 if(event->button.button == 1)
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))))
773 synfigapp::Action::ParamList param_list;
774 param_list.add("canvas",canvas_interface()->get_canvas());
775 param_list.add("canvas_interface",canvas_interface());
777 if(sel_value.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
779 param_list.add("addcanvas",sel_value.get_value().get(Canvas::Handle()));
782 param_list.add("addvaluedesc",sel_value);
786 std::set<synfig::Time>::iterator i = sel_times.begin(), end = sel_times.end();
789 param_list.add("addtime",*i);
791 newset.insert((*i + deltatime).round(get_canvas()->rend_desc().get_frame_rate()));
795 param_list.add("deltatime",deltatime);
796 // param_list.add("time",canvas_interface()->get_time());
798 if(mode & COPY_MASK) //copy
800 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
801 ->process_action("timepoint_copy", param_list);
802 }else if(delmode) //DELETE
804 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
805 ->process_action("timepoint_delete", param_list);
808 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
809 ->process_action("timepoint_move", param_list);
812 //now replace all the selected with the new selected
819 /*if(value_node && selection)
821 if(selected_time==drag_time && event->button.button!=3)
822 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
824 if(event->button.button==1)
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)
830 waypoint.set_time(newtime);
831 signal_waypoint_changed_(waypoint,value_node);
837 // selected_time=iter->time;
838 //selected_time=iter->get_time();
842 //std::cerr<<"unknown event type "<<event->type<<std::endl;
854 Glib::PropertyProxy<synfigapp::ValueDesc>
855 CellRenderer_TimeTrack::property_value_desc()
857 return Glib::PropertyProxy<synfigapp::ValueDesc>(this,"value_desc");
860 Glib::PropertyProxy<synfig::Canvas::Handle>
861 CellRenderer_TimeTrack::property_canvas()
863 return Glib::PropertyProxy<synfig::Canvas::Handle>(this,"canvas");
866 Glib::PropertyProxy<Gtk::Adjustment* >
867 CellRenderer_TimeTrack::property_adjustment()
869 return Glib::PropertyProxy<Gtk::Adjustment* >(this,"adjustment");
873 CellRenderer_TimeTrack::set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface> h)
875 canvas_interface_ = h;
879 set_waypoint_model(std::set<synfig::Waypoint, std::less<UniqueID> > waypoints, Waypoint::Model model, etl::loose_handle<synfigapp::CanvasInterface> canvas_interface)
881 // Create the action group
882 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Change Waypoint Group"));
884 std::set<synfig::Waypoint, std::less<UniqueID> >::const_iterator iter;
885 for(iter=waypoints.begin();iter!=waypoints.end();++iter)
887 Waypoint waypoint(*iter);
888 waypoint.apply_model(model);
890 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set"));
894 action->set_param("canvas",canvas_interface->get_canvas());
895 action->set_param("canvas_interface",canvas_interface);
897 action->set_param("waypoint",waypoint);
898 action->set_param("value_node",waypoint.get_parent_value_node());
900 if(!canvas_interface->get_instance()->perform_action(action))
909 CellRenderer_TimeTrack::show_timepoint_menu(const etl::handle<synfig::Node>& node, const synfig::Time& time, Side side)
911 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
913 n=synfig::waypoint_collect(waypoint_set,time,node);
915 Gtk::Menu* menu(manage(new Gtk::Menu()));
917 // Create the interpolation method menu
918 if(!waypoint_set.empty())
920 Gtk::Menu* interp_menu(manage(new Gtk::Menu()));
921 Waypoint::Model model;
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"),
927 sigc::ptr_fun(set_waypoint_model),
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"),
938 sigc::ptr_fun(set_waypoint_model),
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"),
949 sigc::ptr_fun(set_waypoint_model),
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"),
960 sigc::ptr_fun(set_waypoint_model),
968 menu->items().push_back(
969 Gtk::Menu_Helpers::MenuElem(
970 side==SIDE_LEFT?_("Change \"In\" Interp."):_("Change \"Out\" Interp."),
976 menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-jump-to"),
980 &synfigapp::CanvasInterface::set_time
986 if(!waypoint_set.empty())
988 if(waypoint_set.size()==1)
992 signal_waypoint_clicked_(" ",*waypoint_set.begin(),2);
996 synfig::info("Too many waypoints under me");
999 synfig::info("ZERO waypoints under me");
1001 if(menu)menu->popup(3,gtk_get_current_event_time());