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