2 * Copyright (C) 2001 Brett Viren
3 * Copyright (C) 2001-2015 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2006 Hans Fugal <hans@fugal.net>
5 * Copyright (C) 2007-2009 David Robillard <d@drobilla.net>
6 * Copyright (C) 2012-2017 Tim Mayberry <mojofunk@gmail.com>
7 * Copyright (C) 2015 Nick Mainsbridge <mainsbridge@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include "pbd/xml++.h"
31 #include <sigc++/bind.h>
36 UndoTransaction::UndoTransaction ()
39 gettimeofday (&_timestamp, 0);
42 UndoTransaction::UndoTransaction (const UndoTransaction& rhs)
46 _timestamp = rhs._timestamp;
48 actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
51 UndoTransaction::~UndoTransaction ()
58 command_death (UndoTransaction* ut, Command* c)
64 ut->remove_command (c);
72 UndoTransaction::operator= (const UndoTransaction& rhs)
74 if (this == &rhs) return *this;
77 actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
82 UndoTransaction::add_command (Command *const cmd)
84 /* catch death of command (e.g. caused by death of object to
85 which it refers. command_death() is a normal static function
86 so there is no need to manage this connection.
89 cmd->DropReferences.connect_same_thread (*this, boost::bind (&command_death, this, cmd));
90 actions.push_back (cmd);
94 UndoTransaction::remove_command (Command* const action)
96 actions.remove (action);
100 UndoTransaction::empty () const
102 return actions.empty();
106 UndoTransaction::clear ()
109 for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
117 UndoTransaction::operator() ()
119 for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
125 UndoTransaction::undo ()
127 for (list<Command*>::reverse_iterator i = actions.rbegin(); i != actions.rend(); ++i) {
133 UndoTransaction::redo ()
138 XMLNode &UndoTransaction::get_state()
140 XMLNode *node = new XMLNode ("UndoTransaction");
141 node->set_property("tv-sec", (int64_t)_timestamp.tv_sec);
142 node->set_property("tv-usec", (int64_t)_timestamp.tv_usec);
143 node->set_property("name", _name);
145 list<Command*>::iterator it;
146 for (it=actions.begin(); it!=actions.end(); it++)
147 node->add_child_nocopy((*it)->get_state());
152 class UndoRedoSignaller {
154 UndoRedoSignaller (UndoHistory& uh)
156 _history.BeginUndoRedo();
158 ~UndoRedoSignaller() {
159 _history.EndUndoRedo();
163 UndoHistory& _history;
166 UndoHistory::UndoHistory ()
173 UndoHistory::set_depth (uint32_t d)
176 uint32_t current_depth = UndoList.size();
180 if (d > current_depth) {
181 /* not even transactions to meet request */
187 uint32_t cnt = current_depth - d;
190 ut = UndoList.front();
191 UndoList.pop_front ();
198 UndoHistory::add (UndoTransaction* const ut)
200 uint32_t current_depth = UndoList.size();
202 ut->DropReferences.connect_same_thread (*this, boost::bind (&UndoHistory::remove, this, ut));
204 /* if the current undo history is larger than or equal to the currently
205 requested depth, then pop off at least 1 element to make space
206 at the back for new one.
209 if ((_depth > 0) && current_depth && (current_depth >= _depth)) {
211 uint32_t cnt = 1 + (current_depth - _depth);
215 ut = UndoList.front ();
216 UndoList.pop_front ();
221 UndoList.push_back (ut);
222 /* Adding a transacrion makes the redo list meaningless. */
224 for (std::list<UndoTransaction*>::iterator i = RedoList.begin(); i != RedoList.end(); ++i) {
230 /* we are now owners of the transaction and must delete it when finished with it */
232 Changed (); /* EMIT SIGNAL */
236 UndoHistory::remove (UndoTransaction* const ut)
242 UndoList.remove (ut);
243 RedoList.remove (ut);
245 Changed (); /* EMIT SIGNAL */
248 /** Undo some transactions.
249 * @param n Number of transactions to undo.
252 UndoHistory::undo (unsigned int n)
259 UndoRedoSignaller exception_safe_signaller (*this);
262 if (UndoList.size() == 0) {
265 UndoTransaction* ut = UndoList.back ();
266 UndoList.pop_back ();
268 RedoList.push_back (ut);
272 Changed (); /* EMIT SIGNAL */
276 UndoHistory::redo (unsigned int n)
283 UndoRedoSignaller exception_safe_signaller (*this);
286 if (RedoList.size() == 0) {
289 UndoTransaction* ut = RedoList.back ();
290 RedoList.pop_back ();
292 UndoList.push_back (ut);
296 Changed (); /* EMIT SIGNAL */
300 UndoHistory::clear_redo ()
303 for (std::list<UndoTransaction*>::iterator i = RedoList.begin(); i != RedoList.end(); ++i) {
309 Changed (); /* EMIT SIGNAL */
314 UndoHistory::clear_undo ()
317 for (std::list<UndoTransaction*>::iterator i = UndoList.begin(); i != UndoList.end(); ++i) {
323 Changed (); /* EMIT SIGNAL */
327 UndoHistory::clear ()
332 Changed (); /* EMIT SIGNAL */
336 UndoHistory::get_state (int32_t depth)
338 XMLNode *node = new XMLNode ("UndoHistory");
344 } else if (depth < 0) {
348 for (list<UndoTransaction*>::iterator it = UndoList.begin(); it != UndoList.end(); ++it) {
349 node->add_child_nocopy((*it)->get_state());
354 /* just the last "depth" transactions */
356 list<UndoTransaction*> in_order;
358 for (list<UndoTransaction*>::reverse_iterator it = UndoList.rbegin(); it != UndoList.rend() && depth; ++it, depth--) {
359 in_order.push_front (*it);
362 for (list<UndoTransaction*>::iterator it = in_order.begin(); it != in_order.end(); it++) {
363 node->add_child_nocopy((*it)->get_state());