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>
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 if (getenv("SYNFIG_DEBUG_DESTRUCTORS"))
94 synfig::info("CellRenderer_TimeTrack::~CellRenderer_TimeTrack(): Deleted");
98 CellRenderer_TimeTrack::set_adjustment(Gtk::Adjustment &x)
100 property_adjustment_=&x;
101 // x.signal_value_changed().connect(sigc::mem_fun(*this,&Gtk::Widget::queue_draw));
104 synfig::Canvas::Handle
105 CellRenderer_TimeTrack::get_canvas()const
107 return const_cast<CellRenderer_TimeTrack*>(this)->property_canvas().get_value();
111 CellRenderer_TimeTrack::get_adjustment()
113 return (Gtk::Adjustment*)property_adjustment_;
116 const Gtk::Adjustment *
117 CellRenderer_TimeTrack::get_adjustment()const
119 return (const Gtk::Adjustment*)property_adjustment_;
123 CellRenderer_TimeTrack::is_selected(const Waypoint& waypoint)const
125 return selected==waypoint;
128 const synfig::Time get_time_offset_from_vdesc(const synfigapp::ValueDesc &v)
130 #ifdef ADJUST_WAYPOINTS_FOR_TIME_OFFSET
131 if(v.get_value_type() != synfig::ValueBase::TYPE_CANVAS)
132 return synfig::Time::zero();
134 synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle());
136 return synfig::Time::zero();
138 if (!v.parent_is_layer_param())
139 return synfig::Time::zero();
141 synfig::Layer::Handle layer = v.get_layer();
143 if (layer->get_name()!="PasteCanvas")
144 return synfig::Time::zero();
146 return layer->get_param("time_offset").get(Time());
147 #else // ADJUST_WAYPOINTS_FOR_TIME_OFFSET
148 return synfig::Time::zero();
152 //kind of a hack... pointer is ugly
153 const synfig::Node::time_set *get_times_from_vdesc(const synfigapp::ValueDesc &v)
155 if(v.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
157 synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle());
160 return &canvasparam->get_times();
163 ValueNode *base_value = v.get_value_node().get();
165 ValueNode_DynamicList *parent_value_node =
166 v.parent_is_value_node() ?
167 dynamic_cast<ValueNode_DynamicList *>(v.get_parent_value_node().get()) :
170 //we want a dynamic list entry to override the normal...
171 if(parent_value_node)
173 return &parent_value_node->list[v.get_index()].get_times();
174 }else if(base_value) //don't render stuff if it's just animated...
176 return &base_value->get_times();
181 bool get_closest_time(const synfig::Node::time_set &tset, const Time &t, const Time &range, Time &out)
183 Node::time_set::const_iterator i,j,end = tset.end();
185 // stop the crash mentioned in bug #1689282
186 // doesn't solve the underlying problem though, I don't think
187 if (tset.size() == 0)
189 synfig::error(__FILE__":%d: tset.size() == 0",__LINE__);
193 //TODO add in RangeGet so it's not so damn hard to click on points
195 i = tset.upper_bound(t); //where t is the lower bound, t < [first,i)
198 double dist = Time::end();
203 closest = i->get_time();
204 dist = abs(i->get_time() - t);
207 if(j != end && (abs(j->get_time() - t) < dist) )
209 closest = j->get_time();
210 dist = abs(j->get_time() - t);
213 if( dist <= range/2 )
223 CellRenderer_TimeTrack::render_vfunc(
224 const Glib::RefPtr<Gdk::Drawable>& window,
226 const Gdk::Rectangle& /*background_area*/,
227 const Gdk::Rectangle& area_,
228 const Gdk::Rectangle& /*expose_area*/,
229 Gtk::CellRendererState /*flags*/)
234 Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
235 Glib::RefPtr<Gdk::GC> inactive_gc(Gdk::GC::create(window));
236 Gtk::Adjustment *adjustment=get_adjustment();
237 // Gtk::StateType state = Gtk::STATE_ACTIVE;
238 // Gtk::ShadowType shadow;
241 curr_time_color("#0000ff"),
242 inactive_color("#000000"),
243 keyframe_color("#a07f7f");
244 Gdk::Color activepoint_color[2];
246 activepoint_color[0]=Gdk::Color("#ff0000");
247 activepoint_color[1]=Gdk::Color("#00ff00");
249 inactive_gc->set_rgb_fg_color(inactive_color);
250 inactive_gc->set_stipple(Gdk::Bitmap::create(stipple_xpm,2,2));
251 inactive_gc->set_fill(Gdk::STIPPLED);
253 synfig::Canvas::Handle canvas(property_canvas().get_value());
255 synfigapp::ValueDesc value_desc = property_value_desc().get_value();
256 synfig::ValueNode *base_value = value_desc.get_value_node().get();
257 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(base_value);
259 synfig::ValueNode_DynamicList *parent_value_node(0);
260 if(property_value_desc().get_value().parent_is_value_node())
261 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
263 // If the canvas is defined, then load up the keyframes
266 const synfig::KeyframeList& keyframe_list(canvas->keyframe_list());
267 synfig::KeyframeList::const_iterator iter;
269 for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter)
271 if(!iter->get_time().is_valid())
274 const int x((int)((float)area_.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower())));
275 if(iter->get_time()>=adjustment->get_lower() && iter->get_time()<adjustment->get_upper())
277 gc->set_rgb_fg_color(keyframe_color);
278 window->draw_rectangle(gc, true, area_.get_x()+x, area_.get_y(), 1, area_.get_height()+1);
283 //render all the time points that exist
285 const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc);
289 const synfig::Time time_offset = get_time_offset_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_orig = i->get_time();
312 if(!t_orig.is_valid()) continue;
313 Time t = t_orig - time_offset;
315 //if it found it... (might want to change comparison, and optimize
316 // sel_times.find to not produce an overall nlogn solution)
319 //not dragging... just draw as per normal
320 //if move dragging draw offset
321 //if copy dragging draw both...
323 if(valselected && sel_times.find(t_orig) != sel_times.end())
325 if(dragging) //skip if we're dragging because we'll render it later
327 if(mode & COPY_MASK) // draw both blue and red moved
329 drawredafter.push_back(t + diff.round(cfps));
330 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
331 }else if(mode & DELETE_MASK) //it's just red...
333 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
335 }else //move - draw the red on top of the others...
337 drawredafter.push_back(t + diff.round(cfps));
342 gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
347 gc->set_rgb_fg_color(Gdk::Color("#00EEEE"));
350 //synfig::info("Displaying time: %.3f s",(float)t);
351 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
353 //should draw me a grey filled circle...
354 Gdk::Rectangle area2(
355 area.get_x() - area.get_height()/2 + x + 1,
360 render_time_point_to_window(window,area2,*i - time_offset,selected);
362 /*window->draw_arc(gc,true,
363 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
364 area.get_height()/2, area.get_height()*3/4,
367 gc->set_rgb_fg_color(Gdk::Color("#000000"));
368 window->draw_arc(gc,false,
369 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
370 area.get_height()/2, area.get_height()*3/4,
376 vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end();
379 //find the coordinate in the drawable space...
385 //synfig::info("Displaying time: %.3f s",(float)t);
386 const int x = (int)((t-lower)*area.get_width()/(upper-lower));
388 //should draw me a grey filled circle...
390 Gdk::Rectangle area2(
391 area.get_x() - area.get_height()/2 + x + 1,
396 render_time_point_to_window(window,area2,*i,true);
397 /* gc->set_rgb_fg_color(Gdk::Color("#EE0000"));
398 window->draw_arc(gc,true,
399 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
400 area.get_height()/2, area.get_height()*3/4,
403 gc->set_rgb_fg_color(Gdk::Color("#000000"));
404 window->draw_arc(gc,false,
405 area.get_x() + x - area.get_height()/4, area.get_y() + area.get_height()/8,
406 area.get_height()/2, area.get_height()*3/4,
414 /* THIS IS NOW HANDLED ENTIRELY BY THE TIMEPOINT SYSTEM
415 // This this is an animated value node, then render the waypoints
418 //now render the actual waypoints
419 synfig::ValueNode_Animated::WaypointList::iterator iter;
421 iter=value_node->waypoint_list().begin();
422 iter!=value_node->waypoint_list().end();
426 if(!iter->get_time().is_valid())
430 if(is_selected(*iter))
432 Time t(iter->get_time());
436 t=(t+selected_time-drag_time).round(get_canvas()->rend_desc().get_frame_rate());
438 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(t-adjustment->get_lower()));
439 shadow=Gtk::SHADOW_IN;
444 x=(int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()));
445 shadow=Gtk::SHADOW_OUT;
450 widget.get_style()->paint_diamond(
451 Glib::RefPtr<Gdk::Window>::cast_static(window),
457 area.get_x()+x-area.get_height()/4,
458 area.get_y()+area.get_height()/4,
465 Gdk::Rectangle area(area_);
466 // If the parent of this value node is a dynamic list, then
467 // render the on and off times
468 if(parent_value_node)
470 const int index(property_value_desc().get_value().get_index());
471 const synfig::ValueNode_DynamicList::ListEntry& list_entry(parent_value_node->list[index]);
472 const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(list_entry.timing_info);
473 synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter,next;
476 if(!activepoint_list.empty())
477 is_off=!activepoint_list.front().state;
482 for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++)
484 x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower())));
486 if(x>area.get_width())x=area.get_width();
488 bool status_at_time=0;
489 if(next!=activepoint_list.end())
491 status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0);
494 status_at_time=!list_entry.status_at_time(Time::end());
496 if(!is_off && status_at_time)
502 if(is_off && !status_at_time)
504 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
509 if(!is_off && iter!=activepoint_list.end() && next->state==false && iter->state==false)
514 else if(is_off && next!=activepoint_list.end() && iter->state==false && next->state==true)
516 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), x-xstart, area.get_height());
519 else if(is_off && iter!=activepoint_list.end() && iter->state==true)
521 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), prevx-xstart, area.get_height());
528 if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper())
533 gc->set_rgb_fg_color(activepoint_color[iter->state]);
534 window->draw_rectangle(gc, true, area.get_x()+x-w/2, area.get_y(), w, area.get_height());
540 window->draw_rectangle(inactive_gc, true, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_height());
544 // Render a line that defines the current tick in time
546 gc->set_rgb_fg_color(curr_time_color);
548 const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower())));
550 if(adjustment->get_value()>=adjustment->get_lower() && adjustment->get_value()<adjustment->get_upper())
551 window->draw_rectangle(gc, true, area.get_x()+x, area.get_y(), 1, area.get_height());
555 synfig::ValueNode_Animated::WaypointList::iterator
556 CellRenderer_TimeTrack::find_waypoint(const synfig::Time& /*t*/,const synfig::Time& scope)
558 synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
560 Time nearest(Time::end());
562 synfig::ValueNode_Animated::WaypointList::iterator iter,ret;
567 iter=value_node->waypoint_list().begin();
568 iter!=value_node->waypoint_list().end();
572 Time val=abs(iter->get_time()-selected_time);
580 if(nearest!=Time::end() && nearest<scope)
589 CellRenderer_TimeTrack::activate_vfunc(
591 Gtk::Widget& /*widget*/,
592 const Glib::ustring& treepath,
593 const Gdk::Rectangle& /*background_area*/,
594 const Gdk::Rectangle& cell_area,
595 Gtk::CellRendererState /*flags*/)
598 synfig::ValueNode_Animated::WaypointList::iterator iter;
599 Time nearest=1000000000;
600 Gtk::Adjustment *adjustment=get_adjustment();
602 // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(property_value_desc().get_value().get_value_node().get());
604 synfig::Canvas::Handle canvas(get_canvas());
606 synfig::ValueNode_DynamicList *parent_value_node(0);
607 if(property_value_desc().get_value().parent_is_value_node())
608 parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get());
614 case GDK_MOTION_NOTIFY:
615 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();
619 Gdk::ModifierType mod;
620 Gdk::Event(event).get_state(mod);
624 case GDK_BUTTON_PRESS:
625 case GDK_BUTTON_RELEASE:
627 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();
629 Gdk::ModifierType mod;
630 Gdk::Event(event).get_state(mod);
635 actual_time = curr_time;
637 curr_time=curr_time.round(canvas->rend_desc().get_frame_rate());
638 selected_time=curr_time;
640 Time pixel_width((adjustment->get_upper()-adjustment->get_lower())/cell_area.get_width());
644 case GDK_BUTTON_PRESS:
645 //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();
647 //Deal with time point selection, but only if they aren't involved in the insanity...
648 if(/*!value_node && */event->button.button == 1)
652 /*! UI specification:
654 When nothing is selected, clicking on a point in either normal mode or
655 additive mode will select the time point closest to the click.
656 Subtractive click will do nothing
658 When things are already selected, clicking on a selected point does
659 nothing (in both normal and add mode). Add mode clicking on an unselected
660 point adds it to the set. Normal clicking on an unselected point will
661 select only that one time point. Subtractive clicking on any point
662 will remove it from the the set if it is included.
665 synfigapp::ValueDesc valdesc = property_value_desc().get_value();
666 const Node::time_set *tset = get_times_from_vdesc(valdesc);
667 const synfig::Time time_offset = get_time_offset_from_vdesc(valdesc);
669 bool clickfound = tset && get_closest_time(*tset,actual_time+time_offset,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 should 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);
750 synfig::Time time_offset = get_time_offset_from_vdesc(valdesc);
752 bool clickfound = tset && get_closest_time(*tset,actual_time+time_offset,pixel_width*cell_area.get_height(),stime);
754 etl::handle<synfig::Node> node;
755 if(valdesc.get_value(stime).get_type()==ValueBase::TYPE_CANVAS)
757 node=Canvas::Handle(valdesc.get_value(stime).get(Canvas::Handle()));
759 else //if(valdesc.is_value_node())
761 node=valdesc.get_value_node();
764 if(clickfound && node)
766 show_timepoint_menu(node, stime, time_offset, actual_time+time_offset<stime?Waypoint::SIDE_LEFT:Waypoint::SIDE_RIGHT);
771 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:
779 //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();
782 /*if(event->button.button==3 && selection)
784 signal_waypoint_clicked_cellrenderer_(path,*selected_waypoint,event->button.button-1);
789 //Time point stuff...
790 if(event->button.button == 1)
792 bool delmode = (mode & DELETE_MASK) && !(mode & COPY_MASK);
793 deltatime = actual_time - actual_dragtime;
794 if(sel_times.size() != 0 && (delmode || !deltatime.is_equal(Time(0))))
796 synfigapp::Action::ParamList param_list;
797 param_list.add("canvas",canvas_interface()->get_canvas());
798 param_list.add("canvas_interface",canvas_interface());
800 if(sel_value.get_value_type() == synfig::ValueBase::TYPE_CANVAS)
802 param_list.add("addcanvas",sel_value.get_value().get(Canvas::Handle()));
805 param_list.add("addvaluedesc",sel_value);
809 std::set<synfig::Time>::iterator i = sel_times.begin(), end = sel_times.end();
812 param_list.add("addtime",*i);
814 newset.insert((*i + deltatime).round(get_canvas()->rend_desc().get_frame_rate()));
818 param_list.add("deltatime",deltatime);
819 // param_list.add("time",canvas_interface()->get_time());
821 if(mode & COPY_MASK) //copy
823 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
824 ->process_action("timepoint_copy", param_list);
825 }else if(delmode) //DELETE
827 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
828 ->process_action("timepoint_delete", param_list);
831 etl::handle<studio::Instance>::cast_static(canvas_interface()->get_instance())
832 ->process_action("timepoint_move", param_list);
835 //now replace all the selected with the new selected
842 /*if(value_node && selection)
844 if(selected_time==drag_time && event->button.button!=3)
845 signal_waypoint_clicked_cellrenderer_(path,*selected_waypoint,event->button.button-1);
847 if(event->button.button==1)
849 synfig::Waypoint waypoint(*selected_waypoint);
850 Time newtime((waypoint.get_time()+(selected_time-drag_time)).round(canvas->rend_desc().get_frame_rate()));
851 if(waypoint.get_time()!=newtime)
853 waypoint.set_time(newtime);
854 signal_waypoint_changed_(waypoint,value_node);
860 // selected_time=iter->time;
861 //selected_time=iter->get_time();
865 //std::cerr<<"unknown event type "<<event->type<<std::endl;
877 // The following three functions don't get documented correctly by
878 // doxygen 1.5.[23] because of a bug with any function whose name
879 // begins with 'property'. Fixed in doxygen 1.5.4 apparently. See
880 // http://bugzilla.gnome.org/show_bug.cgi?id=471185 .
881 Glib::PropertyProxy<synfigapp::ValueDesc>
882 CellRenderer_TimeTrack::property_value_desc()
884 return Glib::PropertyProxy<synfigapp::ValueDesc>(this,"value_desc");
887 Glib::PropertyProxy<synfig::Canvas::Handle>
888 CellRenderer_TimeTrack::property_canvas()
890 return Glib::PropertyProxy<synfig::Canvas::Handle>(this,"canvas");
893 Glib::PropertyProxy<Gtk::Adjustment* >
894 CellRenderer_TimeTrack::property_adjustment()
896 return Glib::PropertyProxy<Gtk::Adjustment* >(this,"adjustment");
900 CellRenderer_TimeTrack::set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface> h)
902 canvas_interface_ = h;
906 CellRenderer_TimeTrack::show_timepoint_menu(const etl::handle<synfig::Node>& node, const synfig::Time& time, const synfig::Time& time_offset, Waypoint::Side side)
908 signal_waypoint_clicked_cellrenderer_(node,time,time_offset,2,side);