Merge branch 'master' of ssh://synfig.git.sourceforge.net/gitroot/synfig/synfig into...
[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         String value;
162
163         if(settings.get_value("rotate.scale",value) && value=="0")
164                 set_scale_flag(false);
165         else
166                 set_scale_flag(true);
167 }
168
169 void
170 StateRotate_Context::save_settings()
171 {
172         settings.set_value("rotate.scale",get_scale_flag()?"1":"0");
173 }
174
175 StateRotate_Context::StateRotate_Context(CanvasView* canvas_view):
176         canvas_view_(canvas_view),
177         is_working(*canvas_view),
178         settings(synfigapp::Main::get_selected_input_device()->settings()),
179         duck_dragger_(new DuckDrag_Rotate()),
180         checkbutton_scale(_("Allow Scale"))
181 {
182         duck_dragger_->canvas_view_=get_canvas_view();
183
184         // Set up the tool options dialog
185         options_table.attach(*manage(new Gtk::Label(_("Rotate Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
186         options_table.attach(checkbutton_scale,                                                 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
187
188         checkbutton_scale.signal_toggled().connect(sigc::mem_fun(*this,&StateRotate_Context::refresh_scale_flag));
189
190         options_table.show_all();
191         refresh_tool_options();
192         //App::dialog_tool_options->set_widget(options_table);
193         App::dialog_tool_options->present();
194
195         get_work_area()->set_allow_layer_clicks(true);
196         get_work_area()->set_duck_dragger(duck_dragger_);
197
198         get_work_area()->set_cursor(Gdk::EXCHANGE);
199 //      get_work_area()->reset_cursor();
200
201         App::toolbox->refresh();
202
203         load_settings();
204         refresh_scale_flag();
205 }
206
207 void
208 StateRotate_Context::refresh_tool_options()
209 {
210         App::dialog_tool_options->clear();
211         App::dialog_tool_options->set_widget(options_table);
212         App::dialog_tool_options->set_local_name(_("Rotate Tool"));
213         App::dialog_tool_options->set_name("rotate");
214 }
215
216 Smach::event_result
217 StateRotate_Context::event_refresh_tool_options(const Smach::event& /*x*/)
218 {
219         refresh_tool_options();
220         return Smach::RESULT_ACCEPT;
221 }
222
223 Smach::event_result
224 StateRotate_Context::event_stop_handler(const Smach::event& /*x*/)
225 {
226         throw &state_normal;
227         return Smach::RESULT_OK;
228 }
229
230 StateRotate_Context::~StateRotate_Context()
231 {
232         save_settings();
233
234         get_work_area()->clear_duck_dragger();
235         get_work_area()->reset_cursor();
236
237         App::dialog_tool_options->clear();
238
239         App::toolbox->refresh();
240 }
241
242
243
244
245 DuckDrag_Rotate::DuckDrag_Rotate()
246 {
247         use_magnitude=true;
248 }
249
250 void
251 DuckDrag_Rotate::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
252 {
253         last_rotate=Vector(1,1);
254
255         const DuckList selected_ducks(duckmatic->get_selected_ducks());
256         DuckList::const_iterator iter;
257
258 /*
259         if(duckmatic->get_selected_ducks().size()<2)
260         {
261                 bad_drag=true;
262                 return;
263         }
264 */
265         bad_drag=false;
266
267                 drag_offset=duckmatic->find_duck(offset)->get_trans_point();
268
269                 //snap=drag_offset-duckmatic->snap_point_to_grid(drag_offset);
270                 //snap=offset-drag_offset;
271                 snap=Vector(0,0);
272
273         // Calculate center
274         Point vmin(100000000,100000000);
275         Point vmax(-100000000,-100000000);
276         //std::set<etl::handle<Duck> >::iterator iter;
277         positions.clear();
278         int i;
279         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
280         {
281                 Point p((*iter)->get_trans_point());
282                 vmin[0]=min(vmin[0],p[0]);
283                 vmin[1]=min(vmin[1],p[1]);
284                 vmax[0]=max(vmax[0],p[0]);
285                 vmax[1]=max(vmax[1],p[1]);
286                 positions.push_back(p);
287         }
288         center=(vmin+vmax)*0.5;
289         if((vmin-vmax).mag()<=EPSILON)
290                 move_only=true;
291         else
292                 move_only=false;
293
294
295         synfig::Vector vect(offset-center);
296         original_angle=Angle::tan(vect[1],vect[0]);
297         original_mag=vect.mag();
298 }
299
300
301 void
302 DuckDrag_Rotate::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
303 {
304         if(bad_drag)
305                 return;
306
307         //std::set<etl::handle<Duck> >::iterator iter;
308         synfig::Vector vect(duckmatic->snap_point_to_grid(vector)-center+snap);
309
310         const DuckList selected_ducks(duckmatic->get_selected_ducks());
311         DuckList::const_iterator iter;
312
313         if(move_only)
314         {
315                 int i;
316                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
317                 {
318                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
319
320                         Vector p(positions[i]);
321
322                         p[0]+=vect[0];
323                         p[1]+=vect[1];
324                         (*iter)->set_trans_point(p);
325                 }
326                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
327                 {
328                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
329
330                         Vector p(positions[i]);
331
332                         p[0]+=vect[0];
333                         p[1]+=vect[1];
334                         (*iter)->set_trans_point(p);
335                 }
336                 return;
337         }
338
339         Angle::tan angle(vect[1],vect[0]);
340         angle=original_angle-angle;
341         Real mag(vect.mag()/original_mag);
342         Real sine(Angle::sin(angle).get());
343         Real cosine(Angle::cos(angle).get());
344
345         int i;
346         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
347         {
348                 if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
349
350                 Vector x(positions[i]-center),p;
351
352                 p[0]=cosine*x[0]+sine*x[1];
353                 p[1]=-sine*x[0]+cosine*x[1];
354                 if(use_magnitude)p*=mag;
355                 p+=center;
356                 (*iter)->set_trans_point(p);
357         }
358         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
359         {
360                 if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
361
362                 Vector x(positions[i]-center),p;
363
364                 p[0]=cosine*x[0]+sine*x[1];
365                 p[1]=-sine*x[0]+cosine*x[1];
366                 if(use_magnitude)p*=mag;
367                 p+=center;
368                 (*iter)->set_trans_point(p);
369         }
370
371         last_rotate=vect;
372         //snap=Vector(0,0);
373 }
374
375 bool
376 DuckDrag_Rotate::end_duck_drag(Duckmatic* duckmatic)
377 {
378         if(bad_drag)return false;
379         if(move_only)
380         {
381                 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Move Duck"));
382                 duckmatic->signal_edited_selected_ducks();
383                 return true;
384         }
385
386         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Rotate Ducks"));
387
388         if((last_rotate-Vector(1,1)).mag()>0.0001)
389         {
390                 duckmatic->signal_edited_selected_ducks();
391                 return true;
392         }
393         else
394         {
395                 duckmatic->signal_user_click_selected_ducks(0);
396                 return false;
397         }
398 }