2.X commits up to and including 7519
[ardour.git] / libs / pbd / undo.cc
index 4719d0968eb07cc70301f27ec50f07f291964089..fd7c4d5fb85f6f052be32ae29bca7db9c039e592 100644 (file)
 #include <iostream>
 #include <string>
 #include <sstream>
+#include <time.h>
 
-#include <pbd/undo.h>
-#include <pbd/xml++.h>
-#include <pbd/shiva.h>
+#include "pbd/undo.h"
+#include "pbd/xml++.h"
 
 #include <sigc++/bind.h>
 
@@ -32,21 +32,23 @@ using namespace std;
 using namespace sigc;
 
 UndoTransaction::UndoTransaction ()
+       : _clearing(false)
 {
-       _clearing = false;
+       gettimeofday (&_timestamp, 0);
 }
 
 UndoTransaction::UndoTransaction (const UndoTransaction& rhs)
+       : Command(rhs._name)
+       , _clearing(false)
 {
-       _name = rhs._name;
-       _clearing = false;
+        _timestamp = rhs._timestamp;
        clear ();
        actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
 }
 
 UndoTransaction::~UndoTransaction ()
 {
-       GoingAway ();
+       drop_references ();
        clear ();
 }
 
@@ -75,11 +77,15 @@ UndoTransaction::operator= (const UndoTransaction& rhs)
 }
 
 void
-UndoTransaction::add_command (Command *const action)
+UndoTransaction::add_command (Command *const cmd)
 {
-       /* catch death */
-       new PBD::ProxyShiva<Command,UndoTransaction> (*action, *this, &command_death);
-       actions.push_back (action);
+       /* catch death of command (e.g. caused by death of object to
+          which it refers. command_death() is a normal static function
+          so there is no need to manage this connection.
+        */
+
+       cmd->DropReferences.connect_same_thread (*this, boost::bind (&command_death, this, cmd));
+       actions.push_back (cmd);
 }
 
 void
@@ -145,18 +151,78 @@ 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;
+       _depth = 0;
+}
+
+void
+UndoHistory::set_depth (uint32_t d)
+{
+       UndoTransaction* ut;
+       uint32_t current_depth = UndoList.size();
+
+       _depth = d;
+
+       if (d > current_depth) {
+               /* not even transactions to meet request */
+               return;
+       }
+
+       if (_depth > 0) {
+
+               uint32_t cnt = current_depth - d;
+
+               while (cnt--) {
+                       ut = UndoList.front();
+                       UndoList.pop_front ();
+                       delete ut;
+               }
+       }
 }
 
 void
 UndoHistory::add (UndoTransaction* const ut)
 {
-       ut->GoingAway.connect (bind (mem_fun (*this, &UndoHistory::remove), ut));
+       uint32_t current_depth = UndoList.size();
+
+       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
+          at the back for new one.
+       */
+
+       if ((_depth > 0) && current_depth && (current_depth >= _depth)) {
+
+               uint32_t cnt = 1 + (current_depth - _depth);
+
+               while (cnt--) {
+                       UndoTransaction* ut;
+                       ut = UndoList.front ();
+                       UndoList.pop_front ();
+                       delete ut;
+               }
+       }
+
        UndoList.push_back (ut);
 
-       /* we are now owners of the transaction */
+       /* we are now owners of the transaction and must delete it when finished with it */
 
        Changed (); /* EMIT SIGNAL */
 }
@@ -174,17 +240,28 @@ UndoHistory::remove (UndoTransaction* const ut)
        Changed (); /* EMIT SIGNAL */
 }
 
+/** Undo some transactions.
+ * @param n Number of transactions to undo.
+ */
 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 */
@@ -193,14 +270,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 */
@@ -237,17 +322,22 @@ UndoHistory::clear ()
 }
 
 XMLNode& 
-UndoHistory::get_state (uint32_t depth)
+UndoHistory::get_state (int32_t depth)
 {
     XMLNode *node = new XMLNode ("UndoHistory");
 
     if (depth == 0) {
+
+           return (*node);
+
+    } else if (depth < 0) {
+
            /* everything */
 
            for (list<UndoTransaction*>::iterator it = UndoList.begin(); it != UndoList.end(); ++it) {
                    node->add_child_nocopy((*it)->get_state());
            }
-           
+
     } else {
 
            /* just the last "depth" transactions */
@@ -265,3 +355,5 @@ UndoHistory::get_state (uint32_t depth)
 
     return *node;
 }
+
+