Merged with trunk R992.
[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
25 #include <pbd/undo.h>
26 #include <pbd/xml++.h>
27
28 #include <sigc++/bind.h>
29
30 using namespace std;
31 using namespace sigc;
32
33 /* grrr, strict C++ says that static member functions are not C functions, but we also want
34    to be able to pack this into a sigc::ptr_fun and not sigc::mem_fun, so we have to make
35    it a genuine function rather than a member.
36 */
37
38 static void command_death (UndoTransaction* ut, Command* c)
39 {
40         if (ut->clearing()) {
41                 return;
42         }
43
44         ut->remove_command (c);
45
46         if (ut->empty()) {
47                 delete ut;
48         }
49 }
50
51
52 UndoTransaction::UndoTransaction ()
53 {
54         _clearing = false;
55 }
56
57 UndoTransaction::UndoTransaction (const UndoTransaction& rhs)
58 {
59         _name = rhs._name;
60         _clearing = false;
61         clear ();
62         actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
63 }
64
65 UndoTransaction::~UndoTransaction ()
66 {
67         GoingAway ();
68         clear ();
69 }
70
71 UndoTransaction& 
72 UndoTransaction::operator= (const UndoTransaction& rhs)
73 {
74         if (this == &rhs) return *this;
75         _name = rhs._name;
76         clear ();
77         actions.insert(actions.end(),rhs.actions.begin(),rhs.actions.end());
78         return *this;
79 }
80
81 void
82 UndoTransaction::add_command (Command *const action)
83 {
84         action->GoingAway.connect (bind (sigc::ptr_fun (command_death), this, const_cast<Command*>(action)));
85         actions.push_back (action);
86 }
87
88 void
89 UndoTransaction::remove_command (Command* const action)
90 {
91         actions.remove (action);
92 }
93
94 bool
95 UndoTransaction::empty () const
96 {
97         return actions.empty();
98 }
99
100 void
101 UndoTransaction::clear ()
102 {
103         _clearing = true;
104         for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
105                 delete *i;
106         }
107         actions.clear ();
108         _clearing = false;
109 }
110
111 void
112 UndoTransaction::operator() ()
113 {
114         for (list<Command*>::iterator i = actions.begin(); i != actions.end(); ++i) {
115                 (*(*i))();
116         }
117 }
118
119 void
120 UndoTransaction::undo ()
121 {
122         for (list<Command*>::reverse_iterator i = actions.rbegin(); i != actions.rend(); ++i) {
123                 (*i)->undo();
124         }
125 }
126
127 void
128 UndoTransaction::redo ()
129 {
130         (*this)();
131 }
132
133 XMLNode &UndoTransaction::get_state()
134 {
135     XMLNode *node = new XMLNode ("UndoTransaction");
136     stringstream ss;
137     ss << _timestamp.tv_sec;
138     node->add_property("tv_sec", ss.str());
139     ss.str("");
140     ss << _timestamp.tv_usec;
141     node->add_property("tv_usec", ss.str());
142     node->add_property("name", _name);
143
144     list<Command*>::iterator it;
145     for (it=actions.begin(); it!=actions.end(); it++)
146         node->add_child_nocopy((*it)->get_state());
147
148     return *node;
149 }
150
151 UndoHistory::UndoHistory ()
152 {
153         _clearing = false;
154 }
155
156 void
157 UndoHistory::add (UndoTransaction* const ut)
158 {
159         ut->GoingAway.connect (bind (mem_fun (*this, &UndoHistory::remove), ut));
160         UndoList.push_back (ut);
161
162         /* we are now owners of the transaction */
163 }
164
165 void
166 UndoHistory::remove (UndoTransaction* const ut)
167 {
168         if (_clearing) {
169                 return;
170         }
171
172         UndoList.remove (ut);
173         RedoList.remove (ut);
174 }
175
176 void
177 UndoHistory::undo (unsigned int n)
178 {
179         while (n--) {
180                 if (UndoList.size() == 0) {
181                         return;
182                 }
183                 UndoTransaction* ut = UndoList.back ();
184                 UndoList.pop_back ();
185                 ut->undo ();
186                 RedoList.push_back (ut);
187         }
188 }
189
190 void
191 UndoHistory::redo (unsigned int n)
192 {
193         while (n--) {
194                 if (RedoList.size() == 0) {
195                         return;
196                 }
197                 UndoTransaction* ut = RedoList.back ();
198                 RedoList.pop_back ();
199                 ut->redo ();
200                 UndoList.push_back (ut);
201         }
202 }
203
204 void
205 UndoHistory::clear_redo ()
206 {
207         _clearing = true;
208         RedoList.clear ();
209         _clearing = false;
210 }
211
212 void
213 UndoHistory::clear_undo ()
214 {
215         _clearing = true;
216         UndoList.clear ();
217         _clearing = false;
218 }
219
220 void
221 UndoHistory::clear ()
222 {
223         clear_undo ();
224         clear_redo ();
225 }
226
227 XMLNode & UndoHistory::get_state()
228 {
229     XMLNode *node = new XMLNode ("UndoHistory");
230
231     list<UndoTransaction*>::iterator it;
232     for (it = UndoList.begin(); it != UndoList.end(); it++) {
233             node->add_child_nocopy((*it)->get_state());
234     }
235
236     return *node;
237 }