2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 <-g_undolist---------g_lastundo> <---map data---> <-g_lastredo---------g_redolist->
33 undo/redo on the world_entity is special, only the epair changes are remembered
34 and the world entity never gets deleted.
36 FIXME: maybe reset the Undo system at map load
37 maybe also reset the entityId at map load
44 double time; //time operation was performed
45 int id; //every undo has an unique id
46 int done; //true when undo is build
47 char *operation; //name of the operation
48 brush_t brushlist; //deleted brushes
49 entity_t entitylist; //deleted entities
50 struct undo_s *prev, *next; //next and prev undo in list
53 undo_t *g_undolist; //first undo in the list
54 undo_t *g_lastundo; //last undo in the list
55 undo_t *g_redolist; //first redo in the list
56 undo_t *g_lastredo; //last undo in list
57 int g_undoMaxSize = 64; //maximum number of undos
58 int g_undoSize = 0; //number of undos in the list
59 int g_undoMaxMemorySize = 2*1024*1024; //maximum undo memory (default 2 MB)
60 int g_undoMemorySize = 0; //memory size of undo buffer
61 int g_undoId = 1; //current undo ID (zero is invalid id)
62 int g_redoId = 1; //current redo ID (zero is invalid id)
69 int Undo_MemorySize(void)
71 return g_undoMemorySize;
79 void Undo_ClearRedo(void)
81 undo_t *redo, *nextredo;
82 brush_t *pBrush, *pNextBrush;
83 entity_t *pEntity, *pNextEntity;
85 for (redo = g_redolist; redo; redo = nextredo)
87 nextredo = redo->next;
88 for (pBrush = redo->brushlist.next ; pBrush != NULL && pBrush != &redo->brushlist ; pBrush = pNextBrush)
90 pNextBrush = pBrush->next;
93 for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = pNextEntity)
95 pNextEntity = pEntity->next;
109 Clears the undo buffer.
112 void Undo_Clear(void)
114 undo_t *undo, *nextundo;
115 brush_t *pBrush, *pNextBrush;
116 entity_t *pEntity, *pNextEntity;
119 for (undo = g_undolist; undo; undo = nextundo)
121 nextundo = undo->next;
122 for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)
124 pNextBrush = pBrush->next;
125 g_undoMemorySize -= Brush_MemorySize(pBrush);
128 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)
130 pNextEntity = pEntity->next;
131 g_undoMemorySize -= Entity_MemorySize(pEntity);
132 Entity_Free(pEntity);
134 g_undoMemorySize -= sizeof(undo_t);
140 g_undoMemorySize = 0;
149 void Undo_SetMaxSize(int size)
152 if (size < 1) g_undoMaxSize = 1;
153 else g_undoMaxSize = size;
161 int Undo_GetMaxSize(void)
163 return g_undoMaxSize;
168 Undo_SetMaxMemorySize
171 void Undo_SetMaxMemorySize(int size)
174 if (size < 1024) g_undoMaxMemorySize = 1024;
175 else g_undoMaxMemorySize = size;
180 Undo_GetMaxMemorySize
183 int Undo_GetMaxMemorySize(void)
185 return g_undoMaxMemorySize;
193 void Undo_FreeFirstUndo(void)
196 brush_t *pBrush, *pNextBrush;
197 entity_t *pEntity, *pNextEntity;
199 //remove the oldest undo from the undo buffer
201 g_undolist = g_undolist->next;
202 g_undolist->prev = NULL;
204 for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)
206 pNextBrush = pBrush->next;
207 g_undoMemorySize -= Brush_MemorySize(pBrush);
210 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)
212 pNextEntity = pEntity->next;
213 g_undoMemorySize -= Entity_MemorySize(pEntity);
214 Entity_Free(pEntity);
216 g_undoMemorySize -= sizeof(undo_t);
226 void Undo_GeneralStart(char *operation)
235 if (!g_lastundo->done)
237 Sys_Printf("Undo_Start: WARNING last undo not finished.\n");
241 undo = (undo_t *) malloc(sizeof(undo_t));
244 memset(undo, 0, sizeof(undo_t));
245 undo->brushlist.next = &undo->brushlist;
246 undo->brushlist.prev = &undo->brushlist;
247 undo->entitylist.next = &undo->entitylist;
248 undo->entitylist.prev = &undo->entitylist;
250 g_lastundo->next = undo;
253 undo->prev = g_lastundo;
257 undo->time = Sys_DoubleTime();
259 if (g_undoId > g_undoMaxSize * 2) g_undoId = 1;
260 if (g_undoId <= 0) g_undoId = 1;
261 undo->id = g_undoId++;
263 undo->operation = operation;
264 //reset the undo IDs of all brushes using the new ID
265 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next)
267 if (pBrush->undoId == undo->id)
272 for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
274 if (pBrush->undoId == undo->id)
279 //reset the undo IDs of all entities using thew new ID
280 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
282 if (pEntity->undoId == undo->id)
287 g_undoMemorySize += sizeof(undo_t);
289 //undo buffer is bound to a max
290 if (g_undoSize > g_undoMaxSize)
292 Undo_FreeFirstUndo();
301 int Undo_BrushInUndo(undo_t *undo, brush_t *brush)
305 for (b = undo->brushlist.next; b != &undo->brushlist; b = b->next)
307 // Arnout: NOTE - can't do a pointer compare as the brushes get cloned into the undo brushlist, and not just referenced from it
308 // For entities we have a unique ID, for brushes we have numberID - but brush full clone increases that anyway so it's useless right now.
309 if (b == brush) return true;
311 // Arnout: function is pointless right now, see above explanation
320 int Undo_EntityInUndo(undo_t *undo, entity_t *ent)
324 for (e = undo->entitylist.next; e != &undo->entitylist; e = e->next)
326 // Arnout: NOTE - can't do a pointer compare as the entities get cloned into the undo entitylist, and not just referenced from it
327 //if (e == ent) return true;
328 if( e->entityId == ent->entityId ) return true;
338 void Undo_Start(char *operation)
340 // spog - disable undo if undo levels = 0
341 if (g_PrefsDlg.m_nUndoLevels == 0)
344 Sys_Printf("Undo_Start: undo is disabled.\n");
350 Undo_GeneralStart(operation);
358 void Undo_AddBrush(brush_t *pBrush)
360 // spog - disable undo if undo levels = 0
361 if (g_PrefsDlg.m_nUndoLevels == 0)
364 Sys_Printf("Undo_AddBrush: undo is disabled.\n");
371 Sys_Printf("Undo_AddBrushList: no last undo.\n");
374 if (g_lastundo->entitylist.next != &g_lastundo->entitylist)
376 Sys_Printf("Undo_AddBrushList: WARNING adding brushes after entity.\n");
378 //if the brush is already in the undo
379 if (Undo_BrushInUndo(g_lastundo, pBrush))
382 brush_t* pClone = Brush_FullClone(pBrush);
383 //save the ID of the owner entity
384 pClone->ownerId = pBrush->owner->entityId;
385 //save the old undo ID for previous undos
386 pClone->undoId = pBrush->undoId;
387 Brush_AddToList (pClone, &g_lastundo->brushlist);
389 g_undoMemorySize += Brush_MemorySize(pClone);
395 TTimo: some brushes are just there for UI, and the information is somewhere else
396 for patches it's in the patchMesh_t structure, so when we clone the brush we get that information (brush_t::pPatch)
397 but: models are stored in pBrush->owner->md3Class, and owner epairs and origin parameters are important
398 so, we detect models and push the entity in the undo session (as well as it's BBox brush)
399 same for other items like weapons and ammo etc.
402 void Undo_AddBrushList(brush_t *brushlist)
404 // spog - disable undo if undo levels = 0
405 if (g_PrefsDlg.m_nUndoLevels == 0)
408 Sys_Printf("Undo_AddBrushList: undo is disabled.\n");
417 Sys_Printf("Undo_AddBrushList: no last undo.\n");
420 if (g_lastundo->entitylist.next != &g_lastundo->entitylist)
422 Sys_Printf("Undo_AddBrushList: WARNING adding brushes after entity.\n");
424 //copy the brushes to the undo
425 for (pBrush = brushlist->next ; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)
427 //if the brush is already in the undo
428 //++timo FIXME: when does this happen?
429 if (Undo_BrushInUndo(g_lastundo, pBrush))
431 // do we need to store this brush's entity in the undo?
432 // if it's a fixed size entity, the brush that reprents it is not really relevant, it's used for selecting and moving around
433 // what we want to store for undo is the owner entity, epairs and origin/angle stuff
434 //++timo FIXME: if the entity is not fixed size I don't know, so I don't do it yet
435 if (pBrush->owner->eclass->fixedsize == 1)
436 Undo_AddEntity( pBrush->owner );
438 brush_t* pClone = Brush_FullClone(pBrush);
439 // save the ID of the owner entity
440 pClone->ownerId = pBrush->owner->entityId;
441 // save the old undo ID from previous undos
442 pClone->undoId = pBrush->undoId;
443 Brush_AddToList (pClone, &g_lastundo->brushlist);
444 // track memory size used by undo
445 g_undoMemorySize += Brush_MemorySize(pClone);
454 void Undo_EndBrush(brush_t *pBrush)
456 // spog - disable undo if undo levels = 0
457 if (g_PrefsDlg.m_nUndoLevels == 0)
460 Sys_Printf("Undo_EndBrush: undo is disabled.\n");
468 //Sys_Printf("Undo_End: no last undo.\n");
471 if (g_lastundo->done)
473 //Sys_Printf("Undo_End: last undo already finished.\n");
476 pBrush->undoId = g_lastundo->id;
484 void Undo_EndBrushList(brush_t *brushlist)
486 // spog - disable undo if undo levels = 0
487 if (g_PrefsDlg.m_nUndoLevels == 0)
490 Sys_Printf("Undo_EndBrushList: undo is disabled.\n");
498 //Sys_Printf("Undo_End: no last undo.\n");
501 if (g_lastundo->done)
503 //Sys_Printf("Undo_End: last undo already finished.\n");
506 for (brush_t* pBrush = brushlist->next; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)
508 pBrush->undoId = g_lastundo->id;
517 void Undo_AddEntity(entity_t *entity)
519 // spog - disable undo if undo levels = 0
520 if (g_PrefsDlg.m_nUndoLevels == 0)
523 Sys_Printf("Undo_AddEntity: undo is disabled.\n");
533 Sys_Printf("Undo_AddEntity: no last undo.\n");
536 //if the entity is already in the undo
537 if (Undo_EntityInUndo(g_lastundo, entity))
540 pClone = Entity_Clone(entity);
541 //save the old undo ID for previous undos
542 pClone->undoId = entity->undoId;
543 //save the entity ID (we need a full clone)
544 pClone->entityId = entity->entityId;
546 Entity_AddToList(pClone, &g_lastundo->entitylist);
548 g_undoMemorySize += Entity_MemorySize(pClone);
556 void Undo_EndEntity(entity_t *entity)
558 // spog - disable undo if undo levels = 0
559 if (g_PrefsDlg.m_nUndoLevels == 0)
562 Sys_Printf("Undo_EndEntity: undo is disabled.\n");
571 Sys_Printf("Undo_End: no last undo.\n");
575 if (g_lastundo->done)
578 Sys_Printf("Undo_End: last undo already finished.\n");
582 if (entity == world_entity)
584 //Sys_Printf("Undo_AddEntity: undo on world entity.\n");
585 //NOTE: we never delete the world entity when undoing an operation
586 // we only transfer the epairs
589 entity->undoId = g_lastundo->id;
599 // spog - disable undo if undo levels = 0
600 if (g_PrefsDlg.m_nUndoLevels == 0)
603 Sys_Printf("Undo_End: undo is disabled.\n");
611 //Sys_Printf("Undo_End: no last undo.\n");
614 if (g_lastundo->done)
616 //Sys_Printf("Undo_End: last undo already finished.\n");
619 g_lastundo->done = true;
621 //undo memory size is bound to a max
622 while (g_undoMemorySize > g_undoMaxMemorySize)
624 //always keep one undo
625 if (g_undolist == g_lastundo) break;
626 Undo_FreeFirstUndo();
629 //Sys_Printf("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);
637 void Undo_Undo(boolean bSilent)
639 // spog - disable undo if undo levels = 0
640 if (g_PrefsDlg.m_nUndoLevels == 0)
642 Sys_Printf("Undo_Undo: undo is disabled.\n");
647 brush_t *pBrush, *pNextBrush;
648 entity_t *pEntity, *pNextEntity, *pUndoEntity;
652 Sys_Printf("Nothing left to undo.\n");
655 if (!g_lastundo->done)
657 Sys_Printf("Undo_Undo: WARNING: last undo not yet finished!\n");
661 if (g_lastundo->prev) g_lastundo->prev->next = NULL;
662 else g_undolist = NULL;
663 g_lastundo = g_lastundo->prev;
665 //allocate a new redo
666 redo = (undo_t *) malloc(sizeof(undo_t));
668 memset(redo, 0, sizeof(undo_t));
669 redo->brushlist.next = &redo->brushlist;
670 redo->brushlist.prev = &redo->brushlist;
671 redo->entitylist.next = &redo->entitylist;
672 redo->entitylist.prev = &redo->entitylist;
673 if (g_lastredo) g_lastredo->next = redo;
674 else g_redolist = redo;
675 redo->prev = g_lastredo;
678 redo->time = Sys_DoubleTime();
679 redo->id = g_redoId++;
681 redo->operation = undo->operation;
683 //reset the redo IDs of all brushes using the new ID
684 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next)
686 if (pBrush->redoId == redo->id)
691 for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
693 if (pBrush->redoId == redo->id)
698 //reset the redo IDs of all entities using thew new ID
699 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
701 if (pEntity->redoId == redo->id)
707 // deselect current sutff
709 // move "created" brushes to the redo
710 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush=pNextBrush)
712 pNextBrush = pBrush->next;
713 if (pBrush->undoId == undo->id)
715 //Brush_Free(pBrush);
716 //move the brush to the redo
717 Brush_RemoveFromList(pBrush);
718 Brush_AddToList(pBrush, &redo->brushlist);
719 //make sure the ID of the owner is stored
720 pBrush->ownerId = pBrush->owner->entityId;
721 //unlink the brush from the owner entity
722 Entity_UnlinkBrush(pBrush);
725 // move "created" entities to the redo
726 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
728 pNextEntity = pEntity->next;
729 if (pEntity->undoId == undo->id)
731 // check if this entity is in the undo
732 for (pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next)
734 // move brushes to the undo entity
735 if (pUndoEntity->entityId == pEntity->entityId)
737 pUndoEntity->brushes.next = pEntity->brushes.next;
738 pUndoEntity->brushes.prev = pEntity->brushes.prev;
739 pEntity->brushes.next = &pEntity->brushes;
740 pEntity->brushes.prev = &pEntity->brushes;
744 //Entity_Free(pEntity);
745 //move the entity to the redo
746 Entity_RemoveFromList(pEntity);
747 Entity_AddToList(pEntity, &redo->entitylist);
750 // add the undo entities back into the entity list
751 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = undo->entitylist.next)
753 g_undoMemorySize -= Entity_MemorySize(pEntity);
754 //if this is the world entity
755 if (pEntity->entityId == world_entity->entityId)
757 epair_t* tmp = world_entity->epairs;
758 world_entity->epairs = pEntity->epairs;
759 pEntity->epairs = tmp;
760 Entity_Free(pEntity);
764 Entity_RemoveFromList(pEntity);
765 Entity_AddToList(pEntity, &entities);
766 pEntity->redoId = redo->id;
769 // add the undo brushes back into the selected brushes
770 for (pBrush = undo->brushlist.next; pBrush != NULL && pBrush != &undo->brushlist; pBrush = undo->brushlist.next)
772 //Sys_Printf("Owner ID: %i\n",pBrush->ownerId);
773 g_undoMemorySize -= Brush_MemorySize(pBrush);
774 Brush_RemoveFromList(pBrush);
775 Brush_AddToList(pBrush, &active_brushes);
776 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) // fixes broken undo on entities
778 //Sys_Printf("Entity ID: %i\n",pEntity->entityId);
779 if (pEntity->entityId == pBrush->ownerId)
781 Entity_LinkBrush(pEntity, pBrush);
785 //if the brush is not linked then it should be linked into the world entity
786 //++timo FIXME: maybe not, maybe we've lost this entity's owner!
787 if (pEntity == NULL || pEntity == &entities)
789 Entity_LinkBrush(world_entity, pBrush);
792 //Brush_Build(pBrush);
793 Select_Brush(pBrush);
794 pBrush->redoId = redo->id;
797 Sys_Printf("%s undone.\n", undo->operation);
799 g_undoMemorySize -= sizeof(undo_t);
803 if (g_undoId <= 0) g_undoId = 2 * g_undoMaxSize;
805 g_bScreenUpdates = true;
806 UpdateSurfaceDialog();
807 Sys_UpdateWindows(W_ALL);
817 // spog - disable undo if undo levels = 0
818 if (g_PrefsDlg.m_nUndoLevels == 0)
820 Sys_Printf("Undo_Redo: undo is disabled.\n");
825 brush_t *pBrush, *pNextBrush;
826 entity_t *pEntity, *pNextEntity, *pRedoEntity;
830 Sys_Printf("Nothing left to redo.\n");
835 if (!g_lastundo->done)
837 Sys_Printf("WARNING: last undo not finished.\n");
842 if (g_lastredo->prev) g_lastredo->prev->next = NULL;
843 else g_redolist = NULL;
844 g_lastredo = g_lastredo->prev;
846 Undo_GeneralStart(redo->operation);
847 // remove current selection
849 // move "created" brushes back to the last undo
850 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush)
852 pNextBrush = pBrush->next;
853 if (pBrush->redoId == redo->id)
855 //move the brush to the undo
856 Brush_RemoveFromList(pBrush);
857 Brush_AddToList(pBrush, &g_lastundo->brushlist);
858 g_undoMemorySize += Brush_MemorySize(pBrush);
859 pBrush->ownerId = pBrush->owner->entityId;
860 Entity_UnlinkBrush(pBrush);
863 // move "created" entities back to the last undo
864 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
866 pNextEntity = pEntity->next;
867 if (pEntity->redoId == redo->id)
869 // check if this entity is in the redo
870 for (pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next)
872 // move brushes to the redo entity
873 if (pRedoEntity->entityId == pEntity->entityId)
875 pRedoEntity->brushes.next = pEntity->brushes.next;
876 pRedoEntity->brushes.prev = pEntity->brushes.prev;
877 pEntity->brushes.next = &pEntity->brushes;
878 pEntity->brushes.prev = &pEntity->brushes;
882 //Entity_Free(pEntity);
883 //move the entity to the redo
884 Entity_RemoveFromList(pEntity);
885 Entity_AddToList(pEntity, &g_lastundo->entitylist);
886 g_undoMemorySize += Entity_MemorySize(pEntity);
889 // add the undo entities back into the entity list
890 for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = redo->entitylist.next)
892 //if this is the world entity
893 if (pEntity->entityId == world_entity->entityId)
895 epair_t* tmp = world_entity->epairs;
896 world_entity->epairs = pEntity->epairs;
897 pEntity->epairs = tmp;
898 Entity_Free(pEntity);
902 Entity_RemoveFromList(pEntity);
903 Entity_AddToList(pEntity, &entities);
906 // add the redo brushes back into the selected brushes
907 for (pBrush = redo->brushlist.next; pBrush != NULL && pBrush != &redo->brushlist; pBrush = redo->brushlist.next)
909 Brush_RemoveFromList(pBrush);
910 Brush_AddToList(pBrush, &active_brushes);
911 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) // fixes broken undo on entities
913 if (pEntity->entityId == pBrush->ownerId)
915 Entity_LinkBrush(pEntity, pBrush);
919 //if the brush is not linked then it should be linked into the world entity
920 if (pEntity == NULL || pEntity == &entities)
922 Entity_LinkBrush(world_entity, pBrush);
925 //Brush_Build(pBrush);
926 Select_Brush(pBrush);
931 Sys_Printf("%s redone.\n", redo->operation);
937 g_bScreenUpdates = true;
938 UpdateSurfaceDialog();
939 Sys_UpdateWindows(W_ALL);
947 int Undo_RedoAvailable(void)
949 if (g_lastredo) return true;
953 int Undo_GetUndoId(void)
956 return g_lastundo->id;
965 int Undo_UndoAvailable(void)
969 if (g_lastundo->done)