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 should 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);
749 synfig::Time time_offset = get_time_offset_from_vdesc(valdesc);
751 bool clickfound = tset && get_closest_time(*tset,actual_time+time_offset,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, time_offset, actual_time+time_offset<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, const synfig::Time& time_offset, 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 // attempting to locate the valuenode for the clicked waypoint doesn't work if this is a Canvas parameter,
1023 // so act as if there were multiple waypoints in that case as a workaround
1024 if(waypoint_set.size()==1 && !Canvas::Handle::cast_dynamic(node))
1028 signal_waypoint_clicked_(" ",*waypoint_set.begin(),2);
1032 synfig::info("Too many waypoints under me");
1035 synfig::info("ZERO waypoints under me");
1037 if(menu)menu->popup(3,gtk_get_current_event_time());