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