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