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