Change transform tool to read key states rather than events
[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         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         CanvasView* canvas_view_;
115
116         synfigapp::Settings& settings;
117
118         etl::handle<DuckDrag_Combo> duck_dragger_;
119
120         Gtk::Table options_table;
121
122 public:
123
124         bool get_rotate_flag()const { if(duck_dragger_) return duck_dragger_->rotate; else return false; }
125         void set_rotate_flag(bool x) { if(duck_dragger_ && x!=duck_dragger_->rotate) duck_dragger_->rotate=x; }
126
127         bool get_scale_flag()const { if(duck_dragger_) return duck_dragger_->scale; else return false; }
128         void set_scale_flag(bool x) { if(duck_dragger_ && x!=duck_dragger_->scale) duck_dragger_->scale=x; }
129
130         bool get_constrain_flag()const { if(duck_dragger_) return duck_dragger_->constrain; else return false; }
131         void set_constrain_flag(bool x) { if(duck_dragger_ && x!=duck_dragger_->constrain) duck_dragger_->constrain=x; }
132
133         void refresh_cursor();
134
135         StateNormal_Context(CanvasView* canvas_view);
136
137         ~StateNormal_Context();
138
139         CanvasView* get_canvas_view()const{return canvas_view_;}
140         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
141         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
142         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
143
144         void load_settings();
145         void save_settings();
146
147         Smach::event_result event_stop_handler(const Smach::event& x);
148         Smach::event_result event_refresh_handler(const Smach::event& x);
149         Smach::event_result event_refresh_ducks_handler(const Smach::event& x);
150         Smach::event_result event_undo_handler(const Smach::event& x);
151         Smach::event_result event_redo_handler(const Smach::event& x);
152         Smach::event_result event_mouse_button_down_handler(const Smach::event& x);
153         Smach::event_result event_multiple_ducks_clicked_handler(const Smach::event& x);
154         Smach::event_result event_mouse_motion_handler(const Smach::event& x);
155         Smach::event_result event_refresh_tool_options(const Smach::event& x);
156         void refresh_tool_options();
157         Smach::event_result event_layer_click(const Smach::event& x);
158
159
160 };      // END of class StateNormal_Context
161
162 /* === M E T H O D S ======================================================= */
163
164 StateNormal::StateNormal():
165         Smach::state<StateNormal_Context>("normal")
166 {
167         insert(event_def(EVENT_STOP,&StateNormal_Context::event_stop_handler));
168         insert(event_def(EVENT_REFRESH,&StateNormal_Context::event_refresh_handler));
169         insert(event_def(EVENT_REFRESH_DUCKS,&StateNormal_Context::event_refresh_ducks_handler));
170         insert(event_def(EVENT_UNDO,&StateNormal_Context::event_undo_handler));
171         insert(event_def(EVENT_REDO,&StateNormal_Context::event_redo_handler));
172         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateNormal_Context::event_mouse_button_down_handler));
173         insert(event_def(EVENT_WORKAREA_MULTIPLE_DUCKS_CLICKED,&StateNormal_Context::event_multiple_ducks_clicked_handler));
174         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateNormal_Context::event_refresh_tool_options));
175         insert(event_def(EVENT_WORKAREA_MOUSE_MOTION,           &StateNormal_Context::event_mouse_motion_handler));
176         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,      &StateNormal_Context::event_mouse_motion_handler));
177         insert(event_def(EVENT_WORKAREA_LAYER_CLICKED,&StateNormal_Context::event_layer_click));
178
179 }
180
181 StateNormal::~StateNormal()
182 {
183 }
184
185 void StateNormal_Context::refresh_cursor()
186 {
187         // Check the current state and return when applicable
188         synfig::String sname;
189         sname=get_canvas_view()->get_smach().get_state_name();
190         if (sname=="smooth_move"||sname=="zoom"||sname=="width" ||
191                 sname=="text"||sname=="stroke"||sname=="star"||sname=="sketch"||
192                 sname=="scale"||sname=="zoom"||sname=="rotate"||sname=="rectangle"||
193                 sname=="polygon"||sname=="gradient"||sname=="fill"||sname=="draw"||
194                 sname=="circle")
195                         return;
196
197         // Change the cursor based on key flags
198         if(get_rotate_flag() && !get_scale_flag())
199         {
200                 get_work_area()->set_cursor(Gdk::EXCHANGE);
201                 return;
202         }
203         if(!get_rotate_flag() && get_scale_flag())
204         {
205                 get_work_area()->set_cursor(Gdk::SIZING);
206                 return;
207         }
208         if(get_rotate_flag() && get_scale_flag())
209         {
210                 get_work_area()->set_cursor(Gdk::CROSSHAIR);
211                 return;
212         }
213         // If we are in BLine state and there is not key pressed return to
214         // the bline cursor.
215         if (sname=="bline")
216         {
217                 get_work_area()->set_cursor(Gdk::CROSSHAIR);
218                 return;
219         }
220         // Default cursor for Transform tool
221         get_work_area()->set_cursor(Gdk::ARROW);
222
223 }
224
225 void
226 StateNormal_Context::load_settings()
227 {
228         String value;
229
230         if(settings.get_value("normal.rotate",value) && value=="1")
231                 set_rotate_flag(true);
232         else
233                 set_rotate_flag(false);
234
235         if(settings.get_value("normal.scale",value) && value=="1")
236                 set_scale_flag(true);
237         else
238                 set_scale_flag(false);
239
240         if(settings.get_value("normal.constrain",value) && value=="1")
241                 set_constrain_flag(true);
242         else
243                 set_constrain_flag(false);
244
245 }
246
247 void
248 StateNormal_Context::save_settings()
249 {
250         settings.set_value("normal.rotate",get_rotate_flag()?"1":"0");
251         settings.set_value("normal.scale",get_scale_flag()?"1":"0");
252         settings.set_value("normal.constrain",get_constrain_flag()?"1":"0");
253 }
254
255 StateNormal_Context::StateNormal_Context(CanvasView* canvas_view):
256         canvas_view_(canvas_view),
257         settings(synfigapp::Main::get_selected_input_device()->settings()),
258         duck_dragger_(new DuckDrag_Combo())
259 {
260         duck_dragger_->canvas_view_=get_canvas_view();
261
262         // Set up the tool options dialog
263         options_table.attach(*manage(new Gtk::Label(_("Transform Tool"))),      0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
264         options_table.attach(*manage(new Gtk::Label(_("Ctrl to rotate"), Gtk::ALIGN_LEFT)),     0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
265         options_table.attach(*manage(new Gtk::Label(_("Alt to scale"), Gtk::ALIGN_LEFT)),       0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
266         options_table.attach(*manage(new Gtk::Label(_("Shift to constrain"), Gtk::ALIGN_LEFT)), 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
267
268         options_table.show_all();
269         refresh_tool_options();
270         //App::dialog_tool_options->set_widget(options_table);
271         //App::dialog_tool_options->present();
272
273         get_work_area()->set_allow_layer_clicks(true);
274         get_work_area()->set_duck_dragger(duck_dragger_);
275
276         //these will segfault
277 //      get_work_area()->set_cursor(Gdk::CROSSHAIR);
278 //      get_work_area()->reset_cursor();
279
280         App::toolbox->refresh();
281
282         load_settings();
283 }
284
285 void
286 StateNormal_Context::refresh_tool_options()
287 {
288         App::dialog_tool_options->clear();
289         App::dialog_tool_options->set_widget(options_table);
290         App::dialog_tool_options->set_local_name(_("Transform Tool"));
291         App::dialog_tool_options->set_name("normal");
292 }
293
294
295
296 StateNormal_Context::~StateNormal_Context()
297 {
298         save_settings();
299
300         get_work_area()->clear_duck_dragger();
301         get_work_area()->reset_cursor();
302
303         App::dialog_tool_options->clear();
304
305         App::toolbox->refresh();
306 }
307
308 DuckDrag_Combo::DuckDrag_Combo():
309         scale(false),
310         rotate(false),
311         constrain(false) // Lock aspect for scale; smooth move for translate
312 {
313 }
314
315 void
316 DuckDrag_Combo::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
317 {
318         last_move=Vector(1,1);
319
320         const DuckList selected_ducks(duckmatic->get_selected_ducks());
321         DuckList::const_iterator iter;
322
323         bad_drag=false;
324
325                 drag_offset=duckmatic->find_duck(offset)->get_trans_point();
326
327                 //snap=drag_offset-duckmatic->snap_point_to_grid(drag_offset);
328                 //snap=offset-drag_offset_;
329                 snap=Vector(0,0);
330
331         // Calculate center
332         Point vmin(100000000,100000000);
333         Point vmax(-100000000,-100000000);
334         //std::set<etl::handle<Duck> >::iterator iter;
335         positions.clear();
336         int i;
337         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
338         {
339                 Point p((*iter)->get_trans_point());
340                 vmin[0]=min(vmin[0],p[0]);
341                 vmin[1]=min(vmin[1],p[1]);
342                 vmax[0]=max(vmax[0],p[0]);
343                 vmax[1]=max(vmax[1],p[1]);
344                 positions.push_back(p);
345         }
346         center=(vmin+vmax)*0.5;
347         if((vmin-vmax).mag()<=EPSILON)
348                 move_only=true;
349         else
350                 move_only=false;
351
352
353         synfig::Vector vect(offset-center);
354         original_angle=Angle::tan(vect[1],vect[0]);
355         original_mag=vect.mag();
356 }
357
358
359 void
360 DuckDrag_Combo::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
361 {
362         if (!duckmatic) return;
363
364         if(bad_drag)
365                 return;
366
367         //Override axis lock set in workarea when holding down the shift key
368         if (!move_only && (scale || rotate))
369                 duckmatic->set_axis_lock(false);
370
371         synfig::Vector vect;
372         if (move_only || (!scale && !rotate))
373                 vect= duckmatic->snap_point_to_grid(vector)-drag_offset+snap;
374         else
375                 vect= duckmatic->snap_point_to_grid(vector)-center+snap;
376
377         last_move=vect;
378
379         const DuckList selected_ducks(duckmatic->get_selected_ducks());
380         DuckList::const_iterator iter;
381
382         Time time(duckmatic->get_time());
383
384         int i;
385         if( move_only || (!scale && !rotate) )
386         {
387                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
388                 {
389                         if((*iter)->get_type()==Duck::TYPE_VERTEX || (*iter)->get_type()==Duck::TYPE_POSITION)
390                                 (*iter)->set_trans_point(positions[i]+vect, time);
391                 }
392                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
393                 {
394                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)
395                                 (*iter)->set_trans_point(positions[i]+vect, time);
396                 }
397         }
398
399         if (rotate)
400         {
401                 Angle::deg angle(Angle::tan(vect[1],vect[0]));
402                 angle=original_angle-angle;
403                 if (constrain)
404                 {
405                         float degrees = angle.get()/15;
406                         angle= Angle::deg (degrees>0?std::floor(degrees)*15:std::ceil(degrees)*15);
407                 }
408                 Real mag(vect.mag()/original_mag);
409                 Real sine(Angle::sin(angle).get());
410                 Real cosine(Angle::cos(angle).get());
411
412                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
413                 {
414                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
415
416                         Vector x(positions[i]-center),p;
417
418                         p[0]=cosine*x[0]+sine*x[1];
419                         p[1]=-sine*x[0]+cosine*x[1];
420                         if(scale)p*=mag;
421                         p+=center;
422                         (*iter)->set_trans_point(p, time);
423                 }
424                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
425                 {
426                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
427
428                         Vector x(positions[i]-center),p;
429
430                         p[0]=cosine*x[0]+sine*x[1];
431                         p[1]=-sine*x[0]+cosine*x[1];
432                         if(scale)p*=mag;
433                         p+=center;
434                         (*iter)->set_trans_point(p, time);
435                 }
436         } else if (scale)
437         {
438                 if(!constrain)
439                 {
440                         if(abs(drag_offset[0]-center[0])>EPSILON)
441                                 vect[0]/=drag_offset[0]-center[0];
442                         else
443                                 vect[0]=1;
444                         if(abs(drag_offset[1]-center[1])>EPSILON)
445                                 vect[1]/=drag_offset[1]-center[1];
446                         else
447                                 vect[1]=1;
448                         }
449                 else
450                 {
451                         //vect[0]=vect[1]=vect.mag()*0.707106781;
452                         Real amount(vect.mag()/(drag_offset-center).mag());
453                         vect[0]=vect[1]=amount;
454                 }
455
456                 if(vect[0]<EPSILON && vect[0]>-EPSILON)
457                         vect[0]=1;
458                 if(vect[1]<EPSILON && vect[1]>-EPSILON)
459                         vect[1]=1;
460
461                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
462                 {
463                         if(((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
464
465                         Vector p(positions[i]-center);
466
467                         p[0]*=vect[0];
468                         p[1]*=vect[1];
469                         p+=center;
470                         (*iter)->set_trans_point(p, time);
471                 }
472                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
473                 {
474                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
475
476                         Vector p(positions[i]-center);
477
478                         p[0]*=vect[0];
479                         p[1]*=vect[1];
480                         p+=center;
481                         (*iter)->set_trans_point(p, time);
482                 }
483         }
484
485         // then patch up the tangents for the vertices we've moved
486         duckmatic->update_ducks();
487
488         last_move=vect;
489 }
490
491 bool
492 DuckDrag_Combo::end_duck_drag(Duckmatic* duckmatic)
493 {
494         if(bad_drag)return false;
495
496         //synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Rotate Ducks"));
497
498         if((last_move-Vector(1,1)).mag()>0.0001)
499         {
500                 duckmatic->signal_edited_selected_ducks();
501                 return true;
502         }
503         else
504         {
505                 duckmatic->signal_user_click_selected_ducks(0);
506                 return false;
507         }
508 }
509
510 Smach::event_result
511 StateNormal_Context::event_refresh_tool_options(const Smach::event& /*x*/)
512 {
513         refresh_tool_options();
514         return Smach::RESULT_ACCEPT;
515 }
516
517 Smach::event_result
518 StateNormal_Context::event_stop_handler(const Smach::event& /*x*/)
519 {
520         // synfig::info("STATE NORMAL: Received Stop Event");
521         canvas_view_->stop();
522         return Smach::RESULT_ACCEPT;
523 }
524
525 Smach::event_result
526 StateNormal_Context::event_refresh_handler(const Smach::event& /*x*/)
527 {
528         // synfig::info("STATE NORMAL: Received Refresh Event");
529         canvas_view_->rebuild_tables();
530         canvas_view_->work_area->queue_render_preview();
531         return Smach::RESULT_ACCEPT;
532 }
533
534 Smach::event_result
535 StateNormal_Context::event_refresh_ducks_handler(const Smach::event& /*x*/)
536 {
537         // synfig::info("STATE NORMAL: Received Refresh Ducks");
538         canvas_view_->queue_rebuild_ducks();
539         return Smach::RESULT_ACCEPT;
540 }
541
542 Smach::event_result
543 StateNormal_Context::event_undo_handler(const Smach::event& /*x*/)
544 {
545         // synfig::info("STATE NORMAL: Received Undo Event");
546         canvas_view_->get_instance()->undo();
547         return Smach::RESULT_ACCEPT;
548 }
549
550 Smach::event_result
551 StateNormal_Context::event_redo_handler(const Smach::event& /*x*/)
552 {
553         // synfig::info("STATE NORMAL: Received Redo Event");
554         canvas_view_->get_instance()->redo();
555         return Smach::RESULT_ACCEPT;
556 }
557
558 Smach::event_result
559 StateNormal_Context::event_mouse_button_down_handler(const Smach::event& x)
560 {
561         // synfig::info("STATE NORMAL: Received mouse button down Event");
562
563         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
564
565         switch(event.button)
566         {
567         case BUTTON_RIGHT:
568                 canvas_view_->popup_main_menu();
569                 return Smach::RESULT_ACCEPT;
570         default:
571                 return Smach::RESULT_OK;
572         }
573 }
574
575 Smach::event_result
576 StateNormal_Context::event_mouse_motion_handler(const Smach::event& x)
577 {
578         // synfig::info("STATE NORMAL: Received mouse button down Event");
579
580         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
581
582         set_rotate_flag(event.modifier&GDK_CONTROL_MASK);
583         set_scale_flag(event.modifier&GDK_MOD1_MASK);
584         set_constrain_flag(event.modifier&GDK_SHIFT_MASK);
585
586         return Smach::RESULT_OK;
587 }
588
589 Smach::event_result
590 StateNormal_Context::event_layer_click(const Smach::event& x)
591 {
592         const EventLayerClick& event(*reinterpret_cast<const EventLayerClick*>(&x));
593
594         if(event.layer)
595         {
596                 // synfig::info("STATE NORMAL: Received layer click Event, \"%s\"",event.layer->get_name().c_str());
597         }
598         else
599         {
600                 // synfig::info("STATE NORMAL: Received layer click Event with an empty layer.");
601         }
602
603         switch(event.button)
604         {
605         case BUTTON_LEFT:
606                 if(!(event.modifier&Gdk::CONTROL_MASK))
607                         canvas_view_->get_selection_manager()->clear_selected_layers();
608                 if(event.layer)
609                 {
610                         std::list<Layer::Handle> layer_list(canvas_view_->get_selection_manager()->get_selected_layers());
611                         std::set<Layer::Handle> layers(layer_list.begin(),layer_list.end());
612                         if(layers.count(event.layer))
613                         {
614                                 layers.erase(event.layer);
615                                 layer_list=std::list<Layer::Handle>(layers.begin(),layers.end());
616                                 canvas_view_->get_selection_manager()->clear_selected_layers();
617                                 canvas_view_->get_selection_manager()->set_selected_layers(layer_list);
618                         }
619                         else
620                         {
621                                 canvas_view_->get_selection_manager()->set_selected_layer(event.layer);
622                         }
623                 }
624                 return Smach::RESULT_ACCEPT;
625         case BUTTON_RIGHT:
626                 canvas_view_->popup_layer_menu(event.layer);
627                 return Smach::RESULT_ACCEPT;
628         default:
629                 return Smach::RESULT_OK;
630         }
631 }
632
633 /*
634 void
635 StateNormal_Context::edit_several_waypoints(std::list<synfigapp::ValueDesc> value_desc_list)
636 {
637         Gtk::Dialog dialog(
638                 "Edit Multiple Waypoints",              // Title
639                 true,           // Modal
640                 true            // use_separator
641         );
642
643         Widget_WaypointModel widget_waypoint_model;
644         widget_waypoint_model.show();
645
646         dialog.get_vbox()->pack_start(widget_waypoint_model);
647
648
649         dialog.add_button(Gtk::StockID("gtk-apply"),1);
650         dialog.add_button(Gtk::StockID("gtk-cancel"),0);
651         dialog.show();
652
653         if(dialog.run()==0)
654                 return;
655         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Set Waypoints"));
656
657         std::list<synfigapp::ValueDesc>::iterator iter;
658         for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
659         {
660                 synfigapp::ValueDesc value_desc(*iter);
661
662                 if(!value_desc.is_valid())
663                         continue;
664
665                 ValueNode_Animated::Handle value_node;
666
667                 // If this value isn't a ValueNode_Animated, but
668                 // it is somewhat constant, then go ahead and convert
669                 // it to a ValueNode_Animated.
670                 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
671                 {
672                         ValueBase value;
673                         if(value_desc.is_value_node())
674                                 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
675                         else
676                                 value=value_desc.get_value();
677
678                         value_node=ValueNode_Animated::create(value,get_canvas()->get_time());
679
680                         synfigapp::Action::Handle action;
681
682                         if(!value_desc.is_value_node())
683                         {
684                                 action=synfigapp::Action::create("ValueDescConnect");
685                                 action->set_param("dest",value_desc);
686                                 action->set_param("src",ValueNode::Handle(value_node));
687                         }
688                         else
689                         {
690                                 action=synfigapp::Action::create("ValueNodeReplace");
691                                 action->set_param("dest",value_desc.get_value_node());
692                                 action->set_param("src",ValueNode::Handle(value_node));
693                         }
694
695                         action->set_param("canvas",get_canvas());
696                         action->set_param("canvas_interface",get_canvas_interface());
697
698
699                         if(!get_canvas_interface()->get_instance()->perform_action(action))
700                         {
701                                 get_canvas_view()->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
702                                 group.cancel();
703                                 return;
704                         }
705                 }
706                 else
707                 {
708                         if(value_desc.is_value_node())
709                                 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
710                 }
711
712
713                 if(value_node)
714                 {
715
716                         synfigapp::Action::Handle action(synfigapp::Action::create("WaypointSetSmart"));
717
718                         if(!action)
719                         {
720                                 get_canvas_view()->get_ui_interface()->error(_("Unable to find WaypointSetSmart action"));
721                                 group.cancel();
722                                 return;
723                         }
724
725
726                         action->set_param("canvas",get_canvas());
727                         action->set_param("canvas_interface",get_canvas_interface());
728                         action->set_param("value_node",ValueNode::Handle(value_node));
729                         action->set_param("time",get_canvas()->get_time());
730                         action->set_param("model",widget_waypoint_model.get_waypoint_model());
731
732                         if(!get_canvas_interface()->get_instance()->perform_action(action))
733                         {
734                                 get_canvas_view()->get_ui_interface()->error(_("Unable to set a specific waypoint"));
735                                 group.cancel();
736                                 return;
737                         }
738                 }
739                 else
740                 {
741                         //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
742                         //group.cancel();
743                         //return;
744                 }
745
746         }
747 }
748 */
749
750 Smach::event_result
751 StateNormal_Context::event_multiple_ducks_clicked_handler(const Smach::event& /*x*/)
752 {
753         // synfig::info("STATE NORMAL: Received multiple duck click event");
754
755         //const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
756
757         std::list<synfigapp::ValueDesc> value_desc_list;
758
759         // Create a list of value_descs associated with selection
760         const DuckList selected_ducks(get_work_area()->get_selected_ducks());
761         DuckList::const_iterator iter;
762         for(iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
763         {
764                 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
765
766                 if(!value_desc.is_valid())
767                         continue;
768
769                 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
770                 {
771                         value_desc_list.push_back(
772                                 synfigapp::ValueDesc(
773                                         ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
774                                         ,ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
775                                                                ->get_link_index_from_name("point")
776                                 )
777                         );
778                 }
779                 else
780                         value_desc_list.push_back(value_desc);
781         }
782
783         Gtk::Menu *menu=manage(new Gtk::Menu());
784         menu->signal_hide().connect(sigc::bind(sigc::ptr_fun(&delete_widget), menu));
785
786         canvas_view_->get_instance()->make_param_menu(menu,canvas_view_->get_canvas(),value_desc_list);
787
788         /*
789         synfigapp::Action::ParamList param_list;
790         param_list=get_canvas_interface()->generate_param_list(value_desc_list);
791
792         canvas_view_->add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
793
794         menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
795                 sigc::bind(
796                         sigc::mem_fun(
797                                 *this,
798                                 &studio::StateNormal_Context::edit_several_waypoints
799                         ),
800                         value_desc_list
801                 )
802         ));
803         */
804         menu->popup(3,gtk_get_current_event_time());
805
806         return Smach::RESULT_ACCEPT;
807 }
808
809