Optimize automation-event process splitting
[ardour.git] / libs / pbd / undo.cc
index 5365a98146212be9e46c7ef41480f2b78246ba92..88b3c4ead5b0b2b40f3e1eae7606a44cf45e4e9c 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
     Copyright (C) 2001 Brett Viren & Paul Davis
 
     This program is free software; you can redistribute it and/or modify
@@ -22,8 +22,8 @@
 #include <sstream>
 #include <time.h>
 
-#include <pbd/undo.h>
-#include <pbd/xml++.h>
+#include "pbd/undo.h"
+#include "pbd/xml++.h"
 
 #include <sigc++/bind.h>
 
@@ -40,17 +40,18 @@ UndoTransaction::UndoTransaction (const UndoTransaction& rhs)
        : Command(rhs._name)
        , _clearing(false)
 {
+        _timestamp = rhs._timestamp;
        clear ();
        actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
 }
 
 UndoTransaction::~UndoTransaction ()
 {
-       GoingAway ();
+       drop_references ();
        clear ();
 }
 
-void 
+void
 command_death (UndoTransaction* ut, Command* c)
 {
        if (ut->clearing()) {
@@ -64,7 +65,7 @@ command_death (UndoTransaction* ut, Command* c)
        }
 }
 
-UndoTransaction& 
+UndoTransaction&
 UndoTransaction::operator= (const UndoTransaction& rhs)
 {
        if (this == &rhs) return *this;
@@ -75,13 +76,15 @@ UndoTransaction::operator= (const UndoTransaction& rhs)
 }
 
 void
-UndoTransaction::add_command (Command *const action)
+UndoTransaction::add_command (Command *const cmd)
 {
        /* catch death of command (e.g. caused by death of object to
-          which it refers.
+          which it refers. command_death() is a normal static function
+          so there is no need to manage this connection.
         */
-       shivas.push_back (new PBD::ProxyShiva<Command,UndoTransaction> (*action, *this, &command_death));
-       actions.push_back (action);
+
+       cmd->DropReferences.connect_same_thread (*this, boost::bind (&command_death, this, cmd));
+       actions.push_back (cmd);
 }
 
 void
@@ -90,21 +93,6 @@ UndoTransaction::remove_command (Command* const action)
        actions.remove (action);
 }
 
-void
-UndoTransaction::about_to_explicitly_delete ()
-{
-       /* someone is going to call our destructor and its not Shiva,
-          the god of destruction and chaos. This happens when an UndoHistory
-          is pruning itself. we must remove Shivas to avoid the god
-          striking us down a second time, unnecessarily and illegally.
-       */
-
-       for (list<PBD::ProxyShiva<Command,UndoTransaction>*>::iterator i = shivas.begin(); i != shivas.end(); ++i) {
-               delete *i;
-       }
-       shivas.clear ();
-}
-
 bool
 UndoTransaction::empty () const
 {
@@ -147,13 +135,9 @@ UndoTransaction::redo ()
 XMLNode &UndoTransaction::get_state()
 {
     XMLNode *node = new XMLNode ("UndoTransaction");
-    stringstream ss;
-    ss << _timestamp.tv_sec;
-    node->add_property("tv_sec", ss.str());
-    ss.str("");
-    ss << _timestamp.tv_usec;
-    node->add_property("tv_usec", ss.str());
-    node->add_property("name", _name);
+    node->set_property("tv-sec", (int64_t)_timestamp.tv_sec);
+    node->set_property("tv-usec", (int64_t)_timestamp.tv_usec);
+    node->set_property("name", _name);
 
     list<Command*>::iterator it;
     for (it=actions.begin(); it!=actions.end(); it++)
@@ -162,6 +146,20 @@ XMLNode &UndoTransaction::get_state()
     return *node;
 }
 
+class UndoRedoSignaller {
+public:
+    UndoRedoSignaller (UndoHistory& uh)
+           : _history (uh) {
+           _history.BeginUndoRedo();
+    }
+    ~UndoRedoSignaller() {
+           _history.EndUndoRedo();
+    }
+
+private:
+    UndoHistory& _history;
+};
+
 UndoHistory::UndoHistory ()
 {
        _clearing = false;
@@ -188,7 +186,6 @@ UndoHistory::set_depth (uint32_t d)
                while (cnt--) {
                        ut = UndoList.front();
                        UndoList.pop_front ();
-                       ut->about_to_explicitly_delete ();
                        delete ut;
                }
        }
@@ -199,7 +196,7 @@ UndoHistory::add (UndoTransaction* const ut)
 {
        uint32_t current_depth = UndoList.size();
 
-       ut->GoingAway.connect (bind (mem_fun (*this, &UndoHistory::remove), ut));
+       ut->DropReferences.connect_same_thread (*this, boost::bind (&UndoHistory::remove, this, ut));
 
        /* if the current undo history is larger than or equal to the currently
           requested depth, then pop off at least 1 element to make space
@@ -214,12 +211,18 @@ UndoHistory::add (UndoTransaction* const ut)
                        UndoTransaction* ut;
                        ut = UndoList.front ();
                        UndoList.pop_front ();
-                       ut->about_to_explicitly_delete ();
                        delete ut;
                }
        }
 
        UndoList.push_back (ut);
+       /* Adding a transacrion makes the redo list meaningless. */
+       _clearing = true;
+       for (std::list<UndoTransaction*>::iterator i = RedoList.begin(); i != RedoList.end(); ++i) {
+                delete *i;
+        }
+       RedoList.clear ();
+       _clearing = false;
 
        /* we are now owners of the transaction and must delete it when finished with it */
 
@@ -245,14 +248,22 @@ UndoHistory::remove (UndoTransaction* const ut)
 void
 UndoHistory::undo (unsigned int n)
 {
-       while (n--) {
-               if (UndoList.size() == 0) {
-                       return;
+       if (n == 0) {
+               return;
+       }
+
+       {
+               UndoRedoSignaller exception_safe_signaller (*this);
+
+               while (n--) {
+                       if (UndoList.size() == 0) {
+                               return;
+                       }
+                       UndoTransaction* ut = UndoList.back ();
+                       UndoList.pop_back ();
+                       ut->undo ();
+                       RedoList.push_back (ut);
                }
-               UndoTransaction* ut = UndoList.back ();
-               UndoList.pop_back ();
-               ut->undo ();
-               RedoList.push_back (ut);
        }
 
        Changed (); /* EMIT SIGNAL */
@@ -261,14 +272,22 @@ UndoHistory::undo (unsigned int n)
 void
 UndoHistory::redo (unsigned int n)
 {
-       while (n--) {
-               if (RedoList.size() == 0) {
-                       return;
+       if (n == 0) {
+               return;
+       }
+
+       {
+               UndoRedoSignaller exception_safe_signaller (*this);
+
+               while (n--) {
+                       if (RedoList.size() == 0) {
+                               return;
+                       }
+                       UndoTransaction* ut = RedoList.back ();
+                       RedoList.pop_back ();
+                       ut->redo ();
+                       UndoList.push_back (ut);
                }
-               UndoTransaction* ut = RedoList.back ();
-               RedoList.pop_back ();
-               ut->redo ();
-               UndoList.push_back (ut);
        }
 
        Changed (); /* EMIT SIGNAL */
@@ -278,6 +297,9 @@ void
 UndoHistory::clear_redo ()
 {
        _clearing = true;
+        for (std::list<UndoTransaction*>::iterator i = RedoList.begin(); i != RedoList.end(); ++i) {
+                delete *i;
+        }
        RedoList.clear ();
        _clearing = false;
 
@@ -289,6 +311,9 @@ void
 UndoHistory::clear_undo ()
 {
        _clearing = true;
+        for (std::list<UndoTransaction*>::iterator i = UndoList.begin(); i != UndoList.end(); ++i) {
+                delete *i;
+        }
        UndoList.clear ();
        _clearing = false;
 
@@ -304,7 +329,7 @@ UndoHistory::clear ()
        Changed (); /* EMIT SIGNAL */
 }
 
-XMLNode& 
+XMLNode&
 UndoHistory::get_state (int32_t depth)
 {
     XMLNode *node = new XMLNode ("UndoHistory");