grab stable branch
[synfig.git] / synfig-studio / tags / stable / src / gtkmm / state_draw.cpp
1 /* === S I N F G =========================================================== */
2 /*!     \file rotoscope_bline.cpp
3 **      \brief Template File
4 **
5 **      $Id: state_draw.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_draw.h"
37 #include "state_stroke.h"
38 #include "canvasview.h"
39 #include "workarea.h"
40 #include "app.h"
41 #include <sinfg/valuenode_bline.h>
42 #include <sinfg/valuenode_composite.h>
43 #include <ETL/hermite>
44 #include <ETL/calculus>
45 #include <utility>
46 #include "event_mouse.h"
47 #include "event_layerclick.h"
48 #include "toolbox.h"
49
50 #include <sinfgapp/blineconvert.h>
51 #include <sinfgapp/main.h>
52
53 #include <ETL/gaussian>
54 #include "dialog_tooloptions.h"
55
56 #include <gtkmm/table.h>
57 #include <gtkmm/label.h>
58 #include <gtkmm/button.h>
59 #include <gtkmm/checkbutton.h>
60 #include <gtkmm/scale.h>
61 #include <sigc++/connection.h>
62
63 #endif
64
65 /* === U S I N G =========================================================== */
66
67 using namespace std;
68 using namespace etl;
69 using namespace sinfg;
70 using namespace studio;
71
72 /* === M A C R O S ========================================================= */
73
74 /* === G L O B A L S ======================================================= */
75
76 StateDraw studio::state_draw;
77
78 /* === C L A S S E S & S T R U C T S ======================================= */
79
80 class studio::StateDraw_Context : public sigc::trackable
81 {
82         typedef etl::smart_ptr<std::list<sinfg::Point> > StrokeData;
83         typedef etl::smart_ptr<std::list<sinfg::Real> > WidthData;
84         
85         typedef list< pair<StrokeData,WidthData> > StrokeQueue;
86         
87         StrokeQueue stroke_queue;
88         
89         
90         etl::handle<CanvasView> canvas_view_;
91         CanvasView::IsWorking is_working;
92         
93         bool prev_table_status;
94         bool loop_;
95         bool prev_workarea_layer_status_;
96
97         int nested;
98         SigC::Connection process_queue_connection;
99         
100         ValueNode_BLine::Handle last_stroke;
101         
102         Gtk::Menu menu;
103
104         //Duckmatic::Push duckmatic_push;
105         
106         std::list< etl::smart_ptr<std::list<sinfg::Point> > > stroke_list;
107
108         void refresh_ducks();
109         
110         Duckmatic::Type old_duckmask;
111
112         void fill_last_stroke();
113         
114         Smach::event_result new_bline(std::list<sinfg::BLinePoint> bline,bool loop_bline_flag,float radius);
115
116         Smach::event_result new_region(std::list<sinfg::BLinePoint> bline,sinfg::Real radius);
117
118         Smach::event_result extend_bline_from_begin(ValueNode_BLine::Handle value_node,std::list<sinfg::BLinePoint> bline);
119         Smach::event_result extend_bline_from_end(ValueNode_BLine::Handle value_node,std::list<sinfg::BLinePoint> bline);
120         void reverse_bline(std::list<sinfg::BLinePoint> &bline);
121
122         sinfgapp::Settings& settings;
123
124         Gtk::Table options_table;
125         Gtk::CheckButton checkbutton_pressure_width;
126         Gtk::CheckButton checkbutton_round_ends;
127         Gtk::CheckButton checkbutton_auto_loop;
128         Gtk::CheckButton checkbutton_auto_connect;
129         Gtk::CheckButton checkbutton_region_only;
130         Gtk::Button button_fill_last_stroke;
131         
132         //pressure spinner and such
133         Gtk::Adjustment  adj_min_pressure;
134         Gtk::SpinButton  spin_min_pressure;
135         Gtk::CheckButton check_min_pressure;
136
137         Gtk::Adjustment  adj_feather;
138         Gtk::SpinButton  spin_feather;
139         
140         Gtk::Adjustment  adj_globalthres;
141         Gtk::SpinButton  spin_globalthres;
142         
143         Gtk::Adjustment  adj_localthres;
144         Gtk::CheckButton check_localerror;
145         void UpdateErrorBox();  //switches the stuff if need be :)
146
147         //Added by Adrian - data drive HOOOOO
148         sinfgapp::BLineConverter blineconv;
149
150 public:
151         bool get_pressure_width_flag()const { return checkbutton_pressure_width.get_active(); }
152         void set_pressure_width_flag(bool x) { return checkbutton_pressure_width.set_active(x); }
153
154         bool get_auto_loop_flag()const { return checkbutton_auto_loop.get_active(); }
155         void set_auto_loop_flag(bool x) { return checkbutton_auto_loop.set_active(x); }
156
157         bool get_auto_connect_flag()const { return checkbutton_auto_connect.get_active(); }
158         void set_auto_connect_flag(bool x) { return checkbutton_auto_connect.set_active(x); }
159
160         bool get_region_only_flag()const { return checkbutton_region_only.get_active(); }
161         void set_region_only_flag(bool x) { return checkbutton_region_only.set_active(x); }
162         
163         Real get_min_pressure() const { return adj_min_pressure.get_value(); }
164         void set_min_pressure(Real x) { return adj_min_pressure.set_value(x); }
165
166         Real get_feather() const { return adj_feather.get_value(); }
167         void set_feather(Real x) { return adj_feather.set_value(x); }
168         
169         Real get_gthres() const { return adj_globalthres.get_value(); }
170         void set_gthres(Real x) { return adj_globalthres.set_value(x); }
171         
172         Real get_lthres() const { return adj_localthres.get_value(); }
173         void set_lthres(Real x) { return adj_localthres.set_value(x); }
174         
175         bool get_local_error_flag() const { return check_localerror.get_active(); }
176         void set_local_error_flag(bool x) { check_localerror.set_active(x); }
177         
178         bool get_min_pressure_flag()const { return check_min_pressure.get_active(); }
179         void set_min_pressure_flag(bool x) { check_min_pressure.set_active(x); }
180
181         void load_settings();
182         void save_settings();
183         
184         Smach::event_result event_stop_handler(const Smach::event& x);
185
186         Smach::event_result event_refresh_handler(const Smach::event& x);
187
188         Smach::event_result event_mouse_down_handler(const Smach::event& x);
189
190         Smach::event_result event_stroke(const Smach::event& x);
191         Smach::event_result event_refresh_tool_options(const Smach::event& x);
192         void refresh_tool_options();
193
194         Smach::event_result process_stroke(StrokeData stroke_data, WidthData width_data, bool region_flag=false);
195
196         bool process_queue();
197         
198
199         StateDraw_Context(CanvasView* canvas_view);
200
201         ~StateDraw_Context();
202
203         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
204         etl::handle<sinfgapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
205         sinfg::Time get_time()const { return get_canvas_interface()->get_time(); }
206         sinfg::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
207         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
208         
209         //void on_user_click(sinfg::Point point);
210
211 //      bool run();
212 };      // END of class StateDraw_Context
213
214
215 /* === M E T H O D S ======================================================= */
216
217 StateDraw::StateDraw():
218         Smach::state<StateDraw_Context>("draw")
219 {       
220         insert(event_def(EVENT_STOP,&StateDraw_Context::event_stop_handler));
221         insert(event_def(EVENT_REFRESH,&StateDraw_Context::event_refresh_handler));
222         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateDraw_Context::event_mouse_down_handler));
223         insert(event_def(EVENT_WORKAREA_STROKE,&StateDraw_Context::event_stroke));
224         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateDraw_Context::event_refresh_tool_options));
225 }
226
227 StateDraw::~StateDraw()
228 {
229 }
230
231
232 void
233 StateDraw_Context::load_settings()
234 {       
235         String value;
236
237         if(settings.get_value("draw.pressure_width",value) && value=="0")
238                 set_pressure_width_flag(false);
239         else
240                 set_pressure_width_flag(true);
241
242         if(settings.get_value("draw.auto_loop",value) && value=="0")
243                 set_auto_loop_flag(false);
244         else
245                 set_auto_loop_flag(true);
246
247         if(settings.get_value("draw.auto_connect",value) && value=="0")
248                 set_auto_connect_flag(false);
249         else
250                 set_auto_connect_flag(true);
251
252         if(settings.get_value("draw.region_only",value) && value=="1")
253                 set_region_only_flag(true);
254         else
255                 set_region_only_flag(false);
256         
257         if(settings.get_value("draw.min_pressure_on",value) && value=="0")
258                 set_min_pressure_flag(false);
259         else
260                 set_min_pressure_flag(true);
261         
262         if(settings.get_value("draw.min_pressure",value))
263         {
264                 Real n = atof(value.c_str());
265                 set_min_pressure(n);
266         }else
267                 set_min_pressure(0);
268
269         if(settings.get_value("draw.feather",value))
270         {
271                 Real n = atof(value.c_str());
272                 set_feather(n);
273         }else
274                 set_feather(0); 
275         
276         if(settings.get_value("draw.gthreshold",value))
277         {
278                 Real n = atof(value.c_str());
279                 set_gthres(n);
280         }
281         
282         if(settings.get_value("draw.lthreshold",value))
283         {
284                 Real n = atof(value.c_str());
285                 set_lthres(n);
286         }
287         
288         if(settings.get_value("draw.localize",value) && value == "1")
289                 set_local_error_flag(true);
290         else
291                 set_local_error_flag(false);
292 }
293
294 void
295 StateDraw_Context::save_settings()
296 {       
297         settings.set_value("draw.pressure_width",get_pressure_width_flag()?"1":"0");
298         settings.set_value("draw.auto_loop",get_auto_loop_flag()?"1":"0");
299         settings.set_value("draw.auto_connect",get_auto_connect_flag()?"1":"0");
300         settings.set_value("draw.region_only",get_region_only_flag()?"1":"0");
301         settings.set_value("draw.min_pressure",strprintf("%f",get_min_pressure()));
302         settings.set_value("draw.feather",strprintf("%f",get_feather()));
303         settings.set_value("draw.min_pressure_on",get_min_pressure_flag()?"1":"0");
304         settings.set_value("draw.gthreshold",strprintf("%f",get_gthres()));
305         settings.set_value("draw.lthreshold",strprintf("%f",get_lthres()));     
306         settings.set_value("draw.localize",get_local_error_flag()?"1":"0");
307 }
308
309 StateDraw_Context::StateDraw_Context(CanvasView* canvas_view):
310         canvas_view_(canvas_view),
311         is_working(*canvas_view),
312         loop_(false),
313         prev_workarea_layer_status_(get_work_area()->allow_layer_clicks),
314         settings(sinfgapp::Main::get_selected_input_device()->settings()),
315         checkbutton_pressure_width(_("Pressure Width")),
316         checkbutton_auto_loop(_("Auto Loop")),
317         checkbutton_auto_connect(_("Auto Connect")),
318         checkbutton_region_only(_("Create Region Only")),
319         button_fill_last_stroke(_("Fill Last Stroke")),
320         adj_min_pressure(0,0,1,0.01,0.1),
321         spin_min_pressure(adj_min_pressure,0.1,3),
322         check_min_pressure(_("Min Pressure")),
323         adj_feather(0,0,10000,0.01,0.1),
324         spin_feather(adj_feather,0.01,4),
325         adj_globalthres(.70f,0.01,10000,0.01,0.1),
326         spin_globalthres(adj_globalthres,0.01,3),
327         adj_localthres(20,1,100000,0.1,1),      
328         check_localerror(_("LocalError"))
329         
330 {
331         sinfg::info("STATE SKETCH: entering state");
332
333         nested=0;
334         load_settings();
335         
336         UpdateErrorBox();
337         
338         //options_table.attach(*manage(new Gtk::Label(_("Draw Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);        
339         options_table.attach(checkbutton_pressure_width, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
340         options_table.attach(checkbutton_auto_loop, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);    
341         options_table.attach(checkbutton_auto_connect, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0); 
342         options_table.attach(checkbutton_region_only, 0, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);  
343         
344         options_table.attach(check_min_pressure, 0, 2, 5, 6, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
345         options_table.attach(spin_min_pressure, 0, 2, 6, 7, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
346
347         options_table.attach(*manage(new Gtk::Label(_("Feather"))), 0, 1, 7, 8, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);    
348         options_table.attach(spin_feather, 1, 2, 7, 8, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
349         
350         options_table.attach(check_localerror, 0, 2, 8, 9, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0); 
351         options_table.attach(*manage(new Gtk::Label(_("Smooth"))), 0, 1, 9, 10, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);    
352         options_table.attach(spin_globalthres, 1, 2, 9, 10, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
353
354         //options_table.attach(button_fill_last_stroke, 0, 2, 10, 11, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);      
355         
356         button_fill_last_stroke.signal_pressed().connect(sigc::mem_fun(*this,&StateDraw_Context::fill_last_stroke));
357         check_localerror.signal_toggled().connect(sigc::mem_fun(*this,&StateDraw_Context::UpdateErrorBox));
358         
359         options_table.show_all();
360         refresh_tool_options();
361         //App::dialog_tool_options->set_widget(options_table);
362         App::dialog_tool_options->present();
363         
364         
365         old_duckmask=get_work_area()->get_type_mask();
366         get_work_area()->set_type_mask(Duck::TYPE_ALL-Duck::TYPE_TANGENT-Duck::TYPE_WIDTH);
367         
368         // Turn off layer clicking
369         get_work_area()->allow_layer_clicks=false;
370
371         // Turn off duck clicking
372         get_work_area()->allow_duck_clicks=false;
373         
374         // clear out the ducks
375         //get_work_area()->clear_ducks();
376         
377         // Refresh the work area
378         //get_work_area()->queue_draw();
379         
380         // Hide the tables if they are showing
381         prev_table_status=get_canvas_view()->tables_are_visible();
382         //if(prev_table_status)get_canvas_view()->hide_tables();
383                 
384         // Hide the time bar
385         get_canvas_view()->hide_timebar();
386         
387         // Connect a signal
388         //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateDraw_Context::on_user_click));
389
390         get_canvas_view()->work_area->set_cursor(Gdk::PENCIL);
391
392         App::toolbox->refresh();
393         
394         refresh_ducks();
395 }
396
397
398 void StateDraw_Context::UpdateErrorBox()
399 {
400         if(get_local_error_flag())
401         {
402                 spin_globalthres.set_adjustment(adj_localthres);
403                 spin_globalthres.set_value(adj_localthres.get_value());
404                 spin_globalthres.set_increments(0.1,1);
405         }else
406         {
407                 spin_globalthres.set_adjustment(adj_globalthres);
408                 spin_globalthres.set_value(adj_globalthres.get_value());
409                 spin_globalthres.set_increments(0.01,.1);
410         }
411         
412         spin_globalthres.update();
413 }
414
415 void
416 StateDraw_Context::refresh_tool_options()
417 {
418         App::dialog_tool_options->clear();
419         App::dialog_tool_options->set_widget(options_table);
420         App::dialog_tool_options->set_local_name(_("Draw Tool"));
421         App::dialog_tool_options->set_name("draw");
422         
423         App::dialog_tool_options->add_button(
424                 Gtk::StockID("sinfg-fill"),
425                 _("Fill Last Stroke")
426         )->signal_clicked().connect(
427                 sigc::mem_fun(
428                         *this,
429                         &StateDraw_Context::fill_last_stroke
430                 )
431         );
432
433 }
434
435 Smach::event_result
436 StateDraw_Context::event_refresh_tool_options(const Smach::event& x)
437 {
438         refresh_tool_options();
439         return Smach::RESULT_ACCEPT;
440 }
441
442 StateDraw_Context::~StateDraw_Context()
443 {
444         save_settings();
445
446         App::dialog_tool_options->clear();
447
448         get_work_area()->set_type_mask(old_duckmask);
449         
450         get_canvas_view()->work_area->reset_cursor();
451
452         // Restore layer clicking
453         get_work_area()->allow_layer_clicks=prev_workarea_layer_status_;
454
455         // Restore duck clicking
456         get_work_area()->allow_duck_clicks=true;
457
458         // Show the time bar
459         if(get_canvas_view()->get_canvas()->rend_desc().get_time_start()!=get_canvas_view()->get_canvas()->rend_desc().get_time_end())
460                 get_canvas_view()->show_timebar();
461
462         // Bring back the tables if they were out before
463         if(prev_table_status)get_canvas_view()->show_tables();
464                         
465         // Refresh the work area
466         get_work_area()->queue_draw();
467
468         App::toolbox->refresh();
469 }
470
471 Smach::event_result
472 StateDraw_Context::event_stop_handler(const Smach::event& x)
473 {
474         throw Smach::egress_exception();
475 }
476
477 Smach::event_result
478 StateDraw_Context::event_refresh_handler(const Smach::event& x)
479 {
480         refresh_ducks();
481         return Smach::RESULT_ACCEPT;
482 }
483
484 Smach::event_result
485 StateDraw_Context::event_mouse_down_handler(const Smach::event& x)
486 {
487         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
488         switch(event.button)
489         {
490         case BUTTON_LEFT:
491                 {
492                         // Enter the stroke state to get the stroke
493                         get_canvas_view()->get_smach().push_state(&state_stroke);
494                         return Smach::RESULT_ACCEPT;
495                 }
496         
497         case BUTTON_RIGHT: // Intercept the right-button click to short-circut the pop-up menu
498                 return Smach::RESULT_ACCEPT;
499         
500         default:        
501                 return Smach::RESULT_OK;
502         }
503 }
504
505 #define SIMILAR_TANGENT_THRESHOLD       (0.2)
506
507 struct debugclass
508 {
509         sinfg::String x;
510         debugclass(const sinfg::String &x):x(x)
511         {
512 //              sinfg::warning(">>>>>>>>>>>>>>>>>>> "+x);
513         }
514         ~debugclass()
515         {
516 //              sinfg::warning("<<<<<<<<<<<<<<<<<<< "+x);
517         }
518 };
519
520 struct DepthCounter
521 {
522         int &i;
523         DepthCounter(int &i):i(i) { i++; }
524         ~DepthCounter() { i--; }        
525 };
526
527 Smach::event_result
528 StateDraw_Context::event_stroke(const Smach::event& x)
529 {
530 //      debugclass debugger("StateDraw_Context::event_stroke(const Smach::event& x)");
531                 
532         const EventStroke& event(*reinterpret_cast<const EventStroke*>(&x));
533
534         assert(event.stroke_data);
535
536         get_work_area()->add_stroke(event.stroke_data,sinfgapp::Main::get_foreground_color());
537         
538         if(nested==0)
539         {
540                 DirtyTrap dirty_trap(get_work_area());
541                 Smach::event_result result;
542                 result=process_stroke(event.stroke_data,event.width_data,event.modifier&Gdk::CONTROL_MASK || event.modifier&Gdk::BUTTON2_MASK);
543                 process_queue();
544                 return result;
545         }
546         
547         stroke_queue.push_back(pair<StrokeData,WidthData>(event.stroke_data,event.width_data));
548
549         return Smach::RESULT_ACCEPT;    
550 }
551
552 bool
553 StateDraw_Context::process_queue()
554 {
555 //      debugclass debugger("StateDraw_Context::process_queue()");
556         if(nested)
557                 return true;
558         DepthCounter depth_counter(nested);
559         while(!stroke_queue.empty())
560         {
561                 pair<StrokeData,WidthData> front(stroke_queue.front());
562                 process_stroke(front.first,front.second);
563                 stroke_queue.pop_front();
564         }
565         return false;
566 }
567
568 Smach::event_result
569 StateDraw_Context::process_stroke(StrokeData stroke_data, WidthData width_data, bool region_flag)
570 {
571 //      debugclass debugger("StateDraw_Context::process_stroke");
572         DepthCounter depth_counter(nested);
573         
574         const float radius(sinfgapp::Main::get_bline_width().units(get_canvas()->rend_desc())+(abs(get_work_area()->get_pw())+abs(get_work_area()->get_ph()))*5);
575
576
577         // If we aren't using pressure width,
578         // then set all the width to 1
579         if(!get_pressure_width_flag())
580         {
581                 std::list<sinfg::Real>::iterator iter;
582                 for(iter=width_data->begin();iter!=width_data->end();++iter)
583                 {
584                         *iter=1.0;
585                 }
586         }
587         
588         //get_work_area()->add_stroke(event.stroke_data,sinfgapp::Main::get_foreground_color());
589         //stroke_list.push_back(event.stroke_data);
590         //refresh_ducks();
591                 
592         std::list<sinfg::BLinePoint> bline;
593         bool loop_bline_flag(false);
594         
595         //Changed by Adrian - use resident class :)
596         //sinfgapp::convert_stroke_to_bline(bline, *event.stroke_data,*event.width_data, sinfgapp::Main::get_bline_width());
597         blineconv.width = sinfgapp::Main::get_bline_width().units(get_canvas()->rend_desc());
598         
599         if(get_local_error_flag())
600         {
601                 float pw = get_work_area()->get_pw();
602                 float ph = get_work_area()->get_ph();
603                 
604                 blineconv.pixelwidth = sqrt(pw*pw+ph*ph);
605                 blineconv.smoothness = get_lthres();
606         }else
607         {
608                 blineconv.pixelwidth = 1;
609                 blineconv.smoothness = get_gthres();            
610         }
611         
612         blineconv(bline,*stroke_data,*width_data);
613         
614         //Postprocess to require minimum pressure
615         if(get_min_pressure_flag())
616         {
617                 sinfgapp::BLineConverter::EnforceMinWidth(bline,get_min_pressure());
618         }
619         
620         // If the start and end points are similar, then make then the same point
621         if(get_auto_loop_flag())
622         if(bline.size()>2&&(bline.front().get_vertex()-bline.back().get_vertex()).mag()<=radius)
623         {
624                 loop_bline_flag=true;
625                 Vector tangent;
626                 Real width(0);
627
628                 while(bline.size()>2&&(bline.front().get_vertex()-bline.back().get_vertex()).mag()<=radius)
629                 {
630                         tangent=bline.back().get_tangent1();
631                         width=bline.back().get_width();
632                         bline.pop_back();
633                 }
634                 
635                 if(abs(bline.front().get_tangent1().norm()*tangent.norm().perp())>SIMILAR_TANGENT_THRESHOLD)
636                 {
637                         // If the tangents are not similar, then
638                         // split the tangents
639                         bline.front().set_split_tangent_flag(true);
640                         bline.front().set_tangent1(tangent);
641                 }
642                 else
643                 {
644                         // If the tangents are similar, then set the tangent
645                         // to the average of the two
646                         bline.front().set_tangent((tangent+bline.front().get_tangent1())*0.5f);
647                 }
648                 
649                 // Add the widths of the two points
650                 {       
651                         Real width(bline.front().get_width()+width);
652                         width=width<=1?width:1;
653                         bline.front().set_width(width);
654                 }
655         }
656
657         // If the bline only has once blinepoint, then there is nothing to do.
658         if(bline.size()<=1)
659                 return Smach::RESULT_OK;
660
661         if(region_flag)
662                 return new_region(bline,radius);
663
664         return new_bline(bline,loop_bline_flag,radius);
665 }
666
667 Smach::event_result
668 StateDraw_Context::new_bline(std::list<sinfg::BLinePoint> bline,bool loop_bline_flag,float radius)
669 {
670         // Create the action group
671         sinfgapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Sketch BLine"));
672
673         //ValueNode_BLine::Handle value_node(ValueNode_BLine::create(sinfg::ValueBase(bline,loop_bline_flag)));
674         ValueNode_BLine::Handle value_node;
675
676         {
677                 std::list<sinfg::BLinePoint> trans_bline;
678                 std::list<sinfg::BLinePoint>::iterator iter;
679                 const sinfg::TransformStack& transform(get_canvas_view()->get_curr_transform_stack());
680                 
681                 for(iter=bline.begin();iter!=bline.end();++iter)
682                 {
683                         BLinePoint bline_point(*iter);
684                         Point new_vertex(transform.unperform(bline_point.get_vertex()));
685                         
686                         bline_point.set_tangent1(
687                                 transform.unperform(
688                                         bline_point.get_tangent1()+bline_point.get_vertex()
689                                 ) -new_vertex
690                         );
691
692                         bline_point.set_tangent2(
693                                 transform.unperform(
694                                         bline_point.get_tangent2()+bline_point.get_vertex()
695                                 ) -new_vertex
696                         );
697                         
698                         bline_point.set_vertex(new_vertex);
699                         
700                         trans_bline.push_back(bline_point);
701                 }
702                 value_node=ValueNode_BLine::create(sinfg::ValueBase(trans_bline,loop_bline_flag));
703         }
704
705         // Find any ducks at the start or end that we might attach to
706         // (If we aren't a loop)
707         if(!loop_bline_flag && get_auto_connect_flag())
708         {
709                 
710                 etl::handle<Duck> start_duck(get_work_area()->find_duck(bline.front().get_vertex(),radius,Duck::TYPE_VERTEX));
711                 etl::handle<Duck> finish_duck(get_work_area()->find_duck(bline.back().get_vertex(),radius,Duck::TYPE_VERTEX));
712                 
713                 if(start_duck)do
714                 {
715                         sinfgapp::ValueDesc value_desc(start_duck->get_value_desc());
716                         if(!value_desc)
717                         {
718                                 break;
719                         }
720
721                         ValueNode_BLine::Handle value_node_bline;
722
723                         if(value_desc.parent_is_value_node())
724                         {
725                                 value_node_bline=ValueNode_BLine::Handle::cast_dynamic(value_desc.get_parent_value_node());
726                         }
727                         if(value_node_bline)
728                         {
729                                 if(value_desc.get_index()==0)
730                                 {
731                                         // SPECIAL CASE -- EXTENSION
732                                         // We need to reverse the BLine first.
733                                         bline.pop_front();
734                                         reverse_bline(bline);
735                                         return extend_bline_from_begin(value_node_bline,bline);                                 
736                                 }
737                                 if(value_desc.get_index()==value_node_bline->link_count()-1)
738                                 {
739                                         // SPECIAL CASE -- EXTENSION
740                                         bline.pop_front();
741                                         return extend_bline_from_end(value_node_bline,bline);
742                                 }
743                         }
744
745                         switch(value_desc.get_value_type())
746                         {
747                         case sinfg::ValueBase::TYPE_BLINEPOINT:
748                                 //get_canvas_interface()->auto_export(value_desc);
749                                 //value_node->list.front().value_node=value_desc.get_value_node();
750                                 
751                                 value_desc=sinfgapp::ValueDesc(LinkableValueNode::Handle::cast_dynamic(value_desc.get_value_node()),0);
752                                 //break;
753                         case sinfg::ValueBase::TYPE_VECTOR:
754                                 get_canvas_interface()->auto_export(value_desc);
755                                 LinkableValueNode::Handle::cast_dynamic(value_node->list.front().value_node)->set_link(0,value_desc.get_value_node());
756                                 break;
757                         default:
758                                 break;
759                         }
760                 }while(0);
761                 
762                 if(finish_duck)do
763                 {
764                         sinfgapp::ValueDesc value_desc(finish_duck->get_value_desc());
765                         if(!value_desc)
766                         {
767                                 break;
768                         }
769
770                         ValueNode_BLine::Handle value_node_bline;
771
772                         if(value_desc.parent_is_value_node())
773                                 value_node_bline=ValueNode_BLine::Handle::cast_dynamic(value_desc.get_parent_value_node());
774                         if(value_node_bline)
775                         {
776                                 if(value_desc.get_index()==0)
777                                 {
778                                         // SPECIAL CASE -- EXTENSION
779                                         bline.pop_back();
780                                         return extend_bline_from_begin(value_node_bline,bline);                                 
781                                 }
782                                 if(value_desc.get_index()==value_node_bline->link_count()-1)
783                                 {
784                                         // SPECIAL CASE -- EXTENSION
785                                         // We need to reverse the BLine first.
786                                         bline.pop_back();
787                                         reverse_bline(bline);
788                                         return extend_bline_from_end(value_node_bline,bline);
789                                 }
790                         }
791
792                         switch(value_desc.get_value_type())
793                         {
794                         case sinfg::ValueBase::TYPE_BLINEPOINT:
795                                 //get_canvas_interface()->auto_export(value_desc);
796                                 //value_node->list.back().value_node=value_desc.get_value_node();
797
798                                 value_desc=sinfgapp::ValueDesc(LinkableValueNode::Handle::cast_dynamic(value_desc.get_value_node()),0);
799                                 //break;
800                         case sinfg::ValueBase::TYPE_VECTOR:
801                                 get_canvas_interface()->auto_export(value_desc);
802                                 LinkableValueNode::Handle::cast_dynamic(value_node->list.back().value_node)->set_link(0,value_desc.get_value_node());
803                                 break;
804                         default:
805                                 break;
806                         }
807                         
808                 }while(0);
809         }
810         
811         // Create the layer
812         {
813                 Layer::Handle layer;
814                 Canvas::Handle canvas(get_canvas_view()->get_canvas());
815                 int depth(0);
816                 
817                 // we are temporarily using the layer to hold something
818                 layer=get_canvas_view()->get_selection_manager()->get_selected_layer();
819                 if(layer)
820                 {
821                         depth=layer->get_depth();
822                         canvas=layer->get_canvas();
823                 }
824                 
825                 //int number(sinfg::UniqueID().get_uid());
826                 
827                 sinfgapp::PushMode push_mode(get_canvas_interface(),sinfgapp::MODE_NORMAL);
828                 
829                 if(get_region_only_flag())
830                         layer=get_canvas_interface()->add_layer_to("region",canvas,depth);
831                 else
832                         layer=get_canvas_interface()->add_layer_to("outline",canvas,depth);
833                         
834                 if(get_feather())
835                 {
836                         layer->set_param("feather",get_feather());
837                         get_canvas_interface()->signal_layer_param_changed()(layer,"feather");
838                 }
839                 assert(layer);
840                 //layer->set_description(strprintf("Stroke %d",number));
841                 //get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
842                 
843                 
844                 
845                 sinfgapp::Action::Handle action(sinfgapp::Action::create("layer_param_connect"));
846                 
847                 assert(action);
848                 
849                 action->set_param("canvas",get_canvas());                       
850                 action->set_param("canvas_interface",get_canvas_interface());                   
851                 action->set_param("layer",layer);                       
852                 if(!action->set_param("param",String("bline")))
853                         sinfg::error("LayerParamConnect didn't like \"param\"");
854                 if(!action->set_param("value_node",ValueNode::Handle(value_node)))
855                         sinfg::error("LayerParamConnect didn't like \"value_node\"");
856                 
857                 if(!get_canvas_interface()->get_instance()->perform_action(action))
858                 {
859                         get_canvas_view()->get_ui_interface()->error(_("Unable to create layer"));
860                         group.cancel();
861                         //refresh_ducks();
862                         return Smach::RESULT_ERROR;
863                 }
864                 get_canvas_view()->get_selection_manager()->set_selected_layer(layer);
865                 //refresh_ducks();
866         }
867         
868         last_stroke=value_node;
869         return Smach::RESULT_ACCEPT;
870 }
871
872 Smach::event_result
873 StateDraw_Context::new_region(std::list<sinfg::BLinePoint> bline, sinfg::Real radius)
874 {
875         // Create the action group
876         sinfgapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Define Region"));
877         
878         std::list<sinfgapp::ValueDesc> vertex_list;
879         
880         // First we need to come up with a rough list of
881         // BLinePoints that we are going to be using to
882         // define our region. 
883         {
884                 std::list<sinfg::BLinePoint>::iterator iter;
885                 for(iter=bline.begin();iter!=bline.end();++iter)
886                 {
887                         etl::handle<Duck> duck(get_work_area()->find_duck(iter->get_vertex(),0,Duck::TYPE_VERTEX));
888                         
889                         if(!duck)
890                         {
891                                 sinfg::info(__FILE__":%d: Nothing to enclose!",__LINE__);                               
892                                 return Smach::RESULT_OK;                
893                         }
894                         
895                         
896                         assert(duck->get_type()==Duck::TYPE_VERTEX);
897                         
898                         sinfgapp::ValueDesc value_desc(duck->get_value_desc());
899                         
900                         if(!value_desc)
901                         {
902                                 sinfg::info(__FILE__":%d: Got a hit, but no ValueDesc on this duck",__LINE__);                          
903                                 continue;
904                         }
905                         
906                         switch(value_desc.get_value_type())
907                         {
908                         case sinfg::ValueBase::TYPE_BLINEPOINT:
909                                 //if(vertex_list.empty() || value_desc!=vertex_list.back())
910                                 vertex_list.push_back(value_desc);
911                                 assert(vertex_list.back().is_valid());
912                         
913                                 break;
914                         default:
915                                 break;
916                         }
917                 }
918         }
919         
920         if(vertex_list.size()<=2)
921         {
922                 sinfg::info(__FILE__":%d: Vertex list too small to make region.",__LINE__);
923                 return Smach::RESULT_OK;                
924         }
925
926         assert(vertex_list.back().is_valid());
927         
928         // Remove any duplicates
929         {
930         }
931         
932         // Now we need to clean the list of vertices up
933         // a bit. This includes inserting missing vertices
934         // and removing extraneous ones.
935         // We can do this in multiple passes.
936         int i=0;
937         for(bool done=false;!done && i<30;i++)
938         {
939                 // Set done to "true" for now. If
940                 // any updates are performed, we will
941                 // change it back to false.
942                 done=true;
943                 
944                 std::list<sinfgapp::ValueDesc>::iterator prev,iter,next;
945                 prev=vertex_list.end();prev--;  // Set prev to the last ValueDesc
946                 next=vertex_list.begin();
947                 iter=next++; // Set iter to the first value desc, and next to the second
948                 
949                 for(;iter!=vertex_list.end();prev=iter,iter=next++)
950                 {
951                         sinfgapp::ValueDesc value_prev(*prev);
952                         sinfgapp::ValueDesc value_desc(*iter);
953                         sinfgapp::ValueDesc value_next((next==vertex_list.end())?vertex_list.front():*next);
954                         
955                         assert(value_desc.is_valid());
956                         assert(value_next.is_valid());
957                         assert(value_prev.is_valid());
958                         
959                         //sinfg::info("-------");
960                         //sinfg::info(__FILE__":%d: value_prev 0x%08X:%d",__LINE__,value_prev.get_parent_value_node().get(),value_prev.get_index());
961                         //sinfg::info(__FILE__":%d: value_desc 0x%08X:%d",__LINE__,value_desc.get_parent_value_node().get(),value_desc.get_index());
962                         //sinfg::info(__FILE__":%d: value_next 0x%08X:%d",__LINE__,value_next.get_parent_value_node().get(),value_next.get_index());
963
964                         /*
965                         if(value_prev.parent_is_value_node() && value_desc.parent_is_value_node() && value_next.parent_is_value_node())
966                         {
967                                 // Remove random extraneous vertices
968                                 if(value_prev.get_parent_value_node()==value_next.get_parent_value_node() &&
969                                         value_prev.get_parent_value_node()!=value_desc.get_parent_value_node())
970                                 {
971                                         DEBUGPOINT();
972                                         vertex_list.erase(iter);
973                                         done=false;
974                                         break;
975                                 }
976                         }       
977                         */
978                         
979                         // Remove duplicate vertices
980                         if(value_prev.get_value_node()==value_desc.get_value_node()
981                                 || value_desc.get_value_node()==value_next.get_value_node())
982                         {
983                                 DEBUGPOINT();
984                                 vertex_list.erase(iter);
985                                 done=false;
986                                 break;
987                         }
988                         if(value_prev.get_value_node()==value_next.get_value_node())
989                         {
990                                 DEBUGPOINT();
991                                 vertex_list.erase(prev);
992                                 done=false;
993                                 break;
994                         }
995                         
996                         if(value_desc.parent_is_value_node() && value_next.parent_is_value_node())
997                         if(value_desc.get_parent_value_node()==value_next.get_parent_value_node() && (next!=vertex_list.end()))
998                         {
999                                 // Fill in missing vertices
1000                                 if(value_desc.get_index()<value_next.get_index()-1)
1001                                 {
1002                                         DEBUGPOINT();
1003                                         vertex_list.insert(next,sinfgapp::ValueDesc(value_desc.get_parent_value_node(),value_desc.get_index()+1));
1004                                         done=false;
1005                                         break;
1006                                 }
1007                                 if(value_next.get_index()<value_desc.get_index()-1)
1008                                 {
1009                                         DEBUGPOINT();
1010                                         vertex_list.insert(next,sinfgapp::ValueDesc(value_desc.get_parent_value_node(),value_next.get_index()+1));
1011                                         done=false;
1012                                         break;
1013                                 }
1014                         }
1015                         
1016                         // Ensure that connections
1017                         // between blines are properly
1018                         // connected
1019                         if(value_desc.parent_is_value_node() && value_next.parent_is_value_node())
1020                         if(value_desc.get_parent_value_node()!=value_next.get_parent_value_node() &&
1021                                 value_desc.get_value_node()!=value_next.get_value_node())
1022                         {
1023                                 BLinePoint vertex(value_desc.get_value(get_time()).get(BLinePoint()));
1024                                 BLinePoint vertex_next(value_next.get_value(get_time()).get(BLinePoint()));
1025
1026                                 //sinfg::info("--------");
1027                                 //sinfg::info(__FILE__":%d: vertex: [%f, %f]",__LINE__,vertex.get_vertex()[0],vertex.get_vertex()[1]);
1028                                 //sinfg::info(__FILE__":%d: vertex_next: [%f, %f]",__LINE__,vertex_next.get_vertex()[0],vertex_next.get_vertex()[1]);
1029                                 
1030                                 if((vertex.get_vertex()-vertex_next.get_vertex()).mag_squared()<radius*radius)
1031                                 {
1032                                         DEBUGPOINT();
1033                                         ValueNode_Composite::Handle value_node;
1034                                         ValueNode_Composite::Handle value_node_next;
1035                                         value_node=ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node().clone());
1036                                         value_node_next=ValueNode_Composite::Handle::cast_dynamic(value_next.get_value_node().clone());
1037                                         if(!value_node || !value_node_next)
1038                                         {
1039                                                 sinfg::info(__FILE__":%d: Unable to properly connect blines.",__LINE__);
1040                                                 continue;
1041                                         }
1042                                         DEBUGPOINT();
1043                                         value_node->set_link(5,value_node_next->get_link(5));
1044                                         value_node->set_link(3,ValueNode_Const::create(true));
1045
1046                                         get_canvas_interface()->auto_export(value_node);
1047                                         assert(value_node->is_exported());
1048                                         *iter=sinfgapp::ValueDesc(get_canvas(),value_node->get_id());
1049                                         vertex_list.erase(next);
1050                                         done=false;
1051                                         break;                                  
1052                                 }
1053                                 else
1054                                 {
1055                                         DEBUGPOINT();
1056                                         bool positive_trend(value_desc.get_index()>value_prev.get_index());
1057                                         
1058                                         if(!positive_trend && value_desc.get_index()>0)
1059                                         {
1060                                                 DEBUGPOINT();
1061                                                 vertex_list.insert(next,sinfgapp::ValueDesc(value_desc.get_parent_value_node(),value_desc.get_index()-1));
1062                                                 done=false;
1063                                                 break;                                  
1064                                         }
1065                                         if(positive_trend && value_desc.get_index()<LinkableValueNode::Handle::cast_static(value_desc.get_value_node())->link_count()-1)
1066                                         {
1067                                                 DEBUGPOINT();
1068                                                 vertex_list.insert(next,sinfgapp::ValueDesc(value_desc.get_parent_value_node(),value_desc.get_index()+1));
1069                                                 done=false;
1070                                                 break;                                  
1071                                         }
1072                                 }
1073                                 
1074                         }
1075                 }
1076         }
1077
1078         if(vertex_list.size()<=2)
1079         {
1080                 sinfg::info(__FILE__":%d: Vertex list too small to make region.",__LINE__);
1081                 return Smach::RESULT_OK;                
1082         }
1083         
1084         ValueNode_BLine::Handle value_node_bline;
1085
1086         // Now we need to test for the trivial case,
1087         // which is where all of the vertices
1088         // come from one BLine. 
1089         if(vertex_list.front().parent_is_linkable_value_node())
1090         {
1091                 bool trivial_case(true);
1092                 ValueNode::Handle trivial_case_value_node;
1093                 
1094                 trivial_case_value_node=vertex_list.front().get_parent_value_node();
1095                 
1096                 std::list<sinfgapp::ValueDesc>::iterator iter;
1097                 for(iter=vertex_list.begin();iter!=vertex_list.end();++iter)
1098                 {
1099                         if(trivial_case_value_node!=iter->get_parent_value_node())
1100                         {
1101                                 trivial_case=false;
1102                                 break;
1103                         }
1104                 }
1105                 if(trivial_case)
1106                         value_node_bline=ValueNode_BLine::Handle::cast_dynamic(trivial_case_value_node);
1107         }
1108         
1109         // If we aren't the trivial case,
1110         // then go ahead and create the new
1111         // BLine value node
1112         if(!value_node_bline)
1113         {
1114                 value_node_bline=ValueNode_BLine::create();
1115
1116                 std::list<sinfgapp::ValueDesc>::iterator iter;          
1117                 for(iter=vertex_list.begin();iter!=vertex_list.end();++iter)
1118                 {
1119                         // Ensure that the vertex is exported.
1120                         get_canvas_interface()->auto_export(*iter);     
1121                         
1122                         value_node_bline->add(iter->get_value_node());  
1123                         //value_node_bline->add(ValueNode_BLine::ListEntry(iter->get_value_node()));    
1124                 }
1125                 
1126                 value_node_bline->set_loop(true);
1127         }
1128
1129         get_canvas_interface()->auto_export(value_node_bline);                  
1130
1131         // Now we create the region layer
1132         // Create the layer
1133         {
1134                 Layer::Handle layer;
1135                 Canvas::Handle canvas(get_canvas_view()->get_canvas());
1136                 int depth(0);
1137                 
1138                 // we are temporarily using the layer to hold something
1139                 layer=get_canvas_view()->get_selection_manager()->get_selected_layer();
1140                 if(layer)
1141                 {
1142                         depth=layer->get_depth();
1143                         canvas=layer->get_canvas();
1144                 }
1145                 
1146                 sinfgapp::PushMode push_mode(get_canvas_interface(),sinfgapp::MODE_NORMAL);
1147                 
1148                 layer=get_canvas_interface()->add_layer_to("region",canvas,depth);
1149                 assert(layer);
1150                 layer->set_param("color",sinfgapp::Main::get_background_color());
1151                 if(get_feather())
1152                 {
1153                         layer->set_param("feather",get_feather());
1154                         get_canvas_interface()->signal_layer_param_changed()(layer,"feather");
1155                 }
1156                 get_canvas_interface()->signal_layer_param_changed()(layer,"color");
1157
1158                 sinfgapp::Action::Handle action(sinfgapp::Action::create("layer_param_connect"));
1159                 
1160                 assert(action);
1161                 
1162                 action->set_param("canvas",get_canvas());                       
1163                 action->set_param("canvas_interface",get_canvas_interface());                   
1164                 action->set_param("layer",layer);                       
1165                 if(!action->set_param("param",String("bline")))
1166                         sinfg::error("LayerParamConnect didn't like \"param\"");
1167                 if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
1168                         sinfg::error("LayerParamConnect didn't like \"value_node\"");
1169                 
1170                 if(!get_canvas_interface()->get_instance()->perform_action(action))
1171                 {
1172                         get_canvas_view()->get_ui_interface()->error(_("Unable to create Region layer"));
1173                         group.cancel();
1174                         return Smach::RESULT_ERROR;
1175                 }
1176                 get_canvas_view()->get_selection_manager()->set_selected_layer(layer);
1177         }
1178         
1179         return Smach::RESULT_ACCEPT;
1180 }
1181
1182 void
1183 StateDraw_Context::refresh_ducks()
1184 {
1185         get_canvas_view()->queue_rebuild_ducks();
1186 /*
1187         get_work_area()->clear_ducks();
1188         
1189         
1190         std::list< etl::smart_ptr<std::list<sinfg::Point> > >::iterator iter;
1191         
1192         for(iter=stroke_list.begin();iter!=stroke_list.end();++iter)
1193         {
1194                 get_work_area()->add_stroke(*iter);
1195         }
1196         
1197         get_work_area()->queue_draw();                  
1198 */
1199 }
1200
1201
1202 Smach::event_result
1203 StateDraw_Context::extend_bline_from_begin(ValueNode_BLine::Handle value_node,std::list<sinfg::BLinePoint> bline)
1204 {
1205         // Create the action group
1206         sinfgapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Extend BLine"));
1207         
1208         std::list<sinfg::BLinePoint>::reverse_iterator iter;
1209         iter=bline.rbegin();
1210         for(;!(iter==bline.rend());++iter)
1211         {
1212                 //iter->reverse();
1213                 ValueNode_Composite::Handle composite(ValueNode_Composite::create(*iter));
1214
1215                 sinfgapp::Action::Handle action(sinfgapp::Action::create("value_node_dynamic_list_insert"));
1216                 
1217                 assert(action);
1218                 sinfgapp::ValueDesc value_desc(value_node,0);
1219                 
1220                 action->set_param("canvas",get_canvas());                       
1221                 action->set_param("canvas_interface",get_canvas_interface());                   
1222                 action->set_param("value_desc",value_desc);
1223                 if(!action->set_param("item",ValueNode::Handle(composite)))
1224                         sinfg::error("ACTION didn't like \"item\"");
1225                 
1226                 if(!get_canvas_interface()->get_instance()->perform_action(action))
1227                 {
1228                         get_canvas_view()->get_ui_interface()->error(_("Unable to insert item"));
1229                         group.cancel();
1230                         //refresh_ducks();
1231                         return Smach::RESULT_ERROR;
1232                 }               
1233         }
1234         last_stroke=value_node;
1235         return Smach::RESULT_ACCEPT;
1236 }
1237
1238 Smach::event_result
1239 StateDraw_Context::extend_bline_from_end(ValueNode_BLine::Handle value_node,std::list<sinfg::BLinePoint> bline)
1240 {
1241         // Create the action group
1242         sinfgapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Extend BLine"));
1243
1244         std::list<sinfg::BLinePoint>::iterator iter;
1245         iter=bline.begin();
1246         for(;iter!=bline.end();++iter)
1247         {
1248                 ValueNode_Composite::Handle composite(ValueNode_Composite::create(*iter));
1249
1250                 sinfgapp::Action::Handle action(sinfgapp::Action::create("value_node_dynamic_list_insert"));
1251                 
1252                 assert(action);
1253                 sinfgapp::ValueDesc value_desc(value_node,value_node->link_count());
1254                 
1255                 action->set_param("canvas",get_canvas());                       
1256                 action->set_param("canvas_interface",get_canvas_interface());                   
1257                 action->set_param("value_desc",value_desc);
1258                 if(!action->set_param("item",ValueNode::Handle(composite)))
1259                         sinfg::error("ACTION didn't like \"item\"");
1260                 
1261                 if(!get_canvas_interface()->get_instance()->perform_action(action))
1262                 {
1263                         get_canvas_view()->get_ui_interface()->error(_("Unable to insert item"));
1264                         group.cancel();
1265                         //refresh_ducks();
1266                         return Smach::RESULT_ERROR;
1267                 }               
1268         }
1269         last_stroke=value_node;
1270         return Smach::RESULT_ACCEPT;
1271 }
1272
1273 void
1274 StateDraw_Context::reverse_bline(std::list<sinfg::BLinePoint> &bline)
1275 {
1276         int i;
1277         
1278         std::list<sinfg::BLinePoint>::iterator iter,eiter;
1279         iter=bline.begin();
1280         eiter=bline.end();
1281         eiter--;
1282         for(i=0;i<(int)bline.size()/2;++iter,--eiter,i++)
1283         {
1284                 iter_swap(iter,eiter);
1285                 iter->reverse();
1286                 eiter->reverse();
1287         }
1288 }
1289
1290 void
1291 StateDraw_Context::fill_last_stroke()
1292 {
1293         if(!last_stroke)
1294                 return;
1295         
1296         sinfgapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Fill Stroke"));
1297
1298         Layer::Handle layer;
1299         
1300         get_canvas_interface()->auto_export(last_stroke);                       
1301
1302         sinfgapp::PushMode push_mode(get_canvas_interface(),sinfgapp::MODE_NORMAL);
1303         
1304         layer=get_canvas_interface()->add_layer("region");
1305         assert(layer);
1306         layer->set_param("color",sinfgapp::Main::get_background_color());
1307
1308         sinfgapp::Action::Handle action(sinfgapp::Action::create("layer_param_connect"));
1309         
1310         assert(action);
1311         
1312         action->set_param("canvas",get_canvas());                       
1313         action->set_param("canvas_interface",get_canvas_interface());                   
1314         action->set_param("layer",layer);                       
1315         if(!action->set_param("param",String("segment_list")))
1316                 sinfg::error("LayerParamConnect didn't like \"param\"");
1317         if(!action->set_param("value_node",ValueNode::Handle(last_stroke)))
1318                 sinfg::error("LayerParamConnect didn't like \"value_node\"");
1319         
1320         if(!get_canvas_interface()->get_instance()->perform_action(action))
1321         {
1322                 get_canvas_view()->get_ui_interface()->error(_("Unable to create Region layer"));
1323                 group.cancel();
1324                 return;
1325         }
1326         get_canvas_view()->get_selection_manager()->set_selected_layer(layer);
1327 }