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 #ifdef ADJUST_WAYPOINTS_FOR_TIME_OFFSET // see node.h
137 synfig::Time::value_type time_offset = 0;
138 if (v.parent_is_layer_param())
140 synfig::Layer::Handle layer = v.get_layer();
141 if (layer->get_name()=="PasteCanvas")
142 time_offset = layer->get_param("time_offset").get(Time());
145 const Node::time_set *times = &canvasparam->get_times();
149 //! \todo this is a memory leak - blame the 'kind of hack' above
150 Node::time_set *tmp = new Node::time_set;
151 Node::time_set::iterator i = times->begin(), end = times->end();
152 for (; i != end; ++i)
153 tmp->insert(*i - time_offset);
158 #else // ADJUST_WAYPOINTS_FOR_TIME_OFFSET
159 return &canvasparam->get_times();
164 ValueNode *base_value = v.get_value_node().get();
166 ValueNode_DynamicList *parent_value_node =
167 v.parent_is_value_node() ?
168 dynamic_cast<ValueNode_DynamicList *>(v.get_parent_value_node().get()) :
171 //we want a dynamic list entry to override the normal...
172 if(parent_value_node)
174 return &parent_value_node->list[v.get_index()].get_times();
175 }else if(base_value) //don't render stuff if it's just animated...
177 return &base_value->get_times();
182 bool get_closest_time(const synfig::Node::time_set &tset, const Time &t, const Time &range, Time &out)
184 Node::time_set::const_iterator i,j,end = tset.end();
186 // stop the crash mentioned in bug #1689282
187 // doesn't solve the underlying problem though, I don't think
188 if (tset.size() == 0)
190 synfig::error(__FILE__":%d: tset.size() == 0",__LINE__);
194 //TODO add in RangeGet so it's not so damn hard to click on points
196 i = tset.upper_bound(t); //where t is the lower bound, t < [first,i)
199 double dist = Time::end();
204 closest = i->get_time();
205 dist = abs(i->get_time() - t);
208 if(j != end && (abs(j->get_time() - t) < dist) )
210 closest = j->get_time();
211 dist = abs(j->get_time() - t);
214 if( dist <= range/2 )
224 CellRenderer_TimeTrack::render_vfunc(
225 const Glib::RefPtr<Gdk::Drawable>& window,
227 const Gdk::Rectangle& /*background_area*/,
228 const Gdk::Rectangle& area_,
229 const Gdk::Rectangle& /*expose_area*/,
230 Gtk::CellRendererState /*flags*/)
235 Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
236 Glib::RefPtr<Gdk::GC> inactive_gc(Gdk::GC::create(window));
237 Gtk::Adjustment *adjustment=get_adjustment();
238 // Gtk::StateType state = Gtk::STATE_ACTIVE;
239 // Gtk::ShadowType shadow;
242 curr_time_color("#0000ff"),
243 inactive_color("#000000"),
244 keyframe_color("#a07f7f");
245 Gdk::Color activepoint_color[2];
247 activepoint_color[0]=Gdk::Color("#ff0000");
248 activepoint_color[1]=Gdk::Color("#00ff00");
250 inactive_gc->set_rgb_fg_color(inactive_color);
251 inactive_gc->set_stipple(Gdk::Bitmap::create(stipple_xpm,2,2));
252 inactive_gc->set_fill(Gdk::STIPPLED);
254 synfig::Canvas::Handle canvas(property_canvas().get_value());
256 synfigapp::ValueDesc value_desc = property_value_desc().get_value();
257 synfig::ValueNode *base_value = value_desc.get_value_node().get();
258 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(base_value);
260 synfig::ValueNode_DynamicList *parent_value_node(0);
261 if(property_value_desc().get_value().parent_is_value_node())
262 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
264 // If the canvas is defined, then load up the keyframes
267 const synfig::KeyframeList& keyframe_list(canvas->keyframe_list());
268 synfig::KeyframeList::const_iterator iter;
270 for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter)
272 if(!iter->get_time().is_valid())
275 const int x((int)((float)area_.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower())));
276 if(iter->get_time()>=adjustment->get_lower() && iter->get_time()<adjustment->get_upper())
278 gc->set_rgb_fg_color(keyframe_color);
279 window->draw_rectangle(gc, true, area_.get_x()+x, area_.get_y(), 1, area_.get_height()+1);
284 //render all the time points that exist
286 const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc);
290 synfig::Node::time_set::const_iterator i = tset->begin(), end = tset->end();
292 float lower = adjustment->get_lower(),
293 upper = adjustment->get_upper();
295 Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(widget.get_window());
297 Gdk::Rectangle area(area_);
298 gc->set_clip_rectangle(area);
299 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
301 bool valselected = sel_value.get_value_node() == base_value && !sel_times.empty();
303 float cfps = get_canvas()->rend_desc().get_frame_rate();
305 vector<Time> drawredafter;
307 Time diff = actual_time - actual_dragtime;//selected_time-drag_time;
310 //find the coordinate in the drawable space...
311 Time t = i->get_time();
316 //if it found it... (might want to change comparison, and optimize
317 // sel_times.find to not produce an overall nlogn solution)
320 //not dragging... just draw as per normal
321 //if move dragging draw offset
322 //if copy dragging draw both...
324 if(valselected && sel_times.find(t) != sel_times.end())
326 if(dragging) //skip if we're dragging because we'll render it later
328 if(mode & COPY_MASK) // draw both blue and red moved
330 drawredafter.push_back((t + diff).round(cfps));
331 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
332 }else if(mode & DELETE_MASK) //it's just red...
334 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
336 }else //move - draw the red on top of the others...
338 drawredafter.push_back((t + diff).round(cfps));
343 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
348 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
351 //synfig::info("Displaying time: %.3f s",(float)t);
352 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
354 //should draw me a grey filled circle...
355 Gdk::Rectangle area2(
356 area.get_x() - area.get_height()/2 + x + 1,
361 render_time_point_to_window(window,area2,*i,selected);
363 /*window->draw_arc(gc,true,
364 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
365 area.get_height()/2, area.get_height()*3/4,
368 gc->set_rgb_fg_color(Gdk::Color("#000000"));
369 window->draw_arc(gc,false,
370 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
371 area.get_height()/2, area.get_height()*3/4,
377 vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end();
380 //find the coordinate in the drawable space...
386 //synfig::info("Displaying time: %.3f s",(float)t);
387 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
389 //should draw me a grey filled circle...
391 Gdk::Rectangle area2(
392 area.get_x() - area.get_height()/2 + x + 1,
397 render_time_point_to_window(window,area2,*i,true);
398 /* gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
399 window->draw_arc(gc,true,
400 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
401 area.get_height()/2, area.get_height()*3/4,
404 gc->set_rgb_fg_color(Gdk::Color("#000000"));
405 window->draw_arc(gc,false,
406 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
407 area.get_height()/2, area.get_height()*3/4,
415 /* THIS IS NOW HANDLED ENTIRELY BY THE TIMEPOINT SYSTEM
416 // This this is an animated value node, then render the waypoints
419 //now render the actual waypoints
420 synfig::ValueNode_Animated::WaypointList::iterator iter;
422 iter=value_node->waypoint_list().begin();
423 iter!=value_node->waypoint_list().end();
427 if(!iter->get_time().is_valid())
431 if(is_selected(*iter))
433 Time t(iter->get_time());
437 t=(t+selected_time-drag_time).round(get_canvas()->rend_desc().get_frame_rate());
439 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(t-adjustment->get_lower()));
440 shadow=Gtk::SHADOW_IN;
445 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()));
446 shadow=Gtk::SHADOW_OUT;
451 widget.get_style()->paint_diamond(
452 Glib::RefPtr<Gdk::Window>::cast_static(window),
458 area.get_x()+x-area.get_height()/4,
459 area.get_y()+area.get_height()/4,
466 Gdk::Rectangle area(area_);
467 // If the parent of this value node is a dynamic list, then
468 // render the on and off times
469 if(parent_value_node)
471 const int index(property_value_desc().get_value().get_index());
472 const synfig::ValueNode_DynamicList::ListEntry& list_entry(parent_value_node->list[index]);
473 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(list_entry.timing_info);
474 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter,next;
477 if(!activepoint_list.empty())
478 is_off=!activepoint_list.front().state;
483 for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++)
485 x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower())));
487 if(x>area.get_width())x=area.get_width();
489 bool status_at_time=0;
490 if(next!=activepoint_list.end())
492 status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0);
495 status_at_time=!list_entry.status_at_time(Time::end());
497 if(!is_off && status_at_time)
503 if(is_off && !status_at_time)
505 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
510 if(!is_off && iter!=activepoint_list.end() && next->state==false && iter->state==false)
515 else if(is_off && next!=activepoint_list.end() && iter->state==false && next->state==true)
517 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
520 else if(is_off && iter!=activepoint_list.end() && iter->state==true)
522 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), prevx-xstart, area.get_height());
529 if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper())
534 gc->set_rgb_fg_color(activepoint_color[iter->state]);
535 window->draw_rectangle(gc, true, area.get_x()+x-w/2, area.get_y(), w, area.get_height());
541 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_height());
545 // Render a line that defines the current tick in time
547 gc->set_rgb_fg_color(curr_time_color);
549 const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower())));
551 if(adjustment->get_value()>=adjustment->get_lower() && adjustment->get_value()<adjustment->get_upper())
552 window->draw_rectangle(gc, true, area.get_x()+x, area.get_y(), 1, area.get_height());
556 synfig::ValueNode_Animated::WaypointList::iterator
557 CellRenderer_TimeTrack::find_waypoint(const synfig::Time& /*t*/,const synfig::Time& scope)
559 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
561 Time nearest(Time::end());
563 synfig::ValueNode_Animated::WaypointList::iterator iter,ret;
568 iter=value_node->waypoint_list().begin();
569 iter!=value_node->waypoint_list().end();
573 Time val=abs(iter->get_time()-selected_time);
581 if(nearest!=Time::end() && nearest<scope)
590 CellRenderer_TimeTrack::activate_vfunc(
592 Gtk::Widget& /*widget*/,
593 const Glib::ustring& treepath,
594 const Gdk::Rectangle& /*background_area*/,
595 const Gdk::Rectangle& cell_area,
596 Gtk::CellRendererState /*flags*/)
599 synfig::ValueNode_Animated::WaypointList::iterator iter;
600 Time nearest=1000000000;
601 Gtk::Adjustment *adjustment=get_adjustment();
603 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
605 synfig::Canvas::Handle canvas(get_canvas());
607 synfig::ValueNode_DynamicList *parent_value_node(0);
608 if(property_value_desc().get_value().parent_is_value_node())
609 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
615 case GDK_MOTION_NOTIFY:
616 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();
620 Gdk::ModifierType mod;
621 Gdk::Event(event).get_state(mod);
625 case GDK_BUTTON_PRESS:
626 case GDK_BUTTON_RELEASE:
628 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();
630 Gdk::ModifierType mod;
631 Gdk::Event(event).get_state(mod);
636 actual_time = curr_time;
638 curr_time=curr_time.round(canvas->rend_desc().get_frame_rate());
639 selected_time=curr_time;
641 Time pixel_width((adjustment->get_upper()-adjustment->get_lower())/cell_area.get_width());
645 case GDK_BUTTON_PRESS:
646 //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();
648 //Deal with time point selection, but only if they aren't involved in the insanity...
649 if(/*!value_node && */event->button.button == 1)
653 /*! UI specification:
655 When nothing is selected, clicking on a point in either normal mode or
656 additive mode will select the time point closest to the click.
657 Subtractive click will do nothing
659 When things are already selected, clicking on a selected point does
660 nothing (in both normal and add mode). Add mode clicking on an unselected
661 point adds it to the set. Normal clicking on an unselected point will
662 select only that one time point. Subtractive clicking on any point
663 will remove it from the the set if it is included.
666 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
667 const Node::time_set *tset = get_times_from_vdesc(valdesc);
669 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
670 bool selectmode = mode & SELECT_MASK;
672 //NOTE LATER ON WE SHOULD MAKE IT SO MULTIPLE VALUENODES CAN BE SELECTED AT ONCE
673 //we want to jump to the value desc if we're not currently on it
674 // but only if we want to add the point
675 if(clickfound && !(sel_value == valdesc))
681 //now that we've made sure we're selecting the correct value, deal with the already selected points
682 set<Time>::iterator foundi = clickfound ? sel_times.find(stime) : sel_times.end();
683 bool found = foundi != sel_times.end();
685 //remove all other points from our list... (only select the one we need)
686 if(!selectmode && !found)
691 if(found && selectmode) //remove a single already selected point
693 sel_times.erase(foundi);
694 }else if(clickfound) //otherwise look at adding it
696 //for replace the list was cleared earlier, and for add it wasn't so it works
697 sel_times.insert(stime);
704 iter=find_waypoint(selected_time,pixel_width*cell_area.get_height()/2);
705 selected_waypoint=iter;
713 selected=synfig::UniqueID::nil();
716 if((!sel_times.empty() || selection) && event->button.button==1)
719 drag_time=selected_time;
720 actual_dragtime=actual_time;
722 //selected_time=iter->time;
725 // Activepoint Selection
726 if(parent_value_node)
728 const int index(property_value_desc().get_value().get_index());
729 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(parent_value_node->list[index].timing_info);
730 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter;
732 for(iter=activepoint_list.begin();iter!=activepoint_list.end();++iter)
734 Time val=abs(iter->time-selected_time);
742 // Perhaps I sould signal if we selected this activepoint?
745 if(event->button.button==3)
748 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
749 const Node::time_set *tset = get_times_from_vdesc(valdesc);
751 bool clickfound = tset && get_closest_time(*tset,actual_time,pixel_width*cell_area.get_height(),stime);
753 etl::handle<synfig::Node> node;
754 if(valdesc.get_value(stime).get_type()==ValueBase::TYPE_CANVAS)
756 node=Canvas::Handle(valdesc.get_value(stime).get(Canvas::Handle()));
758 else //if(valdesc.is_value_node())
760 node=valdesc.get_value_node();
763 if(clickfound && node)
765 show_timepoint_menu(node, stime, actual_time<stime?SIDE_LEFT:SIDE_RIGHT);
770 case GDK_MOTION_NOTIFY:
772 //if(selection && dragging)
773 // 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();
777 case GDK_BUTTON_RELEASE:
781 //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();
784 /*if(event->button.button==3 && selection)
786 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
791 //Time point stuff...
792 if(event->button.button == 1)
794 bool delmode = (mode & DELETE_MASK) && !(mode & COPY_MASK);
795 deltatime = actual_time - actual_dragtime;
796 if(sel_times.size() != 0 && (delmode || !deltatime.is_equal(Time(0))))
798 synfigapp::Action::ParamList param_list;
799 param_list.add("canvas",canvas_interface()->get_canvas());
800 param_list.add("canvas_interface",canvas_interface());
802 if(sel_value.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
804 param_list.add("addcanvas",sel_value.get_value().get(Canvas::Handle()));
807 param_list.add("addvaluedesc",sel_value);
811 std::set<synfig::Time>::iterator i = sel_times.begin(), end = sel_times.end();
814 param_list.add("addtime",*i);
816 newset.insert((*i + deltatime).round(get_canvas()->rend_desc().get_frame_rate()));
820 param_list.add("deltatime",deltatime);
821 // param_list.add("time",canvas_interface()->get_time());
823 if(mode & COPY_MASK) //copy
825 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
826 ->process_action("timepoint_copy", param_list);
827 }else if(delmode) //DELETE
829 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
830 ->process_action("timepoint_delete", param_list);
833 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
834 ->process_action("timepoint_move", param_list);
837 //now replace all the selected with the new selected
844 /*if(value_node && selection)
846 if(selected_time==drag_time && event->button.button!=3)
847 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
849 if(event->button.button==1)
851 synfig::Waypoint waypoint(*selected_waypoint);
852 Time newtime((waypoint.get_time()+(selected_time-drag_time)).round(canvas->rend_desc().get_frame_rate()));
853 if(waypoint.get_time()!=newtime)
855 waypoint.set_time(newtime);
856 signal_waypoint_changed_(waypoint,value_node);
862 // selected_time=iter->time;
863 //selected_time=iter->get_time();
867 //std::cerr<<"unknown event type "<<event->type<<std::endl;
879 Glib::PropertyProxy<synfigapp::ValueDesc>
880 CellRenderer_TimeTrack::property_value_desc()
882 return Glib::PropertyProxy<synfigapp::ValueDesc>(this,"value_desc");
885 Glib::PropertyProxy<synfig::Canvas::Handle>
886 CellRenderer_TimeTrack::property_canvas()
888 return Glib::PropertyProxy<synfig::Canvas::Handle>(this,"canvas");
891 Glib::PropertyProxy<Gtk::Adjustment* >
892 CellRenderer_TimeTrack::property_adjustment()
894 return Glib::PropertyProxy<Gtk::Adjustment* >(this,"adjustment");
898 CellRenderer_TimeTrack::set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface> h)
900 canvas_interface_ = h;
904 set_waypoint_model(std::set<synfig::Waypoint, std::less<UniqueID> > waypoints, Waypoint::Model model, etl::loose_handle<synfigapp::CanvasInterface> canvas_interface)
906 // Create the action group
907 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Change Waypoint Group"));
909 std::set<synfig::Waypoint, std::less<UniqueID> >::const_iterator iter;
910 for(iter=waypoints.begin();iter!=waypoints.end();++iter)
912 Waypoint waypoint(*iter);
913 waypoint.apply_model(model);
915 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set"));
919 action->set_param("canvas",canvas_interface->get_canvas());
920 action->set_param("canvas_interface",canvas_interface);
922 action->set_param("waypoint",waypoint);
923 action->set_param("value_node",waypoint.get_parent_value_node());
925 if(!canvas_interface->get_instance()->perform_action(action))
934 CellRenderer_TimeTrack::show_timepoint_menu(const etl::handle<synfig::Node>& node, const synfig::Time& time, Side side)
936 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
938 n=synfig::waypoint_collect(waypoint_set,time,node);
940 Gtk::Menu* menu(manage(new Gtk::Menu()));
942 // Create the interpolation method menu
943 if(!waypoint_set.empty())
945 Gtk::Menu* interp_menu(manage(new Gtk::Menu()));
946 Waypoint::Model model;
948 // note: each of the following 4 'if' blocks provokes these warnings:
949 // /usr/include/sigc++-2.0/sigc++/adaptors/bound_argument.h:57: warning:
950 // 'model.synfig::Waypoint::Model::temporal_tension' is used uninitialized in this function
951 // 'model.synfig::Waypoint::Model::bias' is used uninitialized in this function
952 // 'model.synfig::Waypoint::Model::continuity' is used uninitialized in this function
953 // 'model.synfig::Waypoint::Model::tension' is used uninitialized in this function
954 // 'model.synfig::Waypoint::Model::priority' is used uninitialized in this function
955 // I don't know if that matters or not.
957 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_TCB);
958 else model.set_after(INTERPOLATION_TCB);
959 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("TCB"),
961 sigc::ptr_fun(set_waypoint_model),
968 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_LINEAR);
969 else model.set_after(INTERPOLATION_LINEAR);
970 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Linear"),
972 sigc::ptr_fun(set_waypoint_model),
979 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_HALT);
980 else model.set_after(INTERPOLATION_HALT);
981 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Ease"),
983 sigc::ptr_fun(set_waypoint_model),
990 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_CONSTANT);
991 else model.set_after(INTERPOLATION_CONSTANT);
992 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Constant"),
994 sigc::ptr_fun(set_waypoint_model),
1002 menu->items().push_back(
1003 Gtk::Menu_Helpers::MenuElem(
1004 side==SIDE_LEFT?_("Change \"In\" Interp."):_("Change \"Out\" Interp."),
1010 menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-jump-to"),
1013 *canvas_interface(),
1014 &synfigapp::CanvasInterface::set_time
1020 if(!waypoint_set.empty())
1022 if(waypoint_set.size()==1)
1026 signal_waypoint_clicked_(" ",*waypoint_set.begin(),2);
1030 synfig::info("Too many waypoints under me");
1033 synfig::info("ZERO waypoints under me");
1035 if(menu)menu->popup(3,gtk_get_current_event_time());