e582835960592391ce64c2628a731aae4c7da0cb
[synfig.git] / synfig-studio / src / gui / states / state_rotate.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file state_rotate.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) 2008 Chris Moore
10 **
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.
15 **
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.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include <gtkmm/dialog.h>
34 #include <gtkmm/entry.h>
35
36 #include <synfig/valuenode_dynamiclist.h>
37 #include <synfigapp/action_system.h>
38
39 #include "state_rotate.h"
40 #include "state_normal.h"
41 #include "canvasview.h"
42 #include "workarea.h"
43 #include "app.h"
44
45 #include <synfigapp/action.h>
46 #include "event_mouse.h"
47 #include "event_layerclick.h"
48 #include "toolbox.h"
49 #include "docks/dialog_tooloptions.h"
50 #include <gtkmm/optionmenu.h>
51 #include "duck.h"
52 #include <synfig/angle.h>
53 #include <synfigapp/main.h>
54
55 #include "general.h"
56
57 #endif
58
59 /* === U S I N G =========================================================== */
60
61 using namespace std;
62 using namespace etl;
63 using namespace synfig;
64 using namespace studio;
65
66 /* === M A C R O S ========================================================= */
67
68 #ifndef EPSILON
69 #define EPSILON 0.0000001
70 #endif
71
72 /* === G L O B A L S ======================================================= */
73
74 StateRotate studio::state_rotate;
75
76 /* === C L A S S E S & S T R U C T S ======================================= */
77
78 class DuckDrag_Rotate : public DuckDrag_Base
79 {
80
81         synfig::Vector last_rotate;
82         synfig::Vector drag_offset;
83         synfig::Vector center;
84         synfig::Vector snap;
85
86         Angle original_angle;
87         Real original_mag;
88
89         std::vector<synfig::Vector> positions;
90
91
92         bool bad_drag;
93         bool move_only;
94
95 public:
96         etl::handle<CanvasView> canvas_view_;
97         bool use_magnitude;
98         DuckDrag_Rotate();
99         void begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& begin);
100         bool end_duck_drag(Duckmatic* duckmatic);
101         void duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector);
102
103         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
104 };
105
106
107 class studio::StateRotate_Context : public sigc::trackable
108 {
109         etl::handle<CanvasView> canvas_view_;
110         CanvasView::IsWorking is_working;
111
112         synfigapp::Settings& settings;
113
114         etl::handle<DuckDrag_Rotate> duck_dragger_;
115
116         Gtk::Table options_table;
117
118         Gtk::CheckButton checkbutton_scale;
119
120 public:
121
122         bool get_scale_flag()const { return checkbutton_scale.get_active(); }
123         void set_scale_flag(bool x) { return checkbutton_scale.set_active(x); refresh_scale_flag(); }
124
125         Smach::event_result event_stop_handler(const Smach::event& x);
126         Smach::event_result event_refresh_tool_options(const Smach::event& x);
127
128         void refresh_tool_options();
129
130         void refresh_scale_flag() { if(duck_dragger_)duck_dragger_->use_magnitude=get_scale_flag(); }
131
132         StateRotate_Context(CanvasView* canvas_view);
133
134         ~StateRotate_Context();
135
136         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
137         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
138         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
139         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
140
141         void load_settings();
142         void save_settings();
143 };      // END of class StateRotate_Context
144
145 /* === M E T H O D S ======================================================= */
146
147 StateRotate::StateRotate():
148         Smach::state<StateRotate_Context>("rotate")
149 {
150         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateRotate_Context::event_refresh_tool_options));
151         insert(event_def(EVENT_STOP,&StateRotate_Context::event_stop_handler));
152 }
153
154 StateRotate::~StateRotate()
155 {
156 }
157
158 void
159 StateRotate_Context::load_settings()
160 {
161         try
162         {
163                 SETTINGS_LOCALE_SAFE_AND_BACKUP
164                 String value;
165
166                 if(settings.get_value("rotate.scale",value) && value=="0")
167                         set_scale_flag(false);
168                 else
169                         set_scale_flag(true);
170                 SETTINGS_LOCALE_RESTORE
171         }
172         catch(...)
173         {
174                 synfig::warning("State Rotate: Caught exception when attempting to load settings.");
175         }
176 }
177
178 void
179 StateRotate_Context::save_settings()
180 {
181         try
182         {
183                 SETTINGS_LOCALE_SAFE_AND_BACKUP
184                 settings.set_value("rotate.scale",get_scale_flag()?"1":"0");
185                 SETTINGS_LOCALE_RESTORE
186         }
187         catch(...)
188         {
189                 synfig::warning("State Rotate: Caught exception when attempting to save settings.");
190         }
191 }
192
193 StateRotate_Context::StateRotate_Context(CanvasView* canvas_view):
194         canvas_view_(canvas_view),
195         is_working(*canvas_view),
196         settings(synfigapp::Main::get_selected_input_device()->settings()),
197         duck_dragger_(new DuckDrag_Rotate()),
198         checkbutton_scale(_("Allow Scale"))
199 {
200         duck_dragger_->canvas_view_=get_canvas_view();
201
202         // Set up the tool options dialog
203         options_table.attach(*manage(new Gtk::Label(_("Rotate Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
204         options_table.attach(checkbutton_scale,                                                 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
205
206         checkbutton_scale.signal_toggled().connect(sigc::mem_fun(*this,&StateRotate_Context::refresh_scale_flag));
207
208         options_table.show_all();
209         refresh_tool_options();
210         //App::dialog_tool_options->set_widget(options_table);
211         App::dialog_tool_options->present();
212
213         get_work_area()->set_allow_layer_clicks(true);
214         get_work_area()->set_duck_dragger(duck_dragger_);
215
216         get_work_area()->set_cursor(Gdk::EXCHANGE);
217 //      get_work_area()->reset_cursor();
218
219         App::toolbox->refresh();
220
221         load_settings();
222         refresh_scale_flag();
223 }
224
225 void
226 StateRotate_Context::refresh_tool_options()
227 {
228         App::dialog_tool_options->clear();
229         App::dialog_tool_options->set_widget(options_table);
230         App::dialog_tool_options->set_local_name(_("Rotate Tool"));
231         App::dialog_tool_options->set_name("rotate");
232 }
233
234 Smach::event_result
235 StateRotate_Context::event_refresh_tool_options(const Smach::event& /*x*/)
236 {
237         refresh_tool_options();
238         return Smach::RESULT_ACCEPT;
239 }
240
241 Smach::event_result
242 StateRotate_Context::event_stop_handler(const Smach::event& /*x*/)
243 {
244         throw &state_normal;
245         return Smach::RESULT_OK;
246 }
247
248 StateRotate_Context::~StateRotate_Context()
249 {
250         save_settings();
251
252         get_work_area()->clear_duck_dragger();
253         get_work_area()->reset_cursor();
254
255         App::dialog_tool_options->clear();
256
257         App::toolbox->refresh();
258 }
259
260
261
262
263 DuckDrag_Rotate::DuckDrag_Rotate()
264 {
265         use_magnitude=true;
266 }
267
268 void
269 DuckDrag_Rotate::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
270 {
271         last_rotate=Vector(1,1);
272
273         const DuckList selected_ducks(duckmatic->get_selected_ducks());
274         DuckList::const_iterator iter;
275
276 /*
277         if(duckmatic->get_selected_ducks().size()<2)
278         {
279                 bad_drag=true;
280                 return;
281         }
282 */
283         bad_drag=false;
284
285                 drag_offset=duckmatic->find_duck(offset)->get_trans_point();
286
287                 //snap=drag_offset-duckmatic->snap_point_to_grid(drag_offset);
288                 //snap=offset-drag_offset;
289                 snap=Vector(0,0);
290
291         // Calculate center
292         Point vmin(100000000,100000000);
293         Point vmax(-100000000,-100000000);
294         //std::set<etl::handle<Duck> >::iterator iter;
295         positions.clear();
296         int i;
297         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
298         {
299                 Point p((*iter)->get_trans_point());
300                 vmin[0]=min(vmin[0],p[0]);
301                 vmin[1]=min(vmin[1],p[1]);
302                 vmax[0]=max(vmax[0],p[0]);
303                 vmax[1]=max(vmax[1],p[1]);
304                 positions.push_back(p);
305         }
306         center=(vmin+vmax)*0.5;
307         if((vmin-vmax).mag()<=EPSILON)
308                 move_only=true;
309         else
310                 move_only=false;
311
312
313         synfig::Vector vect(offset-center);
314         original_angle=Angle::tan(vect[1],vect[0]);
315         original_mag=vect.mag();
316 }
317
318
319 void
320 DuckDrag_Rotate::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
321 {
322         if(bad_drag)
323                 return;
324
325         //std::set<etl::handle<Duck> >::iterator iter;
326         synfig::Vector vect(duckmatic->snap_point_to_grid(vector)-center+snap);
327
328         const DuckList selected_ducks(duckmatic->get_selected_ducks());
329         DuckList::const_iterator iter;
330
331         if(move_only)
332         {
333                 int i;
334                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
335                 {
336                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
337
338                         Vector p(positions[i]);
339
340                         p[0]+=vect[0];
341                         p[1]+=vect[1];
342                         (*iter)->set_trans_point(p);
343                 }
344                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
345                 {
346                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
347
348                         Vector p(positions[i]);
349
350                         p[0]+=vect[0];
351                         p[1]+=vect[1];
352                         (*iter)->set_trans_point(p);
353                 }
354                 return;
355         }
356
357         Angle::tan angle(vect[1],vect[0]);
358         angle=original_angle-angle;
359         Real mag(vect.mag()/original_mag);
360         Real sine(Angle::sin(angle).get());
361         Real cosine(Angle::cos(angle).get());
362
363         int i;
364         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
365         {
366                 if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
367
368                 Vector x(positions[i]-center),p;
369
370                 p[0]=cosine*x[0]+sine*x[1];
371                 p[1]=-sine*x[0]+cosine*x[1];
372                 if(use_magnitude)p*=mag;
373                 p+=center;
374                 (*iter)->set_trans_point(p);
375         }
376         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
377         {
378                 if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
379
380                 Vector x(positions[i]-center),p;
381
382                 p[0]=cosine*x[0]+sine*x[1];
383                 p[1]=-sine*x[0]+cosine*x[1];
384                 if(use_magnitude)p*=mag;
385                 p+=center;
386                 (*iter)->set_trans_point(p);
387         }
388
389         last_rotate=vect;
390         //snap=Vector(0,0);
391 }
392
393 bool
394 DuckDrag_Rotate::end_duck_drag(Duckmatic* duckmatic)
395 {
396         if(bad_drag)return false;
397         if(move_only)
398         {
399                 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Move Duck"));
400                 duckmatic->signal_edited_selected_ducks();
401                 return true;
402         }
403
404         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Rotate Ducks"));
405
406         if((last_rotate-Vector(1,1)).mag()>0.0001)
407         {
408                 duckmatic->signal_edited_selected_ducks();
409                 return true;
410         }
411         else
412         {
413                 duckmatic->signal_user_click_selected_ducks(0);
414                 return false;
415         }
416 }