2 Copyright (C) 2001 Brett Viren & Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <pbd/xml++.h>
28 #include <sigc++/bind.h>
33 UndoTransaction::UndoTransaction ()
36 gettimeofday (&_timestamp, 0);
39 UndoTransaction::UndoTransaction (const UndoTransaction& rhs)
44 actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
47 UndoTransaction::~UndoTransaction ()
54 command_death (UndoTransaction* ut, Command* c)
60 ut->remove_command (c);
68 UndoTransaction::operator= (const UndoTransaction& rhs)
70 if (this == &rhs) return *this;
73 actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
78 UndoTransaction::add_command (Command *const action)
80 /* catch death of command (e.g. caused by death of object to
83 shivas.push_back (new PBD::ProxyShiva<Command,UndoTransaction> (*action, *this, &command_death));
84 actions.push_back (action);
88 UndoTransaction::remove_command (Command* const action)
90 actions.remove (action);
94 UndoTransaction::about_to_explicitly_delete ()
96 /* someone is going to call our destructor and its not Shiva,
97 the god of destruction and chaos. This happens when an UndoHistory
98 is pruning itself. we must remove Shivas to avoid the god
99 striking us down a second time, unnecessarily and illegally.
102 for (list<PBD::ProxyShiva<Command,UndoTransaction>*>::iterator i = shivas.begin(); i != shivas.end(); ++i) {
109 UndoTransaction::empty () const
111 return actions.empty();
115 UndoTransaction::clear ()
118 for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
126 UndoTransaction::operator() ()
128 for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
134 UndoTransaction::undo ()
136 for (list<Command*>::reverse_iterator i = actions.rbegin(); i != actions.rend(); ++i) {
142 UndoTransaction::redo ()
147 XMLNode &UndoTransaction::get_state()
149 XMLNode *node = new XMLNode ("UndoTransaction");
151 ss << _timestamp.tv_sec;
152 node->add_property("tv_sec", ss.str());
154 ss << _timestamp.tv_usec;
155 node->add_property("tv_usec", ss.str());
156 node->add_property("name", _name);
158 list<Command*>::iterator it;
159 for (it=actions.begin(); it!=actions.end(); it++)
160 node->add_child_nocopy((*it)->get_state());
165 UndoHistory::UndoHistory ()
172 UndoHistory::set_depth (uint32_t d)
175 uint32_t current_depth = UndoList.size();
179 if (d > current_depth) {
180 /* not even transactions to meet request */
186 uint32_t cnt = current_depth - d;
189 ut = UndoList.front();
190 UndoList.pop_front ();
191 ut->about_to_explicitly_delete ();
198 UndoHistory::add (UndoTransaction* const ut)
200 uint32_t current_depth = UndoList.size();
202 ut->GoingAway.connect (bind (mem_fun (*this, &UndoHistory::remove), 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 ();
217 ut->about_to_explicitly_delete ();
222 UndoList.push_back (ut);
224 /* we are now owners of the transaction and must delete it when finished with it */
226 Changed (); /* EMIT SIGNAL */
230 UndoHistory::remove (UndoTransaction* const ut)
236 UndoList.remove (ut);
237 RedoList.remove (ut);
239 Changed (); /* EMIT SIGNAL */
242 /** Undo some transactions.
243 * @param n Number of transactions to undo.
246 UndoHistory::undo (unsigned int n)
249 if (UndoList.size() == 0) {
252 UndoTransaction* ut = UndoList.back ();
253 UndoList.pop_back ();
255 RedoList.push_back (ut);
258 Changed (); /* EMIT SIGNAL */
262 UndoHistory::redo (unsigned int n)
265 if (RedoList.size() == 0) {
268 UndoTransaction* ut = RedoList.back ();
269 RedoList.pop_back ();
271 UndoList.push_back (ut);
274 Changed (); /* EMIT SIGNAL */
278 UndoHistory::clear_redo ()
284 Changed (); /* EMIT SIGNAL */
289 UndoHistory::clear_undo ()
295 Changed (); /* EMIT SIGNAL */
299 UndoHistory::clear ()
304 Changed (); /* EMIT SIGNAL */
308 UndoHistory::get_state (int32_t depth)
310 XMLNode *node = new XMLNode ("UndoHistory");
316 } else if (depth < 0) {
320 for (list<UndoTransaction*>::iterator it = UndoList.begin(); it != UndoList.end(); ++it) {
321 node->add_child_nocopy((*it)->get_state());
326 /* just the last "depth" transactions */
328 list<UndoTransaction*> in_order;
330 for (list<UndoTransaction*>::reverse_iterator it = UndoList.rbegin(); it != UndoList.rend() && depth; ++it, depth--) {
331 in_order.push_front (*it);
334 for (list<UndoTransaction*>::iterator it = in_order.begin(); it != in_order.end(); it++) {
335 node->add_child_nocopy((*it)->get_state());