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 const 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(const 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(const 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;
509 // http://github.com/mfn/GtkRadiant/commit/ee1ef98536470d5680bd9bfecc5b5c9a62ffe9ab
510 if ( pBrush->owner->eclass->fixedsize == 1 ) {
511 pBrush->owner->undoId = pBrush->undoId;
521 void Undo_AddEntity(entity_t *entity)
523 // spog - disable undo if undo levels = 0
524 if (g_PrefsDlg.m_nUndoLevels == 0)
527 Sys_Printf("Undo_AddEntity: undo is disabled.\n");
537 Sys_Printf("Undo_AddEntity: no last undo.\n");
540 //if the entity is already in the undo
541 if (Undo_EntityInUndo(g_lastundo, entity))
544 pClone = Entity_Clone(entity);
545 //save the old undo ID for previous undos
546 pClone->undoId = entity->undoId;
547 //save the entity ID (we need a full clone)
548 pClone->entityId = entity->entityId;
550 Entity_AddToList(pClone, &g_lastundo->entitylist);
552 g_undoMemorySize += Entity_MemorySize(pClone);
560 void Undo_EndEntity(entity_t *entity)
562 // spog - disable undo if undo levels = 0
563 if (g_PrefsDlg.m_nUndoLevels == 0)
566 Sys_Printf("Undo_EndEntity: undo is disabled.\n");
575 Sys_Printf("Undo_End: no last undo.\n");
579 if (g_lastundo->done)
582 Sys_Printf("Undo_End: last undo already finished.\n");
586 if (entity == world_entity)
588 //Sys_Printf("Undo_AddEntity: undo on world entity.\n");
589 //NOTE: we never delete the world entity when undoing an operation
590 // we only transfer the epairs
593 entity->undoId = g_lastundo->id;
603 // spog - disable undo if undo levels = 0
604 if (g_PrefsDlg.m_nUndoLevels == 0)
607 Sys_Printf("Undo_End: undo is disabled.\n");
615 //Sys_Printf("Undo_End: no last undo.\n");
618 if (g_lastundo->done)
620 //Sys_Printf("Undo_End: last undo already finished.\n");
623 g_lastundo->done = true;
625 //undo memory size is bound to a max
626 while (g_undoMemorySize > g_undoMaxMemorySize)
628 //always keep one undo
629 if (g_undolist == g_lastundo) break;
630 Undo_FreeFirstUndo();
633 //Sys_Printf("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);
641 void Undo_Undo(boolean bSilent)
643 // spog - disable undo if undo levels = 0
644 if (g_PrefsDlg.m_nUndoLevels == 0)
646 Sys_Printf("Undo_Undo: undo is disabled.\n");
651 brush_t *pBrush, *pNextBrush;
652 entity_t *pEntity, *pNextEntity, *pUndoEntity;
656 Sys_Printf("Nothing left to undo.\n");
659 if (!g_lastundo->done)
661 Sys_Printf("Undo_Undo: WARNING: last undo not yet finished!\n");
665 if (g_lastundo->prev) g_lastundo->prev->next = NULL;
666 else g_undolist = NULL;
667 g_lastundo = g_lastundo->prev;
669 //allocate a new redo
670 redo = (undo_t *) malloc(sizeof(undo_t));
672 memset(redo, 0, sizeof(undo_t));
673 redo->brushlist.next = &redo->brushlist;
674 redo->brushlist.prev = &redo->brushlist;
675 redo->entitylist.next = &redo->entitylist;
676 redo->entitylist.prev = &redo->entitylist;
677 if (g_lastredo) g_lastredo->next = redo;
678 else g_redolist = redo;
679 redo->prev = g_lastredo;
682 redo->time = Sys_DoubleTime();
683 redo->id = g_redoId++;
685 redo->operation = undo->operation;
687 //reset the redo IDs of all brushes using the new ID
688 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next)
690 if (pBrush->redoId == redo->id)
695 for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
697 if (pBrush->redoId == redo->id)
702 //reset the redo IDs of all entities using thew new ID
703 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
705 if (pEntity->redoId == redo->id)
711 // deselect current sutff
713 // move "created" brushes to the redo
714 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush=pNextBrush)
716 pNextBrush = pBrush->next;
717 if (pBrush->undoId == undo->id)
719 //Brush_Free(pBrush);
720 //move the brush to the redo
721 Brush_RemoveFromList(pBrush);
722 Brush_AddToList(pBrush, &redo->brushlist);
723 //make sure the ID of the owner is stored
724 pBrush->ownerId = pBrush->owner->entityId;
725 //unlink the brush from the owner entity
726 Entity_UnlinkBrush(pBrush);
729 // move "created" entities to the redo
730 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
732 pNextEntity = pEntity->next;
733 if (pEntity->undoId == undo->id)
735 // check if this entity is in the undo
736 for (pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next)
738 // move brushes to the undo entity
739 if (pUndoEntity->entityId == pEntity->entityId)
741 pUndoEntity->brushes.next = pEntity->brushes.next;
742 pUndoEntity->brushes.prev = pEntity->brushes.prev;
743 pEntity->brushes.next = &pEntity->brushes;
744 pEntity->brushes.prev = &pEntity->brushes;
748 //Entity_Free(pEntity);
749 //move the entity to the redo
750 Entity_RemoveFromList(pEntity);
751 Entity_AddToList(pEntity, &redo->entitylist);
754 // add the undo entities back into the entity list
755 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = undo->entitylist.next)
757 g_undoMemorySize -= Entity_MemorySize(pEntity);
758 //if this is the world entity
759 if (pEntity->entityId == world_entity->entityId)
761 epair_t* tmp = world_entity->epairs;
762 world_entity->epairs = pEntity->epairs;
763 pEntity->epairs = tmp;
764 Entity_Free(pEntity);
768 Entity_RemoveFromList(pEntity);
769 Entity_AddToList(pEntity, &entities);
770 pEntity->redoId = redo->id;
773 // add the undo brushes back into the selected brushes
774 for (pBrush = undo->brushlist.next; pBrush != NULL && pBrush != &undo->brushlist; pBrush = undo->brushlist.next)
776 //Sys_Printf("Owner ID: %i\n",pBrush->ownerId);
777 g_undoMemorySize -= Brush_MemorySize(pBrush);
778 Brush_RemoveFromList(pBrush);
779 Brush_AddToList(pBrush, &active_brushes);
780 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) // fixes broken undo on entities
782 //Sys_Printf("Entity ID: %i\n",pEntity->entityId);
783 if (pEntity->entityId == pBrush->ownerId)
785 Entity_LinkBrush(pEntity, pBrush);
789 //if the brush is not linked then it should be linked into the world entity
790 //++timo FIXME: maybe not, maybe we've lost this entity's owner!
791 if (pEntity == NULL || pEntity == &entities)
793 Entity_LinkBrush(world_entity, pBrush);
796 //Brush_Build(pBrush);
797 Select_Brush(pBrush);
798 pBrush->redoId = redo->id;
801 Sys_Printf("%s undone.\n", undo->operation);
803 g_undoMemorySize -= sizeof(undo_t);
807 if (g_undoId <= 0) g_undoId = 2 * g_undoMaxSize;
809 g_bScreenUpdates = true;
810 UpdateSurfaceDialog();
811 Sys_UpdateWindows(W_ALL);
821 // spog - disable undo if undo levels = 0
822 if (g_PrefsDlg.m_nUndoLevels == 0)
824 Sys_Printf("Undo_Redo: undo is disabled.\n");
829 brush_t *pBrush, *pNextBrush;
830 entity_t *pEntity, *pNextEntity, *pRedoEntity;
834 Sys_Printf("Nothing left to redo.\n");
839 if (!g_lastundo->done)
841 Sys_Printf("WARNING: last undo not finished.\n");
846 if (g_lastredo->prev) g_lastredo->prev->next = NULL;
847 else g_redolist = NULL;
848 g_lastredo = g_lastredo->prev;
850 Undo_GeneralStart(redo->operation);
851 // remove current selection
853 // move "created" brushes back to the last undo
854 for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush)
856 pNextBrush = pBrush->next;
857 if (pBrush->redoId == redo->id)
859 //move the brush to the undo
860 Brush_RemoveFromList(pBrush);
861 Brush_AddToList(pBrush, &g_lastundo->brushlist);
862 g_undoMemorySize += Brush_MemorySize(pBrush);
863 pBrush->ownerId = pBrush->owner->entityId;
864 Entity_UnlinkBrush(pBrush);
867 // move "created" entities back to the last undo
868 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
870 pNextEntity = pEntity->next;
871 if (pEntity->redoId == redo->id)
873 // check if this entity is in the redo
874 for (pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next)
876 // move brushes to the redo entity
877 if (pRedoEntity->entityId == pEntity->entityId)
879 pRedoEntity->brushes.next = pEntity->brushes.next;
880 pRedoEntity->brushes.prev = pEntity->brushes.prev;
881 pEntity->brushes.next = &pEntity->brushes;
882 pEntity->brushes.prev = &pEntity->brushes;
886 //Entity_Free(pEntity);
887 //move the entity to the redo
888 Entity_RemoveFromList(pEntity);
889 Entity_AddToList(pEntity, &g_lastundo->entitylist);
890 g_undoMemorySize += Entity_MemorySize(pEntity);
893 // add the undo entities back into the entity list
894 for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = redo->entitylist.next)
896 //if this is the world entity
897 if (pEntity->entityId == world_entity->entityId)
899 epair_t* tmp = world_entity->epairs;
900 world_entity->epairs = pEntity->epairs;
901 pEntity->epairs = tmp;
902 Entity_Free(pEntity);
906 Entity_RemoveFromList(pEntity);
907 Entity_AddToList(pEntity, &entities);
910 // add the redo brushes back into the selected brushes
911 for (pBrush = redo->brushlist.next; pBrush != NULL && pBrush != &redo->brushlist; pBrush = redo->brushlist.next)
913 Brush_RemoveFromList(pBrush);
914 Brush_AddToList(pBrush, &active_brushes);
915 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) // fixes broken undo on entities
917 if (pEntity->entityId == pBrush->ownerId)
919 Entity_LinkBrush(pEntity, pBrush);
923 //if the brush is not linked then it should be linked into the world entity
924 if (pEntity == NULL || pEntity == &entities)
926 Entity_LinkBrush(world_entity, pBrush);
929 //Brush_Build(pBrush);
930 Select_Brush(pBrush);
935 Sys_Printf("%s redone.\n", redo->operation);
941 g_bScreenUpdates = true;
942 UpdateSurfaceDialog();
943 Sys_UpdateWindows(W_ALL);
951 int Undo_RedoAvailable(void)
953 if (g_lastredo) return true;
957 int Undo_GetUndoId(void)
960 return g_lastundo->id;
969 int Undo_UndoAvailable(void)
973 if (g_lastundo->done)