Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / synfigstudio_0_61_06 / src / gtkmm / state_bline.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file state_bline.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include <gtkmm/dialog.h>
33 #include <gtkmm/entry.h>
34
35 #include <synfig/valuenode_dynamiclist.h>
36
37 #include "state_bline.h"
38 #include "canvasview.h"
39 #include "workarea.h"
40 #include "app.h"
41 #include <synfig/valuenode_bline.h>
42 #include <ETL/hermite>
43 #include <ETL/calculus>
44 #include <utility>
45 #include "event_mouse.h"
46 #include "event_layerclick.h"
47 #include "toolbox.h"
48 #include "dialog_tooloptions.h"
49 #include <gtkmm/spinbutton.h>
50 #include <synfig/transform.h>
51 #include <synfigapp/main.h>
52
53 #endif
54
55 /* === U S I N G =========================================================== */
56
57 using namespace std;
58 using namespace etl;
59 using namespace synfig;
60 using namespace studio;
61
62 /* === M A C R O S ========================================================= */
63
64 /* === G L O B A L S ======================================================= */
65
66 StateBLine studio::state_bline;
67
68 /* === C L A S S E S & S T R U C T S ======================================= */
69
70 class studio::StateBLine_Context : public sigc::trackable
71 {
72         etl::handle<CanvasView> canvas_view_;
73         CanvasView::IsWorking is_working;
74
75         bool prev_table_status;
76         bool loop_;
77         bool prev_workarea_layer_status_;
78
79         int depth;
80         Canvas::Handle canvas;
81
82         Gtk::Menu menu;
83
84         Duckmatic::Push duckmatic_push;
85
86         etl::handle<Duck> curr_duck;
87
88         etl::handle<Duck> next_duck;
89
90         std::list<synfig::ValueNode_Const::Handle> bline_point_list;
91         synfigapp::Settings& settings;
92
93         bool on_vertex_change(const synfig::Point &point, synfig::ValueNode_Const::Handle value_node);
94         bool on_tangent1_change(const synfig::Point &point, synfig::ValueNode_Const::Handle value_node);
95         bool on_tangent2_change(const synfig::Point &point, synfig::ValueNode_Const::Handle value_node);
96
97
98         void popup_handle_menu(synfig::ValueNode_Const::Handle value_node);
99         void popup_vertex_menu(synfig::ValueNode_Const::Handle value_node);
100         void popup_bezier_menu(float location, synfig::ValueNode_Const::Handle value_node);
101
102         void bline_detach_handle(synfig::ValueNode_Const::Handle value_node);
103         void bline_attach_handle(synfig::ValueNode_Const::Handle value_node);
104         void bline_delete_vertex(synfig::ValueNode_Const::Handle value_node);
105         void bline_insert_vertex(synfig::ValueNode_Const::Handle value_node,float origin=0.5);
106         void loop_bline();
107         void unloop_bline();
108
109         void refresh_ducks(bool x=true);
110
111         Gtk::Table options_table;
112         Gtk::Entry entry_id;
113         Gtk::CheckButton checkbutton_layer_region;
114         Gtk::CheckButton checkbutton_layer_bline;
115         Gtk::CheckButton checkbutton_layer_curve_gradient;
116         Gtk::CheckButton checkbutton_auto_export;
117         Gtk::Button button_make;
118         Gtk::Button button_clear;
119         Gtk::Adjustment  adj_feather;
120         Gtk::SpinButton  spin_feather;
121
122
123
124 public:
125
126         int layers_to_create()const
127         {
128                 return
129                         get_layer_region_flag()+
130                         get_layer_bline_flag()+
131                         get_layer_curve_gradient_flag();
132         }
133
134         void sanity_check()
135         {
136                 if(layers_to_create()==0)
137                         set_layer_region_flag(true);
138         }
139
140         bool get_auto_export_flag()const { return checkbutton_auto_export.get_active(); }
141         void set_auto_export_flag(bool x) { return checkbutton_auto_export.set_active(x); }
142
143         bool get_layer_region_flag()const { return checkbutton_layer_region.get_active(); }
144         void set_layer_region_flag(bool x) { return checkbutton_layer_region.set_active(x); }
145
146         bool get_layer_bline_flag()const { return checkbutton_layer_bline.get_active(); }
147         void set_layer_bline_flag(bool x) { return checkbutton_layer_bline.set_active(x); }
148
149         bool get_layer_curve_gradient_flag()const { return checkbutton_layer_curve_gradient.get_active(); }
150         void set_layer_curve_gradient_flag(bool x) { return checkbutton_layer_curve_gradient.set_active(x); }
151
152         Real get_feather() const { return adj_feather.get_value(); }
153         void set_feather(Real x) { return adj_feather.set_value(x); }
154         synfig::String get_id()const { return entry_id.get_text(); }
155         void set_id(const synfig::String& x) { return entry_id.set_text(x); }
156
157         Smach::event_result event_stop_handler(const Smach::event& x);
158
159         Smach::event_result event_refresh_handler(const Smach::event& x);
160
161         Smach::event_result event_mouse_click_handler(const Smach::event& x);
162         Smach::event_result event_mouse_release_handler(const Smach::event& x);
163         Smach::event_result event_mouse_motion_handler(const Smach::event& x);
164         Smach::event_result event_refresh_tool_options(const Smach::event& x);
165
166         Smach::event_result event_hijack(const Smach::event& x) { return Smach::RESULT_ACCEPT; }
167
168         void refresh_tool_options();
169
170         StateBLine_Context(CanvasView* canvas_view);
171
172         ~StateBLine_Context();
173
174         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
175         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
176         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
177         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
178         const synfig::TransformStack& get_transform_stack()const { return canvas_view_->get_curr_transform_stack(); }
179
180         void load_settings();
181         void save_settings();
182         void reset();
183         void increment_id();
184         //void on_user_click(synfig::Point point);
185
186         bool run_();
187         bool run();
188
189         bool no_egress_on_selection_change;
190         Smach::event_result event_layer_selection_changed_handler(const Smach::event& x)
191         {
192                 if(!no_egress_on_selection_change)
193                         throw Smach::egress_exception();
194                 return Smach::RESULT_OK;
195         }
196
197 };      // END of class StateBLine_Context
198
199
200 /* === M E T H O D S ======================================================= */
201
202 StateBLine::StateBLine():
203         Smach::state<StateBLine_Context>("bline")
204 {
205         insert(event_def(EVENT_LAYER_SELECTION_CHANGED,&StateBLine_Context::event_layer_selection_changed_handler));
206         insert(event_def(EVENT_STOP,&StateBLine_Context::event_stop_handler));
207         insert(event_def(EVENT_REFRESH,&StateBLine_Context::event_refresh_handler));
208         insert(event_def(EVENT_REFRESH_DUCKS,&StateBLine_Context::event_hijack));
209         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateBLine_Context::event_mouse_click_handler));
210         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateBLine_Context::event_mouse_release_handler));
211         insert(event_def(EVENT_WORKAREA_MOUSE_MOTION,&StateBLine_Context::event_mouse_motion_handler));
212         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateBLine_Context::event_mouse_motion_handler));
213         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateBLine_Context::event_refresh_tool_options));
214 }
215
216 StateBLine::~StateBLine()
217 {
218 }
219
220 void
221 StateBLine_Context::load_settings()
222 {
223         String value;
224
225         if(settings.get_value("bline.layer_region",value) && value=="0")
226                 set_layer_region_flag(false);
227         else
228                 set_layer_region_flag(true);
229
230         if(settings.get_value("bline.layer_bline",value) && value=="0")
231                 set_layer_bline_flag(false);
232         else
233                 set_layer_bline_flag(true);
234
235         if(settings.get_value("bline.layer_curve_gradient",value) && value=="1")
236                 set_layer_curve_gradient_flag(true);
237         else
238                 set_layer_curve_gradient_flag(false);
239
240         if(settings.get_value("bline.auto_export",value) && value=="1")
241                 set_auto_export_flag(true);
242         else
243                 set_auto_export_flag(false);
244
245         if(settings.get_value("bline.id",value))
246                 set_id(value);
247         else
248                 set_id("NewBLine");
249
250         if(settings.get_value("bline.feather",value))
251         {
252                 Real n = atof(value.c_str());
253                 set_feather(n);
254         }
255
256         sanity_check();
257 }
258
259 void
260 StateBLine_Context::save_settings()
261 {
262         sanity_check();
263         settings.set_value("bline.layer_bline",get_layer_bline_flag()?"1":"0");
264         settings.set_value("bline.layer_region",get_layer_region_flag()?"1":"0");
265         settings.set_value("bline.layer_curve_gradient",get_layer_curve_gradient_flag()?"1":"0");
266         settings.set_value("bline.auto_export",get_auto_export_flag()?"1":"0");
267         settings.set_value("bline.id",get_id().c_str());
268         settings.set_value("bline.feather",strprintf("%f",get_feather()));
269 }
270
271 void
272 StateBLine_Context::reset()
273 {
274         loop_=false;
275         bline_point_list.clear();
276         refresh_ducks();
277 }
278
279 void
280 StateBLine_Context::increment_id()
281 {
282         String id(get_id());
283         int number=1;
284         int digits=0;
285
286         if(id.empty())
287                 id="NewBLine";
288
289         // If there is a number
290         // already at the end of the
291         // id, then remove it.
292         if(id[id.size()-1]<='9' && id[id.size()-1]>='0')
293         {
294                 // figure out how many digits it is
295                 for(digits=0;(int)id.size()-1>=digits && id[id.size()-1-digits]<='9' && id[id.size()-1-digits]>='0';digits++)while(false);
296
297                 String str_number;
298                 str_number=String(id,id.size()-digits,id.size());
299                 id=String(id,0,id.size()-digits);
300                 synfig::info("---------------- \"%s\"",str_number.c_str());
301
302                 number=atoi(str_number.c_str());
303         }
304         else
305         {
306                 number=1;
307                 digits=3;
308         }
309
310         number++;
311
312         // Add the number back onto the id
313         {
314                 const String format(strprintf("%%0%dd",digits));
315                 id+=strprintf(format.c_str(),number);
316         }
317
318         // Set the ID
319         set_id(id);
320 }
321
322
323 StateBLine_Context::StateBLine_Context(CanvasView* canvas_view):
324         canvas_view_(canvas_view),
325         is_working(*canvas_view),
326         loop_(false),
327         prev_workarea_layer_status_(get_work_area()->allow_layer_clicks),
328         duckmatic_push(get_work_area()),
329         settings(synfigapp::Main::get_selected_input_device()->settings()),
330         entry_id(),
331         checkbutton_layer_region(_("Fill")),
332         checkbutton_layer_bline(_("Outline")),
333         checkbutton_layer_curve_gradient(_("Gradient")),
334         checkbutton_auto_export(_("Auto Export")),
335         button_make(_("Make")),
336         button_clear(_("Clear")),
337         adj_feather(0,0,10000,0.01,0.1),
338         spin_feather(adj_feather,0.01,4)
339 {
340         depth=-1;
341         no_egress_on_selection_change=false;
342         load_settings();
343
344         // Set up the tool options dialog
345         //options_table.attach(*manage(new Gtk::Label(_("BLine Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
346         options_table.attach(entry_id, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
347         options_table.attach(checkbutton_layer_region, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
348         options_table.attach(checkbutton_layer_bline, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
349         options_table.attach(checkbutton_layer_curve_gradient, 0, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
350         options_table.attach(*manage(new Gtk::Label(_("Feather"))), 0, 1, 10, 11, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
351         options_table.attach(spin_feather, 1, 2, 10, 11, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
352         options_table.attach(checkbutton_auto_export, 0, 2, 11, 12, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
353         //options_table.attach(button_make, 0, 2, 5, 6, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
354         //button_make.signal_pressed().connect(sigc::mem_fun(*this,&StateBLine_Context::run));
355         options_table.show_all();
356         refresh_tool_options();
357         App::dialog_tool_options->present();
358
359         // Turn off layer clicking
360         get_work_area()->allow_layer_clicks=false;
361
362         // clear out the ducks
363         get_work_area()->clear_ducks();
364
365         // Refresh the work area
366         get_work_area()->queue_draw();
367
368         // Hide the tables if they are showing
369         prev_table_status=get_canvas_view()->tables_are_visible();
370         if(prev_table_status)get_canvas_view()->hide_tables();
371
372         // Disable the time bar
373         get_canvas_view()->set_sensitive_timebar(false);
374
375         // Connect a signal
376         //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateBLine_Context::on_user_click));
377         get_canvas_view()->work_area->set_cursor(Gdk::CROSSHAIR);
378
379         App::toolbox->refresh();
380 }
381
382 void
383 StateBLine_Context::refresh_tool_options()
384 {
385         App::dialog_tool_options->clear();
386         App::dialog_tool_options->set_widget(options_table);
387         App::dialog_tool_options->set_local_name(_("BLine Tool"));
388         App::dialog_tool_options->set_name("bline");
389
390         App::dialog_tool_options->add_button(
391                 Gtk::StockID("gtk-execute"),
392                 _("Make BLine and/or Region")
393         )->signal_clicked().connect(
394                 sigc::hide_return(sigc::mem_fun(
395                         *this,
396                         &StateBLine_Context::run
397                 ))
398         );
399
400         App::dialog_tool_options->add_button(
401                 Gtk::StockID("gtk-clear"),
402                 _("Clear current BLine")
403         )->signal_clicked().connect(
404                 sigc::mem_fun(
405                         *this,
406                         &StateBLine_Context::reset
407                 )
408         );
409 }
410
411 Smach::event_result
412 StateBLine_Context::event_refresh_tool_options(const Smach::event& x)
413 {
414         refresh_tool_options();
415         return Smach::RESULT_ACCEPT;
416 }
417
418 StateBLine_Context::~StateBLine_Context()
419 {
420         run();
421
422         save_settings();
423         App::dialog_tool_options->clear();
424
425         get_canvas_view()->work_area->reset_cursor();
426
427         // Restore layer clicking
428         get_work_area()->allow_layer_clicks=prev_workarea_layer_status_;
429
430         // Enable the time bar
431         get_canvas_view()->set_sensitive_timebar(true);
432
433         // Bring back the tables if they were out before
434         if(prev_table_status)get_canvas_view()->show_tables();
435
436 //      get_canvas_view()->get_smach().process_event(EVENT_REFRESH_DUCKS);
437
438         // Refresh the work area
439         get_work_area()->queue_draw();
440
441         App::toolbox->refresh();
442 }
443
444 Smach::event_result
445 StateBLine_Context::event_stop_handler(const Smach::event& x)
446 {
447         synfig::info("STATE RotoBLine: Received Stop Event");
448 //      run();
449         reset();
450 //      throw Smach::egress_exception();
451 //      get_canvas_view()->get_smach().pop_state();
452         return Smach::RESULT_ACCEPT;
453 }
454
455 Smach::event_result
456 StateBLine_Context::event_refresh_handler(const Smach::event& x)
457 {
458         synfig::info("STATE RotoBLine: Received Refresh Event");
459         refresh_ducks();
460         return Smach::RESULT_ACCEPT;
461 }
462
463 bool
464 StateBLine_Context::run()
465 {
466         sanity_check();
467
468         String err;
469         bool success(false);
470         for(int i=5;i>0 && !success;i--)try
471         {
472                 success=run_();
473         }
474         catch(String s)
475         {
476                 err=s;
477         }
478         if(!success && !err.empty())
479         {
480                 get_canvas_view()->get_ui_interface()->error(err);
481         }
482         return success;
483 }
484
485 bool
486 StateBLine_Context::run_()
487 {
488         curr_duck=0;
489         next_duck=0;
490
491         // Now we need to generate it
492
493         if(bline_point_list.empty())
494         {
495                 return false;
496         }
497         if(bline_point_list.size()<2)
498         {
499                 //get_canvas_view()->get_ui_interface()->error(_("You need at least two (2) points to create a BLine"));
500                 return false;
501         }
502
503         do
504         {
505
506                 // Create the action group
507                 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("New BLine"));
508
509                 std::vector<BLinePoint> new_list;
510                 std::list<synfig::ValueNode_Const::Handle>::iterator iter;
511                 const synfig::TransformStack& transform(get_transform_stack());
512
513                 for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
514                 {
515                         BLinePoint bline_point((*iter)->get_value().get(BLinePoint()));
516                         Point new_vertex(transform.unperform(bline_point.get_vertex()));
517
518                         bline_point.set_tangent1(
519                                 transform.unperform(
520                                         bline_point.get_tangent1()+bline_point.get_vertex()
521                                 ) -new_vertex
522                         );
523
524                         bline_point.set_tangent2(
525                                 transform.unperform(
526                                         bline_point.get_tangent2()+bline_point.get_vertex()
527                                 ) -new_vertex
528                         );
529
530                         bline_point.set_vertex(new_vertex);
531
532                         new_list.push_back(bline_point);
533                 }
534
535                 ValueNode_BLine::Handle value_node_bline(ValueNode_BLine::create(new_list));
536
537                 assert(value_node_bline);
538
539                 // Set the looping flag
540                 value_node_bline->set_loop(loop_);
541
542                 // Add the BLine to the canvas
543                 if(get_auto_export_flag() && !get_canvas_interface()->add_value_node(value_node_bline,get_id()))
544                 {
545                         //get_canvas_view()->get_ui_interface()->error(_("Unable to add value node"));
546                         group.cancel();
547                         increment_id();
548                         throw String(_("Unable to add value node"));
549                         return false;
550                 }
551
552                 Layer::Handle layer;
553
554                 // we are temporarily using the layer to hold something
555                 layer=get_canvas_view()->get_selection_manager()->get_selected_layer();
556
557                 if(layer)
558                 {
559                         if(depth<0)
560                                 depth=layer->get_depth();
561                         if(!canvas)
562                                 canvas=layer->get_canvas();
563                 }
564                 else
565                         depth=0;
566
567                 if(!canvas)
568                         canvas=get_canvas_view()->get_canvas();
569
570                 synfigapp::SelectionManager::LayerList layer_selection;
571
572                 // If we were asked to create a region layer, go ahead and do so
573                 if(get_layer_region_flag())
574                 {
575                         synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
576
577                         Layer::Handle layer(get_canvas_interface()->add_layer_to("region",canvas,depth));
578                         layer_selection.push_back(layer);
579                         assert(layer);
580                         layer->set_description(get_id()+_(" Region"));
581                         get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
582
583                         if(get_feather())
584                         {
585                                 layer->set_param("feather",get_feather());
586                                 get_canvas_interface()->signal_layer_param_changed()(layer,"feather");
587                         }
588
589                         if(get_layer_bline_flag())
590                                 layer->set_param("color",synfigapp::Main::get_background_color());
591
592                         synfigapp::Action::Handle action(synfigapp::Action::create("layer_param_connect"));
593
594                         assert(action);
595
596                         action->set_param("canvas",get_canvas());
597                         action->set_param("canvas_interface",get_canvas_interface());
598                         action->set_param("layer",layer);
599                         if(!action->set_param("param",String("bline")))
600                                 synfig::error("LayerParamConnect didn't like \"param\"");
601                         if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
602                                 synfig::error("LayerParamConnect didn't like \"value_node\"");
603
604                         if(!get_canvas_interface()->get_instance()->perform_action(action))
605                         {
606                                 //get_canvas_view()->get_ui_interface()->error(_("Unable to create Region layer"));
607                                 group.cancel();
608                                 throw String(_("Unable to create Region layer"));
609                                 return false;
610                         }
611                 }
612
613                 // If we were asked to create a BLine layer, go ahead and do so
614                 if(get_layer_bline_flag())
615                 {
616                         synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
617
618                         Layer::Handle layer(get_canvas_interface()->add_layer_to("outline",canvas,depth));
619                         layer_selection.push_back(layer);
620                         layer->set_description(get_id()+_(" Outline"));
621                         get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
622                         if(get_feather())
623                         {
624                                 layer->set_param("feather",get_feather());
625                                 get_canvas_interface()->signal_layer_param_changed()(layer,"feather");
626                         }
627
628                         assert(layer);
629
630
631                         synfigapp::Action::Handle action(synfigapp::Action::create("layer_param_connect"));
632
633                         assert(action);
634
635                         action->set_param("canvas",get_canvas());
636                         action->set_param("canvas_interface",get_canvas_interface());
637                         action->set_param("layer",layer);
638                         if(!action->set_param("param",String("bline")))
639                                 synfig::error("LayerParamConnect didn't like \"param\"");
640                         if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
641                                 synfig::error("LayerParamConnect didn't like \"value_node\"");
642
643                         if(!get_canvas_interface()->get_instance()->perform_action(action))
644                         {
645                                 //get_canvas_view()->get_ui_interface()->error(_("Unable to create BLine layer"));
646                                 group.cancel();
647                                 throw String(_("Unable to create Outline layer"));
648                                 return false;
649                         }
650
651                         /*if(get_layer_region_flag() && !get_auto_export_flag())
652                         {
653                                 get_canvas_interface()->auto_export(synfigapp::ValueDesc(layer,"bline"));
654                         }*/
655                 }
656
657
658
659                 // If we were asked to create a CurveGradient layer, go ahead and do so
660                 if(get_layer_curve_gradient_flag())
661                 {
662                         synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
663
664                         Layer::Handle layer(get_canvas_interface()->add_layer_to("curve_gradient",canvas,depth));
665                         layer_selection.push_back(layer);
666                         layer->set_description(get_id()+_(" Gradient"));
667                         get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
668
669                         assert(layer);
670
671
672                         synfigapp::Action::Handle action(synfigapp::Action::create("layer_param_connect"));
673
674                         assert(action);
675
676                         action->set_param("canvas",get_canvas());
677                         action->set_param("canvas_interface",get_canvas_interface());
678                         action->set_param("layer",layer);
679                         if(!action->set_param("param",String("bline")))
680                                 synfig::error("LayerParamConnect didn't like \"param\"");
681                         if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
682                                 synfig::error("LayerParamConnect didn't like \"value_node\"");
683
684                         if(!get_canvas_interface()->get_instance()->perform_action(action))
685                         {
686                                 //get_canvas_view()->get_ui_interface()->error(_("Unable to create BLine layer"));
687                                 group.cancel();
688                                 throw String(_("Unable to create Gradient layer"));
689                                 return false;
690                         }
691
692                         /*if(get_layer_region_flag() && !get_auto_export_flag())
693                         {
694                                 get_canvas_interface()->auto_export(synfigapp::ValueDesc(layer,"bline"));
695                         }*/
696                 }
697
698                 no_egress_on_selection_change=true;
699                 get_canvas_interface()->get_selection_manager()->clear_selected_layers();
700                 get_canvas_interface()->get_selection_manager()->set_selected_layers(layer_selection);
701                 no_egress_on_selection_change=false;
702
703                 //if(finish_bline_dialog.get_region_flag() || finish_bline_dialog.get_bline_flag())
704                 //      get_canvas_interface()->signal_dirty_preview()();
705
706         } while(0);
707
708         reset();
709         increment_id();
710         return true;
711 }
712
713 Smach::event_result
714 StateBLine_Context::event_mouse_motion_handler(const Smach::event& x)
715 {
716         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
717
718         if(curr_duck)
719         {
720                 //synfig::info("Moved Duck");
721                 Point p(get_work_area()->snap_point_to_grid(event.pos));
722                 curr_duck->set_trans_point(p);
723                 if(next_duck)
724                         next_duck->set_trans_point(p);
725                 get_work_area()->queue_draw();
726                 return Smach::RESULT_ACCEPT;
727         }
728
729         return Smach::RESULT_OK;
730 }
731
732 Smach::event_result
733 StateBLine_Context::event_mouse_release_handler(const Smach::event& x)
734 {
735         if(curr_duck)
736         {
737                 //synfig::info("Released current duck");
738                 curr_duck->signal_edited()(curr_duck->get_point());
739                 if(next_duck)
740                 {
741                         //synfig::info("grabbing next duck");
742                         curr_duck=next_duck;
743                         next_duck=0;
744                 }
745                 return Smach::RESULT_ACCEPT;
746         }
747         return Smach::RESULT_OK;
748 }
749
750 Smach::event_result
751 StateBLine_Context::event_mouse_click_handler(const Smach::event& x)
752 {
753         synfig::info("STATE BLINE: Received mouse button down Event");
754         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
755         switch(event.button)
756         {
757         case BUTTON_LEFT:
758                 {
759                         // If we are already looped up, then don't try to add anything else
760                         if(loop_)
761                                 return Smach::RESULT_OK;
762
763                         BLinePoint bline_point;
764
765                         bline_point.set_vertex(get_work_area()->snap_point_to_grid(event.pos));
766                         //bline_point.set_width(synfigapp::Main::get_bline_width());
767                         bline_point.set_width(1.0f);
768                         bline_point.set_origin(0.5f);
769                         bline_point.set_split_tangent_flag(false);
770                         bline_point.set_tangent1(Vector(0,0));
771
772                         // set the tangent
773                         /*
774                         if(bline_point_list.empty())
775                         {
776                                 bline_point.set_tangent1(Vector(1,1));
777                         }
778                         else
779                         {
780                                 const Vector t(event.pos-bline_point_list.back()->get_value().get(BLinePoint()).get_vertex());
781                                 bline_point.set_tangent1(t);
782                         }
783
784                         if(bline_point_list.size()>1)
785                         {
786                                 std::list<synfig::ValueNode_Const::Handle>::iterator iter;
787                                 iter=bline_point_list.end();
788                                 iter--;iter--;
789                                 BLinePoint prev(bline_point_list.back()->get_value().get(BLinePoint()));
790                                 prev.set_tangent1(event.pos-(*iter)->get_value().get(BLinePoint()).get_vertex());
791                                 bline_point_list.back()->set_value(prev);
792                         };
793                         */
794
795                         bline_point_list.push_back(ValueNode_Const::create(bline_point));
796
797                         refresh_ducks();
798                         return Smach::RESULT_ACCEPT;
799                 }
800
801         case BUTTON_RIGHT: // Intercept the right-button click to short-circut the pop-up menu
802                 return Smach::RESULT_ACCEPT;
803
804         default:
805                 return Smach::RESULT_OK;
806         }
807 }
808
809 void
810 StateBLine_Context::refresh_ducks(bool button_down)
811 {
812         get_work_area()->clear_ducks();
813         get_work_area()->queue_draw();
814
815         if(bline_point_list.empty())
816                 return;
817
818         list<ValueNode_Const::Handle>::iterator iter;
819
820         handle<WorkArea::Bezier> bezier;
821         handle<WorkArea::Duck> duck,tduck;
822         BLinePoint bline_point;
823
824         for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
825         {
826                 ValueNode_Const::Handle value_node(*iter);
827                 bline_point=(value_node->get_value().get(BLinePoint()));
828                 assert(value_node);
829
830
831                 // First add the duck associated with this vertex
832                 duck=new WorkArea::Duck(bline_point.get_vertex());
833                 duck->set_editable(true);
834                 duck->set_type(Duck::TYPE_VERTEX);
835                 duck->set_name(strprintf("%x-vertex",value_node.get()));
836                 duck->signal_edited().connect(
837                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_vertex_change),value_node)
838                 );
839                 duck->signal_user_click(2).connect(
840                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_vertex_menu),value_node)
841                 );
842                 duck->set_guid(value_node->get_guid()^GUID::hasher(0));
843
844                 get_work_area()->add_duck(duck);
845
846                 // Add the tangent1 duck
847                 tduck=new WorkArea::Duck(bline_point.get_tangent1());
848                 tduck->set_editable(true);
849                 tduck->set_name(strprintf("%x-tangent1",value_node.get()));
850                 tduck->set_origin(duck);
851                 tduck->set_scalar(-0.33333333333333333);
852                 tduck->set_tangent(true);
853                 tduck->set_guid(value_node->get_guid()^GUID::hasher(3));
854                 tduck->signal_edited().connect(
855                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_tangent1_change),value_node)
856                 );
857                 tduck->signal_user_click(2).connect(
858                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_handle_menu),value_node)
859                 );
860
861                 // See if we need to add that duck to the previous bezier
862                 if(bezier)
863                 {
864                         get_work_area()->add_duck(tduck);
865                         bezier->p2=duck;
866                         bezier->c2=tduck;
867
868                         bezier->signal_user_click(2).connect(
869                                 sigc::bind(
870                                         sigc::mem_fun(
871                                                 *this,
872                                                 &studio::StateBLine_Context::popup_bezier_menu
873                                         ),
874                                         value_node
875                                 )
876                         );
877
878                         //get_work_area()->add_duck(bezier->c1);
879                         //get_work_area()->add_duck(bezier->c2);
880                         get_work_area()->add_bezier(bezier);
881
882                         bezier=0;
883                 }
884
885                 // Now we see if we need to create a bezier
886                 list<ValueNode_Const::Handle>::iterator next(iter);
887                 next++;
888
889                 // If our next iterator is the end, then we don't need
890                 // to add a bezier.
891                 //if(next==bline_point_list.end() && !loop_)
892                 //      continue;
893
894                 bezier=new WorkArea::Bezier();
895
896                 // Add the tangent2 duck
897                 tduck=new WorkArea::Duck(bline_point.get_tangent2());
898                 tduck->set_editable(true);
899                 tduck->set_origin(duck);
900                 tduck->set_scalar(0.33333333333333333);
901                 tduck->set_tangent(true);
902                 if(bline_point.get_split_tangent_flag())
903                 {
904                         tduck->set_name(strprintf("%x-tangent2",value_node.get()));
905                         tduck->signal_edited().connect(
906                                 sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_tangent2_change),value_node)
907                         );
908                 }
909                 else
910                 {
911                         tduck->set_name(strprintf("%x-tangent1",value_node.get()));
912                         tduck->signal_edited().connect(
913                                 sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_tangent1_change),value_node)
914                         );
915                 }
916                 tduck->set_guid(value_node->get_guid()^GUID::hasher(4));
917                 tduck->signal_user_click(2).connect(
918                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_handle_menu),value_node)
919                 );
920
921                 // Setup the next bezier
922                 bezier->p1=duck;
923                 bezier->c1=tduck;
924
925                 get_work_area()->add_duck(tduck);
926                 curr_duck=tduck;
927         }
928
929         // Add the loop, if requested
930         if(bezier && loop_)
931         {
932                 curr_duck=0;
933                 BLinePoint bline_point(bline_point_list.front()->get_value().get(BLinePoint()));
934
935                 duck=new WorkArea::Duck(bline_point.get_vertex());
936                 duck->set_editable(true);
937                 duck->set_name(strprintf("%x-vertex",bline_point_list.front().get()));
938                 duck->signal_edited().connect(
939                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_vertex_change),bline_point_list.front())
940                 );
941                 duck->signal_user_click(2).connect(
942                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_vertex_menu),bline_point_list.front())
943                 );
944                 get_work_area()->add_duck(duck);
945
946                 // Add the tangent1 duck
947                 tduck=new WorkArea::Duck(bline_point.get_tangent1());
948                 tduck->set_editable(true);
949                 tduck->set_name(strprintf("%x-tangent1",bline_point_list.front().get()));
950                 tduck->set_origin(duck);
951                 tduck->set_scalar(-0.33333333333333333);
952                 tduck->set_tangent(true);
953                 tduck->signal_edited().connect(
954                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_tangent1_change),bline_point_list.front())
955                 );
956                 tduck->signal_user_click(2).connect(
957                         sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_handle_menu),bline_point_list.front())
958                 );
959                 get_work_area()->add_duck(tduck);
960
961                 bezier->p2=duck;
962                 bezier->c2=tduck;
963
964                 bezier->signal_user_click(2).connect(
965                         sigc::bind(
966                                 sigc::mem_fun(
967                                         *this,
968                                         &studio::StateBLine_Context::popup_bezier_menu
969                                 ),
970                                 bline_point_list.front()
971                         )
972                 );
973
974                 //get_work_area()->add_duck(bezier->c1);
975                 get_work_area()->add_bezier(bezier);
976         }
977         if(bezier && !loop_)
978         {
979                 duck=new WorkArea::Duck(bline_point.get_vertex());
980                 duck->set_editable(false);
981                 duck->set_name("temp");
982
983                 // Add the tangent1 duck
984                 tduck=new WorkArea::Duck(Vector(0,0));
985                 tduck->set_editable(false);
986                 tduck->set_name("ttemp");
987                 tduck->set_origin(duck);
988                 tduck->set_scalar(-0.33333333333333333);
989
990                 tduck->set_tangent(true);
991                 bezier->p2=duck;
992                 bezier->c2=tduck;
993
994                 get_work_area()->add_duck(bezier->p2);
995                 //get_work_area()->add_duck(bezier->c2);
996                 get_work_area()->add_bezier(bezier);
997
998                 duck->set_guid(GUID());
999                 tduck->set_guid(GUID());
1000
1001                 next_duck=duck;
1002         }
1003
1004         if(!button_down)
1005         {
1006                 if(curr_duck)
1007                 {
1008                         if(next_duck)
1009                         {
1010                                 curr_duck=next_duck;
1011                                 next_duck=0;
1012                         }
1013                 }
1014         }
1015         get_work_area()->queue_draw();
1016 }
1017
1018
1019 bool
1020 StateBLine_Context::on_vertex_change(const synfig::Point &point, synfig::ValueNode_Const::Handle value_node)
1021 {
1022         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1023         bline_point.set_vertex(point);
1024         value_node->set_value(bline_point);
1025         //refresh_ducks();
1026         return true;
1027 }
1028
1029 bool
1030 StateBLine_Context::on_tangent1_change(const synfig::Point &point, synfig::ValueNode_Const::Handle value_node)
1031 {
1032         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1033         bline_point.set_tangent1(point);
1034         value_node->set_value(bline_point);
1035         //refresh_ducks();
1036         return true;
1037 }
1038
1039 bool
1040 StateBLine_Context::on_tangent2_change(const synfig::Point &point, synfig::ValueNode_Const::Handle value_node)
1041 {
1042         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1043         bline_point.set_tangent2(point);
1044         value_node->set_value(bline_point);
1045         //refresh_ducks();
1046         return true;
1047 }
1048
1049 void
1050 StateBLine_Context::loop_bline()
1051 {
1052         loop_=true;
1053
1054         refresh_ducks(false);
1055 }
1056
1057 void
1058 StateBLine_Context::unloop_bline()
1059 {
1060         loop_=false;
1061
1062         refresh_ducks(false);
1063 }
1064
1065 void
1066 StateBLine_Context::popup_vertex_menu(synfig::ValueNode_Const::Handle value_node)
1067 {
1068         menu.items().clear();
1069
1070         if(loop_)
1071         {
1072                 menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Unloop BLine",
1073                                 sigc::mem_fun(*this,&studio::StateBLine_Context::unloop_bline)
1074                 ));
1075         } else {
1076                 menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Loop BLine",
1077                                 sigc::mem_fun(*this,&studio::StateBLine_Context::loop_bline)
1078                 ));
1079         }
1080
1081         menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Delete Vertex",
1082                 sigc::bind(
1083                         sigc::mem_fun(*this,&studio::StateBLine_Context::bline_delete_vertex),
1084                         value_node
1085                 )
1086         ));
1087
1088         menu.popup(0,0);
1089 }
1090
1091 void
1092 StateBLine_Context::popup_bezier_menu(float location, synfig::ValueNode_Const::Handle value_node)
1093 {
1094         menu.items().clear();
1095
1096         menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Insert Vertex",
1097                 sigc::bind(
1098                         sigc::bind(
1099                                 sigc::mem_fun(*this,&studio::StateBLine_Context::bline_insert_vertex),
1100                                 location
1101                         ),
1102                         value_node
1103                 )
1104         ));
1105
1106         menu.popup(0,0);
1107 }
1108
1109 void
1110 StateBLine_Context::bline_insert_vertex(synfig::ValueNode_Const::Handle value_node, float origin)
1111 {
1112         list<ValueNode_Const::Handle>::iterator iter;
1113
1114         for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
1115                 if(*iter==value_node)
1116                 {
1117                         list<ValueNode_Const::Handle>::iterator prev(iter);
1118                         --prev;
1119
1120                         BLinePoint bline_point;
1121
1122                         BLinePoint next_bline_point((*iter)->get_value().get(BLinePoint()));
1123                         BLinePoint prev_bline_point;
1124
1125                         if(iter!=bline_point_list.begin())
1126                         {
1127                                 prev_bline_point=(*prev)->get_value().get(BLinePoint());
1128                         }
1129                         else
1130                         {
1131                                 prev_bline_point.set_vertex(Point(0,0));
1132                                 prev_bline_point.set_width(next_bline_point.get_width());
1133                                 prev_bline_point.set_origin(0.5);
1134                                 prev_bline_point.set_split_tangent_flag(false);
1135                         }
1136
1137                         etl::hermite<Vector> curve(prev_bline_point.get_vertex(),next_bline_point.get_vertex(),prev_bline_point.get_tangent2(),next_bline_point.get_tangent1());
1138                         etl::derivative< etl::hermite<Vector> > deriv(curve);
1139
1140                         bline_point.set_vertex(curve(origin));
1141                         bline_point.set_width((next_bline_point.get_width()-prev_bline_point.get_width())*origin+prev_bline_point.get_width());
1142 #ifdef ETL_FIXED_DERIVATIVE
1143                         bline_point.set_tangent1(deriv(origin)*std::min(1.0f-origin,origin));
1144 #else
1145                         bline_point.set_tangent1(-deriv(origin)*std::min(1.0f-origin,origin));
1146 #endif
1147                         bline_point.set_tangent2(bline_point.get_tangent1());
1148                         bline_point.set_split_tangent_flag(false);
1149                         bline_point.set_origin(origin);
1150
1151 /*
1152                         bline_point.set_vertex((next_bline_point.get_vertex()+prev_bline_point.get_vertex())*0.5);
1153                         bline_point.set_width((next_bline_point.get_width()+prev_bline_point.get_width())*0.5);
1154                         bline_point.set_origin(origin);
1155                         bline_point.set_split_tangent_flag(false);
1156                         bline_point.set_tangent1((next_bline_point.get_vertex()-prev_bline_point.get_vertex())*0.5);
1157 */
1158
1159                         bline_point_list.insert(iter,ValueNode_Const::create(bline_point));
1160                         break;
1161                 }
1162
1163         if(iter==bline_point_list.end())
1164         {
1165                 get_canvas_view()->get_ui_interface()->error("Unable to find where to insert vertex, internal error, please report this bug");
1166         }
1167
1168         refresh_ducks(false);
1169 }
1170
1171 void
1172 StateBLine_Context::bline_delete_vertex(synfig::ValueNode_Const::Handle value_node)
1173 {
1174         list<ValueNode_Const::Handle>::iterator iter;
1175
1176         for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
1177                 if(*iter==value_node)
1178                 {
1179                         bline_point_list.erase(iter);
1180                         break;
1181                 }
1182         if(iter==bline_point_list.end())
1183         {
1184                 get_canvas_view()->get_ui_interface()->error("Unable to remove vertex, internal error, please report this bug");
1185         }
1186
1187         refresh_ducks(false);
1188 }
1189
1190 void
1191 StateBLine_Context::popup_handle_menu(synfig::ValueNode_Const::Handle value_node)
1192 {
1193         menu.items().clear();
1194
1195         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1196
1197         if(bline_point.get_split_tangent_flag())
1198                 menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Merge Tangents",
1199                         sigc::bind(
1200                                 sigc::mem_fun(*this,&studio::StateBLine_Context::bline_attach_handle),
1201                                 value_node
1202                         )
1203                 ));
1204         else
1205                 menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Split Tangents",
1206                         sigc::bind(
1207                                 sigc::mem_fun(*this,&studio::StateBLine_Context::bline_detach_handle),
1208                                 value_node
1209                         )
1210                 ));
1211
1212         menu.popup(0,0);
1213 }
1214
1215 void
1216 StateBLine_Context::bline_detach_handle(synfig::ValueNode_Const::Handle value_node)
1217 {
1218         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1219         bline_point.set_split_tangent_flag(true);
1220         bline_point.set_tangent2(bline_point.get_tangent1());
1221         value_node->set_value(bline_point);
1222         refresh_ducks(false);
1223 }
1224
1225 void
1226 StateBLine_Context::bline_attach_handle(synfig::ValueNode_Const::Handle value_node)
1227 {
1228         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1229         bline_point.set_tangent1((bline_point.get_tangent1()+bline_point.get_tangent2())*0.5);
1230         bline_point.set_split_tangent_flag(false);
1231         value_node->set_value(bline_point);
1232         refresh_ducks(false);
1233 }