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