]> Shamusworld >> Repos - guemap/blob - src/undo.cpp
Initial commit of GUEmap v3.
[guemap] / src / undo.cpp
1 //
2 // GUEmap
3 // Copyright 1997-2007 by Christopher J. Madsen
4 // (C) 2019 James Hammons
5 //
6 // GUEmap is licensed under either version 2 of the GPL, or (at your option)
7 // any later version.  See LICENSE file for details.
8 //
9 // Implementation of UndoRec and its derived classes
10 //
11
12 #include "undo.h"
13 #include "mapdoc.h"
14
15
16 //
17 // Class UndoRec:
18 //
19 // UndoRec is the virtual base class for all undo records.
20 //
21 // Member Variables:
22 //   oldModified:
23 //     The modification state of the document before the change that
24 //     this undo record applies to was made.
25 //
26
27 UndoRec::UndoRec(bool modified): oldModified(modified)
28 {
29 }
30
31
32 UndoRec::~UndoRec()
33 {
34 }
35
36
37 //
38 // Get the type of undo record:
39 //
40 // Returns:
41 //   The name of this undo operation (The XXX in "Undo XXX")
42 //
43 // char * UndoRec::getName() const
44 //
45 //--------------------------------------------------------------------
46 // Undo:
47 //
48 // Undo the change that this undo record applies to.  This is the main entry
49 // point; it calls undoChange to actually do the work.
50 //
51 // If the document was not modified before the original change, and is
52 // modified now, then it is marked as unmodified.  Otherwise, it is marked as
53 // modified.  This should cause the modification flag to agree with the actual
54 // state of the document in memory vs. the file on disk.
55 //
56 // Input:
57 //   doc:  The MapDoc to change
58 //
59 // Returns:
60 //   A pointer to the "redo" record
61 //   NULL if the operation cannot be redone
62 //
63 // Note:
64 //   undo is a one time function.  After calling undo, you cannot call any
65 //   other member functions; you must delete the object immediately.
66 //
67 UndoRec * UndoRec::undo(MapDoc & doc)
68 {
69         bool newModified = doc.isDirty;
70
71         UndoRec * redo = undoChange(doc);
72
73         doc.isDirty = (!oldModified && newModified ? false : true);
74
75         if (redo)
76                 redo->oldModified = newModified;
77
78         return redo;
79 }
80
81
82 //
83 // Class UndoAdd:
84 //
85 // UndoAdd undoes the addition of 1 or more rooms and/or edges.  Since
86 // edges are always added at the end of the list, we only need to know
87 // how many were added.
88 //
89 // Member Variables:
90 //   edges:    The number of edges that were added
91 //   pageNums: The numbers of the pages that were added
92 //   roomNums: The numbers of the rooms that were added
93 //--------------------------------------------------------------------
94 // Constructor for the redo record for UndoDelete:
95 //
96 // Input:
97 //   modified:  The modification state of the document
98 //   edgeCount: The number of edges being added
99 //   rns:       The numbers of the rooms being added
100 //   pns:       The numbers of the pages being added
101 //
102 UndoAdd::UndoAdd(bool modified, int edgeCount, RoomNumVec & rns, PageNumVec & pns): UndoRec(modified), edges(edgeCount)
103 {
104         pageNums.swap(pns);
105         roomNums.swap(rns);
106 }
107
108
109 //
110 // Constructor for a one-room addition:
111 //
112 // Input:
113 //   modified:  The modification state of the document
114 //   roomNum:   The number of the room being added
115 //   edgeCount: The number of edges being added
116 //
117 UndoAdd::UndoAdd(bool modified, RoomNum roomNum, int edgeCount): UndoRec(modified), edges(edgeCount)
118 {
119         roomNums.push_back(roomNum);
120 }
121
122
123 //
124 // Constructor for a one-page addition:
125 //
126 // Input:
127 //   modified: The modification state of the document
128 //   pageNum:  The number of the page being added
129 //
130 UndoAdd::UndoAdd(bool modified, short pageNum): UndoRec(modified), edges(0)
131 {
132         pageNums.push_back(pageNum);
133 }
134
135
136 //
137 // Constructor for a one-edge addition:
138 //
139 // Input:
140 //   modified:   The modification state of the document
141 //
142 UndoAdd::UndoAdd(bool modified): UndoRec(modified), edges(1)
143 {
144 }
145
146
147 const char * UndoAdd::getName() const
148 {
149         return "Add";
150 }
151
152
153 //
154 // Undo the addition of rooms, edges, and/or pages:
155 //
156 UndoRec * UndoAdd::undoChange(MapDoc & doc)
157 {
158         UndoRec * redo = new UndoDelete(doc, edges, roomNums, pageNums);
159         int e = edges;
160
161         for(int i=doc.edge.size()-1; e; e--)
162                 doc.deleteEdge(i--);
163
164         RNConstItr r = roomNums.end();
165
166         while (r != roomNums.begin())
167                 doc.deleteRoom(*(--r));
168
169         PNConstItr p = pageNums.end();
170
171         while (p != pageNums.begin())
172                 doc.deletePage(*(--p));
173
174         return redo;
175 }
176
177
178 //
179 // Class UndoChangeEdge:
180 //
181 // UndoChangeEdge is used when an existing connection is changed.
182 // Since edges are changed by being deleted and re-added, it assumes
183 // that the edge is the last one in the list.  (N.B.: This is bad, but we'll roll with it for now.)
184 //
185 // Member Variables:
186 //   edge:  The original edge
187 //--------------------------------------------------------------------
188 // Constructor:
189 //
190 // Input:
191 //   modified:  The modification state of the document
192 //   theEdge:   The original edge
193 //
194 UndoChangeEdge::UndoChangeEdge(bool modified, const MapEdge & theEdge): UndoRec(modified), edge(theEdge)
195 {
196 }
197
198
199 const char * UndoChangeEdge::getName() const
200 {
201         return "Reconnection";
202 }
203
204
205 //
206 // Undo a change to a connection:
207 //
208 UndoRec * UndoChangeEdge::undoChange(MapDoc & doc)
209 {
210         const int edgeNum = doc.edge.size() - 1;
211         UndoRec * redo = new UndoChangeEdge(true, doc.edge[edgeNum]);
212
213         doc.deleteEdge(edgeNum);
214         doc.addEdge(edge);
215
216         return redo;
217 }
218
219
220 //
221 // Class UndoChanges:
222 //
223 // UndoChanges combines both UndoAdd & UndoDelete in one record.
224 //--------------------------------------------------------------------
225 // Constructor for adding one room & deleting one edge:
226 //
227 // Input:
228 //   modified:   The modification state of the document
229 //   roomNum:    The number of the room being added
230 //   edgeCount:  The number of edges being added
231 //   edge:       The edge being deleted
232 //
233 UndoChanges::UndoChanges(bool modified, RoomNum roomNum, int edgeCount, const MapEdge & edge): UndoRec(modified), add(new UndoAdd(modified, roomNum, edgeCount)), del(new UndoDelete(modified, edge))
234 {
235 }
236
237
238 //
239 // Constructor for adding one room & deleting one edge:
240 //
241 // Input:
242 //   modified:   The modification state of the document
243 //   roomNum:    The number of the room being added (-1 if no room added)
244 //   edgeCount:  The number of edges being added
245 //   edges:      The edges being deleted
246 //
247 // Output:
248 //   edges:      The vector is emptied
249 //
250 UndoChanges::UndoChanges(bool modified, int roomNum, int edgeCount, EdgeVec & edges): UndoRec(modified), del(new UndoDelete(modified, edges))
251 {
252         if (roomNum >= 0)
253                 add = new UndoAdd(modified, roomNum, edgeCount);
254         else
255                 add = new UndoAdd(modified);
256 }
257
258
259 //
260 // Constructor for removing a corner:
261 //
262 // Input:
263 //   modified:   The modification state of the document
264 //   roomNum:    The number of the room being deleted
265 //   room:       The room being deleted
266 //   edges:      The edges being deleted
267 //
268 // Output:
269 //   edges:  The vector is emptied
270 //
271 UndoChanges::UndoChanges(bool modified, RoomNum roomNum, MapRoom * room, EdgeVec & edges): UndoRec(modified), add(new UndoAdd(modified)),/* Adding 1 edge */ del(new UndoDelete(modified, edges))
272 {
273         static_cast<UndoDelete *>(del)->addRooms(1);
274         static_cast<UndoDelete *>(del)->addRoom(0, roomNum, room);
275 }
276
277
278 const char * UndoChanges::getName() const
279 {
280         return "Change";          // FIXME better name
281 }
282
283
284 UndoRec * UndoChanges::undoChange(MapDoc & doc)
285 {
286         UndoRec * redo = NULL;
287
288         UndoRec * newDel = add->undoChange(doc);
289         delete add;
290
291         add = del->undoChange(doc);
292         delete del;
293         del = newDel;
294
295         if (add && del)
296                 redo = this;
297
298         return redo;
299 }
300
301
302 //
303 // Class UndoDelete:
304 //
305 // UndoDelete undoes the deletion of 1 or more rooms and/or edges.  Edges are
306 // restored to the end of the list, but rooms regain their original numbers.
307 //
308 // Member Variables:
309 //   edges:
310 //     The edges that were deleted.
311 //     They are stored with the original room numbers.
312 //   pages:
313 //     The pages that were deleted.
314 //   pageNums:
315 //     The numbers of the pages that were deleted.
316 //   rooms:
317 //     The rooms that were deleted.
318 //   roomNums:
319 //     The numbers of the rooms that were deleted.
320 //--------------------------------------------------------------------
321 // Constructor used when deleting the selection:
322 //
323 // Builds a list of the selected rooms and all edges which connect to them.
324 //
325 // Input:
326 //   doc:              The document we're working with
327 //   numSelected:      The number of selected rooms
328 //   selectedRooms:    TRUE means the room is selected
329 //   numSelectedPages: The number of selected pages
330 //   selectedPages:    TRUE means the page is selected
331 //
332 UndoDelete::UndoDelete(const MapDoc & doc, int numSelected, const ByteVec & selectedRooms, int numSelectedPages, const ByteVec & selectedPages): UndoRec(doc.isDirty)
333 {
334         int i;
335         addRooms(numSelected);
336         EdgeConstItr edge = doc.edge.begin();
337
338         for(i=doc.edge.size()-1; i>=0; edge++, i--)
339         {
340                 if (selectedRooms[edge->room1] || selectedRooms[edge->room2])
341                         edges.push_back(*edge);
342         }
343
344         pages.resize(numSelectedPages);
345         pageNums.resize(numSelectedPages);
346         PageConstItr page = doc.page.begin();
347         PageItr p = pages.begin();
348         PNItr pn = pageNums.begin();
349
350         for(i=0; numSelectedPages; i++)
351         {
352                 if (selectedPages[i])
353                 {
354                         numSelectedPages--;
355                         *(p++) = page[i];
356                         *(pn++) = i;
357                 }
358         }
359 }
360
361
362 //
363 // Constructor used for the redo record for UndoAdd:
364 //
365 // Input:
366 //   doc:          The document we're working with
367 //   numEdges:     The number of edges being deleted
368 //   theRoomNums:  The numbers of the rooms being deleted
369 //   thePageNums:  The numbers of the pages being deleted
370 //
371 UndoDelete::UndoDelete(const MapDoc & doc, int numEdges, const RoomNumVec & theRoomNums, const PageNumVec & thePageNums): UndoRec(doc.isDirty), pageNums(thePageNums), roomNums(theRoomNums)
372 {
373         edges.insert(edges.begin(), doc.edge.end() - numEdges, doc.edge.end());
374
375         RoomConstItr  room = doc.room.getVector().begin();
376         rooms.reserve(roomNums.size());
377
378         for(RNConstItr r=roomNums.begin(); r!=roomNums.end(); ++r)
379                 rooms.append(new MapRoom(*room[*r]));
380
381         PageConstItr page = doc.page.begin();
382         pages.reserve(pageNums.size());
383
384         for(PNConstItr p=pageNums.begin(); p!=pageNums.end(); ++p)
385                 pages.push_back(page[*p]);
386 }
387
388
389 //
390 // Constructor used when deleting one edge:
391 //
392 // Input:
393 //   modified:  The modification state of the document
394 //   edge:      The edge that was deleted
395 //
396 UndoDelete::UndoDelete(bool modified, const MapEdge & edge): UndoRec(modified)
397 {
398         edges.push_back(edge);
399 }
400
401
402 void UndoDelete::addRoom(VecSize pos, RoomNum n, MapRoom * r)
403 {
404         roomNums[pos] = n;
405         rooms.getVector()[pos] = r;
406 }
407
408
409 void UndoDelete::addRooms(VecSize n)
410 {
411         rooms.resize(n);
412         roomNums.resize(n);
413 }
414
415
416 //
417 // Constructor used when deleting several edges:
418 //
419 // Input:
420 //   modified:  The modification state of the document
421 //   theEdges:  The edges that were deleted
422 //
423 // Output:
424 //   theEdges:  The vector is emptied
425 //
426 UndoDelete::UndoDelete(bool modified, EdgeVec & theEdges): UndoRec(modified)
427 {
428         edges.swap(theEdges);
429 }
430
431
432 const char * UndoDelete::getName() const
433 {
434         return "Deletion";
435 }
436
437
438 //
439 // Undo the deletion of rooms and/or edges:
440 //
441 UndoRec * UndoDelete::undoChange(MapDoc & doc)
442 {
443         ASSERT(rooms.size() == roomNums.size());
444         doc.addRooms(rooms.size());
445         RoomItr r = rooms.getVector().begin();
446
447         for(RNConstItr rn=roomNums.begin(); rn!=roomNums.end(); r++, rn++)
448         {
449                 doc.addRoom(*rn, *r);
450                 *r = NULL;
451         }
452
453         doc.addEdges(edges.size());
454
455         for(EdgeConstItr e=edges.begin(); e!=edges.end(); ++e)
456                 doc.addEdge(*e);
457
458         ASSERT(pages.size() == pageNums.size());
459         doc.addPages(pages.size());
460         PageConstItr p = pages.begin();
461
462         for(PNConstItr pn=pageNums.begin(); pn!=pageNums.end(); p++, pn++)
463                 doc.addPage(*pn, *p);
464
465         return new UndoAdd(true, edges.size(), roomNums, pageNums);
466 }
467
468
469 //
470 // Class UndoMove:
471 //
472 // UndoMove undoes room movement.
473 //
474 // Member Variables:
475 //   offset:    The amount the rooms were moved
476 //   pageNums:  The numbers of the pages that were moved
477 //   roomNums:  The numbers of the rooms that were moved
478 //--------------------------------------------------------------------
479 // Constructor used after dragging the selection:
480 //
481 // Input:
482 //   modified:          The modification state of the document
483 //   moved:             The distance the rooms were moved
484 //   numSelected:       The number of selected rooms
485 //   selectedRooms:     TRUE means the room is selected
486 //   numSelectedPages:  The number of selected pages
487 //   selectedPages:     TRUE means the page is selected
488 //
489 UndoMove::UndoMove(bool modified, const QSize & moved, int numSelected, const ByteVec & selectedRooms, int numSelectedPages, const ByteVec & selectedPages): UndoRec(modified), offset(moved)
490 {
491         int i, j;
492         pageNums.resize(numSelectedPages);
493
494         for(i=0, j=0; i<numSelectedPages; j++)
495         {
496                 if (selectedPages[j])
497                         pageNums[i++] = j;
498         }
499
500         roomNums.resize(numSelected);
501
502         for(i=0, j=0; i<numSelected; j++)
503         {
504                 if (selectedRooms[j])
505                         roomNums[i++] = j;
506         }
507 }
508
509
510 //
511 // Constructor used for the redo record:
512 //
513 // Input:
514 //   modified:  The modification state of the document
515 //   moved:     The distance the rooms were moved
516 //   rooms:     The numbers of the rooms being moved
517 //   pages:     The numbers of the pages being moved
518 //
519 // Output:
520 //   rooms & pages:  Empty
521 //
522 UndoMove::UndoMove(bool modified, const QSize & moved, RoomNumVec & rooms, PageNumVec & pages): UndoRec(modified), offset(moved)
523 {
524         pageNums.swap(pages);
525         roomNums.swap(rooms);
526 }
527
528
529 const char * UndoMove::getName() const
530 {
531         return "Move";
532 }
533
534
535 //
536 // Undo room movement
537 //
538 UndoRec * UndoMove::undoChange(MapDoc & doc)
539 {
540         offset *= -1;
541
542         for(PNConstItr p=pageNums.begin(); p!=pageNums.end(); p++)
543                 doc.movePage(*p, offset);
544
545         for(RNConstItr r=roomNums.begin(); r!=roomNums.end(); r++)
546                 doc.moveRoom(*r, offset);
547
548         return new UndoMove(false, offset, roomNums, pageNums);
549 }
550
551
552 //
553 // Class UndoPaginate:
554 //
555 // UndoPaginate undoes a repagination.
556 //
557 // Member Variables:
558 //   pages:  The old pages
559 //--------------------------------------------------------------------
560 // Constructor:
561 //
562 // This removes the pages from the document.
563 //
564 // Input:
565 //   doc:  The document we're working with
566 //
567 UndoPaginate::UndoPaginate(MapDoc & doc): UndoRec(doc.isDirty)
568 {
569         pages.swap(doc.page);
570 }
571
572
573 const PageVec & UndoPaginate::getPages() const
574 {
575         return pages;
576 }
577
578
579 const char * UndoPaginate::getName() const
580 {
581         return "Page Layout";
582 }
583
584
585 //
586 // Undo the deletion of rooms and/or edges:
587 //
588 UndoRec * UndoPaginate::undoChange(MapDoc & doc)
589 {
590         UndoRec * redo = new UndoPaginate(doc);
591         pages.swap(doc.page);
592         doc.UpdateAllViews(NULL, dupPageCount, NULL);
593
594         return redo;
595 }
596
597
598 //
599 // Class UndoRoomInfo:
600 //
601 // UndoRoomInfo undoes changes made in the Room Properties dialog.
602 // Currently, this is only the room name.
603 //
604 // Member Variables:
605 //   num:   The number of the room that was modified
606 //   room:  The room information
607 //--------------------------------------------------------------------
608 // Constructor:
609 //
610 // Input:
611 //   doc:      The document we're working with
612 //   roomNum:  The number of the room being modified
613 //
614 UndoRoomInfo::UndoRoomInfo(const MapDoc & doc, RoomNum roomNum): UndoRec(doc.isDirty), num(roomNum), room(doc.room[roomNum])
615 {
616 }
617
618
619 const char * UndoRoomInfo::getName() const
620 {
621         return "Room Information";
622 }
623
624
625 //
626 // Undo a change in room information:
627 //
628 UndoRec * UndoRoomInfo::undoChange(MapDoc & doc)
629 {
630         UndoRec * redo = new UndoRoomInfo(doc, num);
631
632         doc.setRoomName(num, room.name.c_str());
633         doc.setRoomNote(num, room.note.c_str());
634
635         return redo;
636 }
637