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 const synfig::Time get_time_offset_from_vdesc(const synfigapp::ValueDesc &v)
129 #ifdef ADJUST_WAYPOINTS_FOR_TIME_OFFSET
130 if(v.get_value_type() != synfig::ValueBase::TYPE_CANVAS)
131 return synfig::Time::zero();
133 synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle());
135 return synfig::Time::zero();
137 if (!v.parent_is_layer_param())
138 return synfig::Time::zero();
140 synfig::Layer::Handle layer = v.get_layer();
142 if (layer->get_name()!="PasteCanvas")
143 return synfig::Time::zero();
145 return layer->get_param("time_offset").get(Time());
146 #else // ADJUST_WAYPOINTS_FOR_TIME_OFFSET
147 return synfig::Time::zero();
151 //kind of a hack... pointer is ugly
152 const synfig::Node::time_set *get_times_from_vdesc(const synfigapp::ValueDesc &v)
154 if(v.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
156 synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle());
159 return &canvasparam->get_times();
162 ValueNode *base_value = v.get_value_node().get();
164 ValueNode_DynamicList *parent_value_node =
165 v.parent_is_value_node() ?
166 dynamic_cast<ValueNode_DynamicList *>(v.get_parent_value_node().get()) :
169 //we want a dynamic list entry to override the normal...
170 if(parent_value_node)
172 return &parent_value_node->list[v.get_index()].get_times();
173 }else if(base_value) //don't render stuff if it's just animated...
175 return &base_value->get_times();
180 bool get_closest_time(const synfig::Node::time_set &tset, const Time &t, const Time &range, Time &out)
182 Node::time_set::const_iterator i,j,end = tset.end();
184 // stop the crash mentioned in bug #1689282
185 // doesn't solve the underlying problem though, I don't think
186 if (tset.size() == 0)
188 synfig::error(__FILE__":%d: tset.size() == 0",__LINE__);
192 //TODO add in RangeGet so it's not so damn hard to click on points
194 i = tset.upper_bound(t); //where t is the lower bound, t < [first,i)
197 double dist = Time::end();
202 closest = i->get_time();
203 dist = abs(i->get_time() - t);
206 if(j != end && (abs(j->get_time() - t) < dist) )
208 closest = j->get_time();
209 dist = abs(j->get_time() - t);
212 if( dist <= range/2 )
222 CellRenderer_TimeTrack::render_vfunc(
223 const Glib::RefPtr<Gdk::Drawable>& window,
225 const Gdk::Rectangle& /*background_area*/,
226 const Gdk::Rectangle& area_,
227 const Gdk::Rectangle& /*expose_area*/,
228 Gtk::CellRendererState /*flags*/)
233 Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
234 Glib::RefPtr<Gdk::GC> inactive_gc(Gdk::GC::create(window));
235 Gtk::Adjustment *adjustment=get_adjustment();
236 // Gtk::StateType state = Gtk::STATE_ACTIVE;
237 // Gtk::ShadowType shadow;
240 curr_time_color("#0000ff"),
241 inactive_color("#000000"),
242 keyframe_color("#a07f7f");
243 Gdk::Color activepoint_color[2];
245 activepoint_color[0]=Gdk::Color("#ff0000");
246 activepoint_color[1]=Gdk::Color("#00ff00");
248 inactive_gc->set_rgb_fg_color(inactive_color);
249 inactive_gc->set_stipple(Gdk::Bitmap::create(stipple_xpm,2,2));
250 inactive_gc->set_fill(Gdk::STIPPLED);
252 synfig::Canvas::Handle canvas(property_canvas().get_value());
254 synfigapp::ValueDesc value_desc = property_value_desc().get_value();
255 synfig::ValueNode *base_value = value_desc.get_value_node().get();
256 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(base_value);
258 synfig::ValueNode_DynamicList *parent_value_node(0);
259 if(property_value_desc().get_value().parent_is_value_node())
260 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
262 // If the canvas is defined, then load up the keyframes
265 const synfig::KeyframeList& keyframe_list(canvas->keyframe_list());
266 synfig::KeyframeList::const_iterator iter;
268 for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter)
270 if(!iter->get_time().is_valid())
273 const int x((int)((float)area_.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower())));
274 if(iter->get_time()>=adjustment->get_lower() && iter->get_time()<adjustment->get_upper())
276 gc->set_rgb_fg_color(keyframe_color);
277 window->draw_rectangle(gc, true, area_.get_x()+x, area_.get_y(), 1, area_.get_height()+1);
282 //render all the time points that exist
284 const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc);
288 const synfig::Time time_offset = get_time_offset_from_vdesc(value_desc);
289 synfig::Node::time_set::const_iterator i = tset->begin(), end = tset->end();
291 float lower = adjustment->get_lower(),
292 upper = adjustment->get_upper();
294 Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(widget.get_window());
296 Gdk::Rectangle area(area_);
297 gc->set_clip_rectangle(area);
298 gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
300 bool valselected = sel_value.get_value_node() == base_value && !sel_times.empty();
302 float cfps = get_canvas()->rend_desc().get_frame_rate();
304 vector<Time> drawredafter;
306 Time diff = actual_time - actual_dragtime;//selected_time-drag_time;
309 //find the coordinate in the drawable space...
310 Time t_orig = i->get_time();
311 if(!t_orig.is_valid()) continue;
312 Time t = t_orig - time_offset;
314 //if it found it... (might want to change comparison, and optimize
315 // sel_times.find to not produce an overall nlogn solution)
318 //not dragging... just draw as per normal
319 //if move dragging draw offset
320 //if copy dragging draw both...
322 if(valselected && sel_times.find(t_orig) != sel_times.end())
324 if(dragging) //skip if we're dragging because we'll render it later
326 if(mode & COPY_MASK) // draw both blue and red moved
328 drawredafter.push_back(t + diff.round(cfps));
329 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
330 }else if(mode & DELETE_MASK) //it's just red...
332 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
334 }else //move - draw the red on top of the others...
336 drawredafter.push_back(t + diff.round(cfps));
341 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
346 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
349 //synfig::info("Displaying time: %.3f s",(float)t);
350 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
352 //should draw me a grey filled circle...
353 Gdk::Rectangle area2(
354 area.get_x() - area.get_height()/2 + x + 1,
359 render_time_point_to_window(window,area2,*i - time_offset,selected);
361 /*window->draw_arc(gc,true,
362 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
363 area.get_height()/2, area.get_height()*3/4,
366 gc->set_rgb_fg_color(Gdk::Color("#000000"));
367 window->draw_arc(gc,false,
368 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
369 area.get_height()/2, area.get_height()*3/4,
375 vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end();
378 //find the coordinate in the drawable space...
384 //synfig::info("Displaying time: %.3f s",(float)t);
385 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
387 //should draw me a grey filled circle...
389 Gdk::Rectangle area2(
390 area.get_x() - area.get_height()/2 + x + 1,
395 render_time_point_to_window(window,area2,*i,true);
396 /* gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
397 window->draw_arc(gc,true,
398 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
399 area.get_height()/2, area.get_height()*3/4,
402 gc->set_rgb_fg_color(Gdk::Color("#000000"));
403 window->draw_arc(gc,false,
404 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
405 area.get_height()/2, area.get_height()*3/4,
413 /* THIS IS NOW HANDLED ENTIRELY BY THE TIMEPOINT SYSTEM
414 // This this is an animated value node, then render the waypoints
417 //now render the actual waypoints
418 synfig::ValueNode_Animated::WaypointList::iterator iter;
420 iter=value_node->waypoint_list().begin();
421 iter!=value_node->waypoint_list().end();
425 if(!iter->get_time().is_valid())
429 if(is_selected(*iter))
431 Time t(iter->get_time());
435 t=(t+selected_time-drag_time).round(get_canvas()->rend_desc().get_frame_rate());
437 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(t-adjustment->get_lower()));
438 shadow=Gtk::SHADOW_IN;
443 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()));
444 shadow=Gtk::SHADOW_OUT;
449 widget.get_style()->paint_diamond(
450 Glib::RefPtr<Gdk::Window>::cast_static(window),
456 area.get_x()+x-area.get_height()/4,
457 area.get_y()+area.get_height()/4,
464 Gdk::Rectangle area(area_);
465 // If the parent of this value node is a dynamic list, then
466 // render the on and off times
467 if(parent_value_node)
469 const int index(property_value_desc().get_value().get_index());
470 const synfig::ValueNode_DynamicList::ListEntry& list_entry(parent_value_node->list[index]);
471 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(list_entry.timing_info);
472 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter,next;
475 if(!activepoint_list.empty())
476 is_off=!activepoint_list.front().state;
481 for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++)
483 x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower())));
485 if(x>area.get_width())x=area.get_width();
487 bool status_at_time=0;
488 if(next!=activepoint_list.end())
490 status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0);
493 status_at_time=!list_entry.status_at_time(Time::end());
495 if(!is_off && status_at_time)
501 if(is_off && !status_at_time)
503 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
508 if(!is_off && iter!=activepoint_list.end() && next->state==false && iter->state==false)
513 else if(is_off && next!=activepoint_list.end() && iter->state==false && next->state==true)
515 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
518 else if(is_off && iter!=activepoint_list.end() && iter->state==true)
520 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), prevx-xstart, area.get_height());
527 if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper())
532 gc->set_rgb_fg_color(activepoint_color[iter->state]);
533 window->draw_rectangle(gc, true, area.get_x()+x-w/2, area.get_y(), w, area.get_height());
539 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_height());
543 // Render a line that defines the current tick in time
545 gc->set_rgb_fg_color(curr_time_color);
547 const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower())));
549 if(adjustment->get_value()>=adjustment->get_lower() && adjustment->get_value()<adjustment->get_upper())
550 window->draw_rectangle(gc, true, area.get_x()+x, area.get_y(), 1, area.get_height());
554 synfig::ValueNode_Animated::WaypointList::iterator
555 CellRenderer_TimeTrack::find_waypoint(const synfig::Time& /*t*/,const synfig::Time& scope)
557 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
559 Time nearest(Time::end());
561 synfig::ValueNode_Animated::WaypointList::iterator iter,ret;
566 iter=value_node->waypoint_list().begin();
567 iter!=value_node->waypoint_list().end();
571 Time val=abs(iter->get_time()-selected_time);
579 if(nearest!=Time::end() && nearest<scope)
588 CellRenderer_TimeTrack::activate_vfunc(
590 Gtk::Widget& /*widget*/,
591 const Glib::ustring& treepath,
592 const Gdk::Rectangle& /*background_area*/,
593 const Gdk::Rectangle& cell_area,
594 Gtk::CellRendererState /*flags*/)
597 synfig::ValueNode_Animated::WaypointList::iterator iter;
598 Time nearest=1000000000;
599 Gtk::Adjustment *adjustment=get_adjustment();
601 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
603 synfig::Canvas::Handle canvas(get_canvas());
605 synfig::ValueNode_DynamicList *parent_value_node(0);
606 if(property_value_desc().get_value().parent_is_value_node())
607 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
613 case GDK_MOTION_NOTIFY:
614 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();
618 Gdk::ModifierType mod;
619 Gdk::Event(event).get_state(mod);
623 case GDK_BUTTON_PRESS:
624 case GDK_BUTTON_RELEASE:
626 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();
628 Gdk::ModifierType mod;
629 Gdk::Event(event).get_state(mod);
634 actual_time = curr_time;
636 curr_time=curr_time.round(canvas->rend_desc().get_frame_rate());
637 selected_time=curr_time;
639 Time pixel_width((adjustment->get_upper()-adjustment->get_lower())/cell_area.get_width());
643 case GDK_BUTTON_PRESS:
644 //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();
646 //Deal with time point selection, but only if they aren't involved in the insanity...
647 if(/*!value_node && */event->button.button == 1)
651 /*! UI specification:
653 When nothing is selected, clicking on a point in either normal mode or
654 additive mode will select the time point closest to the click.
655 Subtractive click will do nothing
657 When things are already selected, clicking on a selected point does
658 nothing (in both normal and add mode). Add mode clicking on an unselected
659 point adds it to the set. Normal clicking on an unselected point will
660 select only that one time point. Subtractive clicking on any point
661 will remove it from the the set if it is included.
664 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
665 const Node::time_set *tset = get_times_from_vdesc(valdesc);
666 const synfig::Time time_offset = get_time_offset_from_vdesc(valdesc);
668 bool clickfound = tset && get_closest_time(*tset,actual_time+time_offset,pixel_width*cell_area.get_height(),stime);
669 bool selectmode = mode & SELECT_MASK;
671 //NOTE LATER ON WE SHOULD MAKE IT SO MULTIPLE VALUENODES CAN BE SELECTED AT ONCE
672 //we want to jump to the value desc if we're not currently on it
673 // but only if we want to add the point
674 if(clickfound && !(sel_value == valdesc))
680 //now that we've made sure we're selecting the correct value, deal with the already selected points
681 set<Time>::iterator foundi = clickfound ? sel_times.find(stime) : sel_times.end();
682 bool found = foundi != sel_times.end();
684 //remove all other points from our list... (only select the one we need)
685 if(!selectmode && !found)
690 if(found && selectmode) //remove a single already selected point
692 sel_times.erase(foundi);
693 }else if(clickfound) //otherwise look at adding it
695 //for replace the list was cleared earlier, and for add it wasn't so it works
696 sel_times.insert(stime);
703 iter=find_waypoint(selected_time,pixel_width*cell_area.get_height()/2);
704 selected_waypoint=iter;
712 selected=synfig::UniqueID::nil();
715 if((!sel_times.empty() || selection) && event->button.button==1)
718 drag_time=selected_time;
719 actual_dragtime=actual_time;
721 //selected_time=iter->time;
724 // Activepoint Selection
725 if(parent_value_node)
727 const int index(property_value_desc().get_value().get_index());
728 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(parent_value_node->list[index].timing_info);
729 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter;
731 for(iter=activepoint_list.begin();iter!=activepoint_list.end();++iter)
733 Time val=abs(iter->time-selected_time);
741 // Perhaps I sould signal if we selected this activepoint?
744 if(event->button.button==3)
747 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
748 const Node::time_set *tset = get_times_from_vdesc(valdesc);
750 bool clickfound = tset && get_closest_time(*tset,actual_time+get_time_offset_from_vdesc(valdesc),pixel_width*cell_area.get_height(),stime);
752 etl::handle<synfig::Node> node;
753 if(valdesc.get_value(stime).get_type()==ValueBase::TYPE_CANVAS)
755 node=Canvas::Handle(valdesc.get_value(stime).get(Canvas::Handle()));
757 else //if(valdesc.is_value_node())
759 node=valdesc.get_value_node();
762 if(clickfound && node)
764 show_timepoint_menu(node, stime, actual_time<stime?SIDE_LEFT:SIDE_RIGHT);
769 case GDK_MOTION_NOTIFY:
771 //if(selection && dragging)
772 // 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();
776 case GDK_BUTTON_RELEASE:
780 //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();
783 /*if(event->button.button==3 && selection)
785 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
790 //Time point stuff...
791 if(event->button.button == 1)
793 bool delmode = (mode & DELETE_MASK) && !(mode & COPY_MASK);
794 deltatime = actual_time - actual_dragtime;
795 if(sel_times.size() != 0 && (delmode || !deltatime.is_equal(Time(0))))
797 synfigapp::Action::ParamList param_list;
798 param_list.add("canvas",canvas_interface()->get_canvas());
799 param_list.add("canvas_interface",canvas_interface());
801 if(sel_value.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
803 param_list.add("addcanvas",sel_value.get_value().get(Canvas::Handle()));
806 param_list.add("addvaluedesc",sel_value);
810 std::set<synfig::Time>::iterator i = sel_times.begin(), end = sel_times.end();
813 param_list.add("addtime",*i);
815 newset.insert((*i + deltatime).round(get_canvas()->rend_desc().get_frame_rate()));
819 param_list.add("deltatime",deltatime);
820 // param_list.add("time",canvas_interface()->get_time());
822 if(mode & COPY_MASK) //copy
824 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
825 ->process_action("timepoint_copy", param_list);
826 }else if(delmode) //DELETE
828 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
829 ->process_action("timepoint_delete", param_list);
832 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
833 ->process_action("timepoint_move", param_list);
836 //now replace all the selected with the new selected
843 /*if(value_node && selection)
845 if(selected_time==drag_time && event->button.button!=3)
846 signal_waypoint_clicked_(path,*selected_waypoint,event->button.button-1);
848 if(event->button.button==1)
850 synfig::Waypoint waypoint(*selected_waypoint);
851 Time newtime((waypoint.get_time()+(selected_time-drag_time)).round(canvas->rend_desc().get_frame_rate()));
852 if(waypoint.get_time()!=newtime)
854 waypoint.set_time(newtime);
855 signal_waypoint_changed_(waypoint,value_node);
861 // selected_time=iter->time;
862 //selected_time=iter->get_time();
866 //std::cerr<<"unknown event type "<<event->type<<std::endl;
878 Glib::PropertyProxy<synfigapp::ValueDesc>
879 CellRenderer_TimeTrack::property_value_desc()
881 return Glib::PropertyProxy<synfigapp::ValueDesc>(this,"value_desc");
884 Glib::PropertyProxy<synfig::Canvas::Handle>
885 CellRenderer_TimeTrack::property_canvas()
887 return Glib::PropertyProxy<synfig::Canvas::Handle>(this,"canvas");
890 Glib::PropertyProxy<Gtk::Adjustment* >
891 CellRenderer_TimeTrack::property_adjustment()
893 return Glib::PropertyProxy<Gtk::Adjustment* >(this,"adjustment");
897 CellRenderer_TimeTrack::set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface> h)
899 canvas_interface_ = h;
903 set_waypoint_model(std::set<synfig::Waypoint, std::less<UniqueID> > waypoints, Waypoint::Model model, etl::loose_handle<synfigapp::CanvasInterface> canvas_interface)
905 // Create the action group
906 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Change Waypoint Group"));
908 std::set<synfig::Waypoint, std::less<UniqueID> >::const_iterator iter;
909 for(iter=waypoints.begin();iter!=waypoints.end();++iter)
911 Waypoint waypoint(*iter);
912 waypoint.apply_model(model);
914 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set"));
918 action->set_param("canvas",canvas_interface->get_canvas());
919 action->set_param("canvas_interface",canvas_interface);
921 action->set_param("waypoint",waypoint);
922 action->set_param("value_node",waypoint.get_parent_value_node());
924 if(!canvas_interface->get_instance()->perform_action(action))
933 CellRenderer_TimeTrack::show_timepoint_menu(const etl::handle<synfig::Node>& node, const synfig::Time& time, Side side)
935 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
937 n=synfig::waypoint_collect(waypoint_set,time,node);
939 Gtk::Menu* menu(manage(new Gtk::Menu()));
941 // Create the interpolation method menu
942 if(!waypoint_set.empty())
944 Gtk::Menu* interp_menu(manage(new Gtk::Menu()));
945 Waypoint::Model model;
947 // note: each of the following 4 'if' blocks provokes these warnings:
948 // /usr/include/sigc++-2.0/sigc++/adaptors/bound_argument.h:57: warning:
949 // 'model.synfig::Waypoint::Model::temporal_tension' is used uninitialized in this function
950 // 'model.synfig::Waypoint::Model::bias' is used uninitialized in this function
951 // 'model.synfig::Waypoint::Model::continuity' is used uninitialized in this function
952 // 'model.synfig::Waypoint::Model::tension' is used uninitialized in this function
953 // 'model.synfig::Waypoint::Model::priority' is used uninitialized in this function
954 // I don't know if that matters or not.
956 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_TCB);
957 else model.set_after(INTERPOLATION_TCB);
958 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("TCB"),
960 sigc::ptr_fun(set_waypoint_model),
967 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_LINEAR);
968 else model.set_after(INTERPOLATION_LINEAR);
969 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Linear"),
971 sigc::ptr_fun(set_waypoint_model),
978 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_HALT);
979 else model.set_after(INTERPOLATION_HALT);
980 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Ease"),
982 sigc::ptr_fun(set_waypoint_model),
989 if(side==SIDE_LEFT)model.set_before(INTERPOLATION_CONSTANT);
990 else model.set_after(INTERPOLATION_CONSTANT);
991 interp_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Constant"),
993 sigc::ptr_fun(set_waypoint_model),
1001 menu->items().push_back(
1002 Gtk::Menu_Helpers::MenuElem(
1003 side==SIDE_LEFT?_("Change \"In\" Interp."):_("Change \"Out\" Interp."),
1009 menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-jump-to"),
1012 *canvas_interface(),
1013 &synfigapp::CanvasInterface::set_time
1019 if(!waypoint_set.empty())
1021 if(waypoint_set.size()==1)
1025 signal_waypoint_clicked_(" ",*waypoint_set.begin(),2);
1029 synfig::info("Too many waypoints under me");
1032 synfig::info("ZERO waypoints under me");
1034 if(menu)menu->popup(3,gtk_get_current_event_time());