Better functionality for ducks linked to BLine
[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         Smach::event_result event_refresh_tool_options(const Smach::event& x);
144         void refresh_tool_options();
145
146         StateNormal_Context(CanvasView* canvas_view);
147
148         ~StateNormal_Context();
149
150         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
151         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
152         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
153         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
154
155         void load_settings();
156         void save_settings();
157
158         bool key_pressed(GdkEventKey *event);
159         bool key_released(GdkEventKey *event);
160
161 };      // END of class StateNormal_Context
162
163 /* === M E T H O D S ======================================================= */
164
165 StateNormal::StateNormal():
166         Smach::state<StateNormal_Context>("normal")
167 {
168         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateNormal_Context::event_refresh_tool_options));
169 }
170
171 StateNormal::~StateNormal()
172 {
173 }
174
175 void
176 StateNormal_Context::load_settings()
177 {
178         String value;
179
180         if(settings.get_value("normal.rotate",value) && value=="1")
181                 set_rotate_flag(true);
182         else
183                 set_rotate_flag(false);
184
185         if(settings.get_value("normal.scale",value) && value=="1")
186                 set_scale_flag(true);
187         else
188                 set_scale_flag(false);
189
190         if(settings.get_value("normal.constrain",value) && value=="1")
191                 set_constrain_flag(true);
192         else
193                 set_constrain_flag(false);
194
195 }
196
197 void
198 StateNormal_Context::save_settings()
199 {
200         settings.set_value("normal.rotate",get_rotate_flag()?"1":"0");
201         settings.set_value("normal.scale",get_scale_flag()?"1":"0");
202         settings.set_value("normal.constrain",get_constrain_flag()?"1":"0");
203 }
204
205 StateNormal_Context::StateNormal_Context(CanvasView* canvas_view):
206         canvas_view_(canvas_view),
207         settings(synfigapp::Main::get_selected_input_device()->settings()),
208         duck_dragger_(new DuckDrag_Combo()),
209         checkbutton_rotate(_("Rotate (Ctrl)")),
210         checkbutton_scale(_("Scale (Alt)")),
211         checkbutton_constrain(_("Constrain (Shift)"))
212 {
213         duck_dragger_->canvas_view_=get_canvas_view();
214
215         // Set up the tool options dialog
216         options_table.attach(*manage(new Gtk::Label(_("Normal Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
217         options_table.attach(checkbutton_rotate,                                                        0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
218         options_table.attach(checkbutton_scale,                                                 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
219         options_table.attach(checkbutton_constrain,                                                     0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
220
221         checkbutton_rotate.signal_toggled().connect(sigc::mem_fun(*this,&StateNormal_Context::refresh_rotate_flag));
222         checkbutton_scale.signal_toggled().connect(sigc::mem_fun(*this,&StateNormal_Context::refresh_scale_flag));
223         checkbutton_constrain.signal_toggled().connect(sigc::mem_fun(*this,&StateNormal_Context::refresh_constrain_flag));
224
225
226         options_table.show_all();
227         refresh_tool_options();
228         //App::dialog_tool_options->set_widget(options_table);
229         App::dialog_tool_options->present();
230
231         get_work_area()->set_allow_layer_clicks(true);
232         get_work_area()->set_duck_dragger(duck_dragger_);
233
234         keypress_connect=get_work_area()->signal_key_press_event().connect(sigc::mem_fun(*this,&StateNormal_Context::key_pressed),false);
235         keyrelease_connect=get_work_area()->signal_key_release_event().connect(sigc::mem_fun(*this,&StateNormal_Context::key_released),false);
236
237 //      get_canvas_view()->work_area->set_cursor(Gdk::CROSSHAIR);
238         get_canvas_view()->work_area->reset_cursor();
239
240         App::toolbox->refresh();
241
242         load_settings();
243         refresh_scale_flag();
244 }
245
246 bool
247 StateNormal_Context::key_pressed(GdkEventKey *event)
248 {
249         switch(event->keyval)
250         {
251                 case GDK_Control_L:
252                 case GDK_Control_R:
253                         set_rotate_flag(true);
254                         break;
255                 case GDK_Alt_L:
256                 case GDK_Alt_R:
257                         set_scale_flag(true);
258                         break;
259                 case GDK_Shift_L:
260                 case GDK_Shift_R:
261                         set_constrain_flag(true);
262                         break;
263                 default:
264                         break;
265         }
266         return false; //Pass on the event to other handlers, just in case
267 }
268
269 bool
270 StateNormal_Context::key_released(GdkEventKey *event)
271 {
272         switch(event->keyval)
273         {
274                 case GDK_Control_L:
275                 case GDK_Control_R:
276                         set_rotate_flag(false);
277                         break;
278                 case GDK_Alt_L:
279                 case GDK_Alt_R:
280                         set_scale_flag(false);
281                         break;
282                 case GDK_Shift_L:
283                 case GDK_Shift_R:
284                         set_constrain_flag(false);
285                         break;
286                 default:
287                         break;
288         }
289         return false; //Pass on the event to other handlers
290 }
291
292 void
293 StateNormal_Context::refresh_tool_options()
294 {
295         App::dialog_tool_options->clear();
296         App::dialog_tool_options->set_widget(options_table);
297         App::dialog_tool_options->set_local_name(_("Normal Tool"));
298         App::dialog_tool_options->set_name("normal");
299 }
300
301
302
303 StateNormal_Context::~StateNormal_Context()
304 {
305         save_settings();
306
307         get_work_area()->clear_duck_dragger();
308         get_canvas_view()->work_area->reset_cursor();
309
310         keypress_connect.disconnect();
311         keyrelease_connect.disconnect();
312
313         App::dialog_tool_options->clear();
314
315         App::toolbox->refresh();
316 }
317
318
319 Smach::event_result
320 StateNormal_Context::event_refresh_tool_options(const Smach::event& /*x*/)
321 {
322         refresh_tool_options();
323         return Smach::RESULT_ACCEPT;
324 }
325
326 DuckDrag_Combo::DuckDrag_Combo():
327         scale(false),
328         rotate(false),
329         constrain(false) // Lock aspect for scale; smooth move for translate
330 {
331 }
332
333 void
334 DuckDrag_Combo::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
335 {
336         last_move=Vector(1,1);
337
338         const DuckList selected_ducks(duckmatic->get_selected_ducks());
339         DuckList::const_iterator iter;
340
341         bad_drag=false;
342
343                 drag_offset=duckmatic->find_duck(offset)->get_trans_point();
344
345                 //snap=drag_offset-duckmatic->snap_point_to_grid(drag_offset);
346                 //snap=offset-drag_offset_;
347                 snap=Vector(0,0);
348
349         // Calculate center
350         Point vmin(100000000,100000000);
351         Point vmax(-100000000,-100000000);
352         //std::set<etl::handle<Duck> >::iterator iter;
353         positions.clear();
354         int i;
355         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
356         {
357                 Point p((*iter)->get_trans_point());
358                 vmin[0]=min(vmin[0],p[0]);
359                 vmin[1]=min(vmin[1],p[1]);
360                 vmax[0]=max(vmax[0],p[0]);
361                 vmax[1]=max(vmax[1],p[1]);
362                 positions.push_back(p);
363         }
364         center=(vmin+vmax)*0.5;
365         if((vmin-vmax).mag()<=EPSILON)
366                 move_only=true;
367         else
368                 move_only=false;
369
370
371         synfig::Vector vect(offset-center);
372         original_angle=Angle::tan(vect[1],vect[0]);
373         original_mag=vect.mag();
374 }
375
376
377 void
378 DuckDrag_Combo::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
379 {
380         if (!duckmatic) return;
381
382         if(bad_drag)
383                 return;
384
385         //Override axis lock set in workarea when holding down the shift key
386         if (!move_only && (scale || rotate))
387                 duckmatic->set_axis_lock(false);
388
389         synfig::Vector vect;
390         if (move_only || (!scale && !rotate))
391                 vect= duckmatic->snap_point_to_grid(vector)-drag_offset+snap;
392         else
393                 vect= duckmatic->snap_point_to_grid(vector)-center+snap;
394
395         last_move=vect;
396
397         const DuckList selected_ducks(duckmatic->get_selected_ducks());
398         DuckList::const_iterator iter;
399
400         Time time(duckmatic->get_time());
401
402         int i;
403         if( move_only || (!scale && !rotate) )
404         {
405                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
406                 {
407                         if((*iter)->get_type()==Duck::TYPE_VERTEX || (*iter)->get_type()==Duck::TYPE_POSITION)
408                                 (*iter)->set_trans_point(positions[i]+vect, time);
409                 }
410                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
411                 {
412                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)
413                                 (*iter)->set_trans_point(positions[i]+vect, time);
414                 }
415         }
416
417         if (rotate)
418         {
419                 Angle::deg angle(Angle::tan(vect[1],vect[0]));
420                 angle=original_angle-angle;
421                 if (constrain)
422                 {
423                         float degrees = angle.get()/15;
424                         angle= Angle::deg (degrees>0?std::floor(degrees)*15:std::ceil(degrees)*15);
425                 }
426                 Real mag(vect.mag()/original_mag);
427                 Real sine(Angle::sin(angle).get());
428                 Real cosine(Angle::cos(angle).get());
429
430                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
431                 {
432                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
433
434                         Vector x(positions[i]-center),p;
435
436                         p[0]=cosine*x[0]+sine*x[1];
437                         p[1]=-sine*x[0]+cosine*x[1];
438                         if(scale)p*=mag;
439                         p+=center;
440                         (*iter)->set_trans_point(p, time);
441                 }
442                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
443                 {
444                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
445
446                         Vector x(positions[i]-center),p;
447
448                         p[0]=cosine*x[0]+sine*x[1];
449                         p[1]=-sine*x[0]+cosine*x[1];
450                         if(scale)p*=mag;
451                         p+=center;
452                         (*iter)->set_trans_point(p, time);
453                 }
454         } else if (scale)
455         {
456                 if(!constrain)
457                 {
458                         if(abs(drag_offset[0]-center[0])>EPSILON)
459                                 vect[0]/=drag_offset[0]-center[0];
460                         else
461                                 vect[0]=1;
462                         if(abs(drag_offset[1]-center[1])>EPSILON)
463                                 vect[1]/=drag_offset[1]-center[1];
464                         else
465                                 vect[1]=1;
466                         }
467                 else
468                 {
469                         //vect[0]=vect[1]=vect.mag()*0.707106781;
470                         Real amount(vect.mag()/(drag_offset-center).mag());
471                         vect[0]=vect[1]=amount;
472                 }
473
474                 if(vect[0]<EPSILON && vect[0]>-EPSILON)
475                         vect[0]=1;
476                 if(vect[1]<EPSILON && vect[1]>-EPSILON)
477                         vect[1]=1;
478
479                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
480                 {
481                         if(((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
482
483                         Vector p(positions[i]-center);
484
485                         p[0]*=vect[0];
486                         p[1]*=vect[1];
487                         p+=center;
488                         (*iter)->set_trans_point(p, time);
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         }
502
503         // then patch up the tangents for the vertices we've moved
504         DuckList duck_list(duckmatic->get_duck_list());
505         for (iter=selected_ducks.begin(); iter!=selected_ducks.end(); ++iter)
506         {
507                 etl::handle<Duck> duck(*iter);
508                 if (duck->get_type() == Duck::TYPE_VERTEX || duck->get_type() == Duck::TYPE_POSITION)
509                 {
510                         ValueNode_Composite::Handle composite;
511
512                         if ((ValueNode_BLineCalcVertex::Handle::cast_dynamic(duck->get_value_desc().get_value_node())) ||
513                                 ((composite = ValueNode_Composite::Handle::cast_dynamic(duck->get_value_desc().get_value_node())) &&
514                                  composite->get_type() == ValueBase::TYPE_BLINEPOINT &&
515                                  (ValueNode_BLineCalcVertex::Handle::cast_dynamic(composite->get_link("point")))))
516                         {
517                                 //! \todo update() will call dynamic cast again, see if we can avoid that
518                                 DuckList::iterator iter;
519                                 for (iter=duck_list.begin(); iter!=duck_list.end(); iter++)
520                                         if ((*iter)->get_origin_duck()==duck 
521                                                 && std::find(selected_ducks.begin(), 
522                                                                          selected_ducks.end(), *iter) == selected_ducks.end())
523                                                 (*iter)->update(time);
524                         }
525                 }
526         }
527
528         last_move=vect;
529 }
530
531 bool
532 DuckDrag_Combo::end_duck_drag(Duckmatic* duckmatic)
533 {
534         if(bad_drag)return false;
535
536         //synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Rotate Ducks"));
537
538         if((last_move-Vector(1,1)).mag()>0.0001)
539         {
540                 duckmatic->signal_edited_selected_ducks();
541                 return true;
542         }
543         else
544         {
545                 duckmatic->signal_user_click_selected_ducks(0);
546                 return false;
547         }
548 }