2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
42 <-g_undolist---------g_lastundo> <---map data---> <-g_lastredo---------g_redolist->
45 undo/redo on the world_entity is special, only the epair changes are remembered
46 and the world entity never gets deleted.
48 FIXME: maybe reset the Undo system at map load
49 maybe also reset the entityId at map load
54 double time; //time operation was performed
55 int id; //every undo has an unique id
56 int done; //true when undo is build
57 char *operation; //name of the operation
58 brush_t brushlist; //deleted brushes
59 entity_t entitylist; //deleted entities
60 struct undo_s *prev, *next; //next and prev undo in list
63 undo_t *g_undolist; //first undo in the list
64 undo_t *g_lastundo; //last undo in the list
65 undo_t *g_redolist; //first redo in the list
66 undo_t *g_lastredo; //last undo in list
67 int g_undoMaxSize = 64; //maximum number of undos
68 int g_undoSize = 0; //number of undos in the list
69 int g_undoMaxMemorySize = 2*1024*1024; //maximum undo memory (default 2 MB)
70 int g_undoMemorySize = 0; //memory size of undo buffer
71 int g_undoId = 1; //current undo ID (zero is invalid id)
72 int g_redoId = 1; //current redo ID (zero is invalid id)
80 int Undo_MemorySize(void)
89 for (undo = g_undolist; undo; undo = undo->next)
91 for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pBrush->next)
93 size += Brush_MemorySize(pBrush);
95 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pEntity->next)
97 size += Entity_MemorySize(pEntity);
99 size += sizeof(undo_t);
103 return g_undoMemorySize;
111 void Undo_ClearRedo(void)
113 undo_t *redo, *nextredo;
114 brush_t *pBrush, *pNextBrush;
115 entity_t *pEntity, *pNextEntity;
117 for (redo = g_redolist; redo; redo = nextredo)
119 nextredo = redo->next;
120 for (pBrush = redo->brushlist.next ; pBrush != NULL && pBrush != &redo->brushlist ; pBrush = pNextBrush)
122 pNextBrush = pBrush->next;
125 for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = pNextEntity)
127 pNextEntity = pEntity->next;
128 Entity_Free(pEntity);
141 Clears the undo buffer.
144 void Undo_Clear(void)
146 undo_t *undo, *nextundo;
147 brush_t *pBrush, *pNextBrush;
148 entity_t *pEntity, *pNextEntity;
151 for (undo = g_undolist; undo; undo = nextundo)
153 nextundo = undo->next;
154 for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)
156 pNextBrush = pBrush->next;
157 g_undoMemorySize -= Brush_MemorySize(pBrush);
160 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)
162 pNextEntity = pEntity->next;
163 g_undoMemorySize -= Entity_MemorySize(pEntity);
164 Entity_Free(pEntity);
166 g_undoMemorySize -= sizeof(undo_t);
172 g_undoMemorySize = 0;
181 void Undo_SetMaxSize(int size)
184 if (size < 1) g_undoMaxSize = 1;
185 else g_undoMaxSize = size;
193 int Undo_GetMaxSize(void)
195 return g_undoMaxSize;
200 Undo_SetMaxMemorySize
203 void Undo_SetMaxMemorySize(int size)
206 if (size < 1024) g_undoMaxMemorySize = 1024;
207 else g_undoMaxMemorySize = size;
212 Undo_GetMaxMemorySize
215 int Undo_GetMaxMemorySize(void)
217 return g_undoMaxMemorySize;
225 void Undo_FreeFirstUndo(void)
228 brush_t *pBrush, *pNextBrush;
229 entity_t *pEntity, *pNextEntity;
231 //remove the oldest undo from the undo buffer
233 g_undolist = g_undolist->next;
234 g_undolist->prev = NULL;
236 for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)
238 pNextBrush = pBrush->next;
239 g_undoMemorySize -= Brush_MemorySize(pBrush);
242 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)
244 pNextEntity = pEntity->next;
245 g_undoMemorySize -= Entity_MemorySize(pEntity);
246 Entity_Free(pEntity);
248 g_undoMemorySize -= sizeof(undo_t);
258 void Undo_GeneralStart(char *operation)
267 if (!g_lastundo->done)
269 common->Printf("Undo_Start: WARNING last undo not finished.\n");
273 undo = (undo_t *) Mem_ClearedAlloc(sizeof(undo_t));
275 memset(undo, 0, sizeof(undo_t));
276 undo->brushlist.next = &undo->brushlist;
277 undo->brushlist.prev = &undo->brushlist;
278 undo->entitylist.next = &undo->entitylist;
279 undo->entitylist.prev = &undo->entitylist;
280 if (g_lastundo) g_lastundo->next = undo;
281 else g_undolist = undo;
282 undo->prev = g_lastundo;
286 undo->time = Sys_DoubleTime();
288 if (g_undoId > g_undoMaxSize * 2) g_undoId = 1;
289 if (g_undoId <= 0) g_undoId = 1;
290 undo->id = g_undoId++;
292 undo->operation = operation;
293 //reset the undo IDs of all brushes using the new ID
294 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next)
296 if (pBrush->undoId == undo->id)
301 for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
303 if (pBrush->undoId == undo->id)
308 //reset the undo IDs of all entities using thew new ID
309 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
311 if (pEntity->undoId == undo->id)
316 g_undoMemorySize += sizeof(undo_t);
318 //undo buffer is bound to a max
319 if (g_undoSize > g_undoMaxSize)
321 Undo_FreeFirstUndo();
330 int Undo_BrushInUndo(undo_t *undo, brush_t *brush)
334 for (b = undo->brushlist.next; b != &undo->brushlist; b = b->next)
336 if (b == brush) return true;
346 int Undo_EntityInUndo(undo_t *undo, entity_t *ent)
350 for (e = undo->entitylist.next; e != &undo->entitylist; e = e->next)
352 if (e == ent) return true;
362 void Undo_Start(char *operation)
365 Undo_GeneralStart(operation);
373 void Undo_AddBrush(brush_t *pBrush)
377 Sys_Status("Undo_AddBrushList: no last undo.\n");
380 if (g_lastundo->entitylist.next != &g_lastundo->entitylist)
382 Sys_Status("Undo_AddBrushList: WARNING adding brushes after entity.\n");
384 //if the brush is already in the undo
385 if (Undo_BrushInUndo(g_lastundo, pBrush))
388 brush_t* pClone = Brush_FullClone(pBrush);
389 //save the ID of the owner entity
390 pClone->ownerId = pBrush->owner->entityId;
392 if (pBrush->owner && !(pBrush->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN)) {
393 Undo_AddEntity(pBrush->owner);
396 //save the old undo ID for previous undos
397 pClone->undoId = pBrush->undoId;
398 Brush_AddToList (pClone, &g_lastundo->brushlist);
400 g_undoMemorySize += Brush_MemorySize(pClone);
408 void Undo_AddBrushList(brush_t *brushlist)
414 Sys_Status("Undo_AddBrushList: no last undo.\n");
417 if (g_lastundo->entitylist.next != &g_lastundo->entitylist)
419 Sys_Status("Undo_AddBrushList: WARNING adding brushes after entity.\n");
421 //copy the brushes to the undo
422 for (pBrush = brushlist->next ; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)
424 //if the brush is already in the undo
425 if (Undo_BrushInUndo(g_lastundo, pBrush))
428 brush_t* pClone = Brush_FullClone(pBrush);
429 //save the ID of the owner entity
430 pClone->ownerId = pBrush->owner->entityId;
431 //save the old undo ID from previous undos
432 pClone->undoId = pBrush->undoId;
434 if ( pBrush->owner && pBrush->owner != world_entity ) {
435 Undo_AddEntity(pBrush->owner);
439 Brush_AddToList (pClone, &g_lastundo->brushlist);
441 g_undoMemorySize += Brush_MemorySize(pClone);
450 void Undo_EndBrush(brush_t *pBrush)
454 //Sys_Status("Undo_End: no last undo.\n");
457 if (g_lastundo->done)
459 //Sys_Status("Undo_End: last undo already finished.\n");
462 pBrush->undoId = g_lastundo->id;
470 void Undo_EndBrushList(brush_t *brushlist)
474 //Sys_Status("Undo_End: no last undo.\n");
477 if (g_lastundo->done)
479 //Sys_Status("Undo_End: last undo already finished.\n");
482 for (brush_t* pBrush = brushlist->next; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)
484 pBrush->undoId = g_lastundo->id;
493 void Undo_AddEntity(entity_t *entity)
499 Sys_Status("Undo_AddEntity: no last undo.\n");
502 //if the entity is already in the undo
503 if (Undo_EntityInUndo(g_lastundo, entity))
506 pClone = Entity_Clone(entity);
507 //NOTE: Entity_Clone adds the entity to the entity list
508 // so we remove it from that list here
509 Entity_RemoveFromList(pClone);
510 //save the old undo ID for previous undos
511 pClone->undoId = entity->undoId;
512 //save the entity ID (we need a full clone)
513 pClone->entityId = entity->entityId;
515 Entity_AddToList(pClone, &g_lastundo->entitylist);
517 g_undoMemorySize += Entity_MemorySize(pClone);
525 void Undo_EndEntity(entity_t *entity)
529 //Sys_Status("Undo_End: no last undo.\n");
532 if (g_lastundo->done)
534 //Sys_Status("Undo_End: last undo already finished.\n");
537 if (entity == world_entity)
539 //Sys_Status("Undo_AddEntity: undo on world entity.\n");
540 //NOTE: we never delete the world entity when undoing an operation
541 // we only transfer the epairs
544 entity->undoId = g_lastundo->id;
556 //Sys_Status("Undo_End: no last undo.\n");
559 if (g_lastundo->done)
561 //Sys_Status("Undo_End: last undo already finished.\n");
564 g_lastundo->done = true;
566 //undo memory size is bound to a max
567 while (g_undoMemorySize > g_undoMaxMemorySize)
569 //always keep one undo
570 if (g_undolist == g_lastundo) break;
571 Undo_FreeFirstUndo();
574 //Sys_Status("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);
585 brush_t *pBrush, *pNextBrush;
586 entity_t *pEntity, *pNextEntity, *pUndoEntity;
590 Sys_Status("Nothing left to undo.\n");
593 if (!g_lastundo->done)
595 Sys_Status("Undo_Undo: WARNING: last undo not yet finished!\n");
599 if (g_lastundo->prev) g_lastundo->prev->next = NULL;
600 else g_undolist = NULL;
601 g_lastundo = g_lastundo->prev;
603 //allocate a new redo
604 redo = (undo_t *) Mem_ClearedAlloc(sizeof(undo_t));
606 memset(redo, 0, sizeof(undo_t));
607 redo->brushlist.next = &redo->brushlist;
608 redo->brushlist.prev = &redo->brushlist;
609 redo->entitylist.next = &redo->entitylist;
610 redo->entitylist.prev = &redo->entitylist;
611 if (g_lastredo) g_lastredo->next = redo;
612 else g_redolist = redo;
613 redo->prev = g_lastredo;
616 redo->time = Sys_DoubleTime();
617 redo->id = g_redoId++;
619 redo->operation = undo->operation;
621 //reset the redo IDs of all brushes using the new ID
622 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next)
624 if (pBrush->redoId == redo->id)
629 for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
631 if (pBrush->redoId == redo->id)
636 //reset the redo IDs of all entities using thew new ID
637 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
639 if (pEntity->redoId == redo->id)
645 // remove current selection
647 // move "created" brushes to the redo
648 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush=pNextBrush)
650 pNextBrush = pBrush->next;
651 if (pBrush->undoId == undo->id)
653 //Brush_Free(pBrush);
654 //move the brush to the redo
655 Brush_RemoveFromList(pBrush);
656 Brush_AddToList(pBrush, &redo->brushlist);
657 //make sure the ID of the owner is stored
658 pBrush->ownerId = pBrush->owner->entityId;
659 //unlink the brush from the owner entity
660 Entity_UnlinkBrush(pBrush);
663 // move "created" entities to the redo
664 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
666 pNextEntity = pEntity->next;
667 if (pEntity->undoId == undo->id)
669 // check if this entity is in the undo
670 for (pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next)
672 // move brushes to the undo entity
673 if (pUndoEntity->entityId == pEntity->entityId)
675 pUndoEntity->brushes.next = pEntity->brushes.next;
676 pUndoEntity->brushes.prev = pEntity->brushes.prev;
677 pEntity->brushes.next = &pEntity->brushes;
678 pEntity->brushes.prev = &pEntity->brushes;
682 //Entity_Free(pEntity);
683 //move the entity to the redo
684 Entity_RemoveFromList(pEntity);
685 Entity_AddToList(pEntity, &redo->entitylist);
688 // add the undo entities back into the entity list
689 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = undo->entitylist.next)
691 g_undoMemorySize -= Entity_MemorySize(pEntity);
692 //if this is the world entity
693 if (pEntity->entityId == world_entity->entityId)
695 //free the epairs of the world entity
696 Entity_FreeEpairs(world_entity);
697 //set back the original epairs
698 world_entity->epairs = pEntity->epairs;
699 //free the world_entity clone that stored the epairs
700 Entity_Free(pEntity);
704 Entity_RemoveFromList(pEntity);
705 Entity_AddToList(pEntity, &entities);
706 pEntity->redoId = redo->id;
709 // add the undo brushes back into the selected brushes
710 for (pBrush = undo->brushlist.next; pBrush != NULL && pBrush != &undo->brushlist; pBrush = undo->brushlist.next)
712 g_undoMemorySize -= Brush_MemorySize(pBrush);
713 Brush_RemoveFromList(pBrush);
714 Brush_AddToList(pBrush, &active_brushes);
715 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
717 if (pEntity->entityId == pBrush->ownerId)
719 Entity_LinkBrush(pEntity, pBrush);
723 //if the brush is not linked then it should be linked into the world entity
724 if (pEntity == NULL || pEntity == &entities)
726 Entity_LinkBrush(world_entity, pBrush);
729 //Brush_Build(pBrush);
730 Select_Brush(pBrush);
731 pBrush->redoId = redo->id;
734 common->Printf("%s undone.\n", undo->operation);
736 g_undoMemorySize -= sizeof(undo_t);
740 if (g_undoId <= 0) g_undoId = 2 * g_undoMaxSize;
745 for (b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next) {
747 Brush_Build( b, true, false, false );
749 for (b = selected_brushes.next ; b != NULL && b != &selected_brushes ; b=next) {
751 Brush_Build( b, true, false, false );
755 g_bScreenUpdates = true;
756 Sys_UpdateWindows(W_ALL);
767 brush_t *pBrush, *pNextBrush;
768 entity_t *pEntity, *pNextEntity, *pRedoEntity;
772 Sys_Status("Nothing left to redo.\n");
777 if (!g_lastundo->done)
779 Sys_Status("WARNING: last undo not finished.\n");
784 if (g_lastredo->prev) g_lastredo->prev->next = NULL;
785 else g_redolist = NULL;
786 g_lastredo = g_lastredo->prev;
788 Undo_GeneralStart(redo->operation);
789 // remove current selection
791 // move "created" brushes back to the last undo
792 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush)
794 pNextBrush = pBrush->next;
795 if (pBrush->redoId == redo->id)
797 //move the brush to the undo
798 Brush_RemoveFromList(pBrush);
799 Brush_AddToList(pBrush, &g_lastundo->brushlist);
800 g_undoMemorySize += Brush_MemorySize(pBrush);
801 pBrush->ownerId = pBrush->owner->entityId;
802 Entity_UnlinkBrush(pBrush);
805 // move "created" entities back to the last undo
806 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
808 pNextEntity = pEntity->next;
809 if (pEntity->redoId == redo->id)
811 // check if this entity is in the redo
812 for (pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next)
814 // move brushes to the redo entity
815 if (pRedoEntity->entityId == pEntity->entityId)
817 pRedoEntity->brushes.next = pEntity->brushes.next;
818 pRedoEntity->brushes.prev = pEntity->brushes.prev;
819 pEntity->brushes.next = &pEntity->brushes;
820 pEntity->brushes.prev = &pEntity->brushes;
824 //Entity_Free(pEntity);
825 //move the entity to the redo
826 Entity_RemoveFromList(pEntity);
827 Entity_AddToList(pEntity, &g_lastundo->entitylist);
828 g_undoMemorySize += Entity_MemorySize(pEntity);
831 // add the undo entities back into the entity list
832 for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = redo->entitylist.next)
834 //if this is the world entity
835 if (pEntity->entityId == world_entity->entityId)
837 //free the epairs of the world entity
838 Entity_FreeEpairs(world_entity);
839 //set back the original epairs
840 world_entity->epairs = pEntity->epairs;
841 //free the world_entity clone that stored the epairs
842 Entity_Free(pEntity);
846 Entity_RemoveFromList(pEntity);
847 Entity_AddToList(pEntity, &entities);
850 // add the redo brushes back into the selected brushes
851 for (pBrush = redo->brushlist.next; pBrush != NULL && pBrush != &redo->brushlist; pBrush = redo->brushlist.next)
853 Brush_RemoveFromList(pBrush);
854 Brush_AddToList(pBrush, &active_brushes);
855 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
857 if (pEntity->entityId == pBrush->ownerId)
859 Entity_LinkBrush(pEntity, pBrush);
863 //if the brush is not linked then it should be linked into the world entity
864 if (pEntity == NULL || pEntity == &entities)
866 Entity_LinkBrush(world_entity, pBrush);
869 //Brush_Build(pBrush);
870 Select_Brush(pBrush);
875 common->Printf("%s redone.\n", redo->operation);
881 g_bScreenUpdates = true;
882 Sys_UpdateWindows(W_ALL);
890 int Undo_RedoAvailable(void)
892 if (g_lastredo) return true;
901 int Undo_UndoAvailable(void)
905 if (g_lastundo->done)