Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / 0.61.08 / 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 #include "general.h"
54
55 #endif
56
57 /* === U S I N G =========================================================== */
58
59 using namespace std;
60 using namespace etl;
61 using namespace synfig;
62 using namespace studio;
63
64 /* === M A C R O S ========================================================= */
65
66 #ifndef EPSILON
67 #define EPSILON 0.0000001
68 #endif
69
70 /* === G L O B A L S ======================================================= */
71
72 StateRotate studio::state_rotate;
73
74 /* === C L A S S E S & S T R U C T S ======================================= */
75
76 class DuckDrag_Rotate : public DuckDrag_Base
77 {
78
79         synfig::Vector last_rotate;
80         synfig::Vector drag_offset;
81         synfig::Vector center;
82         synfig::Vector snap;
83
84         Angle original_angle;
85         Real original_mag;
86
87         std::vector<synfig::Vector> positions;
88
89
90         bool bad_drag;
91         bool move_only;
92
93 public:
94         etl::handle<CanvasView> canvas_view_;
95         bool use_magnitude;
96         DuckDrag_Rotate();
97         void begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& begin);
98         bool end_duck_drag(Duckmatic* duckmatic);
99         void duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector);
100
101         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
102 };
103
104
105 class studio::StateRotate_Context : public sigc::trackable
106 {
107         etl::handle<CanvasView> canvas_view_;
108
109         synfigapp::Settings& settings;
110
111         etl::handle<DuckDrag_Rotate> duck_dragger_;
112
113         Gtk::Table options_table;
114
115         Gtk::CheckButton checkbutton_scale;
116
117 public:
118
119         bool get_scale_flag()const { return checkbutton_scale.get_active(); }
120         void set_scale_flag(bool x) { return checkbutton_scale.set_active(x); refresh_scale_flag(); }
121
122
123         Smach::event_result event_refresh_tool_options(const Smach::event& x);
124
125         void refresh_tool_options();
126
127         void refresh_scale_flag() { if(duck_dragger_)duck_dragger_->use_magnitude=get_scale_flag(); }
128
129         StateRotate_Context(CanvasView* canvas_view);
130
131         ~StateRotate_Context();
132
133         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
134         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
135         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
136         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
137
138         void load_settings();
139         void save_settings();
140 };      // END of class StateRotate_Context
141
142 /* === M E T H O D S ======================================================= */
143
144 StateRotate::StateRotate():
145         Smach::state<StateRotate_Context>("rotate")
146 {
147         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateRotate_Context::event_refresh_tool_options));
148 }
149
150 StateRotate::~StateRotate()
151 {
152 }
153
154 void
155 StateRotate_Context::load_settings()
156 {
157         String value;
158
159         if(settings.get_value("rotate.scale",value) && value=="0")
160                 set_scale_flag(false);
161         else
162                 set_scale_flag(true);
163 }
164
165 void
166 StateRotate_Context::save_settings()
167 {
168         settings.set_value("rotate.scale",get_scale_flag()?"1":"0");
169 }
170
171 StateRotate_Context::StateRotate_Context(CanvasView* canvas_view):
172         canvas_view_(canvas_view),
173         settings(synfigapp::Main::get_selected_input_device()->settings()),
174         duck_dragger_(new DuckDrag_Rotate()),
175         checkbutton_scale(_("Allow Scale"))
176 {
177         duck_dragger_->canvas_view_=get_canvas_view();
178
179         // Set up the tool options dialog
180         //options_table.attach(*manage(new Gtk::Label(_("Rotate Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
181         options_table.attach(checkbutton_scale, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
182
183         checkbutton_scale.signal_toggled().connect(sigc::mem_fun(*this,&StateRotate_Context::refresh_scale_flag));
184
185         options_table.show_all();
186         refresh_tool_options();
187         //App::dialog_tool_options->set_widget(options_table);
188         App::dialog_tool_options->present();
189
190         get_work_area()->set_allow_layer_clicks(true);
191         get_work_area()->set_duck_dragger(duck_dragger_);
192
193 //      get_canvas_view()->work_area->set_cursor(Gdk::CROSSHAIR);
194         get_canvas_view()->work_area->reset_cursor();
195
196         App::toolbox->refresh();
197
198         load_settings();
199         refresh_scale_flag();
200 }
201
202 void
203 StateRotate_Context::refresh_tool_options()
204 {
205         App::dialog_tool_options->clear();
206         App::dialog_tool_options->set_widget(options_table);
207         App::dialog_tool_options->set_local_name(_("Rotate Tool"));
208         App::dialog_tool_options->set_name("rotate");
209 }
210
211 Smach::event_result
212 StateRotate_Context::event_refresh_tool_options(const Smach::event& /*x*/)
213 {
214         refresh_tool_options();
215         return Smach::RESULT_ACCEPT;
216 }
217
218 StateRotate_Context::~StateRotate_Context()
219 {
220         save_settings();
221
222         get_work_area()->clear_duck_dragger();
223         get_canvas_view()->work_area->reset_cursor();
224
225         App::dialog_tool_options->clear();
226
227         App::toolbox->refresh();
228 }
229
230
231
232
233 DuckDrag_Rotate::DuckDrag_Rotate()
234 {
235         use_magnitude=true;
236 }
237
238 void
239 DuckDrag_Rotate::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
240 {
241         last_rotate=Vector(1,1);
242
243         const DuckList selected_ducks(duckmatic->get_selected_ducks());
244         DuckList::const_iterator iter;
245
246 /*
247         if(duckmatic->get_selected_ducks().size()<2)
248         {
249                 bad_drag=true;
250                 return;
251         }
252 */
253         bad_drag=false;
254
255                 drag_offset=duckmatic->find_duck(offset)->get_trans_point();
256
257                 //snap=drag_offset-duckmatic->snap_point_to_grid(drag_offset);
258                 //snap=offset-drag_offset;
259                 snap=Vector(0,0);
260
261         // Calculate center
262         Point vmin(100000000,100000000);
263         Point vmax(-100000000,-100000000);
264         //std::set<etl::handle<Duck> >::iterator iter;
265         positions.clear();
266         int i;
267         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
268         {
269                 Point p((*iter)->get_trans_point());
270                 vmin[0]=min(vmin[0],p[0]);
271                 vmin[1]=min(vmin[1],p[1]);
272                 vmax[0]=max(vmax[0],p[0]);
273                 vmax[1]=max(vmax[1],p[1]);
274                 positions.push_back(p);
275         }
276         center=(vmin+vmax)*0.5;
277         if((vmin-vmax).mag()<=EPSILON)
278                 move_only=true;
279         else
280                 move_only=false;
281
282
283         synfig::Vector vect(offset-center);
284         original_angle=Angle::tan(vect[1],vect[0]);
285         original_mag=vect.mag();
286 }
287
288
289 void
290 DuckDrag_Rotate::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
291 {
292         if(bad_drag)
293                 return;
294
295         //std::set<etl::handle<Duck> >::iterator iter;
296         synfig::Vector vect(duckmatic->snap_point_to_grid(vector)-center+snap);
297
298         const DuckList selected_ducks(duckmatic->get_selected_ducks());
299         DuckList::const_iterator iter;
300
301         if(move_only)
302         {
303                 int i;
304                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
305                 {
306                         if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
307
308                         Vector p(positions[i]);
309
310                         p[0]+=vect[0];
311                         p[1]+=vect[1];
312                         (*iter)->set_trans_point(p);
313                 }
314                 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
315                 {
316                         if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
317
318                         Vector p(positions[i]);
319
320                         p[0]+=vect[0];
321                         p[1]+=vect[1];
322                         (*iter)->set_trans_point(p);
323                 }
324                 return;
325         }
326
327         Angle::tan angle(vect[1],vect[0]);
328         angle=original_angle-angle;
329         Real mag(vect.mag()/original_mag);
330         Real sine(Angle::sin(angle).get());
331         Real cosine(Angle::cos(angle).get());
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 x(positions[i]-center),p;
339
340                 p[0]=cosine*x[0]+sine*x[1];
341                 p[1]=-sine*x[0]+cosine*x[1];
342                 if(use_magnitude)p*=mag;
343                 p+=center;
344                 (*iter)->set_trans_point(p);
345         }
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
359         last_rotate=vect;
360         //snap=Vector(0,0);
361 }
362
363 bool
364 DuckDrag_Rotate::end_duck_drag(Duckmatic* duckmatic)
365 {
366         if(bad_drag)return false;
367         if(move_only)
368         {
369                 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Move Duck"));
370                 duckmatic->signal_edited_selected_ducks();
371                 return true;
372         }
373
374         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Rotate Ducks"));
375
376         if((last_rotate-Vector(1,1)).mag()>0.0001)
377         {
378                 duckmatic->signal_edited_selected_ducks();
379                 return true;
380         }
381         else
382         {
383                 duckmatic->signal_user_click_selected_ducks(0);
384                 return false;
385         }
386 }