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