c8461b5deb23a299f941434e8434e1ed26842245
[synfig.git] / state_gradient.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file state_gradient.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_gradient.h"
40 #include "canvasview.h"
41 #include "workarea.h"
42 #include "app.h"
43
44 #include <synfigapp/action.h>
45 #include "event_mouse.h"
46 #include "event_layerclick.h"
47 #include "toolbox.h"
48 #include "dialog_tooloptions.h"
49 #include <gtkmm/optionmenu.h>
50 #include "duck.h"
51
52 #include "widget_enum.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 enum
69 {
70         GRADIENT_INTERPOLATION_LINEAR=0,
71         GRADIENT_RADIAL=1,
72         GRADIENT_CONICAL=2,
73         GRADIENT_SPIRAL=3
74 };
75
76 /* === G L O B A L S ======================================================= */
77
78 StateGradient studio::state_gradient;
79
80 /* === C L A S S E S & S T R U C T S ======================================= */
81
82 class studio::StateGradient_Context : public sigc::trackable
83 {
84         etl::handle<CanvasView> canvas_view_;
85         CanvasView::IsWorking is_working;
86
87         Duckmatic::Push duckmatic_push;
88
89         synfigapp::Settings& settings;
90
91         Point point_holder;
92
93         etl::handle<Duck> point2_duck;
94
95         void refresh_ducks();
96
97         bool prev_workarea_layer_status_;
98
99         Gtk::Table options_table;
100         Gtk::Entry entry_id;
101         Widget_Enum enum_type;
102 #ifdef BLEND_METHOD_IN_TOOL_OPTIONS
103         Widget_Enum     enum_blend;
104 #endif  // BLEND_METHOD_IN_TOOL_OPTIONS
105
106 public:
107         synfig::String get_id()const { return entry_id.get_text(); }
108         void set_id(const synfig::String& x) { return entry_id.set_text(x); }
109
110         int get_type()const { return enum_type.get_value(); }
111         void set_type(int x) { return enum_type.set_value(x); }
112
113 #ifdef BLEND_METHOD_IN_TOOL_OPTIONS
114         int get_blend()const { return enum_blend.get_value(); }
115         void set_blend(int x) { return enum_blend.set_value(x); }
116 #endif  // BLEND_METHOD_IN_TOOL_OPTIONS
117
118         Smach::event_result event_stop_handler(const Smach::event& x);
119
120         Smach::event_result event_refresh_handler(const Smach::event& x);
121
122         Smach::event_result event_mouse_click_handler(const Smach::event& x);
123         Smach::event_result event_refresh_tool_options(const Smach::event& x);
124
125         void refresh_tool_options();
126
127         StateGradient_Context(CanvasView* canvas_view);
128
129         ~StateGradient_Context();
130
131         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
132         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
133         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
134         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
135
136         //void on_user_click(synfig::Point point);
137         void load_settings();
138         void save_settings();
139         void reset();
140         void increment_id();
141
142         void make_gradient(const Point& p1, const Point& p2);
143         bool egress_on_selection_change;
144         Smach::event_result event_layer_selection_changed_handler(const Smach::event& /*x*/)
145         {
146                 if(egress_on_selection_change)
147                         throw Smach::egress_exception();
148                 return Smach::RESULT_OK;
149         }
150
151 };      // END of class StateGradient_Context
152
153 /* === M E T H O D S ======================================================= */
154
155 StateGradient::StateGradient():
156         Smach::state<StateGradient_Context>("gradient")
157 {
158         insert(event_def(EVENT_LAYER_SELECTION_CHANGED,&StateGradient_Context::event_layer_selection_changed_handler));
159         insert(event_def(EVENT_STOP,&StateGradient_Context::event_stop_handler));
160         insert(event_def(EVENT_TABLES_SHOW,&StateGradient_Context::event_stop_handler));
161         insert(event_def(EVENT_REFRESH,&StateGradient_Context::event_refresh_handler));
162         insert(event_def(EVENT_REFRESH_DUCKS,&StateGradient_Context::event_refresh_handler));
163         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateGradient_Context::event_mouse_click_handler));
164         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateGradient_Context::event_mouse_click_handler));
165         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateGradient_Context::event_mouse_click_handler));
166         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateGradient_Context::event_refresh_tool_options));
167 }
168
169 StateGradient::~StateGradient()
170 {
171 }
172
173 void
174 StateGradient_Context::load_settings()
175 {
176         String value;
177
178         if(settings.get_value("gradient.id",value))
179                 set_id(value);
180         else
181                 set_id("Gradient");
182
183         if(settings.get_value("gradient.type",value))
184                 set_type(atoi(value.c_str()));
185         else
186                 set_type(GRADIENT_INTERPOLATION_LINEAR);
187
188 #ifdef BLEND_METHOD_IN_TOOL_OPTIONS
189         if(settings.get_value("gradient.blend",value))
190                 set_blend(atoi(value.c_str()));
191         else
192                 set_blend(Color::BLEND_COMPOSITE);
193 #endif  // BLEND_METHOD_IN_TOOL_OPTIONS
194 }
195
196 void
197 StateGradient_Context::save_settings()
198 {
199         settings.set_value("gradient.id",get_id().c_str());
200         settings.set_value("gradient.type",strprintf("%d",get_type()));
201 #ifdef BLEND_METHOD_IN_TOOL_OPTIONS
202         settings.set_value("gradient.blend",strprintf("%d",get_blend()));
203 #endif  // BLEND_METHOD_IN_TOOL_OPTIONS
204 }
205
206 void
207 StateGradient_Context::reset()
208 {
209         refresh_ducks();
210 }
211
212 void
213 StateGradient_Context::increment_id()
214 {
215         String id(get_id());
216         int number=1;
217         int digits=0;
218
219         if(id.empty())
220                 id="Gradient";
221
222         // If there is a number
223         // already at the end of the
224         // id, then remove it.
225         if(id[id.size()-1]<='9' && id[id.size()-1]>='0')
226         {
227                 // figure out how many digits it is
228                 for (digits = 0;
229                          (int)id.size()-1 >= digits && id[id.size()-1-digits] <= '9' && id[id.size()-1-digits] >= '0';
230                          digits++)
231                         ;
232
233                 String str_number;
234                 str_number=String(id,id.size()-digits,id.size());
235                 id=String(id,0,id.size()-digits);
236
237                 number=atoi(str_number.c_str());
238         }
239         else
240         {
241                 number=1;
242                 digits=3;
243         }
244
245         number++;
246
247         // Add the number back onto the id
248         {
249                 const String format(strprintf("%%0%dd",digits));
250                 id+=strprintf(format.c_str(),number);
251         }
252
253         // Set the ID
254         set_id(id);
255 }
256
257 StateGradient_Context::StateGradient_Context(CanvasView* canvas_view):
258         canvas_view_(canvas_view),
259         is_working(*canvas_view),
260         duckmatic_push(get_work_area()),
261         settings(synfigapp::Main::get_selected_input_device()->settings()),
262         prev_workarea_layer_status_(get_work_area()->get_allow_layer_clicks()),
263         entry_id()
264 {
265         egress_on_selection_change=true;
266         // Set up the tool options dialog
267         options_table.attach(*manage(new Gtk::Label(_("Gradient Tool"))),       0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
268         options_table.attach(entry_id,                                                                          0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
269
270         enum_type.set_param_desc(ParamDesc("type")
271                 .set_local_name(_("Gradient Type"))
272                 .set_description(_("Determines the type of Gradient used"))
273                 .set_hint("enum")
274                 .add_enum_value(GRADIENT_INTERPOLATION_LINEAR,"linear",_("Linear"))
275                 .add_enum_value(GRADIENT_RADIAL,"radial",_("Radial"))
276                 .add_enum_value(GRADIENT_CONICAL,"conical",_("Conical"))
277                 .add_enum_value(GRADIENT_SPIRAL,"spiral",_("Spiral")));
278
279 #ifdef BLEND_METHOD_IN_TOOL_OPTIONS
280         enum_blend.set_param_desc(ParamDesc(Color::BLEND_COMPOSITE,"blend_method")
281                 .set_local_name(_("Blend Method"))
282                 .set_description(_("The blend method the gradient will use")));
283 #endif  // BLEND_METHOD_IN_TOOL_OPTIONS
284
285         load_settings();
286
287         options_table.attach(enum_type, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
288 #ifdef BLEND_METHOD_IN_TOOL_OPTIONS
289         options_table.attach(enum_blend, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
290 #endif  // BLEND_METHOD_IN_TOOL_OPTIONS
291
292         options_table.show_all();
293         refresh_tool_options();
294         App::dialog_tool_options->present();
295
296
297         // Turn off layer clicking
298         get_work_area()->set_allow_layer_clicks(false);
299
300         get_canvas_view()->work_area->set_cursor(Gdk::CROSSHAIR);
301
302         // clear out the ducks
303         get_work_area()->clear_ducks();
304
305         // Refresh the work area
306         get_work_area()->queue_draw();
307
308         get_work_area()->refresh_cursor();
309
310         // Hide the tables if they are showing
311         get_canvas_view()->hide_tables();
312
313         // Disable the time bar
314         //get_canvas_view()->set_sensitive_timebar(false);
315
316         // Connect a signal
317         //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateGradient_Context::on_user_click));
318
319         App::toolbox->refresh();
320 }
321
322 void
323 StateGradient_Context::refresh_tool_options()
324 {
325         App::dialog_tool_options->clear();
326         App::dialog_tool_options->set_widget(options_table);
327         App::dialog_tool_options->set_local_name(_("Gradient Tool"));
328         App::dialog_tool_options->set_name("gradient");
329 }
330
331 Smach::event_result
332 StateGradient_Context::event_refresh_tool_options(const Smach::event& /*x*/)
333 {
334         refresh_tool_options();
335         return Smach::RESULT_ACCEPT;
336 }
337
338 StateGradient_Context::~StateGradient_Context()
339 {
340         save_settings();
341
342         // Restore layer clicking
343 //      get_work_area()->set_allow_layer_clicks(prev_workarea_layer_status_);
344         get_work_area()->set_allow_layer_clicks(true);
345         get_canvas_view()->work_area->reset_cursor();
346
347         App::dialog_tool_options->clear();
348
349         // Enable the time bar
350         //get_canvas_view()->set_sensitive_timebar(true);
351
352         // Bring back the tables if they were out before
353         //if(prev_table_status)get_canvas_view()->show_tables();
354
355         // Refresh the work area
356         get_work_area()->queue_draw();
357
358         get_canvas_view()->queue_rebuild_ducks();
359
360         //get_canvas_view()->show_tables();
361
362         get_work_area()->refresh_cursor();
363
364         App::toolbox->refresh();
365 }
366
367 Smach::event_result
368 StateGradient_Context::event_stop_handler(const Smach::event& /*x*/)
369 {
370         throw Smach::egress_exception();
371 }
372
373 Smach::event_result
374 StateGradient_Context::event_refresh_handler(const Smach::event& /*x*/)
375 {
376         refresh_ducks();
377         return Smach::RESULT_ACCEPT;
378 }
379
380 void
381 StateGradient_Context::make_gradient(const Point& _p1, const Point& _p2)
382 {
383         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("New Gradient"));
384         synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
385
386         Layer::Handle layer;
387
388         Canvas::Handle canvas(get_canvas_view()->get_canvas());
389         int depth(0);
390
391         // we are temporarily using the layer to hold something
392         layer=get_canvas_view()->get_selection_manager()->get_selected_layer();
393         if(layer)
394         {
395                 depth=layer->get_depth();
396                 canvas=layer->get_canvas();
397         }
398         const synfig::TransformStack& transform(get_canvas_view()->get_curr_transform_stack());
399         const Point p1(transform.unperform(_p1));
400         const Point p2(transform.unperform(_p2));
401
402         switch(get_type())
403         {
404         case GRADIENT_INTERPOLATION_LINEAR:
405
406                 layer=get_canvas_interface()->add_layer_to("linear_gradient",canvas,depth);
407                 if (!layer)
408                 {
409                         get_canvas_view()->get_ui_interface()->error(_("Unable to create layer"));
410                         group.cancel();
411                         return;
412                 }
413                 layer->set_param("p1",p1);
414                 get_canvas_interface()->signal_layer_param_changed()(layer,"p1");
415                 layer->set_param("p2",p2);
416                 get_canvas_interface()->signal_layer_param_changed()(layer,"p2");
417                 break;
418         case GRADIENT_RADIAL:
419                 layer=get_canvas_interface()->add_layer_to("radial_gradient",canvas,depth);
420                 if (!layer)
421                 {
422                         get_canvas_view()->get_ui_interface()->error(_("Unable to create layer"));
423                         group.cancel();
424                         return;
425                 }
426                 layer->set_param("center",p1);
427                 get_canvas_interface()->signal_layer_param_changed()(layer,"center");
428                 layer->set_param("radius",(p2-p1).mag());
429                 get_canvas_interface()->signal_layer_param_changed()(layer,"radius");
430                 break;
431         case GRADIENT_CONICAL:
432                 layer=get_canvas_interface()->add_layer_to("conical_gradient",canvas,depth);
433                 if (!layer)
434                 {
435                         get_canvas_view()->get_ui_interface()->error(_("Unable to create layer"));
436                         group.cancel();
437                         return;
438                 }
439                 layer->set_param("center",p1);
440                 get_canvas_interface()->signal_layer_param_changed()(layer,"center");
441                 {
442                         Vector diff(p2-p1);
443                         layer->set_param("angle",Angle::tan(diff[1],diff[0]));
444                         get_canvas_interface()->signal_layer_param_changed()(layer,"angle");
445                 }
446                 break;
447         case GRADIENT_SPIRAL:
448                 layer=get_canvas_interface()->add_layer_to("spiral_gradient",canvas,depth);
449                 if (!layer)
450                 {
451                         get_canvas_view()->get_ui_interface()->error(_("Unable to create layer"));
452                         group.cancel();
453                         return;
454                 }
455                 layer->set_param("center",p1);
456                 get_canvas_interface()->signal_layer_param_changed()(layer,"center");
457                 layer->set_param("radius",(p2-p1).mag());
458                 get_canvas_interface()->signal_layer_param_changed()(layer,"radius");
459                 {
460                         Vector diff(p2-p1);
461                         layer->set_param("angle",Angle::tan(diff[1],diff[0]));
462                         get_canvas_interface()->signal_layer_param_changed()(layer,"angle");
463                 }
464                 break;
465
466         default:
467                 return;
468         }
469
470 #ifdef BLEND_METHOD_IN_TOOL_OPTIONS
471         layer->set_param("blend_method",get_blend());
472         get_canvas_interface()->signal_layer_param_changed()(layer,"blend_method");
473 #endif  // BLEND_METHOD_IN_TOOL_OPTIONS
474
475         layer->set_description(get_id());
476         get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
477
478         egress_on_selection_change=false;
479         synfigapp::SelectionManager::LayerList layer_selection;
480         if (!getenv("SYNFIG_TOOLS_CLEAR_SELECTION"))
481                 layer_selection = get_canvas_view()->get_selection_manager()->get_selected_layers();
482         get_canvas_interface()->get_selection_manager()->clear_selected_layers();
483         layer_selection.push_back(layer);
484         get_canvas_interface()->get_selection_manager()->set_selected_layers(layer_selection);
485         egress_on_selection_change=true;
486
487         reset();
488         increment_id();
489 }
490
491 Smach::event_result
492 StateGradient_Context::event_mouse_click_handler(const Smach::event& x)
493 {
494         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
495
496         if(event.key==EVENT_WORKAREA_MOUSE_BUTTON_DOWN && event.button==BUTTON_LEFT)
497         {
498                 point_holder=get_work_area()->snap_point_to_grid(event.pos);
499                 etl::handle<Duck> duck=new Duck();
500                 duck->set_point(point_holder);
501                 duck->set_name("p1");
502                 duck->set_type(Duck::TYPE_POSITION);
503                 get_work_area()->add_duck(duck);
504
505                 point2_duck=new Duck();
506                 point2_duck->set_point(point_holder);
507                 point2_duck->set_name("p2");
508                 point2_duck->set_type(Duck::TYPE_POSITION);
509                 get_work_area()->add_duck(point2_duck);
510
511                 handle<Duckmatic::Bezier> bezier(new Duckmatic::Bezier());
512                 bezier->p1=bezier->c1=duck;
513                 bezier->p2=bezier->c2=point2_duck;
514                 get_work_area()->add_bezier(bezier);
515
516                 return Smach::RESULT_ACCEPT;
517         }
518
519         if(event.key==EVENT_WORKAREA_MOUSE_BUTTON_DRAG && event.button==BUTTON_LEFT)
520         {
521                 if (!point2_duck) return Smach::RESULT_OK;
522                 point2_duck->set_point(get_work_area()->snap_point_to_grid(event.pos));
523                 get_work_area()->queue_draw();
524                 return Smach::RESULT_ACCEPT;
525         }
526
527         if(event.key==EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button==BUTTON_LEFT)
528         {
529                 make_gradient(point_holder, get_work_area()->snap_point_to_grid(event.pos));
530                 get_work_area()->clear_ducks();
531                 return Smach::RESULT_ACCEPT;
532         }
533
534         return Smach::RESULT_OK;
535 }
536
537
538 void
539 StateGradient_Context::refresh_ducks()
540 {
541         get_work_area()->clear_ducks();
542         get_work_area()->queue_draw();
543 }