Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / synfigstudio_0_61_03 / synfig-studio / src / gtkmm / state_bline.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file rotoscope_bline.cpp
3 **      \brief Template File
4 **
5 **      $Id: state_bline.cpp,v 1.1.1.1 2005/01/07 03:34:36 darco Exp $
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
108         void refresh_ducks(bool x=true);
109         
110         Gtk::Table options_table;
111         Gtk::Entry entry_id;
112         Gtk::CheckButton checkbutton_layer_region;
113         Gtk::CheckButton checkbutton_layer_bline;
114         Gtk::CheckButton checkbutton_layer_curve_gradient;
115         Gtk::CheckButton checkbutton_auto_export;
116         Gtk::Button button_make;
117         Gtk::Button button_clear;
118         Gtk::Adjustment  adj_feather;
119         Gtk::SpinButton  spin_feather;
120         
121         
122         
123 public:
124
125         int layers_to_create()const
126         {
127                 return
128                         get_layer_region_flag()+
129                         get_layer_bline_flag()+
130                         get_layer_curve_gradient_flag();
131         }
132         
133         void sanity_check()
134         {
135                 if(layers_to_create()==0)
136                         set_layer_region_flag(true);
137         }
138
139         bool get_auto_export_flag()const { return checkbutton_auto_export.get_active(); }
140         void set_auto_export_flag(bool x) { return checkbutton_auto_export.set_active(x); }
141         
142         bool get_layer_region_flag()const { return checkbutton_layer_region.get_active(); }
143         void set_layer_region_flag(bool x) { return checkbutton_layer_region.set_active(x); }
144         
145         bool get_layer_bline_flag()const { return checkbutton_layer_bline.get_active(); }
146         void set_layer_bline_flag(bool x) { return checkbutton_layer_bline.set_active(x); }
147         
148         bool get_layer_curve_gradient_flag()const { return checkbutton_layer_curve_gradient.get_active(); }
149         void set_layer_curve_gradient_flag(bool x) { return checkbutton_layer_curve_gradient.set_active(x); }
150
151         Real get_feather() const { return adj_feather.get_value(); }
152         void set_feather(Real x) { return adj_feather.set_value(x); }
153         synfig::String get_id()const { return entry_id.get_text(); }
154         void set_id(const synfig::String& x) { return entry_id.set_text(x); }
155
156         Smach::event_result event_stop_handler(const Smach::event& x);
157
158         Smach::event_result event_refresh_handler(const Smach::event& x);
159
160         Smach::event_result event_mouse_click_handler(const Smach::event& x);
161         Smach::event_result event_mouse_release_handler(const Smach::event& x);
162         Smach::event_result event_mouse_motion_handler(const Smach::event& x);
163         Smach::event_result event_refresh_tool_options(const Smach::event& x);
164
165         Smach::event_result event_hijack(const Smach::event& x) { return Smach::RESULT_ACCEPT; }
166         
167         void refresh_tool_options();
168
169         StateBLine_Context(CanvasView* canvas_view);
170
171         ~StateBLine_Context();
172
173         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
174         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
175         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
176         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
177         const synfig::TransformStack& get_transform_stack()const { return canvas_view_->get_curr_transform_stack(); }
178         
179         void load_settings();
180         void save_settings();
181         void reset();
182         void increment_id();
183         //void on_user_click(synfig::Point point);
184
185         bool run_();
186         bool run();
187         
188         bool no_egress_on_selection_change;
189         Smach::event_result event_layer_selection_changed_handler(const Smach::event& x)
190         {
191                 if(!no_egress_on_selection_change)
192                         throw Smach::egress_exception();
193                 return Smach::RESULT_OK;
194         }
195         
196 };      // END of class StateBLine_Context
197
198
199 /* === M E T H O D S ======================================================= */
200
201 StateBLine::StateBLine():
202         Smach::state<StateBLine_Context>("bline")
203 {
204         insert(event_def(EVENT_LAYER_SELECTION_CHANGED,&StateBLine_Context::event_layer_selection_changed_handler));
205         insert(event_def(EVENT_STOP,&StateBLine_Context::event_stop_handler));
206         insert(event_def(EVENT_REFRESH,&StateBLine_Context::event_refresh_handler));
207         insert(event_def(EVENT_REFRESH_DUCKS,&StateBLine_Context::event_hijack));
208         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateBLine_Context::event_mouse_click_handler));
209         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateBLine_Context::event_mouse_release_handler));
210         insert(event_def(EVENT_WORKAREA_MOUSE_MOTION,&StateBLine_Context::event_mouse_motion_handler));
211         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateBLine_Context::event_mouse_motion_handler));
212         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateBLine_Context::event_refresh_tool_options));
213 }       
214
215 StateBLine::~StateBLine()
216 {
217 }
218
219 void
220 StateBLine_Context::load_settings()
221 {       
222         String value;
223
224         if(settings.get_value("bline.layer_region",value) && value=="0")
225                 set_layer_region_flag(false);
226         else
227                 set_layer_region_flag(true);
228
229         if(settings.get_value("bline.layer_bline",value) && value=="0")
230                 set_layer_bline_flag(false);
231         else
232                 set_layer_bline_flag(true);
233
234         if(settings.get_value("bline.layer_curve_gradient",value) && value=="1")
235                 set_layer_curve_gradient_flag(true);
236         else
237                 set_layer_curve_gradient_flag(false);
238
239         if(settings.get_value("bline.auto_export",value) && value=="1")
240                 set_auto_export_flag(true);
241         else
242                 set_auto_export_flag(false);
243
244         if(settings.get_value("bline.id",value))
245                 set_id(value);
246         else
247                 set_id("NewBLine");
248
249         if(settings.get_value("bline.feather",value))
250         {
251                 Real n = atof(value.c_str());
252                 set_feather(n);
253         }
254
255         sanity_check();
256 }
257
258 void
259 StateBLine_Context::save_settings()
260 {       
261         sanity_check();
262         settings.set_value("bline.layer_bline",get_layer_bline_flag()?"1":"0");
263         settings.set_value("bline.layer_region",get_layer_region_flag()?"1":"0");
264         settings.set_value("bline.layer_curve_gradient",get_layer_curve_gradient_flag()?"1":"0");
265         settings.set_value("bline.auto_export",get_auto_export_flag()?"1":"0");
266         settings.set_value("bline.id",get_id().c_str());
267         settings.set_value("bline.feather",strprintf("%f",get_feather()));
268 }
269
270 void
271 StateBLine_Context::reset()
272 {
273         loop_=false;
274         bline_point_list.clear();
275         refresh_ducks();
276 }
277
278 void
279 StateBLine_Context::increment_id()
280 {
281         String id(get_id());
282         int number=1;
283         int digits=0;
284         
285         if(id.empty())
286                 id="NewBLine";
287         
288         // If there is a number
289         // already at the end of the
290         // id, then remove it.
291         if(id[id.size()-1]<='9' && id[id.size()-1]>='0')
292         {
293                 // figure out how many digits it is
294                 for(digits=0;(int)id.size()-1>=digits && id[id.size()-1-digits]<='9' && id[id.size()-1-digits]>='0';digits++)while(false);
295                 
296                 String str_number;
297                 str_number=String(id,id.size()-digits,id.size());
298                 id=String(id,0,id.size()-digits);
299                 synfig::info("---------------- \"%s\"",str_number.c_str());
300                 
301                 number=atoi(str_number.c_str());
302         }
303         else
304         {
305                 number=1;
306                 digits=3;
307         }
308         
309         number++;
310         
311         // Add the number back onto the id
312         {
313                 const String format(strprintf("%%0%dd",digits));
314                 id+=strprintf(format.c_str(),number);
315         }
316         
317         // Set the ID
318         set_id(id);
319 }
320
321
322 StateBLine_Context::StateBLine_Context(CanvasView* canvas_view):
323         canvas_view_(canvas_view),
324         is_working(*canvas_view),
325         loop_(false),
326         prev_workarea_layer_status_(get_work_area()->allow_layer_clicks),
327         duckmatic_push(get_work_area()),
328         settings(synfigapp::Main::get_selected_input_device()->settings()),
329         entry_id(),
330         checkbutton_layer_region(_("Fill")),
331         checkbutton_layer_bline(_("Outline")),
332         checkbutton_layer_curve_gradient(_("Gradient")),
333         checkbutton_auto_export(_("Auto Export")),
334         button_make(_("Make")),
335         button_clear(_("Clear")),
336         adj_feather(0,0,10000,0.01,0.1),
337         spin_feather(adj_feather,0.01,4)
338 {
339         depth=-1;
340         no_egress_on_selection_change=false;
341         load_settings();
342                 
343         // Set up the tool options dialog
344         //options_table.attach(*manage(new Gtk::Label(_("BLine Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);       
345         options_table.attach(entry_id, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
346         options_table.attach(checkbutton_layer_region, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0); 
347         options_table.attach(checkbutton_layer_bline, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);  
348         options_table.attach(checkbutton_layer_curve_gradient, 0, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0); 
349         options_table.attach(*manage(new Gtk::Label(_("Feather"))), 0, 1, 10, 11, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);  
350         options_table.attach(spin_feather, 1, 2, 10, 11, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
351         options_table.attach(checkbutton_auto_export, 0, 2, 11, 12, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);        
352         //options_table.attach(button_make, 0, 2, 5, 6, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);    
353         //button_make.signal_pressed().connect(sigc::mem_fun(*this,&StateBLine_Context::run));
354         options_table.show_all();
355         refresh_tool_options();
356         App::dialog_tool_options->present();
357         
358         // Turn off layer clicking
359         get_work_area()->allow_layer_clicks=false;
360         
361         // clear out the ducks
362         get_work_area()->clear_ducks();
363         
364         // Refresh the work area
365         get_work_area()->queue_draw();
366         
367         // Hide the tables if they are showing
368         prev_table_status=get_canvas_view()->tables_are_visible();
369         if(prev_table_status)get_canvas_view()->hide_tables();
370                 
371         // Hide the time bar
372         get_canvas_view()->hide_timebar();
373         
374         // Connect a signal
375         //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateBLine_Context::on_user_click));
376         get_canvas_view()->work_area->set_cursor(Gdk::CROSSHAIR);
377
378         App::toolbox->refresh();
379 }
380
381 void
382 StateBLine_Context::refresh_tool_options()
383 {
384         App::dialog_tool_options->clear();
385         App::dialog_tool_options->set_widget(options_table);
386         App::dialog_tool_options->set_local_name(_("BLine Tool"));
387         App::dialog_tool_options->set_name("bline");
388
389         App::dialog_tool_options->add_button(
390                 Gtk::StockID("gtk-execute"),
391                 _("Make BLine and/or Region")
392         )->signal_clicked().connect(
393                 sigc::hide_return(sigc::mem_fun(
394                         *this,
395                         &StateBLine_Context::run
396                 ))
397         );
398
399         App::dialog_tool_options->add_button(
400                 Gtk::StockID("gtk-clear"),
401                 _("Clear current BLine")
402         )->signal_clicked().connect(
403                 sigc::mem_fun(
404                         *this,
405                         &StateBLine_Context::reset
406                 )
407         );
408 }
409
410 Smach::event_result
411 StateBLine_Context::event_refresh_tool_options(const Smach::event& x)
412 {
413         refresh_tool_options();
414         return Smach::RESULT_ACCEPT;
415 }
416
417 StateBLine_Context::~StateBLine_Context()
418 {
419         run();
420
421         save_settings();
422         App::dialog_tool_options->clear();
423
424         get_canvas_view()->work_area->reset_cursor();
425
426         // Restore layer clicking
427         get_work_area()->allow_layer_clicks=prev_workarea_layer_status_;
428
429         // Show the time bar
430         if(get_canvas_view()->get_canvas()->rend_desc().get_time_start()!=get_canvas_view()->get_canvas()->rend_desc().get_time_end())
431                 get_canvas_view()->show_timebar();
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::popup_vertex_menu(synfig::ValueNode_Const::Handle value_node)
1059 {
1060         menu.items().clear();
1061
1062         if(!loop_ && value_node==bline_point_list.front())
1063         {
1064                 menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Loop BLine",
1065                                 sigc::mem_fun(*this,&studio::StateBLine_Context::loop_bline)
1066                 ));
1067         }
1068         
1069         menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Delete Vertex",
1070                 sigc::bind(
1071                         sigc::mem_fun(*this,&studio::StateBLine_Context::bline_delete_vertex),
1072                         value_node
1073                 )
1074         ));
1075
1076         menu.popup(0,0);
1077 }
1078
1079 void
1080 StateBLine_Context::popup_bezier_menu(float location, synfig::ValueNode_Const::Handle value_node)
1081 {
1082         menu.items().clear();
1083
1084         menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Insert Vertex",
1085                 sigc::bind(
1086                         sigc::bind(
1087                                 sigc::mem_fun(*this,&studio::StateBLine_Context::bline_insert_vertex),
1088                                 location
1089                         ),
1090                         value_node
1091                 )
1092         ));
1093
1094         menu.popup(0,0);
1095 }
1096
1097 void
1098 StateBLine_Context::bline_insert_vertex(synfig::ValueNode_Const::Handle value_node, float origin)
1099 {
1100         list<ValueNode_Const::Handle>::iterator iter;
1101         
1102         for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
1103                 if(*iter==value_node)
1104                 {
1105                         list<ValueNode_Const::Handle>::iterator prev(iter);
1106                         --prev;
1107
1108                         BLinePoint bline_point;
1109                         
1110                         BLinePoint next_bline_point((*iter)->get_value().get(BLinePoint()));
1111                         BLinePoint prev_bline_point;
1112                         
1113                         if(iter!=bline_point_list.begin())
1114                         {
1115                                 prev_bline_point=(*prev)->get_value().get(BLinePoint());
1116                         }
1117                         else
1118                         {
1119                                 prev_bline_point.set_vertex(Point(0,0));
1120                                 prev_bline_point.set_width(next_bline_point.get_width());
1121                                 prev_bline_point.set_origin(0.5);
1122                                 prev_bline_point.set_split_tangent_flag(false);
1123                         }
1124
1125                         etl::hermite<Vector> curve(prev_bline_point.get_vertex(),next_bline_point.get_vertex(),prev_bline_point.get_tangent2(),next_bline_point.get_tangent1());
1126                         etl::derivative< etl::hermite<Vector> > deriv(curve);
1127
1128                         bline_point.set_vertex(curve(origin));
1129                         bline_point.set_width((next_bline_point.get_width()-prev_bline_point.get_width())*origin+prev_bline_point.get_width());
1130 #ifdef ETL_FIXED_DERIVATIVE
1131                         bline_point.set_tangent1(deriv(origin)*std::min(1.0f-origin,origin));
1132 #else
1133                         bline_point.set_tangent1(-deriv(origin)*std::min(1.0f-origin,origin));
1134 #endif
1135                         bline_point.set_tangent2(bline_point.get_tangent1());
1136                         bline_point.set_split_tangent_flag(false);
1137                         bline_point.set_origin(origin);
1138                         
1139 /*
1140                         bline_point.set_vertex((next_bline_point.get_vertex()+prev_bline_point.get_vertex())*0.5);
1141                         bline_point.set_width((next_bline_point.get_width()+prev_bline_point.get_width())*0.5);
1142                         bline_point.set_origin(origin);
1143                         bline_point.set_split_tangent_flag(false);
1144                         bline_point.set_tangent1((next_bline_point.get_vertex()-prev_bline_point.get_vertex())*0.5);
1145 */
1146
1147                         bline_point_list.insert(iter,ValueNode_Const::create(bline_point));
1148                         break;
1149                 }
1150
1151         if(iter==bline_point_list.end())
1152         {
1153                 get_canvas_view()->get_ui_interface()->error("Unable to find where to insert vertex, internal error, please report this bug");
1154         }
1155
1156         refresh_ducks(false);   
1157 }
1158
1159 void
1160 StateBLine_Context::bline_delete_vertex(synfig::ValueNode_Const::Handle value_node)
1161 {
1162         list<ValueNode_Const::Handle>::iterator iter;
1163         
1164         for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
1165                 if(*iter==value_node)
1166                 {
1167                         bline_point_list.erase(iter);
1168                         break;
1169                 }
1170         if(iter==bline_point_list.end())
1171         {
1172                 get_canvas_view()->get_ui_interface()->error("Unable to remove vertex, internal error, please report this bug");
1173         }
1174
1175         refresh_ducks(false);   
1176 }
1177
1178 void
1179 StateBLine_Context::popup_handle_menu(synfig::ValueNode_Const::Handle value_node)
1180 {
1181         menu.items().clear();
1182
1183         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1184
1185         if(bline_point.get_split_tangent_flag())
1186                 menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Merge Tangents",
1187                         sigc::bind(
1188                                 sigc::mem_fun(*this,&studio::StateBLine_Context::bline_attach_handle),
1189                                 value_node
1190                         )
1191                 ));
1192         else
1193                 menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Split Tangents",
1194                         sigc::bind(
1195                                 sigc::mem_fun(*this,&studio::StateBLine_Context::bline_detach_handle),
1196                                 value_node
1197                         )
1198                 ));
1199
1200         menu.popup(0,0);
1201 }
1202
1203 void
1204 StateBLine_Context::bline_detach_handle(synfig::ValueNode_Const::Handle value_node)
1205 {
1206         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1207         bline_point.set_split_tangent_flag(true);
1208         bline_point.set_tangent2(bline_point.get_tangent1());
1209         value_node->set_value(bline_point);
1210         refresh_ducks(false);
1211 }
1212
1213 void
1214 StateBLine_Context::bline_attach_handle(synfig::ValueNode_Const::Handle value_node)
1215 {
1216         BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1217         bline_point.set_tangent1((bline_point.get_tangent1()+bline_point.get_tangent2())*0.5);
1218         bline_point.set_split_tangent_flag(false);
1219         value_node->set_value(bline_point);
1220         refresh_ducks(false);
1221 }