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 ** Copyright (c) 2010 Carlos López
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 <synfig/general.h>
35
36 #include <gtkmm/dialog.h>
37 #include <gtkmm/entry.h>
38
39 #include <synfig/valuenodes/valuenode_dynamiclist.h>
40 #include <synfigapp/action_system.h>
41
42 #include "state_rotate.h"
43 #include "state_normal.h"
44 #include "canvasview.h"
45 #include "workarea.h"
46 #include "app.h"
47
48 #include <synfigapp/action.h>
49 #include "event_mouse.h"
50 #include "event_layerclick.h"
51 #include "docks/dock_toolbox.h"
52 #include "docks/dialog_tooloptions.h"
53 #include "duck.h"
54 #include <synfig/angle.h>
55 #include <synfigapp/main.h>
56
57 #include <gui/localization.h>
58
59 #endif
60
61 /* === U S I N G =========================================================== */
62
63 using namespace std;
64 using namespace etl;
65 using namespace synfig;
66 using namespace studio;
67
68 /* === M A C R O S ========================================================= */
69
70 #ifndef EPSILON
71 #define EPSILON 0.0000001
72 #endif
73
74 /* === G L O B A L S ======================================================= */
75
76 StateRotate studio::state_rotate;
77
78 /* === C L A S S E S & S T R U C T S ======================================= */
79
80 class DuckDrag_Rotate : public DuckDrag_Base
81 {
82
83 synfig::Vector last_rotate;
84 synfig::Vector drag_offset;
85 synfig::Vector center;
86 synfig::Vector snap;
87
88 Angle original_angle;
89 Real original_mag;
90
91 std::vector<synfig::Vector> positions;
92
93
94 bool bad_drag;
95 bool move_only;
96
97 public:
98 etl::handle<CanvasView> canvas_view_;
99 bool use_magnitude;
100 DuckDrag_Rotate();
101 void begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& begin);
102 bool end_duck_drag(Duckmatic* duckmatic);
103 void duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector);
104
get_canvas_interface() const105 etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
106 };
107
108
109 class studio::StateRotate_Context : public sigc::trackable
110 {
111 etl::handle<CanvasView> canvas_view_;
112 CanvasView::IsWorking is_working;
113
114 synfigapp::Settings& settings;
115
116 etl::handle<DuckDrag_Rotate> duck_dragger_;
117
118 Gtk::Table options_table;
119
120 Gtk::CheckButton checkbutton_scale;
121
122 public:
123
get_scale_flag() const124 bool get_scale_flag()const { return checkbutton_scale.get_active(); }
set_scale_flag(bool x)125 void set_scale_flag(bool x) { return checkbutton_scale.set_active(x); refresh_scale_flag(); }
126
127 Smach::event_result event_stop_handler(const Smach::event& x);
128 Smach::event_result event_refresh_tool_options(const Smach::event& x);
129
130 void refresh_tool_options();
131
refresh_scale_flag()132 void refresh_scale_flag() { if(duck_dragger_)duck_dragger_->use_magnitude=get_scale_flag(); }
133
134 StateRotate_Context(CanvasView* canvas_view);
135
136 ~StateRotate_Context();
137
get_canvas_view() const138 const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
get_canvas_interface() const139 etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
get_canvas() const140 synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
get_work_area() const141 WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
142
143 void load_settings();
144 void save_settings();
145 }; // END of class StateRotate_Context
146
147 /* === M E T H O D S ======================================================= */
148
StateRotate()149 StateRotate::StateRotate():
150 Smach::state<StateRotate_Context>("rotate")
151 {
152 insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateRotate_Context::event_refresh_tool_options));
153 insert(event_def(EVENT_STOP,&StateRotate_Context::event_stop_handler));
154 }
155
~StateRotate()156 StateRotate::~StateRotate()
157 {
158 }
159
160 void
load_settings()161 StateRotate_Context::load_settings()
162 {
163 try
164 {
165 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
166 String value;
167
168 if(settings.get_value("rotate.scale",value) && value=="0")
169 set_scale_flag(false);
170 else
171 set_scale_flag(true);
172 }
173 catch(...)
174 {
175 synfig::warning("State Rotate: Caught exception when attempting to load settings.");
176 }
177 }
178
179 void
save_settings()180 StateRotate_Context::save_settings()
181 {
182 try
183 {
184 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
185 settings.set_value("rotate.scale",get_scale_flag()?"1":"0");
186 }
187 catch(...)
188 {
189 synfig::warning("State Rotate: Caught exception when attempting to save settings.");
190 }
191 }
192
StateRotate_Context(CanvasView * canvas_view)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::dock_toolbox->refresh();
220
221 load_settings();
222 refresh_scale_flag();
223 }
224
225 void
refresh_tool_options()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
event_refresh_tool_options(const Smach::event &)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
event_stop_handler(const Smach::event &)242 StateRotate_Context::event_stop_handler(const Smach::event& /*x*/)
243 {
244 canvas_view_->stop();
245 return Smach::RESULT_ACCEPT;
246 //throw &state_normal;
247 //return Smach::RESULT_OK;
248 }
249
~StateRotate_Context()250 StateRotate_Context::~StateRotate_Context()
251 {
252 save_settings();
253
254 get_work_area()->clear_duck_dragger();
255 get_work_area()->reset_cursor();
256
257 App::dialog_tool_options->clear();
258
259 App::dock_toolbox->refresh();
260 }
261
262
263
264
DuckDrag_Rotate()265 DuckDrag_Rotate::DuckDrag_Rotate():
266 original_mag(),
267 bad_drag(),
268 move_only(),
269 use_magnitude(true)
270 { }
271
272 void
begin_duck_drag(Duckmatic * duckmatic,const synfig::Vector & offset)273 DuckDrag_Rotate::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
274 {
275 last_rotate=Vector(1,1);
276
277 const DuckList selected_ducks(duckmatic->get_selected_ducks());
278 DuckList::const_iterator iter;
279
280 /*
281 if(duckmatic->get_selected_ducks().size()<2)
282 {
283 bad_drag=true;
284 return;
285 }
286 */
287 bad_drag=false;
288
289 drag_offset=duckmatic->find_duck(offset)->get_trans_point();
290
291 //snap=drag_offset-duckmatic->snap_point_to_grid(drag_offset);
292 //snap=offset-drag_offset;
293 snap=Vector(0,0);
294
295 // Calculate center
296 Point vmin(100000000,100000000);
297 Point vmax(-100000000,-100000000);
298 //std::set<etl::handle<Duck> >::iterator iter;
299 positions.clear();
300 int i;
301 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
302 {
303 Point p((*iter)->get_trans_point());
304 vmin[0]=min(vmin[0],p[0]);
305 vmin[1]=min(vmin[1],p[1]);
306 vmax[0]=max(vmax[0],p[0]);
307 vmax[1]=max(vmax[1],p[1]);
308 positions.push_back(p);
309 }
310 center=(vmin+vmax)*0.5;
311 if((vmin-vmax).mag()<=EPSILON)
312 move_only=true;
313 else
314 move_only=false;
315
316
317 synfig::Vector vect(offset-center);
318 original_angle=Angle::tan(vect[1],vect[0]);
319 original_mag=vect.mag();
320 }
321
322
323 void
duck_drag(Duckmatic * duckmatic,const synfig::Vector & vector)324 DuckDrag_Rotate::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
325 {
326 if(bad_drag)
327 return;
328
329 //std::set<etl::handle<Duck> >::iterator iter;
330 synfig::Vector vect(duckmatic->snap_point_to_grid(vector)-center+snap);
331
332 const DuckList selected_ducks(duckmatic->get_selected_ducks());
333 DuckList::const_iterator iter;
334
335 if(move_only)
336 {
337 int i;
338 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
339 {
340 if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
341
342 Vector p(positions[i]);
343
344 p[0]+=vect[0];
345 p[1]+=vect[1];
346 (*iter)->set_trans_point(p);
347 }
348 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
349 {
350 if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
351
352 Vector p(positions[i]);
353
354 p[0]+=vect[0];
355 p[1]+=vect[1];
356 (*iter)->set_trans_point(p);
357 }
358 return;
359 }
360
361 Angle::tan angle(vect[1],vect[0]);
362 angle=original_angle-angle;
363 Real mag(vect.mag()/original_mag);
364 Real sine(Angle::sin(angle).get());
365 Real cosine(Angle::cos(angle).get());
366
367 int i;
368 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
369 {
370 if((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION)continue;
371
372 Vector x(positions[i]-center),p;
373
374 p[0]=cosine*x[0]+sine*x[1];
375 p[1]=-sine*x[0]+cosine*x[1];
376 if(use_magnitude)p*=mag;
377 p+=center;
378 (*iter)->set_trans_point(p);
379 }
380 for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
381 {
382 if(!((*iter)->get_type()!=Duck::TYPE_VERTEX&&(*iter)->get_type()!=Duck::TYPE_POSITION))continue;
383
384 Vector x(positions[i]-center),p;
385
386 p[0]=cosine*x[0]+sine*x[1];
387 p[1]=-sine*x[0]+cosine*x[1];
388 if(use_magnitude)p*=mag;
389 p+=center;
390 (*iter)->set_trans_point(p);
391 }
392
393 last_rotate=vect;
394 //snap=Vector(0,0);
395 }
396
397 bool
end_duck_drag(Duckmatic * duckmatic)398 DuckDrag_Rotate::end_duck_drag(Duckmatic* duckmatic)
399 {
400 if(bad_drag)return false;
401 if(move_only)
402 {
403 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Move Handle"));
404 duckmatic->signal_edited_selected_ducks();
405 return true;
406 }
407
408 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Rotate Handle"));
409
410 if((last_rotate-Vector(1,1)).mag()>0.0001)
411 {
412 duckmatic->signal_edited_selected_ducks();
413 return true;
414 }
415 else
416 {
417 duckmatic->signal_user_click_selected_ducks(0);
418 return false;
419 }
420 }
421