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