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