merge from 2.0-ongoing @ 3581
[ardour.git] / libs / pbd / undo.cc
1 /* 
2     Copyright (C) 2001 Brett Viren & Paul Davis
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20
21 #include <iostream>
22 #include <string>
23 #include <sstream>
24 #include <time.h>
25
26 #include <pbd/undo.h>
27 #include <pbd/xml++.h>
28 #include <pbd/shiva.h>
29
30 #include <sigc++/bind.h>
31
32 using namespace std;
33 using namespace sigc;
34
35 UndoTransaction::UndoTransaction ()
36         : _clearing(false)
37 {
38         gettimeofday (&_timestamp, 0);
39 }
40
41 UndoTransaction::UndoTransaction (const UndoTransaction& rhs)
42         : Command(rhs._name)
43         , _clearing(false)
44 {
45         clear ();
46         actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
47 }
48
49 UndoTransaction::~UndoTransaction ()
50 {
51         GoingAway ();
52         clear ();
53 }
54
55 void 
56 command_death (UndoTransaction* ut, Command* c)
57 {
58         if (ut->clearing()) {
59                 return;
60         }
61
62         ut->remove_command (c);
63
64         if (ut->empty()) {
65                 delete ut;
66         }
67 }
68
69 UndoTransaction& 
70 UndoTransaction::operator= (const UndoTransaction& rhs)
71 {
72         if (this == &rhs) return *this;
73         _name = rhs._name;
74         clear ();
75         actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
76         return *this;
77 }
78
79 void
80 UndoTransaction::add_command (Command *const action)
81 {
82         /* catch death */
83         new PBD::ProxyShiva<Command,UndoTransaction> (*action, *this, &command_death);
84         actions.push_back (action);
85 }
86
87 void
88 UndoTransaction::remove_command (Command* const action)
89 {
90         actions.remove (action);
91 }
92
93 bool
94 UndoTransaction::empty () const
95 {
96         return actions.empty();
97 }
98
99 void
100 UndoTransaction::clear ()
101 {
102         _clearing = true;
103         for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
104                 delete *i;
105         }
106         actions.clear ();
107         _clearing = false;
108 }
109
110 void
111 UndoTransaction::operator() ()
112 {
113         for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
114                 (*(*i))();
115         }
116 }
117
118 void
119 UndoTransaction::undo ()
120 {
121         for (list<Command*>::reverse_iterator i = actions.rbegin(); i != actions.rend(); ++i) {
122                 (*i)->undo();
123         }
124 }
125
126 void
127 UndoTransaction::redo ()
128 {
129         (*this)();
130 }
131
132 XMLNode &UndoTransaction::get_state()
133 {
134     XMLNode *node = new XMLNode ("UndoTransaction");
135     stringstream ss;
136     ss << _timestamp.tv_sec;
137     node->add_property("tv_sec", ss.str());
138     ss.str("");
139     ss << _timestamp.tv_usec;
140     node->add_property("tv_usec", ss.str());
141     node->add_property("name", _name);
142
143     list<Command*>::iterator it;
144     for (it=actions.begin(); it!=actions.end(); it++)
145         node->add_child_nocopy((*it)->get_state());
146
147     return *node;
148 }
149
150 UndoHistory::UndoHistory ()
151 {
152         _clearing = false;
153         _depth = 0;
154 }
155
156 void
157 UndoHistory::set_depth (int32_t d)
158 {
159         _depth = d;
160
161         while (_depth > 0 && UndoList.size() > (uint32_t) _depth) {
162                 UndoList.pop_front ();
163         }
164 }
165
166 void
167 UndoHistory::add (UndoTransaction* const ut)
168 {
169         ut->GoingAway.connect (bind (mem_fun (*this, &UndoHistory::remove), ut));
170
171         while (_depth > 0 && UndoList.size() > (uint32_t) _depth) {
172                 UndoList.pop_front ();
173         }
174
175         UndoList.push_back (ut);
176
177         /* we are now owners of the transaction */
178
179         Changed (); /* EMIT SIGNAL */
180 }
181
182 void
183 UndoHistory::remove (UndoTransaction* const ut)
184 {
185         if (_clearing) {
186                 return;
187         }
188
189         UndoList.remove (ut);
190         RedoList.remove (ut);
191
192         Changed (); /* EMIT SIGNAL */
193 }
194
195 /** Undo some transactions.
196  * @param n Number of transactions to undo.
197  */
198 void
199 UndoHistory::undo (unsigned int n)
200 {
201         while (n--) {
202                 if (UndoList.size() == 0) {
203                         return;
204                 }
205                 UndoTransaction* ut = UndoList.back ();
206                 UndoList.pop_back ();
207                 ut->undo ();
208                 RedoList.push_back (ut);
209         }
210
211         Changed (); /* EMIT SIGNAL */
212 }
213
214 void
215 UndoHistory::redo (unsigned int n)
216 {
217         while (n--) {
218                 if (RedoList.size() == 0) {
219                         return;
220                 }
221                 UndoTransaction* ut = RedoList.back ();
222                 RedoList.pop_back ();
223                 ut->redo ();
224                 UndoList.push_back (ut);
225         }
226
227         Changed (); /* EMIT SIGNAL */
228 }
229
230 void
231 UndoHistory::clear_redo ()
232 {
233         _clearing = true;
234         RedoList.clear ();
235         _clearing = false;
236
237         Changed (); /* EMIT SIGNAL */
238
239 }
240
241 void
242 UndoHistory::clear_undo ()
243 {
244         _clearing = true;
245         UndoList.clear ();
246         _clearing = false;
247
248         Changed (); /* EMIT SIGNAL */
249 }
250
251 void
252 UndoHistory::clear ()
253 {
254         clear_undo ();
255         clear_redo ();
256
257         Changed (); /* EMIT SIGNAL */
258 }
259
260 XMLNode& 
261 UndoHistory::get_state (int32_t depth)
262 {
263     XMLNode *node = new XMLNode ("UndoHistory");
264
265     if (depth == 0) {
266
267             return (*node);
268
269     } else if (depth < 0) {
270
271             /* everything */
272
273             for (list<UndoTransaction*>::iterator it = UndoList.begin(); it != UndoList.end(); ++it) {
274                     node->add_child_nocopy((*it)->get_state());
275             }
276
277     } else {
278
279             /* just the last "depth" transactions */
280
281             list<UndoTransaction*> in_order;
282
283             for (list<UndoTransaction*>::reverse_iterator it = UndoList.rbegin(); it != UndoList.rend() && depth; ++it, depth--) {
284                     in_order.push_front (*it);
285             }
286
287             for (list<UndoTransaction*>::iterator it = in_order.begin(); it != in_order.end(); it++) {
288                     node->add_child_nocopy((*it)->get_state());
289             }
290     }
291
292     return *node;
293 }
294
295