1 /* === S Y N F I G ========================================================= */
2 /*! \file cellrenderer_timetrack.cpp
3 ** \brief Template Header
5 ** $Id: cellrenderer_timetrack.cpp,v 1.4 2005/01/13 20:23:01 darco Exp $
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 //TODO add in RangeGet so it's not so damn hard to click on points
163 i = tset.upper_bound(t); //where t is the lower bound, t < [first,i)
166 double dist = Time::end();
171 closest = i->get_time();
172 dist = abs(i->get_time() - t);
175 if(j != end && (abs(j->get_time() - t) < dist) )
177 closest = j->get_time();
178 dist = abs(j->get_time() - t);
181 if( dist <= range/2 )
191 CellRenderer_TimeTrack::render_vfunc(
192 const Glib::RefPtr<Gdk::Drawable>& window,
194 const Gdk::Rectangle& background_area,
195 const Gdk::Rectangle& area_,
196 const Gdk::Rectangle& expose_area,
197 Gtk::CellRendererState flags)
202 Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
203 Glib::RefPtr<Gdk::GC> inactive_gc(Gdk::GC::create(window));
204 Gtk::Adjustment *adjustment=get_adjustment();
205 Gtk::StateType state = Gtk::STATE_ACTIVE;
206 Gtk::ShadowType shadow;
209 curr_time_color("#0000ff"),
210 inactive_color("#000000"),
211 keyframe_color("#a07f7f");
212 Gdk::Color activepoint_color[2];
214 activepoint_color[0]=Gdk::Color("#ff0000");
215 activepoint_color[1]=Gdk::Color("#00ff00");
217 inactive_gc->set_rgb_fg_color(inactive_color);
218 inactive_gc->set_stipple(Gdk::Bitmap::create(stipple_xpm,2,2));
219 inactive_gc->set_fill(Gdk::STIPPLED);
221 synfig::Canvas::Handle canvas(property_canvas().get_value());
223 synfigapp::ValueDesc value_desc = property_value_desc().get_value();
224 synfig::ValueNode *base_value = value_desc.get_value_node().get();
225 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(base_value);
227 synfig::ValueNode_DynamicList *parent_value_node(0);
228 if(property_value_desc().get_value().parent_is_value_node())
229 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
231 // If the canvas is defined, then load up the keyframes
234 const synfig::KeyframeList& keyframe_list(canvas->keyframe_list());
235 synfig::KeyframeList::const_iterator iter;
237 for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter)
239 if(!iter->get_time().is_valid())
242 const int x((int)((float)area_.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower())));
243 if(iter->get_time()>=adjustment->get_lower() && iter->get_time()<adjustment->get_upper())
245 gc->set_rgb_fg_color(keyframe_color);
246 window->draw_rectangle(gc, true, area_.get_x()+x, area_.get_y(), 1, area_.get_height()+1);
251 //render all the time points that exist
253 const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc);
257 synfig::Node::time_set::const_iterator i = tset->begin(), end = tset->end();
259 float lower = adjustment->get_lower(),
260 upper = adjustment->get_upper();
262 Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(widget.get_window());
264 Gdk::Rectangle area(area_);
265 gc->set_clip_rectangle(area);
266 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
268 bool valselected = sel_value.get_value_node() == base_value && !sel_times.empty();
270 float cfps = get_canvas()->rend_desc().get_frame_rate();
272 vector<Time> drawredafter;
274 Time diff = actual_time - actual_dragtime;//selected_time-drag_time;
277 //find the coordinate in the drawable space...
278 Time t = i->get_time();
283 //if it found it... (might want to change comparison, and optimize
284 // sel_times.find to not produce an overall nlogn solution)
287 //not dragging... just draw as per normal
288 //if move dragging draw offset
289 //if copy dragging draw both...
291 if(valselected && sel_times.find(t) != sel_times.end())
293 if(dragging) //skip if we're dragging because we'll render it later
295 if(mode & COPY_MASK) // draw both blue and red moved
297 drawredafter.push_back((t + diff).round(cfps));
298 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
299 }else if(mode & DELETE_MASK) //it's just red...
301 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
303 }else //move - draw the red on top of the others...
305 drawredafter.push_back((t + diff).round(cfps));
310 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
315 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
318 //synfig::info("Displaying time: %.3f s",(float)t);
319 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
321 //should draw me a grey filled circle...
322 Gdk::Rectangle area2(
323 area.get_x() - area.get_height()/2 + x + 1,
328 render_time_point_to_window(window,area2,*i,selected);
330 /*window->draw_arc(gc,true,
331 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
332 area.get_height()/2, area.get_height()*3/4,
335 gc->set_rgb_fg_color(Gdk::Color("#000000"));
336 window->draw_arc(gc,false,
337 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
338 area.get_height()/2, area.get_height()*3/4,
344 vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end();
347 //find the coordinate in the drawable space...
353 //synfig::info("Displaying time: %.3f s",(float)t);
354 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
356 //should draw me a grey filled circle...
358 Gdk::Rectangle area2(
359 area.get_x() - area.get_height()/2 + x + 1,
364 render_time_point_to_window(window,area2,*i,true);
365 /* gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
366 window->draw_arc(gc,true,
367 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
368 area.get_height()/2, area.get_height()*3/4,
371 gc->set_rgb_fg_color(Gdk::Color("#000000"));
372 window->draw_arc(gc,false,
373 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
374 area.get_height()/2, area.get_height()*3/4,
382 /* THIS IS NOW HANDLED ENTIRELY BY THE TIMEPOINT SYSTEM
383 // This this is an animated value node, then render the waypoints
386 //now render the actual waypoints
387 synfig::ValueNode_Animated::WaypointList::iterator iter;
389 iter=value_node->waypoint_list().begin();
390 iter!=value_node->waypoint_list().end();
394 if(!iter->get_time().is_valid())
398 if(is_selected(*iter))
400 Time t(iter->get_time());
404 t=(t+selected_time-drag_time).round(get_canvas()->rend_desc().get_frame_rate());
406 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(t-adjustment->get_lower()));
407 shadow=Gtk::SHADOW_IN;
412 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()));
413 shadow=Gtk::SHADOW_OUT;
418 widget.get_style()->paint_diamond(
419 Glib::RefPtr<Gdk::Window>::cast_static(window),
425 area.get_x()+x-area.get_height()/4,
426 area.get_y()+area.get_height()/4,
433 Gdk::Rectangle area(area_);
434 // If the parent of this value node is a dynamic list, then
435 // render the on and off times
436 if(parent_value_node)
438 const int index(property_value_desc().get_value().get_index());
439 const synfig::ValueNode_DynamicList::ListEntry& list_entry(parent_value_node->list[index]);
440 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(list_entry.timing_info);
441 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter,next;
444 if(!activepoint_list.empty())
445 is_off=!activepoint_list.front().state;
450 for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++)
452 x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower())));
454 if(x>area.get_width())x=area.get_width();
456 bool status_at_time=0;
457 if(next!=activepoint_list.end())
459 status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0);
462 status_at_time=!list_entry.status_at_time(Time::end());
464 if(!is_off && status_at_time)
470 if(is_off && !status_at_time)
472 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
477 if(!is_off && iter!=activepoint_list.end() && next->state==false && iter->state==false)
482 else if(is_off && next!=activepoint_list.end() && iter->state==false && next->state==true)
484 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
487 else if(is_off && iter!=activepoint_list.end() && iter->state==true)
489 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), prevx-xstart, area.get_height());
496 if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper())
501 gc->set_rgb_fg_color(activepoint_color[iter->state]);
502 window->draw_rectangle(gc, true, area.get_x()+x-w/2, area.get_y(), w, area.get_height());
508 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_height());
512 // Render a line that defines the current tick in time
514 gc->set_rgb_fg_color(curr_time_color);
516 const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower())));
518 if(adjustment->get_value()>=adjustment->get_lower() && adjustment->get_value()<adjustment->get_upper())
519 window->draw_rectangle(gc, true, area.get_x()+x, area.get_y(), 1, area.get_height());
523 synfig::ValueNode_Animated::WaypointList::iterator
524 CellRenderer_TimeTrack::find_waypoint(const synfig::Time& t,const synfig::Time& scope)
526 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
528 Time nearest(Time::end());
530 synfig::ValueNode_Animated::WaypointList::iterator iter,ret;
535 iter=value_node->waypoint_list().begin();
536 iter!=value_node->waypoint_list().end();
540 Time val=abs(iter->get_time()-selected_time);
548 if(nearest!=Time::end() && nearest<scope)
557 CellRenderer_TimeTrack::activate_vfunc(
560 const Glib::ustring& treepath,
561 const Gdk::Rectangle& background_area,
562 const Gdk::Rectangle& cell_area,
563 Gtk::CellRendererState flags)
566 synfig::ValueNode_Animated::WaypointList::iterator iter;
567 Time nearest=1000000000;
568 Gtk::Adjustment *adjustment=get_adjustment();
570 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
572 synfig::Canvas::Handle canvas(get_canvas());
574 synfig::ValueNode_DynamicList *parent_value_node(0);
575 if(property_value_desc().get_value().parent_is_value_node())
576 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
582 case GDK_MOTION_NOTIFY:
583 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();
587 Gdk::ModifierType mod;
588 Gdk::Event(event).get_state(mod);
592 case GDK_BUTTON_PRESS:
593 case GDK_BUTTON_RELEASE:
595 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();
597 Gdk::ModifierType mod;
598 Gdk::Event(event).get_state(mod);
603 actual_time = curr_time;
605 curr_time=curr_time.round(canvas->rend_desc().get_frame_rate());
606 selected_time=curr_time;
608 Time pixel_width((adjustment->get_upper()-adjustment->get_lower())/cell_area.get_width());
612 case GDK_BUTTON_PRESS:
613 //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();
615 //Deal with time point selection, but only if they aren't involved in the insanity...
616 if(/*!value_node && */event->button.button == 1)
620 /*! UI specification:
622 When nothing is selected, clicking on a point in either normal mode order
623 addative mode will select the time point closest to the click.
624 Subtractive click will do nothing
626 When things are already selected, clicking on a selected point does
627 nothing (in both normal and add mode). Add mode clicking on an unselected
628 point adds it to the set. Normal clicking on an unselected point will
629 select only that one time point. Subtractive clicking on any point
630 will remove it from the the set if it is included.
633 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
634 const Node::time_set *tset = get_times_from_vdesc(valdesc);
636 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
637 bool selectmode = mode & SELECT_MASK;
639 //NOTE LATER ON WE SHOULD MAKE IT SO MULTIPLE VALUENODES CAN BE SELECTED AT ONCE
640 //we want to jump to the value desc if we're not currently on it
641 // but only if we want to add the point
642 if(clickfound && !(sel_value == valdesc))
648 //now that we've made sure we're selecting the correct value, deal with the already selected points
649 set<Time>::iterator foundi = clickfound ? sel_times.find(stime) : sel_times.end();
650 bool found = foundi != sel_times.end();
652 //remove all other points from our list... (only select the one we need)
653 if(!selectmode && !found)
658 if(found && selectmode) //remove a single already selected point
660 sel_times.erase(foundi);
661 }else if(clickfound) //otherwise look at adding it
663 //for replace the list was cleared earlier, and for add it wasn't so it works
664 sel_times.insert(stime);
671 iter=find_waypoint(selected_time,pixel_width*cell_area.get_height()/2);
672 selected_waypoint=iter;
680 selected=synfig::UniqueID::nil();
683 if((!sel_times.empty() || selection) && event->button.button==1)
686 drag_time=selected_time;
687 actual_dragtime=actual_time;
689 //selected_time=iter->time;
692 // Activepoint Selection
693 if(parent_value_node)
695 const int index(property_value_desc().get_value().get_index());
696 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(parent_value_node->list[index].timing_info);
697 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter;
699 for(iter=activepoint_list.begin();iter!=activepoint_list.end();++iter)
701 Time val=abs(iter->time-selected_time);
709 // Perhaps I sould signal if we selected this activepoint?
712 if(event->button.button==3)
715 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
716 const Node::time_set *tset = get_times_from_vdesc(valdesc);
718 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
720 etl::handle<synfig::Node> node;
721 if(valdesc.get_value(stime).get_type()==ValueBase::TYPE_CANVAS)
723 node=Canvas::Handle(valdesc.get_value(stime).get(Canvas::Handle()));
725 else //if(valdesc.is_value_node())
727 node=valdesc.get_value_node();
730 if(clickfound && node)
732 show_timepoint_menu(node, stime, actual_time<stime?SIDE_LEFT:SIDE_RIGHT);
737 case GDK_MOTION_NOTIFY:
739 //if(selection && dragging)
740 // 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();
744 case GDK_BUTTON_RELEASE:
748 //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();
751 /*if(event->button.button==3 && selection)
753 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
758 //Time point stuff...
759 if(event->button.button == 1)
761 bool delmode = (mode & DELETE_MASK) && !(mode & COPY_MASK);
762 deltatime = actual_time - actual_dragtime;
763 if(sel_times.size() != 0 && (delmode || !deltatime.is_equal(Time(0))))
765 synfigapp::Action::ParamList param_list;
766 param_list.add("canvas",canvas_interface()->get_canvas());
767 param_list.add("canvas_interface",canvas_interface());
769 if(sel_value.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
771 param_list.add("addcanvas",sel_value.get_value().get(Canvas::Handle()));
774 param_list.add("addvaluedesc",sel_value);
778 std::set<synfig::Time>::iterator i = sel_times.begin(), end = sel_times.end();
781 param_list.add("addtime",*i);
783 newset.insert((*i + deltatime).round(get_canvas()->rend_desc().get_frame_rate()));
787 param_list.add("deltatime",deltatime);
788 // param_list.add("time",canvas_interface()->get_time());
790 if(mode & COPY_MASK) //copy
792 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
793 ->process_action("timepoint_copy", param_list);
794 }else if(delmode) //DELETE
796 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
797 ->process_action("timepoint_delete", param_list);
800 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
801 ->process_action("timepoint_move", param_list);
804 //now replace all the selected with the new selected
811 /*if(value_node && selection)
813 if(selected_time==drag_time && event->button.button!=3)
814 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
816 if(event->button.button==1)
818 synfig::Waypoint waypoint(*selected_waypoint);
819 Time newtime((waypoint.get_time()+(selected_time-drag_time)).round(canvas->rend_desc().get_frame_rate()));
820 if(waypoint.get_time()!=newtime)
822 waypoint.set_time(newtime);
823 signal_waypoint_changed_(waypoint,value_node);
829 // selected_time=iter->time;
830 //selected_time=iter->get_time();
834 //std::cerr<<"unknown event type "<<event->type<<std::endl;
846 Glib::PropertyProxy<synfigapp::ValueDesc>
847 CellRenderer_TimeTrack::property_value_desc()
849 return Glib::PropertyProxy<synfigapp::ValueDesc>(this,"value_desc");
852 Glib::PropertyProxy<synfig::Canvas::Handle>
853 CellRenderer_TimeTrack::property_canvas()
855 return Glib::PropertyProxy<synfig::Canvas::Handle>(this,"canvas");
858 Glib::PropertyProxy<Gtk::Adjustment* >
859 CellRenderer_TimeTrack::property_adjustment()
861 return Glib::PropertyProxy<Gtk::Adjustment* >(this,"adjustment");
865 CellRenderer_TimeTrack::set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface> h)
867 canvas_interface_ = h;
871 set_waypoint_model(std::set<synfig::Waypoint, std::less<UniqueID> > waypoints, Waypoint::Model model, etl::loose_handle<synfigapp::CanvasInterface> canvas_interface)
873 // Create the action group
874 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Change Waypoint Group"));
876 std::set<synfig::Waypoint, std::less<UniqueID> >::const_iterator iter;
877 for(iter=waypoints.begin();iter!=waypoints.end();++iter)
879 Waypoint waypoint(*iter);
880 waypoint.apply_model(model);
882 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set"));
886 action->set_param("canvas",canvas_interface->get_canvas());
887 action->set_param("canvas_interface",canvas_interface);
889 action->set_param("waypoint",waypoint);
890 action->set_param("value_node",waypoint.get_parent_value_node());
892 if(!canvas_interface->get_instance()->perform_action(action))
901 CellRenderer_TimeTrack::show_timepoint_menu(const etl::handle<synfig::Node>& node, const synfig::Time& time, Side side)
903 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
905 n=synfig::waypoint_collect(waypoint_set,time,node);
907 Gtk::Menu* menu(manage(new Gtk::Menu()));
909 // Create the interpolation method menu
910 if(!waypoint_set.empty())
912 Gtk::Menu* interp_menu(manage(new Gtk::Menu()));
913 Waypoint::Model model;
915 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_TCB);
916 else model.set_after(INTERPOLATION_TCB);
917 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("TCB"),
919 sigc::ptr_fun(set_waypoint_model),
926 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_LINEAR);
927 else model.set_after(INTERPOLATION_LINEAR);
928 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Linear"),
930 sigc::ptr_fun(set_waypoint_model),
937 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_HALT);
938 else model.set_after(INTERPOLATION_HALT);
939 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Ease"),
941 sigc::ptr_fun(set_waypoint_model),
948 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_CONSTANT);
949 else model.set_after(INTERPOLATION_CONSTANT);
950 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Constant"),
952 sigc::ptr_fun(set_waypoint_model),
960 menu->items().push_back(
961 Gtk::Menu_Helpers::MenuElem(
962 side==SIDE_LEFT?_("Change \"In\" Interp."):_("Change \"Out\" Interp."),
968 menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-jump-to"),
972 &synfigapp::CanvasInterface::set_time
978 if(!waypoint_set.empty())
980 if(waypoint_set.size()==1)
984 signal_waypoint_clicked_(" ",*waypoint_set.begin(),2);
988 synfig::info("Too many waypoints under me");
991 synfig::info("ZERO waypoints under me");
993 if(menu)menu->popup(3,gtk_get_current_event_time());