f8b77a3a054fcf69cb6bf457b35638110b84d446
[synfig.git] / synfig-studio / src / gtkmm / state_sketch.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file state_sketch.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include <gtkmm/dialog.h>
34 #include <gtkmm/entry.h>
35
36 #include <synfig/valuenode_dynamiclist.h>
37
38 #include "state_sketch.h"
39 #include "state_normal.h"
40 #include "state_stroke.h"
41 #include "canvasview.h"
42 #include "workarea.h"
43 #include "app.h"
44 #include <synfig/valuenode_bline.h>
45 #include <ETL/hermite>
46 #include <ETL/calculus>
47 #include <utility>
48 #include "event_mouse.h"
49 #include "event_layerclick.h"
50 #include "toolbox.h"
51
52 #include <synfigapp/blineconvert.h>
53 #include <synfigapp/main.h>
54
55 #include <ETL/gaussian>
56
57 #include "dialog_tooloptions.h"
58
59 #include <gtkmm/table.h>
60 #include <gtkmm/label.h>
61 #include <gtkmm/button.h>
62 #include <gtkmm/checkbutton.h>
63 #include <gtkmm/actiongroup.h>
64
65 #include "general.h"
66
67 #endif
68
69 /* === U S I N G =========================================================== */
70
71 using namespace std;
72 using namespace etl;
73 using namespace synfig;
74 using namespace studio;
75
76 /* === M A C R O S ========================================================= */
77
78 /* === G L O B A L S ======================================================= */
79
80 StateSketch studio::state_sketch;
81
82 /* === C L A S S E S & S T R U C T S ======================================= */
83
84 class studio::StateSketch_Context : public sigc::trackable
85 {
86         Glib::RefPtr<Gtk::ActionGroup> action_group;
87
88         etl::handle<CanvasView> canvas_view_;
89         CanvasView::IsWorking is_working;
90
91         bool prev_table_status;
92         bool prev_workarea_layer_status_;
93
94         Gtk::Table options_table;
95         Gtk::Button button_clear_sketch;
96         Gtk::Button button_undo_stroke;
97         Gtk::Button button_save_sketch;
98         Gtk::Button button_load_sketch;
99         Gtk::CheckButton checkbutton_show_sketch;
100
101         void clear_sketch();
102         void save_sketch();
103         void load_sketch();
104         void undo_stroke();
105         void toggle_show_sketch();
106
107 public:
108
109         Smach::event_result event_stop_handler(const Smach::event& x);
110
111         Smach::event_result event_refresh_handler(const Smach::event& x);
112
113         Smach::event_result event_mouse_down_handler(const Smach::event& x);
114
115         Smach::event_result event_stroke(const Smach::event& x);
116
117         Smach::event_result event_refresh_tool_options(const Smach::event& x);
118         Smach::event_result event_yield_tool_options(const Smach::event& x);
119
120         void refresh_tool_options();
121         void yield_tool_options();
122
123         StateSketch_Context(CanvasView* canvas_view);
124
125         ~StateSketch_Context();
126
127         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
128         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
129         synfig::Time get_time()const { return get_canvas_interface()->get_time(); }
130         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
131         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
132
133 };      // END of class StateSketch_Context
134
135
136 /* === M E T H O D S ======================================================= */
137
138 StateSketch::StateSketch():
139         Smach::state<StateSketch_Context>("sketch")
140 {
141         insert(event_def(EVENT_STOP,&StateSketch_Context::event_stop_handler));
142         //insert(event_def(EVENT_REFRESH,&StateSketch_Context::event_refresh_handler));
143         insert(event_def(EVENT_REFRESH_DUCKS,&StateSketch_Context::event_refresh_handler));
144         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateSketch_Context::event_mouse_down_handler));
145         insert(event_def(EVENT_WORKAREA_STROKE,&StateSketch_Context::event_stroke));
146         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateSketch_Context::event_refresh_tool_options));
147         insert(event_def(EVENT_YIELD_TOOL_OPTIONS,&StateSketch_Context::event_yield_tool_options));
148 }
149
150 StateSketch::~StateSketch()
151 {
152 }
153
154 void
155 StateSketch_Context::save_sketch()
156 {
157         synfig::String filename(basename(get_canvas()->get_file_name())+".sketch");
158
159         while(App::dialog_save_file(_("Save Sketch"), filename, SKETCH_DIR_PREFERENCE))
160         {
161                 // If the filename still has wildcards, then we should
162                 // continue looking for the file we want
163                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
164                         continue;
165
166                 if(get_work_area()->save_sketch(filename))
167                         break;
168
169                 get_canvas_view()->get_ui_interface()->error(_("Unable to save sketch"));
170         }
171 }
172
173 void
174 StateSketch_Context::load_sketch()
175 {
176         synfig::String filename(basename(get_canvas()->get_file_name())+".sketch");
177
178         while(App::dialog_open_file(_("Load Sketch"), filename, SKETCH_DIR_PREFERENCE))
179         {
180                 // If the filename still has wildcards, then we should
181                 // continue looking for the file we want
182                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
183                         continue;
184
185                 if(get_work_area()->load_sketch(filename))
186                         break;
187
188                 get_canvas_view()->get_ui_interface()->error(_("Unable to load sketch"));
189         }
190         get_work_area()->queue_draw();
191 }
192
193 void
194 StateSketch_Context::clear_sketch()
195 {
196         get_work_area()->clear_persistent_strokes();
197
198         // if the sketch is currently shown, make sure it is updated
199         //! \todo is there a better way than this of getting Duckmatic to update its stroke_list_?
200         if (checkbutton_show_sketch.get_active())
201         {
202                 get_work_area()->set_show_persistent_strokes(false);
203                 get_work_area()->set_show_persistent_strokes(true);
204                 get_canvas_view()->get_smach().process_event(EVENT_REFRESH);
205         }
206 }
207
208 void
209 StateSketch_Context::undo_stroke()
210 {
211         if(!get_work_area()->persistent_stroke_list().empty())
212         {
213                 get_work_area()->persistent_stroke_list().pop_back();
214
215                 // if the sketch is currently shown, make sure it is updated
216                 //! \todo is there a better way than this of getting Duckmatic to update its stroke_list_?
217                 if (checkbutton_show_sketch.get_active())
218                 {
219                         get_work_area()->set_show_persistent_strokes(false);
220                         get_work_area()->set_show_persistent_strokes(true);
221                         get_canvas_view()->get_smach().process_event(EVENT_REFRESH);
222                 }
223         }
224 }
225
226 void
227 StateSketch_Context::toggle_show_sketch()
228 {
229         get_work_area()->set_show_persistent_strokes(checkbutton_show_sketch.get_active());
230         get_work_area()->queue_draw();
231 }
232
233 StateSketch_Context::StateSketch_Context(CanvasView* canvas_view):
234         action_group(Gtk::ActionGroup::create("action_group_state_sketch")),
235         canvas_view_(canvas_view),
236         is_working(*canvas_view),
237         prev_workarea_layer_status_(get_work_area()->get_allow_layer_clicks()),
238         button_clear_sketch(_("Clear Sketch")),
239         button_undo_stroke(_("Undo Stroke")),
240         button_save_sketch(_("Save Sketch")),
241         button_load_sketch(_("Load Sketch")),
242         checkbutton_show_sketch(_("Show Sketch"))
243 {
244     Glib::ustring ui_info =
245         "<ui>"
246         "       <toolbar action='toolbar-sketch'>"
247         "       <toolitem action='sketch-undo' />"
248         "       <toolitem action='sketch-clear' />"
249         "       <toolitem action='sketch-save-as' />"
250         "       <toolitem action='sketch-open' />"
251         "       </toolbar>"
252         "</ui>";
253
254         action_group->add(Gtk::Action::create(
255                 "sketch-undo",
256                 Gtk::StockID("gtk-undo"),
257                 _("Undo Last Stroke"),
258                 _("Undo Last Stroke")
259         ),
260                 sigc::mem_fun(
261                         *this,
262                         &studio::StateSketch_Context::undo_stroke
263                 )
264         );
265
266         action_group->add(Gtk::Action::create(
267                 "sketch-clear",
268                 Gtk::StockID("gtk-clear"),
269                 _("Clear Sketch"),
270                 _("Clear Sketch")
271         ),
272                 sigc::mem_fun(
273                         *this,
274                         &studio::StateSketch_Context::clear_sketch
275                 )
276         );
277
278         action_group->add(Gtk::Action::create(
279                 "sketch-save-as",
280                 Gtk::StockID("gtk-save-as"),
281                 _("Save Sketch As..."),
282                 _("Save Sketch As...")
283         ),
284                 sigc::mem_fun(
285                         *this,
286                         &studio::StateSketch_Context::save_sketch
287                 )
288         );
289
290         action_group->add(Gtk::Action::create(
291                 "sketch-save-as",
292                 Gtk::StockID("gtk-save-as"),
293                 _("Save Sketch As..."),
294                 _("Save Sketch As...")
295         ),
296                 sigc::mem_fun(
297                         *this,
298                         &studio::StateSketch_Context::save_sketch
299                 )
300         );
301
302         action_group->add(Gtk::Action::create(
303                 "sketch-open",
304                 Gtk::StockID("gtk-open"),
305                 _("Open a Sketch"),
306                 _("Open a Sketch")
307         ),
308                 sigc::mem_fun(
309                         *this,
310                         &studio::StateSketch_Context::load_sketch
311                 )
312         );
313
314         action_group->add( Gtk::Action::create("toolbar-sketch", "Sketch Toolbar") );
315
316         App::ui_manager()->add_ui_from_string(ui_info);
317
318         checkbutton_show_sketch.set_active(get_work_area()->get_show_persistent_strokes());
319
320         button_clear_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::clear_sketch));
321         button_undo_stroke.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::undo_stroke));
322         button_save_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::save_sketch));
323         button_load_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::load_sketch));
324         checkbutton_show_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::toggle_show_sketch));
325
326         options_table.attach(*manage(new Gtk::Label(_("Sketch Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
327         options_table.attach(checkbutton_show_sketch,                                   0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
328         //options_table.attach(button_undo_stroke, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
329         //options_table.attach(button_clear_sketch, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
330         //options_table.attach(button_save_sketch, 0, 1, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
331         //options_table.attach(button_load_sketch, 1, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
332
333         options_table.show_all();
334         refresh_tool_options();
335         App::dialog_tool_options->present();
336
337         // Turn off layer clicking
338         get_work_area()->set_allow_layer_clicks(false);
339
340         get_work_area()->set_cursor(Gdk::PENCIL);
341
342         // Turn off duck clicking
343         get_work_area()->set_allow_duck_clicks(false);
344
345         // clear out the ducks
346         //get_work_area()->clear_ducks();
347
348         // Refresh the work area
349         //get_work_area()->queue_draw();
350
351         // Hide the tables if they are showing
352         prev_table_status=get_canvas_view()->tables_are_visible();
353         //if(prev_table_status)get_canvas_view()->hide_tables();
354
355         // Disable the time bar
356         //get_canvas_view()->set_sensitive_timebar(false);
357
358         // Connect a signal
359         //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::on_user_click));
360
361         App::toolbox->refresh();
362 }
363
364 StateSketch_Context::~StateSketch_Context()
365 {
366         get_work_area()->reset_cursor();
367
368         App::dialog_tool_options->clear();
369
370         // Restore layer clicking
371         get_work_area()->set_allow_layer_clicks(prev_workarea_layer_status_);
372
373         // Restore duck clicking
374         get_work_area()->set_allow_duck_clicks(true);
375
376         // Enable the time bar
377         //get_canvas_view()->set_sensitive_timebar(true);
378
379         // Bring back the tables if they were out before
380         if(prev_table_status)get_canvas_view()->show_tables();
381
382         // Refresh the work area
383         //get_work_area()->queue_draw();
384
385         App::toolbox->refresh();
386 }
387
388 void
389 StateSketch_Context::yield_tool_options()
390 {
391         App::dialog_tool_options->clear();
392         App::ui_manager()->remove_action_group(action_group);
393 }
394
395 void
396 StateSketch_Context::refresh_tool_options()
397 {
398         App::dialog_tool_options->clear();
399         App::dialog_tool_options->set_widget(options_table);
400         App::dialog_tool_options->set_local_name(_("Sketch Tool"));
401         App::dialog_tool_options->set_name("sketch");
402
403         App::ui_manager()->insert_action_group(action_group);
404         App::dialog_tool_options->set_toolbar(*dynamic_cast<Gtk::Toolbar*>(App::ui_manager()->get_widget("/toolbar-sketch")));
405
406         /*
407         App::dialog_tool_options->add_button(
408                 Gtk::StockID("gtk-undo"),
409                 _("Undo Last Stroke")
410         )->signal_clicked().connect(
411                 sigc::mem_fun(
412                         *this,
413                         &studio::StateSketch_Context::undo_stroke
414                 )
415         );
416         App::dialog_tool_options->add_button(
417                 Gtk::StockID("gtk-clear"),
418                 _("Clear Sketch")
419         )->signal_clicked().connect(
420                 sigc::mem_fun(
421                         *this,
422                         &studio::StateSketch_Context::clear_sketch
423                 )
424         );
425         App::dialog_tool_options->add_button(
426                 Gtk::StockID("gtk-save"),
427                 _("Save Sketch to a File")
428         )->signal_clicked().connect(
429                 sigc::mem_fun(
430                         *this,
431                         &studio::StateSketch_Context::save_sketch
432                 )
433         );
434
435         App::dialog_tool_options->add_button(
436                 Gtk::StockID("gtk-open"),
437                 _("Open a Sketch")
438         )->signal_clicked().connect(
439                 sigc::mem_fun(
440                         *this,
441                         &studio::StateSketch_Context::load_sketch
442                 )
443         );
444         */
445         //button_clear_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::clear_sketch));
446         //button_undo_stroke.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::undo_stroke));
447         //button_save_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::save_sketch));
448         //button_load_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::load_sketch));
449         //checkbutton_show_sketch.signal_clicked().connect(sigc::mem_fun(*this,&studio::StateSketch_Context::toggle_show_sketch));
450 }
451
452 Smach::event_result
453 StateSketch_Context::event_refresh_tool_options(const Smach::event& /*x*/)
454 {
455         refresh_tool_options();
456         return Smach::RESULT_ACCEPT;
457 }
458
459 Smach::event_result
460 StateSketch_Context::event_yield_tool_options(const Smach::event& /*x*/)
461 {
462         yield_tool_options();
463         return Smach::RESULT_ACCEPT;
464 }
465
466 Smach::event_result
467 StateSketch_Context::event_stop_handler(const Smach::event& /*x*/)
468 {
469         //throw Smach::egress_exception();
470         throw &state_normal;
471         return Smach::RESULT_OK;
472 }
473
474 Smach::event_result
475 StateSketch_Context::event_refresh_handler(const Smach::event& /*x*/)
476 {
477         return Smach::RESULT_ACCEPT;
478 }
479
480 Smach::event_result
481 StateSketch_Context::event_mouse_down_handler(const Smach::event& x)
482 {
483         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
484         switch(event.button)
485         {
486         case BUTTON_LEFT:
487                 {
488                         // Enter the stroke state to get the stroke
489                         get_canvas_view()->get_smach().push_state(&state_stroke);
490                         return Smach::RESULT_ACCEPT;
491                 }
492
493         default:
494                 return Smach::RESULT_OK;
495         }
496 }
497
498 Smach::event_result
499 StateSketch_Context::event_stroke(const Smach::event& x)
500 {
501         const EventStroke& event(*reinterpret_cast<const EventStroke*>(&x));
502
503         assert(event.stroke_data);
504
505         get_work_area()->add_persistent_stroke(event.stroke_data,synfigapp::Main::get_outline_color());
506
507         return Smach::RESULT_ACCEPT;
508 }