]> Shamusworld >> Repos - architektonas/blob - src/base/undo.cpp
Bugfixes related to removing Snapper class.
[architektonas] / src / base / undo.cpp
1 // undo.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  06/02/2010  Added this text. :-)
15 //
16
17 #include "undo.h"
18
19 #include "debug.h"
20 #include "undoable.h"
21
22 /**
23  * Default constructor.
24  */
25 Undo::Undo()
26 {
27 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
28 //      undoList.setAutoDelete(true);
29         undoPointer = -1;
30         currentCycle = NULL;
31 }
32
33 /*virtual*/ Undo::~Undo()
34 {
35         while (!undoList.isEmpty())
36                 delete undoList.takeFirst();
37 }
38
39 /**
40  * @return Number of Cycles that can be undone.
41  */
42 int Undo::countUndoCycles()
43 {
44         DEBUG->print("Undo::countUndoCycles");
45
46         return undoPointer + 1;
47 }
48
49 /**
50  * @return Number of Cycles that can be redone.
51  */
52 int Undo::countRedoCycles()
53 {
54         DEBUG->print("Undo::countRedoCycles");
55
56         return (int)undoList.count() - 1 - undoPointer;
57 }
58
59 /**
60  * Adds an Undo Cycle at the current position in the list.
61  * All Cycles after the new one are removed and the Undoabels
62  * on them deleted.
63  */
64 void Undo::addUndoCycle(UndoCycle * i)
65 {
66         DEBUG->print("Undo::addUndoCycle");
67
68         undoList.insert(++undoPointer, i);
69
70         DEBUG->print("Undo::addUndoCycle: ok");
71 }
72
73 /**
74  * Starts a new cycle for one undo step. Every undoable that is
75  * added after calling this method goes into this cycle.
76  */
77 void Undo::startUndoCycle()
78 {
79         DEBUG->print("Undo::startUndoCycle");
80
81         // definitely delete Undo Cycles and all Undoables in them
82         //   that cannot be redone now:
83         while ((int)undoList.count() > undoPointer + 1 && (int)undoList.count() > 0)
84         {
85                 UndoCycle * l = undoList.last();
86
87                 if (l != NULL)
88                 {
89                         Undoable * u = NULL;
90                         bool done = false;
91
92                         do
93                         {
94                                 u = l->getFirstUndoable();
95
96                                 if (u != NULL)
97                                 {
98                                         // Remove the pointer from _all_ cycles:
99 //                                      for(UndoCycle * l2=undoList.first(); l2!=NULL; l2=undoList.next())
100 //                                              l2->removeUndoable(u);
101                                         for(int i=0; i<undoList.size(); i++)
102                                                 undoList[i]->removeUndoable(u);
103
104                                         // Delete the Undoable for good:
105                                         if (u->isUndone())
106                                         {
107                                                 removeUndoable(u);
108                                         }
109                                 }
110                                 else
111                                 {
112                                         done = true;
113                                 }
114                         }
115                         while(!done);
116                 }
117
118                 // Remove obsolete undo cycles:
119                 undoList.removeLast();
120         }
121
122         currentCycle = new UndoCycle();
123 }
124
125 /**
126  * Adds an undoable to the current undo cycle.
127  */
128 void Undo::addUndoable(Undoable * u)
129 {
130         DEBUG->print("Undo::addUndoable");
131
132         if (currentCycle != NULL)
133         {
134                 currentCycle->addUndoable(u);
135         }
136         else
137         {
138                 DEBUG->print(Debug::D_WARNING, "Undo::addUndoable(): No undo cycle active.");
139         }
140 }
141
142 /**
143  * Ends the current undo cycle.
144  */
145 void Undo::endUndoCycle()
146 {
147         addUndoCycle(currentCycle);
148         currentCycle = NULL;
149 }
150
151 /**
152  * Undoes the last undo cycle.
153  */
154 void Undo::undo()
155 {
156         DEBUG->print("Undo::undo");
157
158         if (undoPointer >= 0)
159         {
160                 UndoCycle * i = undoList.at(undoPointer);
161
162                 if (i != NULL)
163                 {
164 //                      for(Undoable * u=i->undoables.first(); u!=NULL; u=i->undoables.next())
165 //                              u->changeUndoState();
166                         for(int j=0; j<i->undoables.size(); j++)
167                                 i->undoables[j]->changeUndoState();
168
169                         undoPointer--;
170                 }
171         }
172 }
173
174 /**
175  * Redoes the undo cycle which was at last undone.
176  */
177 void Undo::redo()
178 {
179         DEBUG->print("Undo::redo");
180
181         if (undoPointer + 1 < (int)undoList.count())
182         {
183                 undoPointer++;
184                 UndoCycle * i = undoList.at(undoPointer);
185
186                 if (i != NULL)
187                 {
188 //            for(Undoable * u=i->undoables.first(); u!=NULL; u=i->undoables.next())
189 //                u->changeUndoState();
190                         for(int j=0; j<i->undoables.size(); j++)
191                                 i->undoables[j]->changeUndoState();
192                 }
193         }
194 }
195
196 /**
197  * @return The undo item that is next if we're about to undo
198  * or NULL.
199  */
200 UndoCycle * Undo::getUndoCycle()
201 {
202         UndoCycle * ret = NULL;
203
204         DEBUG->print("Undo::getUndoCycle");
205
206         if (undoPointer >= 0 && undoPointer < (int)undoList.count())
207                 ret = undoList.at(undoPointer);
208
209         DEBUG->print("Undo::getUndoCycle: OK");
210
211         return ret;
212 }
213
214 /**
215  * @return The redo item that is next if we're about to redo
216  * or NULL.
217  */
218 UndoCycle * Undo::getRedoCycle()
219 {
220         DEBUG->print("Undo::getRedoCycle");
221
222         if (undoPointer + 1 >= 0 && undoPointer + 1 < (int)undoList.count())
223                 return undoList.at(undoPointer + 1);
224
225         return NULL;
226 }
227
228 /**
229  * Dumps the undo list to stdout.
230  */
231 std::ostream & operator<<(std::ostream & os, Undo & l)
232 {
233         os << "Undo List: " <<  "\n";
234         os << " Pointer is at: " << l.undoPointer << "\n";
235
236 //      for(UndoCycle * i=l.undoList.first(); i!=NULL; i=l.undoList.next())
237         for(int j=0; j<l.undoList.size(); j++)
238         {
239                 UndoCycle * i = l.undoList[j];
240
241 //              if (l.undoList.at() == l.undoPointer)
242                 if (j == l.undoPointer)
243                         os << " -->";
244                 else
245                         os << "    ";
246
247                 os << *i << "\n";
248         }
249
250         return os;
251 }
252
253 //huh? how is this getting defined???
254 #undef RS_TEST
255 #ifdef RS_TEST
256 /**
257  * Testing Undoables, Undo Cycles and the Undo container.
258  */
259 /*static*/ bool Undo::test()
260 {
261         int i, k;
262         UndoStub undo;
263         //UndoCycle * c1;
264         Undoable * u1;
265
266         std::cout << "Testing Undo\n";
267         std::cout << "  Adding 500 cycles..";
268
269         // Add 500 Undo Cycles with i Undoables in every Cycle
270         for(i=1; i<=500; ++i)
271         {
272                 //c1 = new UndoCycle();
273                 undo.startUndoCycle();
274
275                 for(k=1; k<=i; ++k)
276                 {
277                         u1 = new Undoable();
278                         //c1->
279                         undo.addUndoable(u1);
280                 }
281
282                 //undo.addUndoCycle(c1);
283                 undo.endUndoCycle();
284         }
285
286         std::cout << "OK\n";
287
288         assert(undo.countUndoCycles() == 500);
289         assert(undo.countRedoCycles() == 0);
290
291         std::cout << "  Undo 500 cycles..";
292         // Undo all 500 cycles
293
294         for(i=1; i<=500; ++i)
295                 undo.undo();
296
297         std::cout << "OK\n";
298
299         assert(undo.countUndoCycles() == 0);
300         assert(undo.countRedoCycles() == 500);
301
302         std::cout << "  Redo 500 cycles..";
303         // Redo all 500 cycles
304
305         for(i=1; i<=500; ++i)
306                 undo.redo();
307
308         std::cout << "OK\n";
309
310         assert(undo.countUndoCycles() == 500);
311         assert(undo.countRedoCycles() == 0);
312
313         std::cout << "  Undo 250 cycles..";
314         // Undo all 500 cycles
315
316         for(i=1; i<=250; ++i)
317                 undo.undo();
318
319         std::cout << "OK\n";
320
321         assert(undo.countUndoCycles() == 250);
322         assert(undo.countRedoCycles() == 250);
323
324         std::cout << "  Adding 10 cycles..";
325
326         for(i=1; i<=10; ++i)
327         {
328                 //c1 = new UndoCycle();
329                 undo.startUndoCycle();
330
331                 for(k=1; k<=10; ++k)
332                 {
333                         u1 = new Undoable();
334                         //c1->addUndoable(u1);
335                         undo.addUndoable(u1);
336                 }
337
338                 //undo.addUndoCycle(c1);
339                 undo.endUndoCycle();
340         }
341
342         std::cout << "OK\n";
343
344         assert(undo.countUndoCycles() == 260);
345         assert(undo.countRedoCycles() == 0);
346
347         std::cout << "  Undo 5 cycles..";
348
349         for(i=1; i<=5; ++i)
350                 undo.undo();
351
352         std::cout << "OK\n";
353
354         assert(undo.countUndoCycles() == 255);
355         assert(undo.countRedoCycles() == 5);
356
357         std::cout << "  Redo 5 cycles..";
358
359         for(i=1; i<=5; ++i)
360                 undo.redo();
361
362         std::cout << "OK\n";
363
364         assert(undo.countUndoCycles() == 260);
365         assert(undo.countRedoCycles() == 0);
366
367         std::cout << "  Undo 15 cycles..";
368
369         for(i=1; i<=15; ++i)
370                 undo.undo();
371
372         std::cout << "OK\n";
373
374         assert(undo.countUndoCycles() == 245);
375         assert(undo.countRedoCycles() == 15);
376
377         std::cout << "  Adding 1 cycle..";
378
379         for(i=1; i<=1; ++i)
380         {
381                 //c1 = new UndoCycle();
382                 undo.startUndoCycle();
383
384                 for(k=1; k<=10; ++k)
385                 {
386                         u1 = new Undoable();
387                         //c1->addUndoable(u1);
388                         undo.addUndoable(u1);
389                 }
390
391                 //undo.addUndoCycle(c1);
392                 undo.endUndoCycle();
393         }
394
395         std::cout << "OK\n";
396
397         assert(undo.countUndoCycles() == 246);
398         assert(undo.countRedoCycles() == 0);
399
400         return true;
401 }
402 #endif