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