1 /* === S Y N F I G ========================================================= */
2 /*!	\file state_bline.cpp
3 **	\brief Template File
4 **
5 **	$Id$
6 **
7 **	\legal
8 **	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **	Copyright (c) 2007, 2008 Chris Moore
10 **  Copyright (c) 2010, 2011 Carlos López
11 **
12 **	This package is free software; you can redistribute it and/or
13 **	modify it under the terms of the GNU General Public License as
14 **	published by the Free Software Foundation; either version 2 of
15 **	the License, or (at your option) any later version.
16 **
17 **	This package is distributed in the hope that it will be useful,
18 **	but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **	General Public License for more details.
21 **	\endlegal
22 */
23 /* ========================================================================= */
24 
25 /* === H E A D E R S ======================================================= */
26 
27 #ifdef USING_PCH
28 #	include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #	include <config.h>
32 #endif
33 
34 #include <synfig/general.h>
35 
36 #include <synfig/valuenodes/valuenode_dynamiclist.h>
37 
38 #include "state_bline.h"
39 #include "state_normal.h"
40 #include "canvasview.h"
41 #include "workarea.h"
42 #include "app.h"
43 #include <synfig/valuenodes/valuenode_bline.h>
44 #include <ETL/hermite>
45 #include <ETL/calculus>
46 #include <utility>
47 #include "event_mouse.h"
48 #include "event_layerclick.h"
49 #include "docks/dock_toolbox.h"
50 #include "docks/dialog_tooloptions.h"
51 #include "widgets/widget_enum.h"
52 #include "widgets/widget_distance.h"
53 #include <synfig/transform.h>
54 #include <synfigapp/main.h>
55 
56 #include <gui/localization.h>
57 
58 #include <gtkmm/separatormenuitem.h>
59 #include <gtkmm/imagemenuitem.h>
60 #endif
61 
62 /* === U S I N G =========================================================== */
63 
64 using namespace std;
65 using namespace etl;
66 using namespace synfig;
67 using namespace studio;
68 
69 /* === M A C R O S ========================================================= */
70 
71 // if defined, show the first duck as green while drawing
72 #define DISTINGUISH_FIRST_DUCK
73 
74 #ifndef LAYER_CREATION
75 #define LAYER_CREATION(button, stockid, tooltip)	\
76 	{ \
77 		Gtk::Image *icon = manage(new Gtk::Image(Gtk::StockID(stockid), \
78 			Gtk::ICON_SIZE_SMALL_TOOLBAR)); \
79 		button.add(*icon); \
80 	} \
81 	button.set_relief(Gtk::RELIEF_NONE); \
82 	button.set_tooltip_text(tooltip) ;\
83 	button.signal_toggled().connect(sigc::mem_fun(*this, \
84 		&studio::StateBLine_Context::toggle_layer_creation))
85 #endif
86 
87 // indentation for options layout
88 #ifndef SPACING
89 #define SPACING(name, px) \
90 	Gtk::Alignment *name = Gtk::manage(new Gtk::Alignment()); \
91 	name->set_size_request(px)
92 #endif
93 
94 #define GAP	(3)
95 #define INDENTATION (6)
96 
97 /* === G L O B A L S ======================================================= */
98 
99 StateBLine studio::state_bline;
100 
101 /* === C L A S S E S & S T R U C T S ======================================= */
102 
103 class studio::StateBLine_Context : public sigc::trackable
104 {
105 	etl::handle<CanvasView> canvas_view_;
106 	CanvasView::IsWorking is_working;
107 
108 	bool prev_table_status;
109 	bool loop_;
110 	bool prev_workarea_layer_status_;
111 
112 	int depth;
113 	Canvas::Handle canvas;
114 
115 	Gtk::Menu menu;
116 
117 	Duckmatic::Push duckmatic_push;
118 
119 	etl::handle<Duck> curr_duck;
120 
121 	etl::handle<Duck> next_duck;
122 
123 	std::list<synfig::ValueNode_Const::Handle> bline_point_list;
124 
125 	bool on_vertex_change(const studio::Duck &duck, synfig::ValueNode_Const::Handle value_node);
126 	bool on_tangent1_change(const studio::Duck &duck, handle<WorkArea::Duck> other_duck, synfig::ValueNode_Const::Handle value_node);
127 	bool on_tangent2_change(const studio::Duck &duck, handle<WorkArea::Duck> other_duck, synfig::ValueNode_Const::Handle value_node);
128 
129 	void popup_handle_menu(synfig::ValueNode_Const::Handle value_node);
130 	void popup_vertex_menu(synfig::ValueNode_Const::Handle value_node);
131 	void popup_bezier_menu(float location, synfig::ValueNode_Const::Handle value_node);
132 
133 	void bline_set_split_handle(synfig::ValueNode_Const::Handle value_node, bool merge_radius, bool merge_angle);
134 	void bline_delete_vertex(synfig::ValueNode_Const::Handle value_node);
135 	void bline_insert_vertex(synfig::ValueNode_Const::Handle value_node,float origin=0.5);
136 	void loop_bline();
137 	void unloop_bline();
138 
139 	void refresh_ducks(bool x=true);
140 
141 	//Toolbox settings
142 	synfigapp::Settings& settings;
143 
144 	// holder of options
145 	Gtk::Table options_table;
146 
147 	// title
148 	Gtk::Label title_label;
149 
150 	// layer name:
151 	Gtk::Label id_label;
152 	Gtk::HBox id_box;
153 	Gtk::Entry id_entry;
154 
155 	// layer types to create:
156 	Gtk::Label layer_types_label;
157 	Gtk::ToggleButton layer_region_togglebutton;
158 	Gtk::ToggleButton layer_outline_togglebutton;
159 	Gtk::ToggleButton layer_advanced_outline_togglebutton;
160 	Gtk::ToggleButton layer_curve_gradient_togglebutton;
161 	Gtk::ToggleButton layer_plant_togglebutton;
162 	Gtk::HBox layer_types_box;
163 
164 	// blend method
165 	Gtk::Label blend_label;
166 	Gtk::HBox blend_box;
167 	Widget_Enum blend_enum;
168 
169 	// opacity
170 	Gtk::Label opacity_label;
171 	Gtk::HScale opacity_hscl;
172 
173 	// brush size
174 	Gtk::Label bline_width_label;
175 	Widget_Distance bline_width_dist;
176 
177 	// feather size
178 	Gtk::Label feather_label;
179 	Widget_Distance feather_dist;
180 
181 	// link origins
182 	Gtk::Label link_origins_label;
183 	Gtk::CheckButton layer_link_origins_checkbutton;
184 	Gtk::HBox link_origins_box;
185 
186 	// auto export
187 	Gtk::Label auto_export_label;
188 	Gtk::CheckButton auto_export_checkbutton;
189 	Gtk::HBox auto_export_box;
190 
191 	// toolbar buttons
192 	Gtk::Button button_make;
193 	Gtk::Button button_clear;
194 
195 public:
196 
layers_to_create() const197 	int layers_to_create()const
198 	{
199 		return
200 			get_layer_region_flag() +
201 			get_layer_outline_flag() +
202 			get_layer_advanced_outline_flag()+
203 			get_layer_curve_gradient_flag() +
204 			get_layer_plant_flag();
205 	}
206 
sanity_check()207 	void sanity_check()
208 	{
209 		if(layers_to_create()==0)
210 			set_layer_region_flag(true);
211 	}
212 
get_id() const213 	synfig::String get_id()const { return id_entry.get_text(); }
set_id(const synfig::String & x)214 	void set_id(const synfig::String& x) { return id_entry.set_text(x); }
215 
get_blend() const216 		int get_blend()const { return blend_enum.get_value(); }
set_blend(int x)217 	void set_blend(int x) { return blend_enum.set_value(x); }
218 
get_opacity() const219 	Real get_opacity()const { return opacity_hscl.get_value(); }
set_opacity(Real x)220 	void set_opacity(Real x) { opacity_hscl.set_value(x); }
221 
get_bline_width() const222 	Real get_bline_width() const {
223 		return bline_width_dist.get_value().get(
224 			Distance::SYSTEM_UNITS,
225 			get_canvas_view()->get_canvas()->rend_desc()
226 		);
227 	}
set_bline_width(Distance x)228 	void set_bline_width(Distance x) { return bline_width_dist.set_value(x);}
229 
get_feather_size() const230 	Real get_feather_size() const {
231 		return feather_dist.get_value().get(
232 			Distance::SYSTEM_UNITS,
233 			get_canvas_view()->get_canvas()->rend_desc()
234 		);
235 	}
set_feather_size(Distance x)236 	void set_feather_size(Distance x) { return feather_dist.set_value(x);}
237 
get_layer_region_flag() const238 	bool get_layer_region_flag()const { return layer_region_togglebutton.get_active(); }
set_layer_region_flag(bool x)239 	void set_layer_region_flag(bool x) { return layer_region_togglebutton.set_active(x); }
240 
get_layer_outline_flag() const241 	bool get_layer_outline_flag()const { return layer_outline_togglebutton.get_active(); }
set_layer_outline_flag(bool x)242 	void set_layer_outline_flag(bool x) { return layer_outline_togglebutton.set_active(x); }
243 
get_layer_advanced_outline_flag() const244 	bool get_layer_advanced_outline_flag()const { return layer_advanced_outline_togglebutton.get_active(); }
set_layer_advanced_outline_flag(bool x)245 	void set_layer_advanced_outline_flag(bool x) { return layer_advanced_outline_togglebutton.set_active(x); }
246 
get_layer_curve_gradient_flag() const247 	bool get_layer_curve_gradient_flag()const { return layer_curve_gradient_togglebutton.get_active(); }
set_layer_curve_gradient_flag(bool x)248 	void set_layer_curve_gradient_flag(bool x) { return layer_curve_gradient_togglebutton.set_active(x); }
249 
get_layer_plant_flag() const250 	bool get_layer_plant_flag()const { return layer_plant_togglebutton.get_active(); }
set_layer_plant_flag(bool x)251 	void set_layer_plant_flag(bool x) { return layer_plant_togglebutton.set_active(x); }
252 
get_layer_link_origins_flag() const253 	bool get_layer_link_origins_flag()const { return layer_link_origins_checkbutton.get_active(); }
set_layer_link_origins_flag(bool x)254 	void set_layer_link_origins_flag(bool x) { return layer_link_origins_checkbutton.set_active(x); }
255 
get_auto_export_flag() const256 	bool get_auto_export_flag()const { return auto_export_checkbutton.get_active(); }
set_auto_export_flag(bool x)257 	void set_auto_export_flag(bool x) { return auto_export_checkbutton.set_active(x); }
258 
259   bool layer_region_flag;
260   bool layer_outline_flag;
261   bool layer_advanced_outline_flag;
262   bool layer_curve_gradient_flag;
263   bool layer_plant_flag;
264 
265 	Smach::event_result event_stop_handler(const Smach::event& x);
266 
267 	Smach::event_result event_refresh_handler(const Smach::event& x);
268 
269 	Smach::event_result event_mouse_click_handler(const Smach::event& x);
270 	Smach::event_result event_mouse_release_handler(const Smach::event& x);
271 	Smach::event_result event_mouse_motion_handler(const Smach::event& x);
272 	Smach::event_result event_refresh_tool_options(const Smach::event& x);
273 
event_hijack(const Smach::event &)274 	Smach::event_result event_hijack(const Smach::event& /*x*/) { return Smach::RESULT_ACCEPT; }
275 
276 	void refresh_tool_options();
277 
278 	StateBLine_Context(CanvasView* canvas_view);
279 
280 	~StateBLine_Context();
281 
get_canvas_view() const282 	const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
get_canvas_interface() const283 	etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
get_canvas() const284 	synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
get_work_area() const285 	WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
get_transform_stack() const286 	const synfig::TransformStack& get_transform_stack()const { return get_work_area()->get_curr_transform_stack(); }
287 
288 	void load_settings();
289 	void save_settings();
290 	void reset();
291 	void increment_id();
292 
293 	bool run_();
294 	bool run();
295 
296 	bool egress_on_selection_change;
event_layer_selection_changed_handler(const Smach::event &)297 	Smach::event_result event_layer_selection_changed_handler(const Smach::event& /*x*/)
298 	{
299 		if(egress_on_selection_change)
300 			throw &state_normal;
301 		return Smach::RESULT_OK;
302 	}
303 
304 	void toggle_layer_creation();
305 
306 };	// END of class StateBLine_Context
307 
308 
309 /* === M E T H O D S ======================================================= */
310 
StateBLine()311 StateBLine::StateBLine():
312 	Smach::state<StateBLine_Context>("bline")
313 {
314 	insert(event_def(EVENT_LAYER_SELECTION_CHANGED,		&StateBLine_Context::event_layer_selection_changed_handler));
315 	insert(event_def(EVENT_STOP,						&StateBLine_Context::event_stop_handler));
316 	insert(event_def(EVENT_REFRESH,						&StateBLine_Context::event_refresh_handler));
317 	insert(event_def(EVENT_REFRESH_DUCKS,				&StateBLine_Context::event_hijack));
318 	insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,	&StateBLine_Context::event_mouse_click_handler));
319 	insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,	&StateBLine_Context::event_mouse_release_handler));
320 	insert(event_def(EVENT_WORKAREA_MOUSE_MOTION,		&StateBLine_Context::event_mouse_motion_handler));
321 	insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,	&StateBLine_Context::event_mouse_motion_handler));
322 	insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,		&StateBLine_Context::event_refresh_tool_options));
323 }
324 
~StateBLine()325 StateBLine::~StateBLine()
326 {
327 }
328 
329 void
load_settings()330 StateBLine_Context::load_settings()
331 {
332 	try
333 	{
334 		synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
335 		String value;
336 
337 		if(settings.get_value("bline.id",value))
338 			set_id(value);
339 		else
340 			set_id(_("NewSpline"));
341 
342 		if(settings.get_value("bline.blend",value) && value != "")
343 			set_blend(atoi(value.c_str()));
344 		else
345 			set_blend(0);//(int)Color::BLEND_COMPOSITE); //0 should be blend composites value
346 
347 		if(settings.get_value("bline.opacity",value))
348 			set_opacity(atof(value.c_str()));
349 		else
350 			set_opacity(1);
351 
352 		if(settings.get_value("bline.bline_width",value) && value != "")
353 			set_bline_width(Distance(atof(value.c_str()), App::distance_system));
354 		else
355 			set_bline_width(Distance(1, App::distance_system)); // default width
356 
357 		if(settings.get_value("bline.layer_region",value) && value=="0")
358 			set_layer_region_flag(false);
359 		else
360 			set_layer_region_flag(true);
361 
362 		if(settings.get_value("bline.layer_outline",value) && value=="1")
363 			set_layer_outline_flag(true);
364 		else
365 			set_layer_outline_flag(false);
366 
367 		if(settings.get_value("bline.layer_advanced_outline",value) && value=="0")
368 			set_layer_advanced_outline_flag(false);
369 		else
370 			set_layer_advanced_outline_flag(true);
371 
372 		if(settings.get_value("bline.layer_curve_gradient",value) && value=="1")
373 			set_layer_curve_gradient_flag(true);
374 		else
375 			set_layer_curve_gradient_flag(false);
376 
377 		if(settings.get_value("bline.layer_plant",value) && value=="1")
378 			set_layer_plant_flag(true);
379 		else
380 			set_layer_plant_flag(false);
381 
382 		if(settings.get_value("bline.layer_link_origins",value) && value=="0")
383 			set_layer_link_origins_flag(false);
384 		else
385 			set_layer_link_origins_flag(true);
386 
387 		if(settings.get_value("bline.auto_export",value) && value=="1")
388 			set_auto_export_flag(true);
389 		else
390 			set_auto_export_flag(false);
391 
392 		if(settings.get_value("bline.feather",value))
393 			set_feather_size(Distance(atof(value.c_str()), App::distance_system));
394 		else
395 			set_feather_size(Distance(0, App::distance_system));
396 
397 	  // determine layer flags
398 	  layer_region_flag = get_layer_region_flag();
399 	  layer_outline_flag = get_layer_outline_flag();
400 	  layer_advanced_outline_flag = get_layer_outline_flag();
401 	  layer_curve_gradient_flag = get_layer_curve_gradient_flag();
402 	  layer_plant_flag = get_layer_plant_flag();
403 
404 		sanity_check();
405 	}
406 	catch(...)
407 	{
408 		synfig::warning("State Spline: Caught exception when attempting to load settings.");
409 	}
410 }
411 
412 void
save_settings()413 StateBLine_Context::save_settings()
414 {
415 	try
416 	{
417 		synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
418 		sanity_check();
419 		settings.set_value("bline.id",get_id().c_str());
420 		settings.set_value("bline.layer_outline",get_layer_outline_flag()?"1":"0");
421 		settings.set_value("bline.layer_advanced_outline",get_layer_advanced_outline_flag()?"1":"0");
422 		settings.set_value("bline.layer_region",get_layer_region_flag()?"1":"0");
423 		settings.set_value("bline.layer_curve_gradient",get_layer_curve_gradient_flag()?"1":"0");
424 		settings.set_value("bline.layer_plant",get_layer_plant_flag()?"1":"0");
425 		settings.set_value("bline.layer_link_origins",get_layer_link_origins_flag()?"1":"0");
426 		settings.set_value("bline.blend",strprintf("%d",get_blend()));
427 		settings.set_value("bline.opacity",strprintf("%f",(float)get_opacity()));
428 		settings.set_value("bline.bline_width", bline_width_dist.get_value().get_string());
429 		settings.set_value("bline.feather", feather_dist.get_value().get_string());
430 		settings.set_value("bline.auto_export",get_auto_export_flag()?"1":"0");
431 
432 	}
433 	catch(...)
434 	{
435 		synfig::warning("State Spline : Caught exception when attempting to save settings.");
436 	}
437 }
438 
439 void
reset()440 StateBLine_Context::reset()
441 {
442 	loop_=false;
443 	bline_point_list.clear();
444 	refresh_ducks();
445 }
446 
447 void
increment_id()448 StateBLine_Context::increment_id()
449 {
450 	String id(get_id());
451 	int number=1;
452 	int digits=0;
453 
454 	if(id.empty())
455 		id="NewSpline";
456 
457 	// If there is a number
458 	// already at the end of the
459 	// id, then remove it.
460 	if(id[id.size()-1]<='9' && id[id.size()-1]>='0')
461 	{
462 		// figure out how many digits it is
463 		for (digits = 0;
464 			 (int)id.size()-1 >= digits && id[id.size()-1-digits] <= '9' && id[id.size()-1-digits] >= '0';
465 			 digits++)
466 			;
467 
468 		String str_number;
469 		str_number=String(id,id.size()-digits,id.size());
470 		id=String(id,0,id.size()-digits);
471 		number=atoi(str_number.c_str());
472 	}
473 	else
474 	{
475 		number=1;
476 		digits=3;
477 	}
478 
479 	number++;
480 
481 	// Add the number back onto the id
482 	{
483 		const String format(strprintf("%%0%dd",digits));
484 		id+=strprintf(format.c_str(),number);
485 	}
486 
487 	// Set the ID
488 	set_id(id);
489 }
490 
491 
StateBLine_Context(CanvasView * canvas_view)492 StateBLine_Context::StateBLine_Context(CanvasView* canvas_view):
493 	canvas_view_(canvas_view),
494 	is_working(*canvas_view),
495 	prev_table_status(false),
496 	loop_(false),
497 	prev_workarea_layer_status_(get_work_area()->get_allow_layer_clicks()),
498 	depth(-1),
499 	duckmatic_push(get_work_area()),
500 	settings(synfigapp::Main::get_selected_input_device()->settings()),
501 	opacity_hscl(0.0f, 1.0125f, 0.0125f),
502 	button_make(_("Make")),
503 	button_clear(_("Clear"))
504 {
505 	egress_on_selection_change=true;
506 
507 	/* Set up the tool options dialog */
508 
509 	// 0, title
510 	title_label.set_label(_("Spline Creation"));
511 	Pango::AttrList list;
512 	Pango::AttrInt attr = Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD);
513 	list.insert(attr);
514 	title_label.set_attributes(list);
515 	title_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
516 
517 	// 1, layer name label and entry
518 	id_label.set_label(_("Name:"));
519 	id_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
520 	SPACING(id_gap, GAP);
521 	id_box.pack_start(id_label, Gtk::PACK_SHRINK);
522 	id_box.pack_start(*id_gap, Gtk::PACK_SHRINK);
523 
524 	id_box.pack_start(id_entry);
525 
526 	// 2, layer types creation
527 	layer_types_label.set_label(_("Layer Type:"));
528 	layer_types_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
529 
530 	LAYER_CREATION(layer_region_togglebutton,
531 		("synfig-layer_geometry_region"), _("Create a region layer"));
532 
533 	LAYER_CREATION(layer_outline_togglebutton,
534 		("synfig-layer_geometry_outline"), _("Create a outline layer"));
535 
536 	LAYER_CREATION(layer_advanced_outline_togglebutton,
537 		("synfig-layer_geometry_advanced_outline"), _("Create a advanced outline layer"));
538 
539 	LAYER_CREATION(layer_plant_togglebutton,
540 		("synfig-layer_other_plant"), _("Create a plant layer"));
541 
542 	LAYER_CREATION(layer_curve_gradient_togglebutton,
543 		("synfig-layer_gradient_curve"), _("Create a gradient layer"));
544 
545 	SPACING(layer_types_indent, INDENTATION);
546 
547 	layer_types_box.pack_start(*layer_types_indent, Gtk::PACK_SHRINK);
548 	layer_types_box.pack_start(layer_region_togglebutton, Gtk::PACK_SHRINK);
549 	layer_types_box.pack_start(layer_outline_togglebutton, Gtk::PACK_SHRINK);
550 	layer_types_box.pack_start(layer_advanced_outline_togglebutton, Gtk::PACK_SHRINK);
551 	layer_types_box.pack_start(layer_plant_togglebutton, Gtk::PACK_SHRINK);
552 	layer_types_box.pack_start(layer_curve_gradient_togglebutton, Gtk::PACK_SHRINK);
553 
554 	// 3, blend method label and dropdown list
555 	blend_label.set_label(_("Blend Method:"));
556 	blend_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
557 	SPACING(blend_gap, GAP);
558 	blend_box.pack_start(blend_label, Gtk::PACK_SHRINK);
559 	blend_box.pack_start(*blend_gap, Gtk::PACK_SHRINK);
560 
561 	blend_enum.set_param_desc(ParamDesc(Color::BLEND_COMPOSITE,"blend_method")
562 		.set_local_name(_("Blend Method"))
563 		.set_description(_("Defines the blend method to be used for splines")));
564 
565 	// 4, opacity label and slider
566 	opacity_label.set_label(_("Opacity:"));
567 	opacity_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
568 
569 	opacity_hscl.set_digits(2);
570 	opacity_hscl.set_value_pos(Gtk::POS_LEFT);
571 	opacity_hscl.set_tooltip_text(_("Opacity"));
572 
573 	// 5, brush size
574 	bline_width_label.set_label(_("Brush Size:"));
575 	bline_width_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
576 	bline_width_label.set_sensitive(false);
577 
578 	bline_width_dist.set_digits(2);
579 	bline_width_dist.set_range(0,10000000);
580 	bline_width_dist.set_sensitive(false);
581 
582 	// 6, feather
583 	feather_label.set_label(_("Feather:"));
584 	feather_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
585 	feather_label.set_sensitive(false);
586 
587 	feather_dist.set_digits(2);
588 	feather_dist.set_range(0,10000000);
589 	feather_dist.set_sensitive(false);
590 
591 	// 7, link origins
592 	link_origins_label.set_label(_("Link Origins"));
593 	link_origins_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
594 
595 	link_origins_box.pack_start(link_origins_label);
596 	link_origins_box.pack_end(layer_link_origins_checkbutton, Gtk::PACK_SHRINK);
597 	link_origins_box.set_sensitive(false);
598 
599 	// 8, auto export
600 	auto_export_label.set_label(_("Auto Export"));
601 	auto_export_label.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
602 
603 	auto_export_box.pack_start(auto_export_label);
604 	auto_export_box.pack_end(auto_export_checkbutton, Gtk::PACK_SHRINK);
605 	auto_export_box.set_sensitive(true);
606 
607 	load_settings();
608 
609 	// pack all options to the options_table
610 
611 	// 0, title
612 	options_table.attach(title_label,
613 		0, 2, 0, 1, Gtk::FILL, Gtk::FILL, 0, 0
614 		);
615 	// 1, name
616 	options_table.attach(id_box,
617 		0, 2, 1, 2, Gtk::FILL, Gtk::FILL, 0, 0
618 		);
619 	// 2, layer types creation
620 	options_table.attach(layer_types_label,
621 		0, 2, 2, 3, Gtk::FILL, Gtk::FILL, 0, 0
622 		);
623 	options_table.attach(layer_types_box,
624 		0, 2, 3, 4, Gtk::FILL, Gtk::FILL, 0, 0
625 		);
626 	// 3, blend method
627 	options_table.attach(blend_box,
628 		0, 1, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
629 		);
630 	options_table.attach(blend_enum,
631 		1, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
632 		);
633 	// 4, opacity
634 	options_table.attach(opacity_label,
635 		0, 1, 5, 6, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
636 		);
637 	options_table.attach(opacity_hscl,
638 		1, 2, 5, 6, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
639 		);
640 	// 5, brush size
641 	options_table.attach(bline_width_label,
642 		0, 1, 6, 7, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
643 		);
644 	options_table.attach(bline_width_dist,
645 		1, 2, 6, 7, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
646 		);
647 	// 6, feather
648 	options_table.attach(feather_label,
649 		0, 1, 7, 8, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
650 		);
651 	options_table.attach(feather_dist,
652 		1, 2, 7, 8, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0
653 		);
654 	// 7, link origins
655 	options_table.attach(link_origins_box,
656 		0, 2, 8, 9, Gtk::FILL, Gtk::FILL, 0, 0
657 		);
658 	// 8, auto export
659 	options_table.attach(auto_export_box,
660 		0, 2, 9, 10, Gtk::FILL, Gtk::FILL, 0, 0
661 		);
662 
663 	// fine-tune options layout
664 	options_table.set_border_width(GAP*2); // border width
665 	options_table.set_row_spacings(GAP); // row gap
666 	options_table.set_row_spacing(0, GAP*2); // the gap between first and second row.
667 	options_table.set_row_spacing(2, 1); // row gap between label and icon of layer type
668 	//options_table.set_row_spacing(10, 0); //// the final row using border width of table
669 	options_table.set_margin_bottom(0);
670 	options_table.show_all();
671 
672 	refresh_tool_options();
673 	App::dialog_tool_options->present();
674 
675 	// Turn off layer clicking
676 	get_work_area()->set_allow_layer_clicks(false);
677 
678 	// clear out the ducks
679 	get_work_area()->clear_ducks();
680 
681 	// Refresh the work area
682 	get_work_area()->queue_draw();
683 
684 	// Hide the tables if they are showing
685 	prev_table_status=get_canvas_view()->tables_are_visible();
686 	if(prev_table_status)get_canvas_view()->hide_tables();
687 
688 	// Disable the time bar
689 	get_canvas_view()->set_sensitive_timebar(false);
690 
691 	get_work_area()->set_cursor(Gdk::CROSSHAIR);
692 
693 	App::dock_toolbox->refresh();
694 }
695 
696 void
refresh_tool_options()697 StateBLine_Context::refresh_tool_options()
698 {
699 	App::dialog_tool_options->clear();
700 	App::dialog_tool_options->set_widget(options_table);
701 	App::dialog_tool_options->set_local_name(_("Spline Tool"));
702 	App::dialog_tool_options->set_name("bline");
703 
704 	App::dialog_tool_options->add_button(
705 		Gtk::StockID("gtk-execute"),
706 		_("Make Spline")
707 	)->signal_clicked().connect(
708 		sigc::hide_return(sigc::mem_fun(
709 			*this,
710 			&StateBLine_Context::run
711 		))
712 	);
713 
714 	App::dialog_tool_options->add_button(
715 		Gtk::StockID("gtk-clear"),
716 		_("Clear current Spline")
717 	)->signal_clicked().connect(
718 		sigc::mem_fun(
719 			*this,
720 			&StateBLine_Context::reset
721 		)
722 	);
723 }
724 
725 Smach::event_result
event_refresh_tool_options(const Smach::event &)726 StateBLine_Context::event_refresh_tool_options(const Smach::event& /*x*/)
727 {
728 	refresh_tool_options();
729 	return Smach::RESULT_ACCEPT;
730 }
731 
~StateBLine_Context()732 StateBLine_Context::~StateBLine_Context()
733 {
734 	run();
735 
736 	save_settings();
737 	App::dialog_tool_options->clear();
738 
739 	get_work_area()->reset_cursor();
740 
741 	// Restore layer clicking
742 	get_work_area()->set_allow_layer_clicks(prev_workarea_layer_status_);
743 
744 	// Enable the time bar
745 	get_canvas_view()->set_sensitive_timebar(true);
746 
747 	// Bring back the tables if they were out before
748 	if(prev_table_status)get_canvas_view()->show_tables();
749 
750 	// Refresh the work area
751 	get_work_area()->queue_draw();
752 
753 	App::dock_toolbox->refresh();
754 }
755 
756 Smach::event_result
event_stop_handler(const Smach::event &)757 StateBLine_Context::event_stop_handler(const Smach::event& /*x*/)
758 {
759 	reset();
760 	return Smach::RESULT_ACCEPT;
761 }
762 
763 Smach::event_result
event_refresh_handler(const Smach::event &)764 StateBLine_Context::event_refresh_handler(const Smach::event& /*x*/)
765 {
766 	refresh_ducks();
767 	return Smach::RESULT_ACCEPT;
768 }
769 
770 bool
run()771 StateBLine_Context::run()
772 {
773 	sanity_check();
774 
775 	String err;
776 	bool success(false);
777 	for(int i=5;i>0 && !success;i--)try
778 	{
779 		success=run_();
780 	}
781 	catch(String s)
782 	{
783 		err=s;
784 	}
785 	if(!success && !err.empty())
786 	{
787 		get_canvas_view()->get_ui_interface()->error(err);
788 	}
789 	return success;
790 }
791 
792 bool
run_()793 StateBLine_Context::run_()
794 {
795 	curr_duck=0;
796 	next_duck=0;
797 
798 	// Now we need to generate it
799 	if(bline_point_list.empty())
800 	{
801 		return false;
802 	}
803 	if(bline_point_list.size()<2)
804 	{
805 		get_canvas_view()->get_ui_interface()->task(_("Information: You need at least two (2) points to create a spline"));
806 		return false;
807 	}
808 
809 	do
810 	{
811 
812 		// Create the action group
813 		synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("New Spline"));
814 
815 		std::vector<BLinePoint> new_list;
816 		std::list<synfig::ValueNode_Const::Handle>::iterator iter;
817 		const synfig::TransformStack& transform(get_transform_stack());
818 
819 		for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
820 		{
821 			BLinePoint bline_point((*iter)->get_value().get(BLinePoint()));
822 			Point new_vertex(transform.unperform(bline_point.get_vertex()));
823 
824 			bline_point.set_tangent1(
825 				transform.unperform(
826 					bline_point.get_tangent1()+bline_point.get_vertex()
827 				) -new_vertex
828 			);
829 
830 			bline_point.set_tangent2(
831 				transform.unperform(
832 					bline_point.get_tangent2()+bline_point.get_vertex()
833 				) -new_vertex
834 			);
835 
836 			bline_point.set_vertex(new_vertex);
837 
838 			new_list.push_back(bline_point);
839 		}
840 
841 		ValueNode_BLine::Handle value_node_bline(ValueNode_BLine::create(new_list));
842 		assert(value_node_bline);
843 
844 		ValueNode::Handle value_node_origin(ValueNode_Const::create(Vector()));
845 		assert(value_node_origin);
846 
847 		// Set the looping flag
848 		value_node_bline->set_loop(loop_);
849 
850 		// Add the BLine to the canvas
851 		if(get_auto_export_flag() && !get_canvas_interface()->add_value_node(value_node_bline,get_id()))
852 		{
853 			group.cancel();
854 			increment_id();
855 			throw String(_("Unable to add value node"));
856 			return false;
857 		}
858 
859 		Layer::Handle layer;
860 
861 		// we are temporarily using the layer to hold something
862 		layer=get_canvas_view()->get_selection_manager()->get_selected_layer();
863 
864 		if(layer)
865 		{
866 			if(depth<0)
867 				depth=layer->get_depth();
868 			if(!canvas)
869 				canvas=layer->get_canvas();
870 		}
871 		else
872 			depth=0;
873 
874 		if(!canvas)
875 			canvas=get_canvas_view()->get_canvas();
876 
877 		value_node_bline->set_member_canvas(canvas);
878 
879 		synfigapp::SelectionManager::LayerList layer_selection;
880 		if (!getenv("SYNFIG_TOOLS_CLEAR_SELECTION"))
881 			layer_selection = get_canvas_view()->get_selection_manager()->get_selected_layers();
882 
883 		// count how many layers we're going to be creating
884 		int layers_to_create = this->layers_to_create();
885 
886 		///////////////////////////////////////////////////////////////////////////
887 		//   C U R V E   G R A D I E N T
888 		///////////////////////////////////////////////////////////////////////////
889 
890 		if(get_layer_curve_gradient_flag())
891 		{
892 			synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
893 
894 			egress_on_selection_change=false;
895 			Layer::Handle layer(get_canvas_interface()->add_layer_to("curve_gradient",canvas,depth));
896 			egress_on_selection_change=true;
897 			if (!layer)
898 			{
899 				group.cancel();
900 				throw String(_("Unable to create layer"));
901 			}
902 			layer_selection.push_back(layer);
903 			layer->set_description(get_id()+_(" Gradient"));
904 			get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
905 
906 			layer->set_param("blend_method",get_blend());
907 			get_canvas_interface()->signal_layer_param_changed()(layer,"blend_method");
908 
909 			layer->set_param("amount",get_opacity());
910 			get_canvas_interface()->signal_layer_param_changed()(layer,"amount");
911 
912 			{
913 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
914 				assert(action);
915 
916 				action->set_param("canvas",get_canvas());
917 				action->set_param("canvas_interface",get_canvas_interface());
918 				action->set_param("layer",layer);
919 				if(!action->set_param("param",String("bline")))
920 					synfig::error("LayerParamConnect didn't like \"param\"");
921 				if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
922 					synfig::error("LayerParamConnect didn't like \"value_node\"");
923 
924 				if(!get_canvas_interface()->get_instance()->perform_action(action))
925 				{
926 					group.cancel();
927 					throw String(_("Unable to create Gradient layer"));
928 					return false;
929 				}
930 			}
931 
932 			// only link the curve gradient's origin parameter if the option is selected and we're creating more than one layer
933 			if (get_layer_link_origins_flag() && layers_to_create > 1)
934 			{
935 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
936 				assert(action);
937 
938 				action->set_param("canvas",get_canvas());
939 				action->set_param("canvas_interface",get_canvas_interface());
940 				action->set_param("layer",layer);
941 				if(!action->set_param("param",String("origin")))
942 					synfig::error("LayerParamConnect didn't like \"param\"");
943 				if(!action->set_param("value_node",ValueNode::Handle(value_node_origin)))
944 					synfig::error("LayerParamConnect didn't like \"value_node\"");
945 
946 				if(!get_canvas_interface()->get_instance()->perform_action(action))
947 				{
948 					//get_canvas_view()->get_ui_interface()->error(_("Unable to create BLine layer"));
949 					group.cancel();
950 					throw String(_("Unable to create Gradient layer"));
951 					return false;
952 				}
953 			}
954 		}
955 
956 		///////////////////////////////////////////////////////////////////////////
957 		//   P L A N T
958 		///////////////////////////////////////////////////////////////////////////
959 
960 		if(get_layer_plant_flag())
961 		{
962 			synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
963 
964 			egress_on_selection_change=false;
965 			Layer::Handle layer(get_canvas_interface()->add_layer_to("plant",canvas,depth));
966 			egress_on_selection_change=true;
967 			if (!layer)
968 			{
969 				group.cancel();
970 				throw String(_("Unable to create layer"));
971 			}
972 			layer_selection.push_back(layer);
973 
974 			layer->set_description(get_id()+_(" Plant"));
975 			get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
976 
977 			layer->set_param("blend_method",get_blend());
978 			get_canvas_interface()->signal_layer_param_changed()(layer,"blend_method");
979 
980 			layer->set_param("amount",get_opacity());
981 			get_canvas_interface()->signal_layer_param_changed()(layer,"amount");
982 
983 			{
984 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
985 				assert(action);
986 
987 				action->set_param("canvas",get_canvas());
988 				action->set_param("canvas_interface",get_canvas_interface());
989 				action->set_param("layer",layer);
990 				if(!action->set_param("param",String("bline")))
991 					synfig::error("LayerParamConnect didn't like \"param\"");
992 				if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
993 					synfig::error("LayerParamConnect didn't like \"value_node\"");
994 
995 				if(!get_canvas_interface()->get_instance()->perform_action(action))
996 				{
997 					group.cancel();
998 					throw String(_("Unable to create Plant layer"));
999 					return false;
1000 				}
1001 			}
1002 
1003 			// only link the plant's origin parameter if the option is selected and we're creating more than one layer
1004 			if (get_layer_link_origins_flag() && layers_to_create > 1)
1005 			{
1006 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
1007 				assert(action);
1008 
1009 				action->set_param("canvas",get_canvas());
1010 				action->set_param("canvas_interface",get_canvas_interface());
1011 				action->set_param("layer",layer);
1012 				if(!action->set_param("param",String("origin")))
1013 					synfig::error("LayerParamConnect didn't like \"param\"");
1014 				if(!action->set_param("value_node",ValueNode::Handle(value_node_origin)))
1015 					synfig::error("LayerParamConnect didn't like \"value_node\"");
1016 
1017 				if(!get_canvas_interface()->get_instance()->perform_action(action))
1018 				{
1019 					group.cancel();
1020 					throw String(_("Unable to create Plant layer"));
1021 					return false;
1022 				}
1023 			}
1024 		}
1025 
1026 		///////////////////////////////////////////////////////////////////////////
1027 		//   R E G I O N
1028 		///////////////////////////////////////////////////////////////////////////
1029 
1030 		if(get_layer_region_flag())
1031 		{
1032 			synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
1033 
1034 			egress_on_selection_change=false;
1035 			Layer::Handle layer(get_canvas_interface()->add_layer_to("region",canvas,depth));
1036 			egress_on_selection_change=true;
1037 			if (!layer)
1038 			{
1039 				group.cancel();
1040 				throw String(_("Unable to create layer"));
1041 			}
1042 			layer_selection.push_back(layer);
1043 
1044 			layer->set_description(get_id()+_(" Region"));
1045 			get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
1046 
1047 			layer->set_param("blend_method",get_blend());
1048 			get_canvas_interface()->signal_layer_param_changed()(layer,"blend_method");
1049 
1050 			layer->set_param("amount",get_opacity());
1051 			get_canvas_interface()->signal_layer_param_changed()(layer,"amount");
1052 
1053 			if(get_feather_size())
1054 			{
1055 				layer->set_param("feather",get_feather_size());
1056 				get_canvas_interface()->signal_layer_param_changed()(layer,"feather");
1057 			}
1058 
1059 			// I don't know if it's safe to reuse the same LayerParamConnect action, so I'm
1060 			// using 2 separate ones.
1061 			{
1062 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
1063 				assert(action);
1064 
1065 				action->set_param("canvas",get_canvas());
1066 				action->set_param("canvas_interface",get_canvas_interface());
1067 				action->set_param("layer",layer);
1068 				if(!action->set_param("param",String("bline")))
1069 					synfig::error("LayerParamConnect didn't like \"param\"");
1070 				if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
1071 					synfig::error("LayerParamConnect didn't like \"value_node\"");
1072 
1073 				if(!get_canvas_interface()->get_instance()->perform_action(action))
1074 				{
1075 					group.cancel();
1076 					throw String(_("Unable to create Region layer"));
1077 					return false;
1078 				}
1079 			}
1080 
1081 			// only link the region's origin parameter if the option is selected and we're creating more than one layer
1082 			if (get_layer_link_origins_flag() && layers_to_create > 1)
1083 			{
1084 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
1085 				assert(action);
1086 
1087 				action->set_param("canvas",get_canvas());
1088 				action->set_param("canvas_interface",get_canvas_interface());
1089 				action->set_param("layer",layer);
1090 				if(!action->set_param("param",String("origin")))
1091 					synfig::error("LayerParamConnect didn't like \"param\"");
1092 				if(!action->set_param("value_node",ValueNode::Handle(value_node_origin)))
1093 					synfig::error("LayerParamConnect didn't like \"value_node\"");
1094 
1095 				if(!get_canvas_interface()->get_instance()->perform_action(action))
1096 				{
1097 					group.cancel();
1098 					throw String(_("Unable to create Region layer"));
1099 					return false;
1100 				}
1101 			}
1102 		}
1103 
1104 		///////////////////////////////////////////////////////////////////////////
1105 		//   O U T L I N E
1106 		///////////////////////////////////////////////////////////////////////////
1107 
1108 		if(get_layer_outline_flag())
1109 		{
1110 			synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
1111 
1112 			egress_on_selection_change=false;
1113 			Layer::Handle layer(get_canvas_interface()->add_layer_to("outline",canvas,depth));
1114 			egress_on_selection_change=true;
1115 			if (!layer)
1116 			{
1117 				group.cancel();
1118 				throw String(_("Unable to create layer"));
1119 			}
1120 			layer_selection.push_back(layer);
1121 
1122 			layer->set_description(get_id()+_(" Outline"));
1123 			get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
1124 
1125 			layer->set_param("blend_method",get_blend());
1126 			get_canvas_interface()->signal_layer_param_changed()(layer,"blend_method");
1127 
1128 			layer->set_param("amount",get_opacity());
1129 			get_canvas_interface()->signal_layer_param_changed()(layer,"amount");
1130 
1131 			layer->set_param("width",get_bline_width());
1132 			get_canvas_interface()->signal_layer_param_changed()(layer,"width");
1133 
1134 			if(get_feather_size())
1135 			{
1136 				layer->set_param("feather",get_feather_size());
1137 				get_canvas_interface()->signal_layer_param_changed()(layer,"feather");
1138 			}
1139 
1140 			{
1141 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
1142 				assert(action);
1143 
1144 				action->set_param("canvas",get_canvas());
1145 				action->set_param("canvas_interface",get_canvas_interface());
1146 				action->set_param("layer",layer);
1147 				if(!action->set_param("param",String("bline")))
1148 					synfig::error("LayerParamConnect didn't like \"param\"");
1149 				if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
1150 					synfig::error("LayerParamConnect didn't like \"value_node\"");
1151 
1152 				if(!get_canvas_interface()->get_instance()->perform_action(action))
1153 				{
1154 					group.cancel();
1155 					throw String(_("Unable to create Outline layer"));
1156 					return false;
1157 				}
1158 			}
1159 
1160 			// only link the outline's origin parameter if the option is selected and we're creating more than one layer
1161 			if (get_layer_link_origins_flag() && layers_to_create > 1)
1162 			{
1163 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
1164 				assert(action);
1165 
1166 				action->set_param("canvas",get_canvas());
1167 				action->set_param("canvas_interface",get_canvas_interface());
1168 				action->set_param("layer",layer);
1169 				if(!action->set_param("param",String("origin")))
1170 					synfig::error("LayerParamConnect didn't like \"param\"");
1171 				if(!action->set_param("value_node",ValueNode::Handle(value_node_origin)))
1172 					synfig::error("LayerParamConnect didn't like \"value_node\"");
1173 
1174 				if(!get_canvas_interface()->get_instance()->perform_action(action))
1175 				{
1176 					group.cancel();
1177 					throw String(_("Unable to create Outline layer"));
1178 					return false;
1179 				}
1180 			}
1181 		}
1182 
1183 		///////////////////////////////////////////////////////////////////////////
1184 		//   A D V A N C E D   O U T L I N E
1185 		///////////////////////////////////////////////////////////////////////////
1186 
1187 		if(get_layer_advanced_outline_flag())
1188 		{
1189 			synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);
1190 
1191 			egress_on_selection_change=false;
1192 			Layer::Handle layer(get_canvas_interface()->add_layer_to("advanced_outline",canvas,depth));
1193 			egress_on_selection_change=true;
1194 			if (!layer)
1195 			{
1196 				group.cancel();
1197 				throw String(_("Unable to create layer"));
1198 			}
1199 			layer_selection.push_back(layer);
1200 
1201 			layer->set_description(get_id()+_(" Advanced Outline"));
1202 			get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
1203 
1204 			layer->set_param("blend_method",get_blend());
1205 			get_canvas_interface()->signal_layer_param_changed()(layer,"blend_method");
1206 
1207 			layer->set_param("amount",get_opacity());
1208 			get_canvas_interface()->signal_layer_param_changed()(layer,"amount");
1209 
1210 			layer->set_param("width",get_bline_width());
1211 			get_canvas_interface()->signal_layer_param_changed()(layer,"width");
1212 
1213 			if(get_feather_size())
1214 			{
1215 				layer->set_param("feather",get_feather_size());
1216 				get_canvas_interface()->signal_layer_param_changed()(layer,"feather");
1217 			}
1218 
1219 			{
1220 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
1221 				assert(action);
1222 
1223 				action->set_param("canvas",get_canvas());
1224 				action->set_param("canvas_interface",get_canvas_interface());
1225 				action->set_param("layer",layer);
1226 				if(!action->set_param("param",String("bline")))
1227 					synfig::error("LayerParamConnect didn't like \"param\"");
1228 				if(!action->set_param("value_node",ValueNode::Handle(value_node_bline)))
1229 					synfig::error("LayerParamConnect didn't like \"value_node\"");
1230 
1231 				if(!get_canvas_interface()->get_instance()->perform_action(action))
1232 				{
1233 					group.cancel();
1234 					throw String(_("Unable to create Advanced Outline layer"));
1235 					return false;
1236 				}
1237 			}
1238 
1239 			// only link the advanced outline's origin parameter if the option is selected and we're creating more than one layer
1240 			if (get_layer_link_origins_flag() && layers_to_create > 1)
1241 			{
1242 				synfigapp::Action::Handle action(synfigapp::Action::create("LayerParamConnect"));
1243 				assert(action);
1244 
1245 				action->set_param("canvas",get_canvas());
1246 				action->set_param("canvas_interface",get_canvas_interface());
1247 				action->set_param("layer",layer);
1248 				if(!action->set_param("param",String("origin")))
1249 					synfig::error("LayerParamConnect didn't like \"param\"");
1250 				if(!action->set_param("value_node",ValueNode::Handle(value_node_origin)))
1251 					synfig::error("LayerParamConnect didn't like \"value_node\"");
1252 
1253 				if(!get_canvas_interface()->get_instance()->perform_action(action))
1254 				{
1255 					group.cancel();
1256 					throw String(_("Unable to create Advanced Outline layer"));
1257 					return false;
1258 				}
1259 			}
1260 		}
1261 
1262 		egress_on_selection_change=false;
1263 		get_canvas_interface()->get_selection_manager()->clear_selected_layers();
1264 		get_canvas_interface()->get_selection_manager()->set_selected_layers(layer_selection);
1265 		egress_on_selection_change=true;
1266 
1267 	} while(0);
1268 
1269 	reset();
1270 	increment_id();
1271 	return true;
1272 }
1273 
1274 Smach::event_result
event_mouse_motion_handler(const Smach::event & x)1275 StateBLine_Context::event_mouse_motion_handler(const Smach::event& x)
1276 {
1277 	const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
1278 
1279 	if(curr_duck)
1280 	{
1281 		Point p(get_work_area()->snap_point_to_grid(event.pos));
1282 		curr_duck->set_trans_point(p);
1283 		curr_duck->signal_edited()(*curr_duck);
1284 		if(next_duck)
1285 			next_duck->set_trans_point(p);
1286 		get_work_area()->queue_draw();
1287 		return Smach::RESULT_ACCEPT;
1288 	}
1289 
1290 	return Smach::RESULT_OK;
1291 }
1292 
1293 Smach::event_result
event_mouse_release_handler(const Smach::event &)1294 StateBLine_Context::event_mouse_release_handler(const Smach::event& /*x*/)
1295 {
1296 	if(curr_duck)
1297 	{
1298 		curr_duck->signal_edited()(*curr_duck);
1299 		if(next_duck)
1300 		{
1301 			curr_duck=next_duck;
1302 			next_duck=0;
1303 		}
1304 		return Smach::RESULT_ACCEPT;
1305 	}
1306 	return Smach::RESULT_OK;
1307 }
1308 
1309 Smach::event_result
event_mouse_click_handler(const Smach::event & x)1310 StateBLine_Context::event_mouse_click_handler(const Smach::event& x)
1311 {
1312 	const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
1313 	switch(event.button)
1314 	{
1315 	case BUTTON_LEFT:
1316 		{
1317 			// If we are already looped up, then don't try to add anything else
1318 			if(loop_)
1319 				return Smach::RESULT_OK;
1320 			BLinePoint bline_point;
1321 			bline_point.set_vertex(get_work_area()->snap_point_to_grid(event.pos));
1322 			bline_point.set_width(1.0f);
1323 			bline_point.set_origin(0.5f);
1324 			bline_point.set_split_tangent_radius(false);
1325 			bline_point.set_split_tangent_angle(false);
1326 			bline_point.set_tangent(Vector(0,0));
1327 			bline_point_list.push_back(ValueNode_Const::Handle::cast_dynamic(ValueNode_Const::create(bline_point)));
1328 
1329 			refresh_ducks();
1330 			return Smach::RESULT_ACCEPT;
1331 		}
1332 
1333 	default:
1334 		return Smach::RESULT_OK;
1335 	}
1336 }
1337 
1338 void
refresh_ducks(bool button_down)1339 StateBLine_Context::refresh_ducks(bool button_down)
1340 {
1341 	get_work_area()->clear_ducks();
1342 	get_work_area()->queue_draw();
1343 
1344 	if(bline_point_list.empty())
1345 		return;
1346 
1347 	list<ValueNode_Const::Handle>::iterator iter;
1348 
1349 	handle<WorkArea::Bezier> bezier;
1350 	handle<WorkArea::Duck> duck,tduck1,tduck2,first_tduck1,first_tduck2;
1351 	BLinePoint bline_point;
1352 
1353 	for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
1354 	{
1355 		ValueNode_Const::Handle value_node(*iter);
1356 		bline_point=(value_node->get_value().get(BLinePoint()));
1357 		assert(value_node);
1358 
1359 		// First add the duck associated with this vertex
1360 		duck=new WorkArea::Duck(bline_point.get_vertex());
1361 		duck->set_editable(true);
1362 #ifdef DISTINGUISH_FIRST_DUCK
1363 		if (iter!=bline_point_list.begin())
1364 			duck->set_type(Duck::TYPE_VERTEX);
1365 #else
1366 		duck->set_type(Duck::TYPE_VERTEX);
1367 #endif
1368 		duck->set_name(strprintf("%x-vertex",value_node.get()));
1369 		duck->signal_edited().connect(
1370 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_vertex_change),value_node)
1371 		);
1372 		duck->signal_user_click(2).connect(
1373 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_vertex_menu),value_node)
1374 		);
1375 		duck->set_guid(value_node->get_guid()^synfig::GUID::hasher(0));
1376 
1377 		get_work_area()->add_duck(duck);
1378 
1379 		tduck1=new WorkArea::Duck(bline_point.get_tangent1());
1380 		tduck2=new WorkArea::Duck(bline_point.get_tangent2());
1381 		if (!first_tduck1) first_tduck1 = tduck1;
1382 		if (!first_tduck2) first_tduck2 = tduck2;
1383 
1384 		// Add the tangent1 duck
1385 		tduck1->set_editable(true);
1386 		tduck1->set_edit_immediatelly(true);
1387 		tduck1->set_name(strprintf("%x-tangent1",value_node.get()));
1388 		tduck1->set_origin(duck);
1389 		tduck1->set_scalar(-0.33333333333333333);
1390 		tduck1->set_tangent(true);
1391 		tduck1->set_guid(value_node->get_guid()^synfig::GUID::hasher(3));
1392 		tduck1->signal_edited().connect(
1393 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_tangent1_change),tduck2,value_node)
1394 		);
1395 		tduck1->signal_user_click(2).connect(
1396 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_handle_menu),value_node)
1397 		);
1398 
1399 		// See if we need to add that duck to the previous bezier
1400 		if(bezier)
1401 		{
1402 			get_work_area()->add_duck(tduck1);
1403 			bezier->p2=duck;
1404 			bezier->c2=tduck1;
1405 
1406 			bezier->signal_user_click(2).connect(
1407 				sigc::bind(
1408 					sigc::mem_fun(
1409 						*this,
1410 						&studio::StateBLine_Context::popup_bezier_menu
1411 					),
1412 					value_node
1413 				)
1414 			);
1415 			get_work_area()->add_bezier(bezier);
1416 
1417 			bezier=0;
1418 		}
1419 
1420 		// Now we see if we need to create a bezier
1421 		list<ValueNode_Const::Handle>::iterator next(iter);
1422 		next++;
1423 
1424 		bezier=new WorkArea::Bezier();
1425 
1426 		// Add the tangent2 duck
1427 		tduck2->set_editable(true);
1428 		tduck2->set_edit_immediatelly(true);
1429 		tduck2->set_origin(duck);
1430 		tduck2->set_scalar(0.33333333333333333);
1431 		tduck2->set_tangent(true);
1432 
1433 		tduck2->set_name(strprintf("%x-tangent2",value_node.get()));
1434 		tduck2->signal_edited().connect(
1435 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_tangent2_change),tduck1,value_node)
1436 		);
1437 
1438 		tduck2->set_guid(value_node->get_guid()^synfig::GUID::hasher(4));
1439 		tduck2->signal_user_click(2).connect(
1440 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_handle_menu),value_node)
1441 		);
1442 
1443 		// Setup the next bezier
1444 		bezier->p1=duck;
1445 		bezier->c1=tduck2;
1446 
1447 		get_work_area()->add_duck(tduck2);
1448 		curr_duck=tduck2;
1449 	}
1450 
1451 	// Add the loop, if requested
1452 	if(bezier && loop_)
1453 	{
1454 		curr_duck=0;
1455 		BLinePoint bline_point(bline_point_list.front()->get_value().get(BLinePoint()));
1456 
1457 		duck=new WorkArea::Duck(bline_point.get_vertex());
1458 		duck->set_editable(true);
1459 #ifndef DISTINGUISH_FIRST_DUCK
1460 		duck->set_type(Duck::TYPE_VERTEX);
1461 #endif
1462 		duck->set_name(strprintf("%x-vertex",bline_point_list.front().get()));
1463 		duck->signal_edited().connect(
1464 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_vertex_change),bline_point_list.front())
1465 		);
1466 		duck->signal_user_click(2).connect(
1467 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_vertex_menu),bline_point_list.front())
1468 		);
1469 		get_work_area()->add_duck(duck);
1470 
1471 		/*
1472 		tduck1=new WorkArea::Duck(bline_point.get_tangent1());
1473 
1474 		// Add the tangent1 duck
1475 		tduck1->set_editable(true);
1476 		tduck1->set_name(strprintf("%x-tangent1",bline_point_list.front().get()));
1477 		tduck1->set_origin(duck);
1478 		tduck1->set_scalar(-0.33333333333333333);
1479 		tduck1->set_tangent(true);
1480 		tduck1->signal_edited().connect(
1481 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::on_tangent1_change),first_tduck2,bline_point_list.front())
1482 		);
1483 		tduck1->signal_user_click(2).connect(
1484 			sigc::bind(sigc::mem_fun(*this,&studio::StateBLine_Context::popup_handle_menu),bline_point_list.front())
1485 		);
1486 		get_work_area()->add_duck(tduck1);
1487 		*/
1488 		get_work_area()->add_duck(first_tduck1);
1489 
1490 		bezier->p2=duck;
1491 		bezier->c2=first_tduck1;
1492 
1493 		bezier->signal_user_click(2).connect(
1494 			sigc::bind(
1495 				sigc::mem_fun(
1496 					*this,
1497 					&studio::StateBLine_Context::popup_bezier_menu
1498 				),
1499 				bline_point_list.front()
1500 			)
1501 		);
1502 
1503 		get_work_area()->add_bezier(bezier);
1504 	}
1505 	if(bezier && !loop_)
1506 	{
1507 		duck=new WorkArea::Duck(bline_point.get_vertex());
1508 		duck->set_ignore(true);
1509 		duck->set_name("temp");
1510 
1511 		// Add the tangent1 duck
1512 		tduck1=new WorkArea::Duck(Vector(0,0));
1513 		tduck1->set_ignore(true);
1514 		tduck1->set_name("ttemp");
1515 		tduck1->set_origin(duck);
1516 		tduck1->set_scalar(-0.33333333333333333);
1517 
1518 		tduck1->set_tangent(true);
1519 		bezier->p2=duck;
1520 		bezier->c2=tduck1;
1521 
1522 		get_work_area()->add_duck(bezier->p2);
1523 		get_work_area()->add_bezier(bezier);
1524 
1525 		duck->set_guid(synfig::GUID());
1526 		tduck1->set_guid(synfig::GUID());
1527 
1528 		next_duck=duck;
1529 	}
1530 
1531 	if(!button_down)
1532 	{
1533 		if(curr_duck)
1534 		{
1535 			if(next_duck)
1536 			{
1537 				curr_duck=next_duck;
1538 				next_duck=0;
1539 			}
1540 		}
1541 	}
1542 	get_work_area()->queue_draw();
1543 }
1544 
1545 
1546 bool
on_vertex_change(const studio::Duck & duck,synfig::ValueNode_Const::Handle value_node)1547 StateBLine_Context::on_vertex_change(const studio::Duck &duck, synfig::ValueNode_Const::Handle value_node)
1548 {
1549 	BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1550 	bline_point.set_vertex(duck.get_point());
1551 	value_node->set_value(bline_point);
1552 	return true;
1553 }
1554 
1555 bool
on_tangent1_change(const studio::Duck & duck,handle<WorkArea::Duck> other_duck,synfig::ValueNode_Const::Handle value_node)1556 StateBLine_Context::on_tangent1_change(const studio::Duck &duck, handle<WorkArea::Duck> other_duck, synfig::ValueNode_Const::Handle value_node)
1557 {
1558 	BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1559 	bline_point.set_tangent1(duck.get_point());
1560 	value_node->set_value(bline_point);
1561 	if (other_duck)
1562 	{
1563 		other_duck->set_point(bline_point.get_tangent2());
1564 		get_work_area()->update_ducks();
1565 		get_work_area()->queue_draw();
1566 	}
1567 	return true;
1568 }
1569 
1570 bool
on_tangent2_change(const studio::Duck & duck,handle<WorkArea::Duck> other_duck,synfig::ValueNode_Const::Handle value_node)1571 StateBLine_Context::on_tangent2_change(const studio::Duck &duck, handle<WorkArea::Duck> other_duck, synfig::ValueNode_Const::Handle value_node)
1572 {
1573 	BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1574 
1575 	bool split_angle = bline_point.get_split_tangent_angle();
1576 	bool split_radius = bline_point.get_split_tangent_radius();
1577 
1578 	if (split_angle && split_radius) {
1579 		bline_point.set_tangent2(duck.get_point());
1580 	} else
1581 	if (split_angle && !split_radius) {
1582 		bline_point.set_tangent1(Vector(duck.get_point().mag(), bline_point.get_tangent1().angle()));
1583 		bline_point.set_tangent2(duck.get_point());
1584 	} else
1585 	if (!split_angle && split_radius) {
1586 		bline_point.set_tangent1(Vector(bline_point.get_tangent1().mag(), duck.get_point().angle()));
1587 		bline_point.set_tangent2(duck.get_point());
1588 	} else
1589 	if (!split_angle && !split_radius) {
1590 		bline_point.set_tangent1(duck.get_point());
1591 	}
1592 
1593 	value_node->set_value(bline_point);
1594 	if (other_duck)
1595 	{
1596 		other_duck->set_point(bline_point.get_tangent1());
1597 		get_work_area()->update_ducks();
1598 		get_work_area()->queue_draw();
1599 	}
1600 	return true;
1601 }
1602 
1603 void
loop_bline()1604 StateBLine_Context::loop_bline()
1605 {
1606 	loop_=true;
1607 
1608 	refresh_ducks(false);
1609 }
1610 
1611 void
unloop_bline()1612 StateBLine_Context::unloop_bline()
1613 {
1614 	loop_=false;
1615 
1616 	refresh_ducks(false);
1617 }
1618 
1619 void
popup_vertex_menu(synfig::ValueNode_Const::Handle value_node)1620 StateBLine_Context::popup_vertex_menu(synfig::ValueNode_Const::Handle value_node)
1621 {
1622 	std::vector<Gtk::Widget*> children = menu.get_children();
1623 	for(std::vector<Gtk::Widget*>::iterator i = children.begin(); i != children.end(); ++i)
1624 		menu.remove(**i);
1625 
1626 	Gtk::MenuItem *item = NULL;
1627 	Gtk::ImageMenuItem *item2 = NULL;
1628 
1629 	BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1630 	#define STATE_BLINE_ADD_MENU_ITEM(title, split_angle, split_radius, icon) \
1631 	do {                                                                \
1632 		item2 = manage(new Gtk::ImageMenuItem(                       \
1633 				*manage(new Gtk::Image(			    \
1634 					Gtk::StockID(icon),		\
1635 					Gtk::ICON_SIZE_MENU )),			\
1636 				_(title)));                     \
1637 		item2->signal_activate().connect(                                \
1638 				sigc::bind(                                             \
1639 					sigc::mem_fun(*this,&studio::StateBLine_Context::bline_set_split_handle), \
1640 					value_node, split_angle, split_radius ));           \
1641 		item2->show();                                                   \
1642 		menu.append(*item2);                                             \
1643 	} while (false)
1644 
1645 	bool split_angle = bline_point.get_split_tangent_angle();
1646 	bool split_radius = bline_point.get_split_tangent_radius();
1647 
1648 	if (split_angle && split_radius)
1649 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents", false, false, "gtk-connect");
1650 	else if (!split_angle && !split_radius)
1651 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents", true, true, "gtk-disconnect");
1652 	else if (!split_angle && split_radius)
1653 	{
1654 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents", true, true, "gtk-disconnect");
1655 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents", false, false, "gtk-connect");
1656 	}
1657 	else if (split_angle && !split_radius)
1658 	{
1659 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents", false, false, "gtk-connect");
1660 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents", true, true, "gtk-disconnect");
1661 	}
1662 
1663 	item = manage(new Gtk::SeparatorMenuItem());
1664 	item->show();
1665 	menu.append(*item);
1666 
1667 	if (split_angle)
1668 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents's Angle", false, split_radius, "synfig-type_angle");
1669 	else
1670 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents's Angle", true, split_radius, "synfig-type_angle");
1671 
1672 	if (split_radius)
1673 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents's Radius", split_angle, false, "synfig-type_vector");
1674 	else
1675 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents's Radius", split_angle, true, "synfig-type_vector");
1676 
1677 	#undef STATE_BLINE_ADD_MENU_ITEM
1678 
1679 	item = manage(new Gtk::SeparatorMenuItem());
1680 	item->show();
1681 	menu.append(*item);
1682 
1683 	if(loop_)
1684 	{
1685 		item = manage(new Gtk::MenuItem(_("Unloop Spline")));
1686 		item->signal_activate().connect(
1687 			sigc::mem_fun(*this,&studio::StateBLine_Context::unloop_bline) );
1688 		item->show();
1689 		menu.append(*item);
1690 	} else {
1691 		item = manage(new Gtk::MenuItem(_("Loop Spline")));
1692 		item->signal_activate().connect(
1693 			sigc::mem_fun(*this,&studio::StateBLine_Context::loop_bline) );
1694 		item->show();
1695 		menu.append(*item);
1696 	}
1697 
1698 	item = manage(new Gtk::SeparatorMenuItem());
1699 	item->show();
1700 	menu.append(*item);
1701 
1702 	item = manage(new Gtk::MenuItem(_("Delete Vertex")));
1703 	item->signal_activate().connect(
1704 		sigc::bind(
1705 				sigc::mem_fun(*this,&studio::StateBLine_Context::bline_delete_vertex),
1706 				value_node ));
1707 	item->show();
1708 	menu.append(*item);
1709 
1710 	menu.popup(0,0);
1711 }
1712 
1713 void
popup_bezier_menu(float location,synfig::ValueNode_Const::Handle value_node)1714 StateBLine_Context::popup_bezier_menu(float location, synfig::ValueNode_Const::Handle value_node)
1715 {
1716 	std::vector<Gtk::Widget*> children = menu.get_children();
1717 	for(std::vector<Gtk::Widget*>::iterator i = children.begin(); i != children.end(); ++i)
1718 		menu.remove(**i);
1719 
1720 	Gtk::MenuItem *item = NULL;
1721 	item = manage(new Gtk::MenuItem(_("Insert Vertex")));
1722 	item->signal_activate().connect(
1723 		sigc::bind(
1724 			sigc::bind(
1725 				sigc::mem_fun(*this,&studio::StateBLine_Context::bline_insert_vertex),
1726 				location
1727 			),
1728 			value_node ));
1729 	item->show();
1730 	menu.append(*item);
1731 	item = manage(new Gtk::SeparatorMenuItem());
1732 	item->show();
1733 	menu.append(*item);
1734 
1735 	if(loop_)
1736 	{
1737 		item = manage(new Gtk::MenuItem(_("Unloop Spline")));
1738 		item->signal_activate().connect(
1739 			sigc::mem_fun(*this,&studio::StateBLine_Context::unloop_bline) );
1740 		item->show();
1741 		menu.append(*item);
1742 	} else {
1743 		item = manage(new Gtk::MenuItem(_("Loop Spline")));
1744 		item->signal_activate().connect(
1745 			sigc::mem_fun(*this,&studio::StateBLine_Context::loop_bline) );
1746 		item->show();
1747 		menu.append(*item);
1748 	}
1749 	menu.popup(0,0);
1750 }
1751 
1752 void
bline_insert_vertex(synfig::ValueNode_Const::Handle value_node,float origin)1753 StateBLine_Context::bline_insert_vertex(synfig::ValueNode_Const::Handle value_node, float origin)
1754 {
1755 	list<ValueNode_Const::Handle>::iterator iter;
1756 
1757 	for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
1758 		if(*iter==value_node)
1759 		{
1760 			BLinePoint bline_point;
1761 			BLinePoint next_bline_point((*iter)->get_value().get(BLinePoint()));
1762 			BLinePoint prev_bline_point;
1763 
1764 			list<ValueNode_Const::Handle>::iterator prev(iter);
1765 			if(iter==bline_point_list.begin())
1766 			{
1767 				assert(loop_);
1768 				prev = bline_point_list.end();
1769 			}
1770 			prev--;
1771 
1772 			prev_bline_point=(*prev)->get_value().get(BLinePoint());
1773 
1774 			etl::hermite<Vector> curve(prev_bline_point.get_vertex(),
1775 									   next_bline_point.get_vertex(),
1776 									   prev_bline_point.get_tangent2(),
1777 									   next_bline_point.get_tangent1());
1778 			etl::derivative< etl::hermite<Vector> > deriv(curve);
1779 
1780 			bline_point.set_split_tangent_angle(false);
1781 			bline_point.set_split_tangent_radius(false);
1782 			bline_point.set_vertex(curve(origin));
1783 			bline_point.set_width((next_bline_point.get_width()-prev_bline_point.get_width())*origin+prev_bline_point.get_width());
1784 			bline_point.set_tangent1(deriv(origin)*std::min(1.0f-origin,origin));
1785 			bline_point.set_tangent2(bline_point.get_tangent1());
1786 			bline_point.set_origin(origin);
1787 			bline_point_list.insert(iter,ValueNode_Const::Handle::cast_dynamic(ValueNode_Const::create(bline_point)));
1788 
1789 			break;
1790 		}
1791 
1792 	if(iter==bline_point_list.end())
1793 	{
1794 		get_canvas_view()->get_ui_interface()->error(_("Unable to find where to insert vertex, internal error, please report this bug"));
1795 	}
1796 
1797 	refresh_ducks(false);
1798 }
1799 
1800 void
bline_delete_vertex(synfig::ValueNode_Const::Handle value_node)1801 StateBLine_Context::bline_delete_vertex(synfig::ValueNode_Const::Handle value_node)
1802 {
1803 	list<ValueNode_Const::Handle>::iterator iter;
1804 
1805 	for(iter=bline_point_list.begin();iter!=bline_point_list.end();++iter)
1806 		if(*iter==value_node)
1807 		{
1808 			bline_point_list.erase(iter);
1809 			break;
1810 		}
1811 	if(iter==bline_point_list.end())
1812 	{
1813 		get_canvas_view()->get_ui_interface()->error(_("Unable to remove vertex, internal error, please report this bug"));
1814 	}
1815 
1816 	refresh_ducks(false);
1817 }
1818 
1819 void
popup_handle_menu(synfig::ValueNode_Const::Handle value_node)1820 StateBLine_Context::popup_handle_menu(synfig::ValueNode_Const::Handle value_node)
1821 {
1822 	std::vector<Gtk::Widget*> children = menu.get_children();
1823 	for(std::vector<Gtk::Widget*>::iterator i = children.begin(); i != children.end(); ++i)
1824 		menu.remove(**i);
1825 
1826 	BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1827 
1828 	Gtk::MenuItem *item = NULL;
1829 	Gtk::ImageMenuItem *item2 = NULL;
1830 	#define STATE_BLINE_ADD_MENU_ITEM(title, split_angle, split_radius, icon)	\
1831 	do {                                                                \
1832 		item2 = manage(new Gtk::ImageMenuItem(                       \
1833 				*Gtk::manage(new Gtk::Image(			    \
1834 					Gtk::StockID(icon),		\
1835 					Gtk::ICON_SIZE_MENU )),			\
1836 				_(title)));                     \
1837 		item2->signal_activate().connect(                                \
1838 			sigc::bind(													\
1839 				sigc::mem_fun(*this,&studio::StateBLine_Context::bline_set_split_handle), \
1840 				value_node, split_angle, split_radius ));               \
1841 		item2->show();                                                   \
1842 		menu.append(*item2);                                             \
1843 	} while(false)
1844 
1845 	bool split_angle = bline_point.get_split_tangent_angle();
1846 	bool split_radius = bline_point.get_split_tangent_radius();
1847 
1848 	if (split_angle && split_radius)
1849 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents", false, false, "gtk-connect");
1850 	else if (!split_angle && !split_radius)
1851 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents", true, true, "gtk-disconnect");
1852 	else if (!split_angle && split_radius)
1853 	{
1854 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents", true, true, "gtk-disconnect");
1855 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents", false, false, "gtk-connect");
1856 	}
1857 	else if (split_angle && !split_radius)
1858 	{
1859 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents", false, false, "gtk-connect");
1860 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents", true, true, "gtk-disconnect");
1861 	}
1862 
1863 	item = manage(new Gtk::SeparatorMenuItem());
1864 	item->show();
1865 	menu.append(*item);
1866 
1867 	if (split_angle)
1868 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents's Angle", false, split_radius, "synfig-type_angle");
1869 	else
1870 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents's Angle", true, split_radius, "synfig-type_angle");
1871 
1872 	if (split_radius)
1873 		STATE_BLINE_ADD_MENU_ITEM("Merge Tangents's Radius", split_angle, false, "synfig-type_vector");
1874 	else
1875 		STATE_BLINE_ADD_MENU_ITEM("Split Tangents's Radius", split_angle, true, "synfig-type_vector");
1876 
1877 	#undef STATE_BLINE_ADD_MENU_ITEM
1878 
1879 	item = manage(new Gtk::SeparatorMenuItem());
1880 	item->show();
1881 	menu.append(*item);
1882 	if(loop_)
1883 	{
1884 		item = manage(new Gtk::MenuItem(_("Unloop Spline")));
1885 		item->signal_activate().connect(
1886 			sigc::mem_fun(*this,&studio::StateBLine_Context::unloop_bline) );
1887 		item->show();
1888 		menu.append(*item);
1889 	} else {
1890 		item = manage(new Gtk::MenuItem(_("Loop Spline")));
1891 		item->signal_activate().connect(
1892 			sigc::mem_fun(*this,&studio::StateBLine_Context::loop_bline) );
1893 		item->show();
1894 		menu.append(*item);
1895 	}
1896 	item = manage(new Gtk::SeparatorMenuItem());
1897 	item->show();
1898 	menu.append(*item);
1899 
1900 	item = manage(new Gtk::MenuItem(_("Delete Vertex")));
1901 	item->signal_activate().connect(
1902 		sigc::bind(
1903 			sigc::mem_fun(*this,&studio::StateBLine_Context::bline_delete_vertex),
1904 			value_node ));
1905 	item->show();
1906 	menu.append(*item);
1907 
1908 	menu.popup(0,0);
1909 }
1910 
1911 void
bline_set_split_handle(synfig::ValueNode_Const::Handle value_node,bool split_angle,bool split_radius)1912 StateBLine_Context::bline_set_split_handle(synfig::ValueNode_Const::Handle value_node, bool split_angle, bool split_radius)
1913 {
1914 	BLinePoint bline_point(value_node->get_value().get(BLinePoint()));
1915 
1916 	if (bline_point.get_split_tangent_angle() != split_angle)
1917 	{
1918 		bline_point.set_tangent2(Vector(bline_point.get_tangent2().mag(), bline_point.get_tangent1().angle()));
1919 		bline_point.set_split_tangent_angle(split_angle);
1920 	}
1921 
1922 	if (bline_point.get_split_tangent_radius() != split_radius)
1923 	{
1924 		bline_point.set_tangent2(Vector(bline_point.get_tangent1().mag(), bline_point.get_tangent2().angle()));
1925 		bline_point.set_split_tangent_radius(split_radius);
1926 	}
1927 
1928 	value_node->set_value(bline_point);
1929 	refresh_ducks(false);
1930 }
1931 
1932 
1933 void
toggle_layer_creation()1934 StateBLine_Context::toggle_layer_creation()
1935 {
1936   // don't allow none layer creation
1937   if (get_layer_region_flag() +
1938      get_layer_outline_flag() +
1939      get_layer_advanced_outline_flag() +
1940      get_layer_curve_gradient_flag() +
1941      get_layer_plant_flag() == 0)
1942   {
1943     if(layer_region_flag) set_layer_region_flag(true);
1944     else if(layer_outline_flag) set_layer_outline_flag(true);
1945     else if(layer_advanced_outline_flag) set_layer_advanced_outline_flag(true);
1946     else if(layer_curve_gradient_flag) set_layer_curve_gradient_flag(true);
1947     else if(layer_plant_flag) set_layer_plant_flag(true);
1948   }
1949 
1950 	// brush size
1951 	if (get_layer_outline_flag() ||
1952 		get_layer_advanced_outline_flag() ||
1953 		get_layer_curve_gradient_flag())
1954 	{
1955 		bline_width_label.set_sensitive(true);
1956 		bline_width_dist.set_sensitive(true);
1957 	}
1958 	else
1959 	{
1960 		bline_width_label.set_sensitive(false);
1961 		bline_width_dist.set_sensitive(false);
1962 	}
1963 
1964 	// feather size
1965 	if (get_layer_region_flag() ||
1966 		get_layer_outline_flag() ||
1967 		get_layer_advanced_outline_flag())
1968 	{
1969 		feather_label.set_sensitive(true);
1970 		feather_dist.set_sensitive(true);
1971 	}
1972 	else
1973 	{
1974 		feather_label.set_sensitive(false);
1975 		feather_dist.set_sensitive(false);
1976 	}
1977 
1978 	// link origins
1979 	if (get_layer_region_flag() +
1980 		get_layer_outline_flag() +
1981 		get_layer_advanced_outline_flag() +
1982 		get_layer_plant_flag() +
1983 		get_layer_curve_gradient_flag() >= 2)
1984 		{
1985 			link_origins_box.set_sensitive(true);
1986 		}
1987 	else link_origins_box.set_sensitive(false);
1988 
1989   // update layer flags
1990   layer_region_flag = get_layer_region_flag();
1991   layer_outline_flag = get_layer_outline_flag();
1992   layer_advanced_outline_flag = get_layer_advanced_outline_flag();
1993   layer_curve_gradient_flag = get_layer_curve_gradient_flag();
1994   layer_plant_flag = get_layer_plant_flag();
1995 }
1996