89b39c9263a808e17e4ebe916a1930a3f9b7eea5
[synfig.git] / synfig-studio / src / gtkmm / mod_mirror / state_mirror.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file state_mirror.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) 2009 Nikita Kitaev
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_mirror.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 "../dialog_tooloptions.h"
50 #include <gtkmm/optionmenu.h>
51 #include "../duck.h"
52 #include <synfigapp/main.h>
53
54 #include "../general.h"
55
56 #endif
57
58 /* === U S I N G =========================================================== */
59
60 using namespace std;
61 using namespace etl;
62 using namespace synfig;
63 using namespace studio;
64
65 /* === M A C R O S ========================================================= */
66
67 enum Axis {
68         AXIS_X,
69         AXIS_Y
70 } ;
71
72 /* === G L O B A L S ======================================================= */
73
74 StateMirror studio::state_mirror;
75
76 /* === C L A S S E S & S T R U C T S ======================================= */
77
78 class DuckDrag_Mirror : public DuckDrag_Base
79 {
80         synfig::Vector center;
81
82         std::vector<synfig::Vector> positions;
83
84 public:
85         Axis axis;
86
87         DuckDrag_Mirror();
88         void begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& begin);
89         bool end_duck_drag(Duckmatic* duckmatic);
90         void duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector);
91 };
92
93 class studio::StateMirror_Context : public sigc::trackable
94 {
95         etl::handle<CanvasView> canvas_view_;
96         CanvasView::IsWorking is_working;
97
98         etl::handle<DuckDrag_Mirror> duck_dragger_;
99
100         Gtk::Table options_table;
101
102 public:
103
104         Axis get_axis()const { return duck_dragger_->axis; }
105         void set_axis(Axis a)
106         {
107                 if (duck_dragger_->axis != a)
108                 {
109                         duck_dragger_->axis = a;
110                         get_work_area()->set_cursor(get_axis() == AXIS_X?Gdk::SB_H_DOUBLE_ARROW:Gdk::SB_V_DOUBLE_ARROW);                        
111                 }
112         }
113
114         Smach::event_result event_stop_handler(const Smach::event& x);
115         Smach::event_result event_refresh_tool_options(const Smach::event& x);
116         Smach::event_result event_mouse_motion_handler(const Smach::event& x);
117
118         void refresh_tool_options();
119
120         StateMirror_Context(CanvasView* canvas_view);
121
122         ~StateMirror_Context();
123
124         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
125         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
126         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
127         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
128
129 };      // END of class StateMirror_Context
130
131 /* === M E T H O D S ======================================================= */
132
133 StateMirror::StateMirror():
134         Smach::state<StateMirror_Context>("mirror")
135 {
136         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateMirror_Context::event_refresh_tool_options));
137         insert(event_def(EVENT_STOP,&StateMirror_Context::event_stop_handler));
138         insert(event_def(EVENT_WORKAREA_MOUSE_MOTION,           &StateMirror_Context::event_mouse_motion_handler));
139         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,      &StateMirror_Context::event_mouse_motion_handler));
140 }
141
142 StateMirror::~StateMirror()
143 {
144 }
145
146 StateMirror_Context::StateMirror_Context(CanvasView* canvas_view):
147         canvas_view_(canvas_view),
148         is_working(*canvas_view),
149         duck_dragger_(new DuckDrag_Mirror())
150 {
151         // Set up the tool options dialog
152         options_table.attach(*manage(new Gtk::Label(_("Mirror Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
153         options_table.attach(*manage(new Gtk::Label(_("Shift to mirror horizontally"), Gtk::ALIGN_LEFT)), 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
154
155         options_table.show_all();
156         refresh_tool_options();
157         App::dialog_tool_options->present();
158
159         get_work_area()->set_allow_layer_clicks(true);
160         get_work_area()->set_duck_dragger(duck_dragger_);
161
162         get_work_area()->set_cursor(Gdk::SB_H_DOUBLE_ARROW);
163 //      get_work_area()->reset_cursor();
164
165         App::toolbox->refresh();
166
167         set_axis(AXIS_Y);
168 }
169
170 void
171 StateMirror_Context::refresh_tool_options()
172 {
173         App::dialog_tool_options->clear();
174         App::dialog_tool_options->set_widget(options_table);
175         App::dialog_tool_options->set_local_name(_("Mirror Tool"));
176         App::dialog_tool_options->set_name("mirror");
177 }
178
179 Smach::event_result
180 StateMirror_Context::event_refresh_tool_options(const Smach::event& /*x*/)
181 {
182         refresh_tool_options();
183         return Smach::RESULT_ACCEPT;
184 }
185
186 Smach::event_result
187 StateMirror_Context::event_stop_handler(const Smach::event& /*x*/)
188 {
189         throw &state_normal;
190         return Smach::RESULT_OK;
191 }
192
193 Smach::event_result
194 StateMirror_Context::event_mouse_motion_handler(const Smach::event& x)
195 {
196         // synfig::info("STATE NORMAL: Received mouse button down Event");
197
198         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
199
200         set_axis(event.modifier&GDK_SHIFT_MASK ? AXIS_X : AXIS_Y);
201
202         return Smach::RESULT_OK;
203 }
204
205 StateMirror_Context::~StateMirror_Context()
206 {
207         get_work_area()->clear_duck_dragger();
208         get_work_area()->reset_cursor();
209
210         App::dialog_tool_options->clear();
211
212         App::toolbox->refresh();
213 }
214
215 DuckDrag_Mirror::DuckDrag_Mirror():
216         axis(AXIS_X)
217 {
218 }
219
220 #ifndef EPSILON
221 #define EPSILON 0.0000001
222 #endif
223
224 void
225 DuckDrag_Mirror::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& /*offset*/)
226 {
227         const DuckList selected_ducks(duckmatic->get_selected_ducks());
228         DuckList::const_iterator iter;
229
230         positions.clear();
231         int i;
232         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
233         {
234                 Point p((*iter)->get_trans_point());
235                 positions.push_back(p);
236         }
237
238 }
239
240 void
241 DuckDrag_Mirror::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
242 {
243         center=vector;
244         int i;
245
246         const DuckList selected_ducks(duckmatic->get_selected_ducks());
247         DuckList::const_iterator iter;
248
249         Time time(duckmatic->get_time());
250
251         // do the Vertex and Position ducks first
252         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
253                 if ((*iter)->get_type() == Duck::TYPE_VERTEX ||
254                         (*iter)->get_type() == Duck::TYPE_POSITION)
255                 {
256                         Vector p(positions[i]);
257
258                         if              (axis==AXIS_X) p[0] = -(p[0]-center[0]) + center[0];
259                         else if (axis==AXIS_Y) p[1] = -(p[1]-center[1]) + center[1];
260
261                         (*iter)->set_trans_point(p);
262                 }
263
264         // then do the other ducks
265         for(i=0,iter=selected_ducks.begin();iter!=selected_ducks.end();++iter,i++)
266                 if ((*iter)->get_type() != Duck::TYPE_VERTEX &&
267                         (*iter)->get_type() != Duck::TYPE_POSITION)
268                 {
269                         // we don't need to mirror radius ducks - they're one-dimensional
270                         if ((*iter)->is_radius())
271                                 continue;
272
273                         Vector p(positions[i]);
274
275                         if              (axis==AXIS_X) p[0] = -(p[0]-center[0]) + center[0];
276                         else if (axis==AXIS_Y) p[1] = -(p[1]-center[1]) + center[1];
277
278                         (*iter)->set_trans_point(p);
279                 }
280 }
281
282 bool
283 DuckDrag_Mirror::end_duck_drag(Duckmatic* duckmatic)
284 {
285         duckmatic->signal_edited_selected_ducks();
286         return true;
287 }