1 /* === S Y N F I G ========================================================= */
2 /*! \file state_width.cpp
3 ** \brief Template File
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2008 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
33 #include <gtkmm/dialog.h>
34 #include <gtkmm/entry.h>
38 #include <synfig/valuenode_dynamiclist.h>
39 #include <synfigapp/action_system.h>
41 #include "state_width.h"
42 #include "state_normal.h"
43 #include "canvasview.h"
47 #include <synfigapp/action.h>
48 #include "event_mouse.h"
49 #include "event_layerclick.h"
51 #include "docks/dialog_tooloptions.h"
52 #include <gtkmm/optionmenu.h>
55 //#include <synfigapp/value_desc.h>
56 #include <synfigapp/main.h>
64 /* === U S I N G =========================================================== */
68 using namespace synfig;
69 using namespace synfigapp;
70 using namespace studio;
72 /* === M A C R O S ========================================================= */
74 /* === G L O B A L S ======================================================= */
76 StateWidth studio::state_width;
78 /* === C L A S S E S & S T R U C T S ======================================= */
80 class studio::StateWidth_Context : public sigc::trackable
82 etl::handle<CanvasView> canvas_view_;
83 CanvasView::IsWorking is_working;
89 handle<Duck> closestpoint;
91 map<handle<Duck>,Real> changetable;
100 bool prev_workarea_layer_clicking;
101 bool prev_workarea_duck_clicking;
102 Duckmatic::Type old_duckmask;
105 synfigapp::Settings& settings;
108 Gtk::Table options_table;
110 Gtk::Adjustment adj_delta;
111 Gtk::SpinButton spin_delta;
113 Gtk::Adjustment adj_radius;
114 Gtk::SpinButton spin_radius;
116 Gtk::CheckButton check_relative;
118 void AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert);
122 Real get_delta()const { return adj_delta.get_value(); }
123 void set_delta(Real f) { adj_delta.set_value(f); }
125 Real get_radius()const { return adj_radius.get_value(); }
126 void set_radius(Real f) { adj_radius.set_value(f); }
128 bool get_relative() const { return check_relative.get_active(); }
129 void set_relative(bool r) { check_relative.set_active(r); }
131 void refresh_tool_options(); //to refresh the toolbox
134 Smach::event_result event_stop_handler(const Smach::event& x);
135 Smach::event_result event_refresh_handler(const Smach::event& x);
136 Smach::event_result event_mouse_handler(const Smach::event& x);
137 Smach::event_result event_refresh_tool_options(const Smach::event& x);
139 //constructor destructor
140 StateWidth_Context(CanvasView* canvas_view);
141 ~StateWidth_Context();
144 const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
145 etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
146 synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
147 WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
149 //Modifying settings etc.
150 void load_settings();
151 void save_settings();
154 }; // END of class StateWidth_Context
156 /* === M E T H O D S ======================================================= */
158 StateWidth::StateWidth():
159 Smach::state<StateWidth_Context>("width")
161 insert(event_def(EVENT_STOP,&StateWidth_Context::event_stop_handler));
162 insert(event_def(EVENT_REFRESH,&StateWidth_Context::event_refresh_handler));
163 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateWidth_Context::event_mouse_handler));
164 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateWidth_Context::event_mouse_handler));
165 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateWidth_Context::event_mouse_handler));
166 insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateWidth_Context::event_refresh_tool_options));
169 StateWidth::~StateWidth()
174 StateWidth_Context::load_settings()
178 SETTINGS_LOCALE_SAFE_AND_BACKUP
181 //parse the arguments yargh!
182 if(settings.get_value("width.delta",value))
183 set_delta(atof(value.c_str()));
187 if(settings.get_value("width.radius",value))
188 set_radius(atof(value.c_str()));
193 if(settings.get_value("width.relative",value) && value == "1")
197 SETTINGS_LOCALE_RESTORE
201 synfig::warning("State Width: Caught exception when attempting to load settings.");
206 StateWidth_Context::save_settings()
210 SETTINGS_LOCALE_SAFE_AND_BACKUP
211 settings.set_value("width.delta",strprintf("%f",get_delta()));
212 settings.set_value("width.radius",strprintf("%f",get_radius()));
213 settings.set_value("width.relative",get_relative()?"1":"0");
214 SETTINGS_LOCALE_RESTORE
218 synfig::warning("State Width: Caught exception when attempting to save settings.");
223 StateWidth_Context::reset()
228 StateWidth_Context::StateWidth_Context(CanvasView* canvas_view):
229 canvas_view_(canvas_view),
230 is_working(*canvas_view),
231 prev_workarea_layer_clicking(get_work_area()->get_allow_layer_clicks()),
232 prev_workarea_duck_clicking(get_work_area()->get_allow_duck_clicks()),
233 old_duckmask(get_work_area()->get_type_mask()),
235 settings(synfigapp::Main::get_selected_input_device()->settings()),
237 adj_delta(6,0,20,0.01,0.1),
238 spin_delta(adj_delta,0.01,3),
240 adj_radius(200,0,1e50,1,10),
241 spin_radius(adj_radius,1,1),
243 check_relative(_("Relative Growth"))
247 // Set up the tool options dialog
248 options_table.attach(*manage(new Gtk::Label(_("Width Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
251 options_table.attach(*manage(new Gtk::Label(_("Growth:"))), 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
252 options_table.attach(spin_delta, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
254 options_table.attach(*manage(new Gtk::Label(_("Radius:"))), 0, 1, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
255 options_table.attach(spin_radius, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
257 options_table.attach(check_relative, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
259 options_table.show_all();
261 refresh_tool_options();
262 App::dialog_tool_options->present();
264 // Turn off layer clicking
265 get_work_area()->set_allow_layer_clicks(false);
267 // clear out the ducks
268 //get_work_area()->clear_ducks();
270 // Refresh the work area
271 get_work_area()->queue_draw();
273 //Create the new ducks
279 center->set_name("p1");
280 center->set_type(Duck::TYPE_POSITION);
286 radius->set_origin(center);
287 radius->set_radius(true);
288 radius->set_type(Duck::TYPE_RADIUS);
289 radius->set_name("radius");
294 closestpoint = new Duck();
295 closestpoint->set_name("closest");
296 closestpoint->set_type(Duck::TYPE_POSITION);
299 //Disable duck clicking for the maximum coolness :)
300 get_work_area()->set_allow_duck_clicks(false);
301 get_work_area()->set_type_mask((Duck::Type)((int)Duck::TYPE_WIDTH + (int)Duck::TYPE_RADIUS));
303 // Turn the mouse pointer to crosshairs
304 get_work_area()->set_cursor(Gdk::CROSSHAIR);
306 // Hide the tables if they are showing
307 //prev_table_status=get_canvas_view()->tables_are_visible();
308 //if(prev_table_status)get_canvas_view()->hide_tables();
310 // Disable the time bar
311 //get_canvas_view()->set_sensitive_timebar(false);
314 //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateWidth_Context::on_user_click));
316 App::toolbox->refresh();
320 StateWidth_Context::refresh_tool_options()
322 App::dialog_tool_options->clear();
323 App::dialog_tool_options->set_widget(options_table);
324 App::dialog_tool_options->set_local_name(_("Width Tool"));
325 App::dialog_tool_options->set_name("width");
329 StateWidth_Context::event_refresh_tool_options(const Smach::event& /*x*/)
331 refresh_tool_options();
332 return Smach::RESULT_ACCEPT;
335 StateWidth_Context::~StateWidth_Context()
339 //remove ducks if need be
342 get_work_area()->erase_duck(center);
343 get_work_area()->erase_duck(radius);
344 get_work_area()->erase_duck(closestpoint);
348 // Restore Duck clicking
349 get_work_area()->set_allow_duck_clicks(prev_workarea_duck_clicking);
351 // Restore layer clicking
352 get_work_area()->set_allow_layer_clicks(prev_workarea_layer_clicking);
354 // Restore the mouse pointer
355 get_work_area()->reset_cursor();
357 // Restore duck masking
358 get_work_area()->set_type_mask(old_duckmask);
360 // Tool options be rid of ye!!
361 App::dialog_tool_options->clear();
363 // Enable the time bar
364 //get_canvas_view()->set_sensitive_timebar(true);
366 // Bring back the tables if they were out before
367 //if(prev_table_status)get_canvas_view()->show_tables();
369 // Refresh the work area
370 get_work_area()->queue_draw();
372 App::toolbox->refresh();
376 StateWidth_Context::event_stop_handler(const Smach::event& /*x*/)
378 //throw Smach::egress_exception();
380 return Smach::RESULT_OK;
384 StateWidth_Context::event_refresh_handler(const Smach::event& /*x*/)
387 return Smach::RESULT_ACCEPT;
391 StateWidth_Context::AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert)
393 //Leave the function if there is no curve
396 Real amount1=0,amount2=0;
398 //decide how much to change each width
402 both pressure and multiply amount are in mult
403 (may want to change this to allow different types of falloff)
405 rsq is the squared distance from the point on the curve (also part of the falloff)
409 //may want to provide a different falloff function...
417 amount1 = (1-t)*mult;
427 handle<Duck> p1 = c->p1;
428 handle<Duck> p2 = c->p2;
434 const DuckList dl = get_work_area()->get_duck_list();
436 DuckList::const_iterator i = dl.begin();
438 for(;i != dl.end(); ++i)
440 if((*i)->get_type() == Duck::TYPE_WIDTH)
442 if((*i)->get_origin_duck() == p1)
447 if((*i)->get_origin_duck() == p2)
455 if(amount1 != 0 && w1)
457 Real width = w1->get_point().mag();
460 w1->set_point(Vector(width,0));
462 //log in the list of changes...
463 //to truly be changed after everything is said and done
464 changetable[w1] = width;
467 if(amount2 != 0 && w2)
469 Real width = w2->get_point().mag();
472 w2->set_point(Vector(width,0));
474 //log in the list of changes...
475 //to truly be changed after everything is said and done
476 changetable[w2] = width;
481 StateWidth_Context::event_mouse_handler(const Smach::event& x)
483 const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
486 if( (event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN || event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
487 && event.button == BUTTON_LEFT )
489 const Real pw = get_work_area()->get_pw();
490 const Real ph = get_work_area()->get_ph();
491 const Real scale = sqrt(pw*pw+ph*ph);
492 const Real rad = get_relative() ? scale * get_radius() : get_radius();
494 bool invert = (event.modifier&Gdk::CONTROL_MASK);
496 const Real threshold = 0.08;
503 //if we're dragging get the difference in time between now and then
504 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
506 dtime = min(1/15.0,clocktime());
510 //make way for new ducks
511 //get_work_area()->clear_ducks();
514 //mouse_pos = event.pos;
516 center->set_point(event.pos);
517 if(!added)get_work_area()->add_duck(center);
519 radius->set_scalar(rad);
520 if(!added)get_work_area()->add_duck(radius);
522 //the other duck is at the current duck
523 closestpoint->set_point(event.pos);
524 if(!added)get_work_area()->add_duck(closestpoint);
526 //get the closest curve...
527 handle<Duckmatic::Bezier> c;
528 if(event.pressure >= threshold)
529 c = get_work_area()->find_bezier(event.pos,scale*8,rad,&t);
531 //run algorithm on event.pos to get 2nd placement
537 curve[0] = c->p1->get_trans_point();
538 curve[1] = c->c1->get_trans_point();
539 curve[2] = c->c2->get_trans_point();
540 curve[3] = c->p2->get_trans_point();
543 rsq = (p-event.pos).mag_squared();
545 const Real r = rad*rad;
549 closestpoint->set_point(curve(t));
551 //adjust the width...
552 //squared falloff for radius... [0,1]
554 Real ri = (r - rsq)/r;
555 AdjustWidth(c,t,ri*event.pressure*get_delta()*dtime,invert);
559 //the points have been added
562 //draw where it is yo!
563 get_work_area()->queue_draw();
565 return Smach::RESULT_ACCEPT;
568 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button == BUTTON_LEFT)
572 get_work_area()->erase_duck(center);
573 get_work_area()->erase_duck(radius);
574 get_work_area()->erase_duck(closestpoint);
578 //Affect the width changes here...
579 map<handle<Duck>,Real>::iterator i = changetable.begin();
581 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Sketch Width"));
582 for(; i != changetable.end(); ++i)
584 //for each duck modify IT!!!
585 ValueDesc desc = i->first->get_value_desc();
587 if( desc.get_value_type() == ValueBase::TYPE_REAL )
589 Action::Handle action(Action::create("ValueDescSet"));
592 action->set_param("canvas",get_canvas());
593 action->set_param("canvas_interface",get_canvas_interface());
595 action->set_param("value_desc",desc);
596 action->set_param("new_value",ValueBase(i->second));
597 action->set_param("time",get_canvas_view()->get_time());
599 if(!action->is_ready() || !get_canvas_view()->get_instance()->perform_action(action))
602 synfig::warning("Changing the width action has failed");
603 return Smach::RESULT_ERROR;
610 get_work_area()->queue_draw();
612 return Smach::RESULT_ACCEPT;
615 return Smach::RESULT_OK;
620 StateWidth_Context::refresh_ducks()
622 get_work_area()->clear_ducks();
623 get_work_area()->queue_draw();