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
9 ** Copyright (c) 2007 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
33 #include <gtkmm/label.h>
34 #include "cellrenderer_timetrack.h"
36 #include <gtkmm/spinbutton.h>
37 #include <gtkmm/combo.h>
38 #include <ETL/stringf>
39 #include "widget_value.h"
41 #include <gtkmm/menu.h>
42 #include <gtkmm/optionmenu.h>
43 #include "widget_time.h"
44 #include "widget_timeslider.h"
46 #include <synfigapp/canvasinterface.h>
49 #include <synfig/timepointcollect.h>
53 using namespace synfig;
56 using namespace studio;
58 /* === M A C R O S ========================================================= */
60 /* === G L O B A L S ======================================================= */
62 static char stipple_xpm[] = { 2, 0 };
64 //mode for modifier keys
68 SELECT_MASK = Gdk::CONTROL_MASK,
69 COPY_MASK = Gdk::SHIFT_MASK,
70 DELETE_MASK = Gdk::MOD1_MASK
73 /* === P R O C E D U R E S ================================================= */
75 /* === M E T H O D S ======================================================= */
77 CellRenderer_TimeTrack::CellRenderer_TimeTrack():
78 Glib::ObjectBase (typeid(CellRenderer_TimeTrack)),
80 adjustment_ (10,10,20,0,0,0),
82 property_valuedesc_ (*this,"value_desc",synfigapp::ValueDesc()),
83 property_canvas_ (*this,"canvas",synfig::Canvas::Handle()),
84 property_adjustment_(*this,"adjustment",&adjustment_),
85 property_enable_timing_info_(*this,"enable-timing-info", false)
91 CellRenderer_TimeTrack::~CellRenderer_TimeTrack()
93 synfig::info("CellRenderer_TimeTrack::~CellRenderer_TimeTrack(): deleted");
97 CellRenderer_TimeTrack::set_adjustment(Gtk::Adjustment &x)
99 property_adjustment_=&x;
100 // x.signal_value_changed().connect(sigc::mem_fun(*this,&Gtk::Widget::queue_draw));
103 synfig::Canvas::Handle
104 CellRenderer_TimeTrack::get_canvas()const
106 return const_cast<CellRenderer_TimeTrack*>(this)->property_canvas().get_value();
110 CellRenderer_TimeTrack::get_adjustment()
112 return (Gtk::Adjustment*)property_adjustment_;
115 const Gtk::Adjustment *
116 CellRenderer_TimeTrack::get_adjustment()const
118 return (const Gtk::Adjustment*)property_adjustment_;
122 CellRenderer_TimeTrack::is_selected(const Waypoint& waypoint)const
124 return selected==waypoint;
127 //kind of a hack... pointer is ugly
128 const synfig::Node::time_set *get_times_from_vdesc(const synfigapp::ValueDesc &v)
130 if(v.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
132 synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle());
136 return &canvasparam->get_times();
140 ValueNode *base_value = v.get_value_node().get();
142 ValueNode_DynamicList *parent_value_node =
143 v.parent_is_value_node() ?
144 dynamic_cast<ValueNode_DynamicList *>(v.get_parent_value_node().get()) :
147 //we want a dynamic list entry to override the normal...
148 if(parent_value_node)
150 return &parent_value_node->list[v.get_index()].get_times();
151 }else if(base_value) //don't render stuff if it's just animated...
153 return &base_value->get_times();
158 bool get_closest_time(const synfig::Node::time_set &tset, const Time &t, const Time &range, Time &out)
160 Node::time_set::const_iterator i,j,end = tset.end();
162 // stop the crash mentioned in bug #1689282
163 // doesn't solve the underlying problem though, I don't think
164 if (tset.size() == 0)
166 synfig::error(__FILE__":%d: tset.size() == 0",__LINE__);
170 //TODO add in RangeGet so it's not so damn hard to click on points
172 i = tset.upper_bound(t); //where t is the lower bound, t < [first,i)
175 double dist = Time::end();
180 closest = i->get_time();
181 dist = abs(i->get_time() - t);
184 if(j != end && (abs(j->get_time() - t) < dist) )
186 closest = j->get_time();
187 dist = abs(j->get_time() - t);
190 if( dist <= range/2 )
200 CellRenderer_TimeTrack::render_vfunc(
201 const Glib::RefPtr<Gdk::Drawable>& window,
203 const Gdk::Rectangle& /*background_area*/,
204 const Gdk::Rectangle& area_,
205 const Gdk::Rectangle& /*expose_area*/,
206 Gtk::CellRendererState /*flags*/)
211 Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
212 Glib::RefPtr<Gdk::GC> inactive_gc(Gdk::GC::create(window));
213 Gtk::Adjustment *adjustment=get_adjustment();
214 // Gtk::StateType state = Gtk::STATE_ACTIVE;
215 // Gtk::ShadowType shadow;
218 curr_time_color("#0000ff"),
219 inactive_color("#000000"),
220 keyframe_color("#a07f7f");
221 Gdk::Color activepoint_color[2];
223 activepoint_color[0]=Gdk::Color("#ff0000");
224 activepoint_color[1]=Gdk::Color("#00ff00");
226 inactive_gc->set_rgb_fg_color(inactive_color);
227 inactive_gc->set_stipple(Gdk::Bitmap::create(stipple_xpm,2,2));
228 inactive_gc->set_fill(Gdk::STIPPLED);
230 synfig::Canvas::Handle canvas(property_canvas().get_value());
232 synfigapp::ValueDesc value_desc = property_value_desc().get_value();
233 synfig::ValueNode *base_value = value_desc.get_value_node().get();
234 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(base_value);
236 synfig::ValueNode_DynamicList *parent_value_node(0);
237 if(property_value_desc().get_value().parent_is_value_node())
238 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
240 // If the canvas is defined, then load up the keyframes
243 const synfig::KeyframeList& keyframe_list(canvas->keyframe_list());
244 synfig::KeyframeList::const_iterator iter;
246 for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter)
248 if(!iter->get_time().is_valid())
251 const int x((int)((float)area_.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower())));
252 if(iter->get_time()>=adjustment->get_lower() && iter->get_time()<adjustment->get_upper())
254 gc->set_rgb_fg_color(keyframe_color);
255 window->draw_rectangle(gc, true, area_.get_x()+x, area_.get_y(), 1, area_.get_height()+1);
260 //render all the time points that exist
262 const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc);
266 synfig::Node::time_set::const_iterator i = tset->begin(), end = tset->end();
268 float lower = adjustment->get_lower(),
269 upper = adjustment->get_upper();
271 Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(widget.get_window());
273 Gdk::Rectangle area(area_);
274 gc->set_clip_rectangle(area);
275 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
277 bool valselected = sel_value.get_value_node() == base_value && !sel_times.empty();
279 float cfps = get_canvas()->rend_desc().get_frame_rate();
281 vector<Time> drawredafter;
283 Time diff = actual_time - actual_dragtime;//selected_time-drag_time;
286 //find the coordinate in the drawable space...
287 Time t = i->get_time();
292 //if it found it... (might want to change comparison, and optimize
293 // sel_times.find to not produce an overall nlogn solution)
296 //not dragging... just draw as per normal
297 //if move dragging draw offset
298 //if copy dragging draw both...
300 if(valselected && sel_times.find(t) != sel_times.end())
302 if(dragging) //skip if we're dragging because we'll render it later
304 if(mode & COPY_MASK) // draw both blue and red moved
306 drawredafter.push_back((t + diff).round(cfps));
307 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
308 }else if(mode & DELETE_MASK) //it's just red...
310 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
312 }else //move - draw the red on top of the others...
314 drawredafter.push_back((t + diff).round(cfps));
319 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
324 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
327 //synfig::info("Displaying time: %.3f s",(float)t);
328 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
330 //should draw me a grey filled circle...
331 Gdk::Rectangle area2(
332 area.get_x() - area.get_height()/2 + x + 1,
337 render_time_point_to_window(window,area2,*i,selected);
339 /*window->draw_arc(gc,true,
340 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
341 area.get_height()/2, area.get_height()*3/4,
344 gc->set_rgb_fg_color(Gdk::Color("#000000"));
345 window->draw_arc(gc,false,
346 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
347 area.get_height()/2, area.get_height()*3/4,
353 vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end();
356 //find the coordinate in the drawable space...
362 //synfig::info("Displaying time: %.3f s",(float)t);
363 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
365 //should draw me a grey filled circle...
367 Gdk::Rectangle area2(
368 area.get_x() - area.get_height()/2 + x + 1,
373 render_time_point_to_window(window,area2,*i,true);
374 /* gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
375 window->draw_arc(gc,true,
376 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
377 area.get_height()/2, area.get_height()*3/4,
380 gc->set_rgb_fg_color(Gdk::Color("#000000"));
381 window->draw_arc(gc,false,
382 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
383 area.get_height()/2, area.get_height()*3/4,
391 /* THIS IS NOW HANDLED ENTIRELY BY THE TIMEPOINT SYSTEM
392 // This this is an animated value node, then render the waypoints
395 //now render the actual waypoints
396 synfig::ValueNode_Animated::WaypointList::iterator iter;
398 iter=value_node->waypoint_list().begin();
399 iter!=value_node->waypoint_list().end();
403 if(!iter->get_time().is_valid())
407 if(is_selected(*iter))
409 Time t(iter->get_time());
413 t=(t+selected_time-drag_time).round(get_canvas()->rend_desc().get_frame_rate());
415 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(t-adjustment->get_lower()));
416 shadow=Gtk::SHADOW_IN;
421 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()));
422 shadow=Gtk::SHADOW_OUT;
427 widget.get_style()->paint_diamond(
428 Glib::RefPtr<Gdk::Window>::cast_static(window),
434 area.get_x()+x-area.get_height()/4,
435 area.get_y()+area.get_height()/4,
442 Gdk::Rectangle area(area_);
443 // If the parent of this value node is a dynamic list, then
444 // render the on and off times
445 if(parent_value_node)
447 const int index(property_value_desc().get_value().get_index());
448 const synfig::ValueNode_DynamicList::ListEntry& list_entry(parent_value_node->list[index]);
449 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(list_entry.timing_info);
450 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter,next;
453 if(!activepoint_list.empty())
454 is_off=!activepoint_list.front().state;
459 for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++)
461 x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower())));
463 if(x>area.get_width())x=area.get_width();
465 bool status_at_time=0;
466 if(next!=activepoint_list.end())
468 status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0);
471 status_at_time=!list_entry.status_at_time(Time::end());
473 if(!is_off && status_at_time)
479 if(is_off && !status_at_time)
481 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
486 if(!is_off && iter!=activepoint_list.end() && next->state==false && iter->state==false)
491 else if(is_off && next!=activepoint_list.end() && iter->state==false && next->state==true)
493 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
496 else if(is_off && iter!=activepoint_list.end() && iter->state==true)
498 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), prevx-xstart, area.get_height());
505 if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper())
510 gc->set_rgb_fg_color(activepoint_color[iter->state]);
511 window->draw_rectangle(gc, true, area.get_x()+x-w/2, area.get_y(), w, area.get_height());
517 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_height());
521 // Render a line that defines the current tick in time
523 gc->set_rgb_fg_color(curr_time_color);
525 const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower())));
527 if(adjustment->get_value()>=adjustment->get_lower() && adjustment->get_value()<adjustment->get_upper())
528 window->draw_rectangle(gc, true, area.get_x()+x, area.get_y(), 1, area.get_height());
532 synfig::ValueNode_Animated::WaypointList::iterator
533 CellRenderer_TimeTrack::find_waypoint(const synfig::Time& /*t*/,const synfig::Time& scope)
535 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
537 Time nearest(Time::end());
539 synfig::ValueNode_Animated::WaypointList::iterator iter,ret;
544 iter=value_node->waypoint_list().begin();
545 iter!=value_node->waypoint_list().end();
549 Time val=abs(iter->get_time()-selected_time);
557 if(nearest!=Time::end() && nearest<scope)
566 CellRenderer_TimeTrack::activate_vfunc(
568 Gtk::Widget& /*widget*/,
569 const Glib::ustring& treepath,
570 const Gdk::Rectangle& /*background_area*/,
571 const Gdk::Rectangle& cell_area,
572 Gtk::CellRendererState /*flags*/)
575 synfig::ValueNode_Animated::WaypointList::iterator iter;
576 Time nearest=1000000000;
577 Gtk::Adjustment *adjustment=get_adjustment();
579 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
581 synfig::Canvas::Handle canvas(get_canvas());
583 synfig::ValueNode_DynamicList *parent_value_node(0);
584 if(property_value_desc().get_value().parent_is_value_node())
585 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
591 case GDK_MOTION_NOTIFY:
592 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();
596 Gdk::ModifierType mod;
597 Gdk::Event(event).get_state(mod);
601 case GDK_BUTTON_PRESS:
602 case GDK_BUTTON_RELEASE:
604 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();
606 Gdk::ModifierType mod;
607 Gdk::Event(event).get_state(mod);
612 actual_time = curr_time;
614 curr_time=curr_time.round(canvas->rend_desc().get_frame_rate());
615 selected_time=curr_time;
617 Time pixel_width((adjustment->get_upper()-adjustment->get_lower())/cell_area.get_width());
621 case GDK_BUTTON_PRESS:
622 //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();
624 //Deal with time point selection, but only if they aren't involved in the insanity...
625 if(/*!value_node && */event->button.button == 1)
629 /*! UI specification:
631 When nothing is selected, clicking on a point in either normal mode order
632 addative mode will select the time point closest to the click.
633 Subtractive click will do nothing
635 When things are already selected, clicking on a selected point does
636 nothing (in both normal and add mode). Add mode clicking on an unselected
637 point adds it to the set. Normal clicking on an unselected point will
638 select only that one time point. Subtractive clicking on any point
639 will remove it from the the set if it is included.
642 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
643 const Node::time_set *tset = get_times_from_vdesc(valdesc);
645 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
646 bool selectmode = mode & SELECT_MASK;
648 //NOTE LATER ON WE SHOULD MAKE IT SO MULTIPLE VALUENODES CAN BE SELECTED AT ONCE
649 //we want to jump to the value desc if we're not currently on it
650 // but only if we want to add the point
651 if(clickfound && !(sel_value == valdesc))
657 //now that we've made sure we're selecting the correct value, deal with the already selected points
658 set<Time>::iterator foundi = clickfound ? sel_times.find(stime) : sel_times.end();
659 bool found = foundi != sel_times.end();
661 //remove all other points from our list... (only select the one we need)
662 if(!selectmode && !found)
667 if(found && selectmode) //remove a single already selected point
669 sel_times.erase(foundi);
670 }else if(clickfound) //otherwise look at adding it
672 //for replace the list was cleared earlier, and for add it wasn't so it works
673 sel_times.insert(stime);
680 iter=find_waypoint(selected_time,pixel_width*cell_area.get_height()/2);
681 selected_waypoint=iter;
689 selected=synfig::UniqueID::nil();
692 if((!sel_times.empty() || selection) && event->button.button==1)
695 drag_time=selected_time;
696 actual_dragtime=actual_time;
698 //selected_time=iter->time;
701 // Activepoint Selection
702 if(parent_value_node)
704 const int index(property_value_desc().get_value().get_index());
705 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(parent_value_node->list[index].timing_info);
706 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter;
708 for(iter=activepoint_list.begin();iter!=activepoint_list.end();++iter)
710 Time val=abs(iter->time-selected_time);
718 // Perhaps I sould signal if we selected this activepoint?
721 if(event->button.button==3)
724 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
725 const Node::time_set *tset = get_times_from_vdesc(valdesc);
727 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
729 etl::handle<synfig::Node> node;
730 if(valdesc.get_value(stime).get_type()==ValueBase::TYPE_CANVAS)
732 node=Canvas::Handle(valdesc.get_value(stime).get(Canvas::Handle()));
734 else //if(valdesc.is_value_node())
736 node=valdesc.get_value_node();
739 if(clickfound && node)
741 show_timepoint_menu(node, stime, actual_time<stime?SIDE_LEFT:SIDE_RIGHT);
746 case GDK_MOTION_NOTIFY:
748 //if(selection && dragging)
749 // 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();
753 case GDK_BUTTON_RELEASE:
757 //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();
760 /*if(event->button.button==3 && selection)
762 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
767 //Time point stuff...
768 if(event->button.button == 1)
770 bool delmode = (mode & DELETE_MASK) && !(mode & COPY_MASK);
771 deltatime = actual_time - actual_dragtime;
772 if(sel_times.size() != 0 && (delmode || !deltatime.is_equal(Time(0))))
774 synfigapp::Action::ParamList param_list;
775 param_list.add("canvas",canvas_interface()->get_canvas());
776 param_list.add("canvas_interface",canvas_interface());
778 if(sel_value.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
780 param_list.add("addcanvas",sel_value.get_value().get(Canvas::Handle()));
783 param_list.add("addvaluedesc",sel_value);
787 std::set<synfig::Time>::iterator i = sel_times.begin(), end = sel_times.end();
790 param_list.add("addtime",*i);
792 newset.insert((*i + deltatime).round(get_canvas()->rend_desc().get_frame_rate()));
796 param_list.add("deltatime",deltatime);
797 // param_list.add("time",canvas_interface()->get_time());
799 if(mode & COPY_MASK) //copy
801 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
802 ->process_action("timepoint_copy", param_list);
803 }else if(delmode) //DELETE
805 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
806 ->process_action("timepoint_delete", param_list);
809 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
810 ->process_action("timepoint_move", param_list);
813 //now replace all the selected with the new selected
820 /*if(value_node && selection)
822 if(selected_time==drag_time && event->button.button!=3)
823 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
825 if(event->button.button==1)
827 synfig::Waypoint waypoint(*selected_waypoint);
828 Time newtime((waypoint.get_time()+(selected_time-drag_time)).round(canvas->rend_desc().get_frame_rate()));
829 if(waypoint.get_time()!=newtime)
831 waypoint.set_time(newtime);
832 signal_waypoint_changed_(waypoint,value_node);
838 // selected_time=iter->time;
839 //selected_time=iter->get_time();
843 //std::cerr<<"unknown event type "<<event->type<<std::endl;
855 Glib::PropertyProxy<synfigapp::ValueDesc>
856 CellRenderer_TimeTrack::property_value_desc()
858 return Glib::PropertyProxy<synfigapp::ValueDesc>(this,"value_desc");
861 Glib::PropertyProxy<synfig::Canvas::Handle>
862 CellRenderer_TimeTrack::property_canvas()
864 return Glib::PropertyProxy<synfig::Canvas::Handle>(this,"canvas");
867 Glib::PropertyProxy<Gtk::Adjustment* >
868 CellRenderer_TimeTrack::property_adjustment()
870 return Glib::PropertyProxy<Gtk::Adjustment* >(this,"adjustment");
874 CellRenderer_TimeTrack::set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface> h)
876 canvas_interface_ = h;
880 set_waypoint_model(std::set<synfig::Waypoint, std::less<UniqueID> > waypoints, Waypoint::Model model, etl::loose_handle<synfigapp::CanvasInterface> canvas_interface)
882 // Create the action group
883 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Change Waypoint Group"));
885 std::set<synfig::Waypoint, std::less<UniqueID> >::const_iterator iter;
886 for(iter=waypoints.begin();iter!=waypoints.end();++iter)
888 Waypoint waypoint(*iter);
889 waypoint.apply_model(model);
891 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set"));
895 action->set_param("canvas",canvas_interface->get_canvas());
896 action->set_param("canvas_interface",canvas_interface);
898 action->set_param("waypoint",waypoint);
899 action->set_param("value_node",waypoint.get_parent_value_node());
901 if(!canvas_interface->get_instance()->perform_action(action))
910 CellRenderer_TimeTrack::show_timepoint_menu(const etl::handle<synfig::Node>& node, const synfig::Time& time, Side side)
912 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
914 n=synfig::waypoint_collect(waypoint_set,time,node);
916 Gtk::Menu* menu(manage(new Gtk::Menu()));
918 // Create the interpolation method menu
919 if(!waypoint_set.empty())
921 Gtk::Menu* interp_menu(manage(new Gtk::Menu()));
922 Waypoint::Model model;
924 // note: each of the following 4 'if' blocks provokes these warnings:
925 // /usr/include/sigc++-2.0/sigc++/adaptors/bound_argument.h:57: warning:
926 // 'model.synfig::Waypoint::Model::temporal_tension' is used uninitialized in this function
927 // 'model.synfig::Waypoint::Model::bias' is used uninitialized in this function
928 // 'model.synfig::Waypoint::Model::continuity' is used uninitialized in this function
929 // 'model.synfig::Waypoint::Model::tension' is used uninitialized in this function
930 // 'model.synfig::Waypoint::Model::priority' is used uninitialized in this function
931 // I don't know if that matters or not.
933 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_TCB);
934 else model.set_after(INTERPOLATION_TCB);
935 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("TCB"),
937 sigc::ptr_fun(set_waypoint_model),
944 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_LINEAR);
945 else model.set_after(INTERPOLATION_LINEAR);
946 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Linear"),
948 sigc::ptr_fun(set_waypoint_model),
955 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_HALT);
956 else model.set_after(INTERPOLATION_HALT);
957 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Ease"),
959 sigc::ptr_fun(set_waypoint_model),
966 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_CONSTANT);
967 else model.set_after(INTERPOLATION_CONSTANT);
968 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Constant"),
970 sigc::ptr_fun(set_waypoint_model),
978 menu->items().push_back(
979 Gtk::Menu_Helpers::MenuElem(
980 side==SIDE_LEFT?_("Change \"In\" Interp."):_("Change \"Out\" Interp."),
986 menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-jump-to"),
990 &synfigapp::CanvasInterface::set_time
996 if(!waypoint_set.empty())
998 if(waypoint_set.size()==1)
1002 signal_waypoint_clicked_(" ",*waypoint_set.begin(),2);
1006 synfig::info("Too many waypoints under me");
1009 synfig::info("ZERO waypoints under me");
1011 if(menu)menu->popup(3,gtk_get_current_event_time());