Add actions (add to group, ,remove from group, export, un-export, set layer descripti...
[synfig.git] / synfig-studio / src / gtkmm / state_normal.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file state_normal.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **      Copyright (c) 2009 Nikita Kitaev
11 **
12 **      This package is free software; you can redistribute it and/or
13 **      modify it under the terms of the GNU General Public License as
14 **      published by the Free Software Foundation; either version 2 of
15 **      the License, or (at your option) any later version.
16 **
17 **      This package is distributed in the hope that it will be useful,
18 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **      General Public License for more details.
21 **      \endlegal
22 */
23 /* ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #include <gtkmm/dialog.h>
35 #include <gtkmm/entry.h>
36
37 #include <synfig/valuenode_animated.h>
38 #include <synfig/valuenode_blinecalcvertex.h>
39 #include <synfig/valuenode_composite.h>
40 #include <synfig/valuenode_const.h>
41 #include <synfig/valuenode_dynamiclist.h>
42 #include <synfigapp/action_system.h>
43
44 #include "state_normal.h"
45 #include "canvasview.h"
46 #include "workarea.h"
47 #include "app.h"
48
49 #include <synfigapp/action.h>
50 #include "event_mouse.h"
51 #include "event_layerclick.h"
52 #include "toolbox.h"
53 #include "dialog_tooloptions.h"
54 #include <gtkmm/optionmenu.h>
55 #include "duck.h"
56 #include <synfig/angle.h>
57 #include <synfigapp/main.h>
58
59 #include "general.h"
60 #endif
61
62 /* === U S I N G =========================================================== */
63
64 using namespace std;
65 using namespace etl;
66 using namespace synfig;
67 using namespace studio;
68
69 /* === M A C R O S ========================================================= */
70
71 #ifndef EPSILON
72 #define EPSILON 0.0000001
73 #endif
74
75 /* === G L O B A L S ======================================================= */
76
77 StateNormal studio::state_normal;
78
79 /* === C L A S S E S & S T R U C T S ======================================= */
80
81 class DuckDrag_Combo : public DuckDrag_Base
82 {
83         synfig::Vector last_move;
84         synfig::Vector drag_offset;
85         synfig::Vector center;
86         synfig::Vector snap;
87
88         synfig::Angle original_angle;
89         synfig::Real original_mag;
90
91         std::vector<synfig::Vector> last_;
92         std::vector<synfig::Vector> positions;
93
94
95         bool bad_drag;
96         bool move_only;
97
98 public:
99         etl::handle<CanvasView> canvas_view_;
100         bool scale;
101         bool rotate;
102         bool constrain;
103         DuckDrag_Combo();
104         void begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& begin);
105         bool end_duck_drag(Duckmatic* duckmatic);
106         void duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector);
107
108         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
109 };
110
111
112 class studio::StateNormal_Context : public sigc::trackable
113 {
114         etl::handle<CanvasView> canvas_view_;
115
116         synfigapp::Settings& settings;
117
118         sigc::connection keypress_connect;
119         sigc::connection keyrelease_connect;
120
121         etl::handle<DuckDrag_Combo> duck_dragger_;
122
123         Gtk::Table options_table;
124
125         Gtk::CheckButton checkbutton_rotate;
126         Gtk::CheckButton checkbutton_scale;
127         Gtk::CheckButton checkbutton_constrain;
128
129 public:
130
131         bool get_rotate_flag()const { return checkbutton_rotate.get_active(); }
132         void set_rotate_flag(bool x) { checkbutton_rotate.set_active(x); refresh_rotate_flag(); }
133         void refresh_rotate_flag() { if(duck_dragger_)duck_dragger_->rotate=get_rotate_flag(); }
134
135         bool get_scale_flag()const { return checkbutton_scale.get_active(); }
136         void set_scale_flag(bool x) { checkbutton_scale.set_active(x); refresh_scale_flag(); }
137         void refresh_scale_flag() { if(duck_dragger_)duck_dragger_->scale=get_scale_flag(); }
138
139         bool get_constrain_flag()const { return checkbutton_constrain.get_active(); }
140         void set_constrain_flag(bool x) { checkbutton_constrain.set_active(x); refresh_constrain_flag(); }
141         void refresh_constrain_flag() { if(duck_dragger_)duck_dragger_->constrain=get_constrain_flag(); }
142
143         StateNormal_Context(CanvasView* canvas_view);
144
145         ~StateNormal_Context();
146
147         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
148         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
149         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
150         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
151
152         void load_settings();
153         void save_settings();
154
155         bool key_pressed(GdkEventKey *event);
156         bool key_released(GdkEventKey *event);
157
158         Smach::event_result event_stop_handler(const Smach::event& x);
159         Smach::event_result event_refresh_handler(const Smach::event& x);
160         Smach::event_result event_refresh_ducks_handler(const Smach::event& x);
161         Smach::event_result event_undo_handler(const Smach::event& x);
162         Smach::event_result event_redo_handler(const Smach::event& x);
163         Smach::event_result event_mouse_button_down_handler(const Smach::event& x);
164         Smach::event_result event_multiple_ducks_clicked_handler(const Smach::event& x);
165         Smach::event_result event_refresh_tool_options(const Smach::event& x);
166         void refresh_tool_options();
167         Smach::event_result event_layer_click(const Smach::event& x);
168
169
170 };      // END of class StateNormal_Context
171
172 /* === M E T H O D S ======================================================= */
173
174 StateNormal::StateNormal():
175         Smach::state<StateNormal_Context>("normal")
176 {
177         insert(event_def(EVENT_STOP,&StateNormal_Context::event_stop_handler));
178         insert(event_def(EVENT_REFRESH,&StateNormal_Context::event_refresh_handler));
179         insert(event_def(EVENT_REFRESH_DUCKS,&StateNormal_Context::event_refresh_ducks_handler));
180         insert(event_def(EVENT_UNDO,&StateNormal_Context::event_undo_handler));
181         insert(event_def(EVENT_REDO,&StateNormal_Context::event_redo_handler));
182         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateNormal_Context::event_mouse_button_down_handler));
183         insert(event_def(EVENT_WORKAREA_MULTIPLE_DUCKS_CLICKED,&StateNormal_Context::event_multiple_ducks_clicked_handler));
184         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateNormal_Context::event_refresh_tool_options));
185         insert(event_def(EVENT_WORKAREA_LAYER_CLICKED,&StateNormal_Context::event_layer_click));
186
187 }
188
189 StateNormal::~StateNormal()
190 {
191 }
192
193 void
194 StateNormal_Context::load_settings()
195 {
196         String value;
197
198         if(settings.get_value("normal.rotate",value) && value=="1")
199                 set_rotate_flag(true);
200         else
201                 set_rotate_flag(false);
202
203         if(settings.get_value("normal.scale",value) && value=="1")
204                 set_scale_flag(true);
205         else
206                 set_scale_flag(false);
207
208         if(settings.get_value("normal.constrain",value) && value=="1")
209                 set_constrain_flag(true);
210         else
211                 set_constrain_flag(false);
212
213 }
214
215 void
216 StateNormal_Context::save_settings()
217 {
218         settings.set_value("normal.rotate",get_rotate_flag()?"1":"0");
219         settings.set_value("normal.scale",get_scale_flag()?"1":"0");
220         settings.set_value("normal.constrain",get_constrain_flag()?"1":"0");
221 }
222
223 StateNormal_Context::StateNormal_Context(CanvasView* canvas_view):
224         canvas_view_(canvas_view),
225         settings(synfigapp::Main::get_selected_input_device()->settings()),
226         duck_dragger_(new DuckDrag_Combo()),
227         checkbutton_rotate(_("Rotate (Ctrl)")),
228         checkbutton_scale(_("Scale (Alt)")),
229         checkbutton_constrain(_("Constrain (Shift)"))
230 {
231         duck_dragger_->canvas_view_=get_canvas_view();
232
233         // Set up the tool options dialog
234         options_table.attach(*manage(new Gtk::Label(_("Normal Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
235         options_table.attach(checkbutton_rotate,                                                        0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
236         options_table.attach(checkbutton_scale,                                                 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
237         options_table.attach(checkbutton_constrain,                                                     0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
238
239         checkbutton_rotate.signal_toggled().connect(sigc::mem_fun(*this,&StateNormal_Context::refresh_rotate_flag));
240         checkbutton_scale.signal_toggled().connect(sigc::mem_fun(*this,&StateNormal_Context::refresh_scale_flag));
241         checkbutton_constrain.signal_toggled().connect(sigc::mem_fun(*this,&StateNormal_Context::refresh_constrain_flag));
242
243
244         options_table.show_all();
245         refresh_tool_options();
246         //App::dialog_tool_options->set_widget(options_table);
247         //App::dialog_tool_options->present();
248
249         get_work_area()->set_allow_layer_clicks(true);
250         get_work_area()->set_duck_dragger(duck_dragger_);
251
252         keypress_connect=get_work_area()->signal_key_press_event().connect(sigc::mem_fun(*this,&StateNormal_Context::key_pressed),false);
253         keyrelease_connect=get_work_area()->signal_key_release_event().connect(sigc::mem_fun(*this,&StateNormal_Context::key_released),false);
254
255         //these will segfault
256 //      get_work_area()->set_cursor(Gdk::CROSSHAIR);
257 //      get_work_area()->reset_cursor();
258
259         App::toolbox->refresh();
260
261         load_settings();
262         refresh_scale_flag();
263 }
264
265 bool
266 StateNormal_Context::key_pressed(GdkEventKey *event)
267 {
268         switch(event->keyval)
269         {
270                 case GDK_Control_L:
271                 case GDK_Control_R:
272                         set_rotate_flag(true);
273                         break;
274                 case GDK_Alt_L:
275                 case GDK_Alt_R:
276                         set_scale_flag(true);
277                         break;
278                 case GDK_Shift_L:
279                 case GDK_Shift_R:
280                         set_constrain_flag(true);
281                         break;
282                 default:
283                         break;
284         }
285         return false; //Pass on the event to other handlers, just in case
286 }
287
288 bool
289 StateNormal_Context::key_released(GdkEventKey *event)
290 {
291         switch(event->keyval)
292         {
293                 case GDK_Control_L:
294                 case GDK_Control_R:
295                         set_rotate_flag(false);
296                         break;
297                 case GDK_Alt_L:
298                 case GDK_Alt_R:
299                         set_scale_flag(false);
300                         break;
301                 case GDK_Shift_L:
302                 case GDK_Shift_R:
303                         set_constrain_flag(false);
304                         break;
305                 default:
306                         break;
307         }
308         return false; //Pass on the event to other handlers
309 }
310
311 void
312 StateNormal_Context::refresh_tool_options()
313 {
314         App::dialog_tool_options->clear();
315         App::dialog_tool_options->set_widget(options_table);
316         App::dialog_tool_options->set_local_name(_("Normal Tool"));
317         App::dialog_tool_options->set_name("normal");
318 }
319
320
321
322 StateNormal_Context::~StateNormal_Context()
323 {
324         save_settings();
325
326         get_work_area()->clear_duck_dragger();
327         get_work_area()->reset_cursor();
328
329         keypress_connect.disconnect();
330         keyrelease_connect.disconnect();
331
332         App::dialog_tool_options->clear();
333
334         App::toolbox->refresh();
335 }
336
337 DuckDrag_Combo::DuckDrag_Combo():
338         scale(false),
339         rotate(false),
340         constrain(false) // Lock aspect for scale; smooth move for translate
341 {
342 }
343
344 void
345 DuckDrag_Combo::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
346 {
347         last_move=Vector(1,1);
348
349         const DuckList selected_ducks(duckmatic->get_selected_ducks());
350         DuckList::const_iterator iter;
351
352         bad_drag=false;
353
354                 drag_offset=duckmatic->find_duck(offset)->get_trans_point();
355
356                 //snap=drag_offset-duckmatic->snap_point_to_grid(drag_offset);
357                 //snap=offset-drag_offset_;
358                 snap=Vector(0,0);
359
360         // Calculate center
361         Point vmin(100000000,100000000);
362         Point vmax(-100000000,-100000000);
363         //std::set<etl::handle<Duck> >::iterator iter;
364         positions.clear();
365         int i;
366         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
367         {
368                 Point p((*iter)->get_trans_point());
369                 vmin[0]=min(vmin[0],p[0]);
370                 vmin[1]=min(vmin[1],p[1]);
371                 vmax[0]=max(vmax[0],p[0]);
372                 vmax[1]=max(vmax[1],p[1]);
373                 positions.push_back(p);
374         }
375         center=(vmin+vmax)*0.5;
376         if((vmin-vmax).mag()<=EPSILON)
377                 move_only=true;
378         else
379                 move_only=false;
380
381
382         synfig::Vector vect(offset-center);
383         original_angle=Angle::tan(vect[1],vect[0]);
384         original_mag=vect.mag();
385 }
386
387
388 void
389 DuckDrag_Combo::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
390 {
391         if (!duckmatic) return;
392
393         if(bad_drag)
394                 return;
395
396         //Override axis lock set in workarea when holding down the shift key
397         if (!move_only && (scale || rotate))
398                 duckmatic->set_axis_lock(false);
399
400         synfig::Vector vect;
401         if (move_only || (!scale && !rotate))
402                 vect= duckmatic->snap_point_to_grid(vector)-drag_offset+snap;
403         else
404                 vect= duckmatic->snap_point_to_grid(vector)-center+snap;
405
406         last_move=vect;
407
408         const DuckList selected_ducks(duckmatic->get_selected_ducks());
409         DuckList::const_iterator iter;
410
411         Time time(duckmatic->get_time());
412
413         int i;
414         if( move_only || (!scale && !rotate) )
415         {
416                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
417                 {
418                         if((*iter)->get_type()==Duck::TYPE_VERTEX || (*iter)->get_type()==Duck::TYPE_POSITION)
419                                 (*iter)->set_trans_point(positions[i]+vect, time);
420                 }
421                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
422                 {
423                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)
424                                 (*iter)->set_trans_point(positions[i]+vect, time);
425                 }
426         }
427
428         if (rotate)
429         {
430                 Angle::deg angle(Angle::tan(vect[1],vect[0]));
431                 angle=original_angle-angle;
432                 if (constrain)
433                 {
434                         float degrees = angle.get()/15;
435                         angle= Angle::deg (degrees>0?std::floor(degrees)*15:std::ceil(degrees)*15);
436                 }
437                 Real mag(vect.mag()/original_mag);
438                 Real sine(Angle::sin(angle).get());
439                 Real cosine(Angle::cos(angle).get());
440
441                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
442                 {
443                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
444
445                         Vector x(positions[i]-center),p;
446
447                         p[0]=cosine*x[0]+sine*x[1];
448                         p[1]=-sine*x[0]+cosine*x[1];
449                         if(scale)p*=mag;
450                         p+=center;
451                         (*iter)->set_trans_point(p, time);
452                 }
453                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
454                 {
455                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
456
457                         Vector x(positions[i]-center),p;
458
459                         p[0]=cosine*x[0]+sine*x[1];
460                         p[1]=-sine*x[0]+cosine*x[1];
461                         if(scale)p*=mag;
462                         p+=center;
463                         (*iter)->set_trans_point(p, time);
464                 }
465         } else if (scale)
466         {
467                 if(!constrain)
468                 {
469                         if(abs(drag_offset[0]-center[0])>EPSILON)
470                                 vect[0]/=drag_offset[0]-center[0];
471                         else
472                                 vect[0]=1;
473                         if(abs(drag_offset[1]-center[1])>EPSILON)
474                                 vect[1]/=drag_offset[1]-center[1];
475                         else
476                                 vect[1]=1;
477                         }
478                 else
479                 {
480                         //vect[0]=vect[1]=vect.mag()*0.707106781;
481                         Real amount(vect.mag()/(drag_offset-center).mag());
482                         vect[0]=vect[1]=amount;
483                 }
484
485                 if(vect[0]<EPSILON && vect[0]>-EPSILON)
486                         vect[0]=1;
487                 if(vect[1]<EPSILON && vect[1]>-EPSILON)
488                         vect[1]=1;
489
490                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
491                 {
492                         if(((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
493
494                         Vector p(positions[i]-center);
495
496                         p[0]*=vect[0];
497                         p[1]*=vect[1];
498                         p+=center;
499                         (*iter)->set_trans_point(p, time);
500                 }
501                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
502                 {
503                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
504
505                         Vector p(positions[i]-center);
506
507                         p[0]*=vect[0];
508                         p[1]*=vect[1];
509                         p+=center;
510                         (*iter)->set_trans_point(p, time);
511                 }
512         }
513
514         // then patch up the tangents for the vertices we've moved
515         duckmatic->update_ducks();
516
517         last_move=vect;
518 }
519
520 bool
521 DuckDrag_Combo::end_duck_drag(Duckmatic* duckmatic)
522 {
523         if(bad_drag)return false;
524
525         //synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Rotate Ducks"));
526
527         if((last_move-Vector(1,1)).mag()>0.0001)
528         {
529                 duckmatic->signal_edited_selected_ducks();
530                 return true;
531         }
532         else
533         {
534                 duckmatic->signal_user_click_selected_ducks(0);
535                 return false;
536         }
537 }
538
539 Smach::event_result
540 StateNormal_Context::event_refresh_tool_options(const Smach::event& /*x*/)
541 {
542         refresh_tool_options();
543         return Smach::RESULT_ACCEPT;
544 }
545
546 Smach::event_result
547 StateNormal_Context::event_stop_handler(const Smach::event& /*x*/)
548 {
549         // synfig::info("STATE NORMAL: Received Stop Event");
550         canvas_view_->stop();
551         return Smach::RESULT_ACCEPT;
552 }
553
554 Smach::event_result
555 StateNormal_Context::event_refresh_handler(const Smach::event& /*x*/)
556 {
557         // synfig::info("STATE NORMAL: Received Refresh Event");
558         canvas_view_->rebuild_tables();
559         canvas_view_->work_area->queue_render_preview();
560         return Smach::RESULT_ACCEPT;
561 }
562
563 Smach::event_result
564 StateNormal_Context::event_refresh_ducks_handler(const Smach::event& /*x*/)
565 {
566         // synfig::info("STATE NORMAL: Received Refresh Ducks");
567         canvas_view_->queue_rebuild_ducks();
568         return Smach::RESULT_ACCEPT;
569 }
570
571 Smach::event_result
572 StateNormal_Context::event_undo_handler(const Smach::event& /*x*/)
573 {
574         // synfig::info("STATE NORMAL: Received Undo Event");
575         canvas_view_->get_instance()->undo();
576         return Smach::RESULT_ACCEPT;
577 }
578
579 Smach::event_result
580 StateNormal_Context::event_redo_handler(const Smach::event& /*x*/)
581 {
582         // synfig::info("STATE NORMAL: Received Redo Event");
583         canvas_view_->get_instance()->redo();
584         return Smach::RESULT_ACCEPT;
585 }
586
587 Smach::event_result
588 StateNormal_Context::event_mouse_button_down_handler(const Smach::event& x)
589 {
590         // synfig::info("STATE NORMAL: Received mouse button down Event");
591
592         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
593
594         switch(event.button)
595         {
596         case BUTTON_RIGHT:
597                 canvas_view_->popup_main_menu();
598                 return Smach::RESULT_ACCEPT;
599         default:
600                 return Smach::RESULT_OK;
601         }
602 }
603
604 Smach::event_result
605 StateNormal_Context::event_layer_click(const Smach::event& x)
606 {
607         const EventLayerClick& event(*reinterpret_cast<const EventLayerClick*>(&x));
608
609         if(event.layer)
610         {
611                 // synfig::info("STATE NORMAL: Received layer click Event, \"%s\"",event.layer->get_name().c_str());
612         }
613         else
614         {
615                 // synfig::info("STATE NORMAL: Received layer click Event with an empty layer.");
616         }
617
618         switch(event.button)
619         {
620         case BUTTON_LEFT:
621                 if(!(event.modifier&Gdk::CONTROL_MASK))
622                         canvas_view_->get_selection_manager()->clear_selected_layers();
623                 if(event.layer)
624                 {
625                         std::list<Layer::Handle> layer_list(canvas_view_->get_selection_manager()->get_selected_layers());
626                         std::set<Layer::Handle> layers(layer_list.begin(),layer_list.end());
627                         if(layers.count(event.layer))
628                         {
629                                 layers.erase(event.layer);
630                                 layer_list=std::list<Layer::Handle>(layers.begin(),layers.end());
631                                 canvas_view_->get_selection_manager()->clear_selected_layers();
632                                 canvas_view_->get_selection_manager()->set_selected_layers(layer_list);
633                         }
634                         else
635                         {
636                                 canvas_view_->get_selection_manager()->set_selected_layer(event.layer);
637                         }
638                 }
639                 return Smach::RESULT_ACCEPT;
640         case BUTTON_RIGHT:
641                 canvas_view_->popup_layer_menu(event.layer);
642                 return Smach::RESULT_ACCEPT;
643         default:
644                 return Smach::RESULT_OK;
645         }
646 }
647
648 /*
649 void
650 StateNormal_Context::edit_several_waypoints(std::list<synfigapp::ValueDesc> value_desc_list)
651 {
652         Gtk::Dialog dialog(
653                 "Edit Multiple Waypoints",              // Title
654                 true,           // Modal
655                 true            // use_separator
656         );
657
658         Widget_WaypointModel widget_waypoint_model;
659         widget_waypoint_model.show();
660
661         dialog.get_vbox()->pack_start(widget_waypoint_model);
662
663
664         dialog.add_button(Gtk::StockID("gtk-apply"),1);
665         dialog.add_button(Gtk::StockID("gtk-cancel"),0);
666         dialog.show();
667
668         if(dialog.run()==0)
669                 return;
670         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Set Waypoints"));
671
672         std::list<synfigapp::ValueDesc>::iterator iter;
673         for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
674         {
675                 synfigapp::ValueDesc value_desc(*iter);
676
677                 if(!value_desc.is_valid())
678                         continue;
679
680                 ValueNode_Animated::Handle value_node;
681
682                 // If this value isn't a ValueNode_Animated, but
683                 // it is somewhat constant, then go ahead and convert
684                 // it to a ValueNode_Animated.
685                 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
686                 {
687                         ValueBase value;
688                         if(value_desc.is_value_node())
689                                 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
690                         else
691                                 value=value_desc.get_value();
692
693                         value_node=ValueNode_Animated::create(value,get_canvas()->get_time());
694
695                         synfigapp::Action::Handle action;
696
697                         if(!value_desc.is_value_node())
698                         {
699                                 action=synfigapp::Action::create("ValueDescConnect");
700                                 action->set_param("dest",value_desc);
701                                 action->set_param("src",ValueNode::Handle(value_node));
702                         }
703                         else
704                         {
705                                 action=synfigapp::Action::create("ValueNodeReplace");
706                                 action->set_param("dest",value_desc.get_value_node());
707                                 action->set_param("src",ValueNode::Handle(value_node));
708                         }
709
710                         action->set_param("canvas",get_canvas());
711                         action->set_param("canvas_interface",get_canvas_interface());
712
713
714                         if(!get_canvas_interface()->get_instance()->perform_action(action))
715                         {
716                                 get_canvas_view()->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
717                                 group.cancel();
718                                 return;
719                         }
720                 }
721                 else
722                 {
723                         if(value_desc.is_value_node())
724                                 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
725                 }
726
727
728                 if(value_node)
729                 {
730
731                         synfigapp::Action::Handle action(synfigapp::Action::create("WaypointSetSmart"));
732
733                         if(!action)
734                         {
735                                 get_canvas_view()->get_ui_interface()->error(_("Unable to find WaypointSetSmart action"));
736                                 group.cancel();
737                                 return;
738                         }
739
740
741                         action->set_param("canvas",get_canvas());
742                         action->set_param("canvas_interface",get_canvas_interface());
743                         action->set_param("value_node",ValueNode::Handle(value_node));
744                         action->set_param("time",get_canvas()->get_time());
745                         action->set_param("model",widget_waypoint_model.get_waypoint_model());
746
747                         if(!get_canvas_interface()->get_instance()->perform_action(action))
748                         {
749                                 get_canvas_view()->get_ui_interface()->error(_("Unable to set a specific waypoint"));
750                                 group.cancel();
751                                 return;
752                         }
753                 }
754                 else
755                 {
756                         //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
757                         //group.cancel();
758                         //return;
759                 }
760
761         }
762 }
763 */
764
765 Smach::event_result
766 StateNormal_Context::event_multiple_ducks_clicked_handler(const Smach::event& /*x*/)
767 {
768         // synfig::info("STATE NORMAL: Received multiple duck click event");
769
770         //const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
771
772         std::list<synfigapp::ValueDesc> value_desc_list;
773
774         // Create a list of value_descs associated with selection
775         const DuckList selected_ducks(get_work_area()->get_selected_ducks());
776         DuckList::const_iterator iter;
777         for(iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
778         {
779                 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
780
781                 if(!value_desc.is_valid())
782                         continue;
783
784                 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
785                 {
786                         value_desc_list.push_back(
787                                 synfigapp::ValueDesc(
788                                         ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
789                                         ,ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
790                                                                ->get_link_index_from_name("point")
791                                 )
792                         );
793                 }
794                 else
795                         value_desc_list.push_back(value_desc);
796         }
797
798         Gtk::Menu *menu=manage(new Gtk::Menu());
799         menu->signal_hide().connect(sigc::bind(sigc::ptr_fun(&delete_widget), menu));
800
801         canvas_view_->get_instance()->make_param_menu(menu,canvas_view_->get_canvas(),value_desc_list);
802
803         /*
804         synfigapp::Action::ParamList param_list;
805         param_list=get_canvas_interface()->generate_param_list(value_desc_list);
806
807         canvas_view_->add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
808
809         menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
810                 sigc::bind(
811                         sigc::mem_fun(
812                                 *this,
813                                 &studio::StateNormal_Context::edit_several_waypoints
814                         ),
815                         value_desc_list
816                 )
817         ));
818         */
819         menu->popup(3,gtk_get_current_event_time());
820
821         return Smach::RESULT_ACCEPT;
822 }
823
824