Same as before but for Rectangle and Star Tools.
[synfig.git] / synfig-studio / src / synfigapp / action_system.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file action_system.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 "action_system.h"
33 #include "instance.h"
34 #include "canvasinterface.h"
35
36 #include "general.h"
37
38 #endif
39
40 /* === U S I N G =========================================================== */
41
42 using namespace std;
43 using namespace etl;
44 using namespace synfigapp;
45 using namespace synfig;
46
47 /* === M A C R O S ========================================================= */
48
49 /* === G L O B A L S ======================================================= */
50
51 /* === P R O C E D U R E S ================================================= */
52
53 /* === M E T H O D S ======================================================= */
54
55
56
57 Action::System::System():
58         action_count_(0)
59 {
60         unset_ui_interface();
61         clear_redo_stack_on_new_action_=false;
62 }
63
64 Action::System::~System()
65 {
66 }
67
68 bool
69 Action::System::perform_action(etl::handle<Action::Base> action)
70 {
71         //// debug actions
72         // synfig::info("%s:%d perform_action: '%s'", __FILE__, __LINE__, action->get_name().c_str());
73
74         handle<UIInterface> uim(get_ui_interface());
75
76         assert(action);
77
78         if(!action->is_ready())
79         {
80                 uim->error(action->get_local_name()+": "+_("Action is not ready."));
81                 return false;
82         }
83
84         most_recent_action_=action;
85
86         static bool inuse=false;
87
88         if(inuse) return false;
89
90         inuse=true;
91         try {
92
93         assert(action);
94
95         Action::CanvasSpecific* canvas_specific(dynamic_cast<Action::CanvasSpecific*>(action.get()));
96
97         if(canvas_specific && canvas_specific->get_canvas())
98         {
99                 handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas_specific->get_canvas());
100                 assert(canvas_interface);
101                 uim=canvas_interface->get_ui_interface();
102         }
103
104         handle<Action::Undoable> undoable_action=handle<Action::Undoable>::cast_dynamic(action);
105
106         // If we cannot undo this action, make sure
107         // that the user knows this.
108         if(!undoable_action)
109         {
110                 if(uim->yes_no(
111                         action->get_local_name(),
112                         _("This action cannot be undone! Are you sure you want to continue?"),
113                         UIInterface::RESPONSE_NO
114                         ) == UIInterface::RESPONSE_NO
115                 )
116                         return false;
117                 else
118                 {
119                         // Because this action cannot be undone,
120                         // we need to clear the undo stack
121                         clear_undo_stack();
122                 }
123         }
124         else
125                 assert(undoable_action->is_active());
126
127         // Perform the action
128         try { action->perform(); }
129         catch(Action::Error err)
130         {
131                 uim->task(action->get_local_name()+' '+_("Failed"));
132                 inuse=false;
133
134                 if(err.get_type()!=Action::Error::TYPE_UNABLE)
135                 {
136                         if(err.get_desc().empty())
137                                 uim->error(action->get_local_name()+": "+strprintf("%d",err.get_type()));
138                         else
139                                 uim->error(action->get_local_name()+": "+err.get_desc());
140                 }
141
142                 // If action failed for whatever reason, just return false and do
143                 // not add the action onto the list
144                 return false;
145         }
146         catch(std::exception err)
147         {
148                 uim->task(action->get_local_name()+' '+_("Failed"));
149                 inuse=false;
150
151                 uim->error(action->get_local_name()+": "+err.what());
152
153                 // If action failed for whatever reason, just return false and do
154                 // not add the action onto the list
155                 return false;
156         }
157         catch(...)
158         {
159                 uim->task(action->get_local_name()+' '+_("Failed"));
160                 inuse=false;
161
162                 // If action failed for whatever reason, just return false and do
163                 // not add the action onto the list
164                 return false;
165         }
166
167         // Clear the redo stack
168         if(clear_redo_stack_on_new_action_)
169                 clear_redo_stack();
170
171         if(!group_stack_.empty())
172                 group_stack_.front()->inc_depth();
173         else
174                 inc_action_count();
175
176         // Push this action onto the action list if we can undo it
177         if(undoable_action)
178         {
179                 // If necessary, signal the change in status of undo
180                 if(undo_action_stack_.empty()) signal_undo_status_(true);
181
182                 // Add it to the list
183                 undo_action_stack_.push_front(undoable_action);
184
185                 // Signal that a new action has been added
186                 if(group_stack_.empty())
187                         signal_new_action()(undoable_action);
188         }
189
190         inuse=false;
191
192         uim->task(action->get_local_name()+' '+_("Successful"));
193
194         // If the action has "dirtied" the preview, signal it.
195         if(0)if(canvas_specific && canvas_specific->is_dirty())
196         {
197                 Canvas::Handle canvas=canvas_specific->get_canvas();
198                 if(!group_stack_.empty())
199                         group_stack_.front()->request_redraw(canvas_specific->get_canvas_interface());
200                 else
201                 {
202                         handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas);
203                         assert(canvas_interface);
204                         //canvas_interface->signal_dirty_preview()();
205                 }
206         }
207
208         }catch(...) { inuse=false; throw; }
209
210         return true;
211 }
212
213 bool
214 synfigapp::Action::System::undo_(etl::handle<UIInterface> uim)
215 {
216         handle<Action::Undoable> action(undo_action_stack().front());
217         most_recent_action_=action;
218
219         try { if(action->is_active()) action->undo(); }
220         catch(Action::Error err)
221         {
222                 if(err.get_type()!=Action::Error::TYPE_UNABLE)
223                 {
224                         if(err.get_desc().empty())
225                                 uim->error(action->get_local_name()+_(" (Undo): ")+strprintf("%d",err.get_type()));
226                         else
227                                 uim->error(action->get_local_name()+_(" (Undo): ")+err.get_desc());
228                 }
229
230                 return false;
231         }
232         catch(std::runtime_error x)
233         {
234                 uim->error(x.what());
235                 return false;
236         }
237         catch(...)
238         {
239                 return false;
240         }
241
242         dec_action_count();
243
244         if(redo_action_stack_.empty())  signal_redo_status()(true);
245
246         redo_action_stack_.push_front(undo_action_stack_.front());
247         undo_action_stack_.pop_front();
248
249         if(undo_action_stack_.empty())  signal_undo_status()(false);
250
251         if(!group_stack_.empty())
252                 group_stack_.front()->dec_depth();
253
254         signal_undo_();
255
256         return true;
257 }
258
259 bool
260 synfigapp::Action::System::undo()
261 {
262         //! \todo This function is not exception safe!
263         static bool inuse=false;
264
265         // If there is nothing on the action list, there is nothing to undo
266         if(undo_action_stack().empty() || inuse)
267                 return false;
268
269         handle<Action::Undoable> action=undo_action_stack().front();
270         Action::CanvasSpecific* canvas_specific(dynamic_cast<Action::CanvasSpecific*>(action.get()));
271
272         handle<UIInterface> uim;
273         if(canvas_specific && canvas_specific->get_canvas())
274         {
275                 Canvas::Handle canvas=canvas_specific->get_canvas();
276                 handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas);
277                 assert(canvas_interface);
278                 uim=canvas_interface->get_ui_interface();
279         }
280         else
281                 uim=get_ui_interface();
282
283         inuse=true;
284
285         if(!undo_(uim))
286         {
287                 uim->error(undo_action_stack_.front()->get_local_name()+": "+_("Failed to undo."));
288                 inuse=false;
289                 return false;
290         }
291
292         inuse=false;
293
294         // If the action has "dirtied" the preview, signal it.
295         if(0)if(action->is_active() && canvas_specific && canvas_specific->is_dirty())
296         {
297                 Canvas::Handle canvas=canvas_specific->get_canvas();
298                 if(!group_stack_.empty())
299                         group_stack_.front()->request_redraw(canvas_specific->get_canvas_interface());
300                 else
301                 {
302                         handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas);
303                         assert(canvas_interface);
304                         //canvas_interface->signal_dirty_preview()();
305                 }
306         }
307
308         return true;
309 }
310
311 bool
312 Action::System::redo_(etl::handle<UIInterface> uim)
313 {
314         handle<Action::Undoable> action(redo_action_stack().front());
315         most_recent_action_=action;
316
317         try { if(action->is_active()) action->perform(); }
318         catch(Action::Error err)
319         {
320                 if(err.get_type()!=Action::Error::TYPE_UNABLE)
321                 {
322                         if(err.get_desc().empty())
323                                 uim->error(action->get_local_name()+_(" (Redo): ")+strprintf("%d",err.get_type()));
324                         else
325                                 uim->error(action->get_local_name()+_(" (Redo): ")+err.get_desc());
326                 }
327
328                 return false;
329         }
330         catch(std::runtime_error x)
331         {
332                 uim->error(x.what());
333                 return false;
334         }
335         catch(...)
336         {
337                 return false;
338         }
339
340         inc_action_count();
341
342         if(undo_action_stack_.empty())  signal_undo_status()(true);
343
344         undo_action_stack_.push_front(redo_action_stack_.front());
345         redo_action_stack_.pop_front();
346
347         if(redo_action_stack_.empty())  signal_redo_status()(false);
348
349         if(!group_stack_.empty())
350                 group_stack_.front()->inc_depth();
351
352         signal_redo_();
353
354         return true;
355 }
356
357 bool
358 Action::System::redo()
359 {
360         //! \todo This function is not exception safe!
361         static bool inuse=false;
362
363         // If there is nothing on the action list, there is nothing to undo
364         if(redo_action_stack_.empty() || inuse)
365                 return false;
366
367         inuse=true;
368
369         handle<Action::Undoable> action=redo_action_stack().front();
370         Action::CanvasSpecific* canvas_specific(dynamic_cast<Action::CanvasSpecific*>(action.get()));
371
372         handle<UIInterface> uim;
373         if(canvas_specific && canvas_specific->get_canvas())
374         {
375                 Canvas::Handle canvas=canvas_specific->get_canvas();
376                 handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas);
377                 assert(canvas_interface);
378                 uim=canvas_interface->get_ui_interface();
379         }
380         else
381                 uim=get_ui_interface();
382
383         if(!redo_(uim))
384         {
385                 uim->error(redo_action_stack_.front()->get_local_name()+": "+_("Failed to redo."));
386                 inuse=false;
387                 return false;
388         }
389
390         inuse=false;
391
392         // If the action has "dirtied" the preview, signal it.
393         if(0)if(action->is_active() && canvas_specific && canvas_specific->is_dirty())
394         {
395                 Canvas::Handle canvas=canvas_specific->get_canvas();
396                 if(!group_stack_.empty())
397                         group_stack_.front()->request_redraw(canvas_specific->get_canvas_interface());
398                 else
399                 {
400                         handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas);
401                         assert(canvas_interface);
402                         //canvas_interface->signal_dirty_preview()();
403                 }
404         }
405
406         return true;
407 }
408
409 void
410 Action::System::inc_action_count()const
411 {
412         action_count_++;
413         if(action_count_==1)
414                 signal_unsaved_status_changed_(true);
415         if(!action_count_)
416                 signal_unsaved_status_changed_(false);
417 }
418
419 void
420 Action::System::dec_action_count()const
421 {
422         action_count_--;
423         if(action_count_==-1)
424                 signal_unsaved_status_changed_(true);
425         if(!action_count_)
426                 signal_unsaved_status_changed_(false);
427 }
428
429 void
430 Action::System::reset_action_count()const
431 {
432         if(!action_count_)
433                 return;
434
435         action_count_=0;
436         signal_unsaved_status_changed_(false);
437 }
438
439 void
440 Action::System::clear_undo_stack()
441 {
442         if(undo_action_stack_.empty()) return;
443         undo_action_stack_.clear();
444         signal_undo_status_(false);
445         signal_undo_stack_cleared_();
446 }
447
448 void
449 Action::System::clear_redo_stack()
450 {
451         if(redo_action_stack_.empty()) return;
452         redo_action_stack_.clear();
453         signal_redo_status_(false);
454         signal_redo_stack_cleared_();
455 }
456
457 bool
458 Action::System::set_action_status(etl::handle<Action::Undoable> action, bool x)
459 {
460         Stack::iterator iter;
461         int failed=false;
462
463         if(action->is_active()==x)
464                 return true;
465
466         handle<Action::Undoable> cur_pos=undo_action_stack_.front();
467
468         Action::CanvasSpecific* canvas_specific(dynamic_cast<Action::CanvasSpecific*>(action.get()));
469
470         handle<UIInterface> uim=new ConfidentUIInterface();
471
472         iter=find(undo_action_stack_.begin(),undo_action_stack_.end(),action);
473         if(iter!=undo_action_stack_.end())
474         {
475                 while(undo_action_stack_.front()!=action)
476                 {
477                         if(!undo_(uim))
478                         {
479                                 return false;
480                         }
481                 }
482                 if(!undo_(uim))
483                 {
484                         return false;
485                 }
486
487                 action->set_active(x);
488
489                 if(redo_(get_ui_interface()))
490                 {
491                         signal_action_status_changed_(action);
492                 }
493                 else
494                 {
495                         action->set_active(!x);
496                         failed=true;
497                 }
498
499
500                 while(undo_action_stack_.front()!=cur_pos)
501                 {
502                         if(!redo_(uim))
503                         {
504                                 redo_action_stack_.front()->set_active(false);
505                                 signal_action_status_changed_(redo_action_stack_.front());
506                         }
507                 }
508
509                 if(!failed && canvas_specific && canvas_specific->is_dirty())
510                 {
511                         Canvas::Handle canvas=canvas_specific->get_canvas();
512                         handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas);
513                         assert(canvas_interface);
514                         //canvas_interface->signal_dirty_preview()();
515                 }
516
517                 return true;
518         }
519
520         iter=find(redo_action_stack_.begin(),redo_action_stack_.end(),action);
521         if(iter!=redo_action_stack_.end())
522         {
523                 action->set_active(x);
524                 signal_action_status_changed_(action);
525
526
527
528
529                 if(canvas_specific && canvas_specific->is_dirty())
530                 {
531                         Canvas::Handle canvas=canvas_specific->get_canvas();
532                         handle<CanvasInterface> canvas_interface=static_cast<Instance*>(this)->find_canvas_interface(canvas);
533                         assert(canvas_interface);
534                         //canvas_interface->signal_dirty_preview()();
535                 }
536
537                 return true;
538         }
539
540         return false;
541 }
542
543 Action::PassiveGrouper::PassiveGrouper(etl::loose_handle<System> instance_,synfig::String name_):
544         instance_(instance_),
545         name_(name_),
546         redraw_requested_(false),
547         depth_(0)
548 {
549         // Add this group onto the group stack
550         instance_->group_stack_.push_front(this);
551 }
552
553 void
554 Action::PassiveGrouper::request_redraw(etl::handle<CanvasInterface> x)
555 {
556 /*      if(instance_->group_stack_.empty())
557         {
558                 if(x!=canvas_interface_)
559                 {
560                         x->signal_dirty_preview()();
561                 }
562
563                 redraw_requested_=false;
564         }
565         else
566         {
567                 if(instance_->group_stack_.back()==this)
568                 {
569                         redraw_requested_=true;
570                 }
571                 else
572                 {
573                         instance_->group_stack_.back()->request_redraw(x);
574                         redraw_requested_=false;
575                 }
576         }
577 */
578         if(x)
579         {
580                 redraw_requested_=true;
581                 canvas_interface_=x;
582         }
583 }
584
585 Action::PassiveGrouper::~PassiveGrouper()
586 {
587         assert(instance_->group_stack_.front()==this);
588
589         // Remove this group from the group stack
590         instance_->group_stack_.pop_front();
591
592         handle<Action::Group> group;
593
594         if(depth_==1)
595         {
596                 handle<Action::Undoable> action(instance_->undo_action_stack_.front());
597
598                 group=handle<Action::Group>::cast_dynamic(action);
599
600                 if(group)
601                 {
602                         // If the only action inside of us is a group,
603                         // then we should rename the group to our name.
604                         group->set_name(name_);
605                 }
606                 else
607                 {
608                         Action::CanvasSpecific* canvas_specific(dynamic_cast<Action::CanvasSpecific*>(action.get()));
609
610                         if(0)if(canvas_specific && canvas_specific->is_dirty() && canvas_specific->get_canvas_interface())
611                         {
612                                 if(instance_->group_stack_.empty())
613                                         request_redraw(canvas_specific->get_canvas_interface());
614                         }
615                 }
616
617                 if(instance_->group_stack_.empty())
618                 {
619                         instance_->inc_action_count();
620                         instance_->signal_new_action()(instance_->undo_action_stack_.front());
621                 }
622                 else
623                         instance_->group_stack_.front()->inc_depth();
624
625         }
626         else
627         if(depth_>0)
628         {
629                 group=new Action::Group(name_);
630
631                 for(int i=0;i<depth_;i++)
632         //      for(;depth_;depth_--)
633                 {
634                         handle<Action::Undoable> action(instance_->undo_action_stack_.front());
635                         Action::CanvasSpecific* canvas_specific(dynamic_cast<Action::CanvasSpecific*>(action.get()));
636
637                         if(0)if(canvas_specific && canvas_specific->is_dirty())
638                         {
639                                 group->set_dirty(true);
640                                 group->set_canvas(canvas_specific->get_canvas());
641                                 group->set_canvas_interface(canvas_specific->get_canvas_interface());
642                         }
643
644                         // Copy the action from the undo stack to the group
645                         group->add_action_front(action);
646
647                         // Remove the action from the undo stack
648                         instance_->undo_action_stack_.pop_front();
649                 }
650
651                 // Push the group onto the stack
652                 instance_->undo_action_stack_.push_front(group);
653
654                 if(0)if(group->is_dirty())
655                         request_redraw(group->get_canvas_interface());
656                 //      group->get_canvas_interface()->signal_dirty_preview()();
657
658                 if(instance_->group_stack_.empty())
659                 {
660                         instance_->inc_action_count();
661                         instance_->signal_new_action()(instance_->undo_action_stack_.front());
662                 }
663                 else
664                         instance_->group_stack_.front()->inc_depth();
665         }
666
667         if(0)if(redraw_requested_)
668         {
669                 if(instance_->group_stack_.empty())
670                 {
671                         assert(canvas_interface_);
672                         canvas_interface_->signal_dirty_preview()();
673                 }
674                 else
675                 {
676                         instance_->group_stack_.front()->request_redraw(canvas_interface_);
677                         redraw_requested_=false;
678                 }
679         }
680 }
681
682 void
683 Action::PassiveGrouper::cancel()
684 {
685         bool error=false;
686
687         // Cancel any groupers that may be on top of us first
688         //while(instance_->group_stack_.front()!=this)
689         //      instance_->group_stack_.front()->cancel();
690
691         synfig::warning("Cancel depth: %d",depth_);
692
693         while(depth_)
694                 if(!instance_->undo())
695                 {
696                         error=true;
697                         break;
698                 }
699
700         if(error)
701                 instance_->get_ui_interface()->error(_("State restore failure"));
702         else
703                 redraw_requested_=false;
704 }