2effb73c7f6c1ef93eb673c5f44bf8dd5cb06002
[synfig.git] / synfig-studio / trunk / 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 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include <gtkmm/dialog.h>
33 #include <gtkmm/entry.h>
34
35 #include <synfig/valuenode_dynamiclist.h>
36
37 #include "state_sketch.h"
38 #include "state_stroke.h"
39 #include "canvasview.h"
40 #include "workarea.h"
41 #include "app.h"
42 #include <synfig/valuenode_bline.h>
43 #include <ETL/hermite>
44 #include <ETL/calculus>
45 #include <utility>
46 #include "event_mouse.h"
47 #include "event_layerclick.h"
48 #include "toolbox.h"
49
50 #include <synfigapp/blineconvert.h>
51 #include <synfigapp/main.h>
52
53 #include <ETL/gaussian>
54
55 #include "dialog_tooloptions.h"
56
57 #include <gtkmm/table.h>
58 #include <gtkmm/label.h>
59 #include <gtkmm/button.h>
60 #include <gtkmm/checkbutton.h>
61 #include <gtkmm/actiongroup.h>
62
63 #include "general.h"
64
65 #endif
66
67 /* === U S I N G =========================================================== */
68
69 using namespace std;
70 using namespace etl;
71 using namespace synfig;
72 using namespace studio;
73
74 /* === M A C R O S ========================================================= */
75
76 /* === G L O B A L S ======================================================= */
77
78 StateSketch studio::state_sketch;
79
80 /* === C L A S S E S & S T R U C T S ======================================= */
81
82 class studio::StateSketch_Context : public sigc::trackable
83 {
84         Glib::RefPtr<Gtk::ActionGroup> action_group;
85
86         etl::handle<CanvasView> canvas_view_;
87         CanvasView::IsWorking is_working;
88
89         bool prev_table_status;
90         bool prev_workarea_layer_status_;
91
92         Gtk::Table options_table;
93         Gtk::Button button_clear_sketch;
94         Gtk::Button button_undo_stroke;
95         Gtk::Button button_save_sketch;
96         Gtk::Button button_load_sketch;
97         Gtk::CheckButton checkbutton_show_sketch;
98
99         void clear_sketch();
100         void save_sketch();
101         void load_sketch();
102         void undo_stroke();
103         void toggle_show_sketch();
104
105 public:
106
107         Smach::event_result event_stop_handler(const Smach::event& x);
108
109         Smach::event_result event_refresh_handler(const Smach::event& x);
110
111         Smach::event_result event_mouse_down_handler(const Smach::event& x);
112
113         Smach::event_result event_stroke(const Smach::event& x);
114
115         Smach::event_result event_refresh_tool_options(const Smach::event& x);
116         Smach::event_result event_yield_tool_options(const Smach::event& x);
117
118         void refresh_tool_options();
119         void yield_tool_options();
120
121         StateSketch_Context(CanvasView* canvas_view);
122
123         ~StateSketch_Context();
124
125         const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
126         etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
127         synfig::Time get_time()const { return get_canvas_interface()->get_time(); }
128         synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
129         WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
130
131 };      // END of class StateSketch_Context
132
133
134 /* === M E T H O D S ======================================================= */
135
136 StateSketch::StateSketch():
137         Smach::state<StateSketch_Context>("sketch")
138 {
139         insert(event_def(EVENT_STOP,&StateSketch_Context::event_stop_handler));
140         //insert(event_def(EVENT_REFRESH,&StateSketch_Context::event_refresh_handler));
141         insert(event_def(EVENT_REFRESH_DUCKS,&StateSketch_Context::event_refresh_handler));
142         insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateSketch_Context::event_mouse_down_handler));
143         insert(event_def(EVENT_WORKAREA_STROKE,&StateSketch_Context::event_stroke));
144         insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateSketch_Context::event_refresh_tool_options));
145         insert(event_def(EVENT_YIELD_TOOL_OPTIONS,&StateSketch_Context::event_yield_tool_options));
146 }
147
148 StateSketch::~StateSketch()
149 {
150 }
151
152 void
153 StateSketch_Context::save_sketch()
154 {
155         synfig::String filename(basename(get_canvas()->get_file_name())+".sketch");
156
157         while(App::dialog_save_file(_("Save Sketch"), filename, SKETCH_DIR_PREFERENCE))
158         {
159                 // If the filename still has wildcards, then we should
160                 // continue looking for the file we want
161                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
162                         continue;
163
164                 if(get_work_area()->save_sketch(filename))
165                         break;
166
167                 get_canvas_view()->get_ui_interface()->error(_("Unable to save sketch"));
168         }
169 }
170
171 void
172 StateSketch_Context::load_sketch()
173 {
174         synfig::String filename(basename(get_canvas()->get_file_name())+".sketch");
175
176         while(App::dialog_open_file(_("Load Sketch"), filename, SKETCH_DIR_PREFERENCE))
177         {
178                 // If the filename still has wildcards, then we should
179                 // continue looking for the file we want
180                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
181                         continue;
182
183                 if(get_work_area()->load_sketch(filename))
184                         break;
185
186                 get_canvas_view()->get_ui_interface()->error(_("Unable to load sketch"));
187         }
188         get_work_area()->queue_draw();
189 }
190
191 void
192 StateSketch_Context::clear_sketch()
193 {
194         get_work_area()->clear_persistent_strokes();
195
196         // if the sketch is currently shown, make sure it is updated
197         //! \todo is there a better way than this of getting Duckmatic to update its stroke_list_?
198         if (checkbutton_show_sketch.get_active())
199         {
200                 get_work_area()->set_show_persistent_strokes(false);
201                 get_work_area()->set_show_persistent_strokes(true);
202                 get_canvas_view()->get_smach().process_event(EVENT_REFRESH);
203         }
204 }
205
206 void
207 StateSketch_Context::undo_stroke()
208 {
209         if(!get_work_area()->persistent_stroke_list().empty())
210         {
211                 get_work_area()->persistent_stroke_list().pop_back();
212
213                 // if the sketch is currently shown, make sure it is updated
214                 //! \todo is there a better way than this of getting Duckmatic to update its stroke_list_?
215                 if (checkbutton_show_sketch.get_active())
216                 {
217                         get_work_area()->set_show_persistent_strokes(false);
218                         get_work_area()->set_show_persistent_strokes(true);
219                         get_canvas_view()->get_smach().process_event(EVENT_REFRESH);
220                 }
221         }
222 }
223
224 void
225 StateSketch_Context::toggle_show_sketch()
226 {
227         get_work_area()->set_show_persistent_strokes(checkbutton_show_sketch.get_active());
228         get_work_area()->queue_draw();
229 }
230
231 StateSketch_Context::StateSketch_Context(CanvasView* canvas_view):
232         action_group(Gtk::ActionGroup::create()),
233         canvas_view_(canvas_view),
234         is_working(*canvas_view),
235         prev_workarea_layer_status_(get_work_area()->get_allow_layer_clicks()),
236         button_clear_sketch(_("Clear Sketch")),
237         button_undo_stroke(_("Undo Stroke")),
238         button_save_sketch(_("Save Sketch")),
239         button_load_sketch(_("Load Sketch")),
240         checkbutton_show_sketch(_("Show Sketch"))
241 {
242     Glib::ustring ui_info =
243         "<ui>"
244         "       <toolbar action='toolbar-sketch'>"
245         "       <toolitem action='sketch-undo' />"
246         "       <toolitem action='sketch-clear' />"
247         "       <toolitem action='sketch-save-as' />"
248         "       <toolitem action='sketch-open' />"
249         "       </toolbar>"
250         "</ui>";
251
252         action_group->add(Gtk::Action::create(
253                 "sketch-undo",
254                 Gtk::StockID("gtk-undo"),
255                 _("Undo Last Stroke"),
256                 _("Undo Last Stroke")
257         ),
258                 sigc::mem_fun(
259                         *this,
260                         &studio::StateSketch_Context::undo_stroke
261                 )
262         );
263
264         action_group->add(Gtk::Action::create(
265                 "sketch-clear",
266                 Gtk::StockID("gtk-clear"),
267                 _("Clear Sketch"),
268                 _("Clear Sketch")
269         ),
270                 sigc::mem_fun(
271                         *this,
272                         &studio::StateSketch_Context::clear_sketch
273                 )
274         );
275
276         action_group->add(Gtk::Action::create(
277                 "sketch-save-as",
278                 Gtk::StockID("gtk-save-as"),
279                 _("Save Sketch As..."),
280                 _("Save Sketch As...")
281         ),
282                 sigc::mem_fun(
283                         *this,
284                         &studio::StateSketch_Context::save_sketch
285                 )
286         );
287
288         action_group->add(Gtk::Action::create(
289                 "sketch-save-as",
290                 Gtk::StockID("gtk-save-as"),
291                 _("Save Sketch As..."),
292                 _("Save Sketch As...")
293         ),
294                 sigc::mem_fun(
295                         *this,
296                         &studio::StateSketch_Context::save_sketch
297                 )
298         );
299
300         action_group->add(Gtk::Action::create(
301                 "sketch-open",
302                 Gtk::StockID("gtk-open"),
303                 _("Open a Sketch"),
304                 _("Open a Sketch")
305         ),
306                 sigc::mem_fun(
307                         *this,
308                         &studio::StateSketch_Context::load_sketch
309                 )
310         );
311
312         action_group->add( Gtk::Action::create("toolbar-sketch", "Sketch Toolbar") );
313
314
315         App::ui_manager()->add_ui_from_string(ui_info);
316
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         //options_table.attach(*manage(new Gtk::Label(_("Sketch Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
326         options_table.attach(checkbutton_show_sketch, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
327         //options_table.attach(button_undo_stroke, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
328         //options_table.attach(button_clear_sketch, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
329         //options_table.attach(button_save_sketch, 0, 1, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
330         //options_table.attach(button_load_sketch, 1, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
331
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_canvas_view()->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_canvas_view()->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 }
471
472 Smach::event_result
473 StateSketch_Context::event_refresh_handler(const Smach::event& /*x*/)
474 {
475         return Smach::RESULT_ACCEPT;
476 }
477
478 Smach::event_result
479 StateSketch_Context::event_mouse_down_handler(const Smach::event& x)
480 {
481         const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
482         switch(event.button)
483         {
484         case BUTTON_LEFT:
485                 {
486                         // Enter the stroke state to get the stroke
487                         get_canvas_view()->get_smach().push_state(&state_stroke);
488                         return Smach::RESULT_ACCEPT;
489                 }
490
491         case BUTTON_RIGHT: // Intercept the right-button click to short-circuit the pop-up menu
492                 if (!getenv("SYNFIG_ENABLE_POPUP_MENU_IN_ALL_TOOLS"))
493                         return Smach::RESULT_ACCEPT;
494
495         default:
496                 return Smach::RESULT_OK;
497         }
498 }
499
500 Smach::event_result
501 StateSketch_Context::event_stroke(const Smach::event& x)
502 {
503         const EventStroke& event(*reinterpret_cast<const EventStroke*>(&x));
504
505         assert(event.stroke_data);
506
507         get_work_area()->add_persistent_stroke(event.stroke_data,synfigapp::Main::get_foreground_color());
508
509         return Smach::RESULT_ACCEPT;
510 }