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