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 ){
70 return g_undoMemorySize;
78 void Undo_ClearRedo( void ){
79 undo_t *redo, *nextredo;
80 brush_t *pBrush, *pNextBrush;
81 entity_t *pEntity, *pNextEntity;
83 for ( redo = g_redolist; redo; redo = nextredo )
85 nextredo = redo->next;
86 for ( pBrush = redo->brushlist.next ; pBrush != NULL && pBrush != &redo->brushlist ; pBrush = pNextBrush )
88 pNextBrush = pBrush->next;
91 for ( pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = pNextEntity )
93 pNextEntity = pEntity->next;
94 Entity_Free( pEntity );
107 Clears the undo buffer.
110 void Undo_Clear( void ){
111 undo_t *undo, *nextundo;
112 brush_t *pBrush, *pNextBrush;
113 entity_t *pEntity, *pNextEntity;
116 for ( undo = g_undolist; undo; undo = nextundo )
118 nextundo = undo->next;
119 for ( pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush )
121 pNextBrush = pBrush->next;
122 g_undoMemorySize -= Brush_MemorySize( pBrush );
123 Brush_Free( pBrush );
125 for ( pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity )
127 pNextEntity = pEntity->next;
128 g_undoMemorySize -= Entity_MemorySize( pEntity );
129 Entity_Free( pEntity );
131 g_undoMemorySize -= sizeof( undo_t );
137 g_undoMemorySize = 0;
146 void Undo_SetMaxSize( int size ){
151 else{g_undoMaxSize = size; }
159 int Undo_GetMaxSize( void ){
160 return g_undoMaxSize;
165 Undo_SetMaxMemorySize
168 void Undo_SetMaxMemorySize( int size ){
171 g_undoMaxMemorySize = 1024;
173 else{g_undoMaxMemorySize = size; }
178 Undo_GetMaxMemorySize
181 int Undo_GetMaxMemorySize( void ){
182 return g_undoMaxMemorySize;
190 void Undo_FreeFirstUndo( void ){
192 brush_t *pBrush, *pNextBrush;
193 entity_t *pEntity, *pNextEntity;
195 //remove the oldest undo from the undo buffer
197 g_undolist = g_undolist->next;
198 g_undolist->prev = NULL;
200 for ( pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush )
202 pNextBrush = pBrush->next;
203 g_undoMemorySize -= Brush_MemorySize( pBrush );
204 Brush_Free( pBrush );
206 for ( pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity )
208 pNextEntity = pEntity->next;
209 g_undoMemorySize -= Entity_MemorySize( pEntity );
210 Entity_Free( pEntity );
212 g_undoMemorySize -= sizeof( undo_t );
222 void Undo_GeneralStart( const char *operation ){
229 if ( !g_lastundo->done ) {
230 Sys_Printf( "Undo_Start: WARNING last undo not finished.\n" );
234 undo = (undo_t *) malloc( sizeof( undo_t ) );
238 memset( undo, 0, sizeof( undo_t ) );
239 undo->brushlist.next = &undo->brushlist;
240 undo->brushlist.prev = &undo->brushlist;
241 undo->entitylist.next = &undo->entitylist;
242 undo->entitylist.prev = &undo->entitylist;
244 g_lastundo->next = undo;
249 undo->prev = g_lastundo;
253 undo->time = Sys_DoubleTime();
255 if ( g_undoId > g_undoMaxSize * 2 ) {
258 if ( g_undoId <= 0 ) {
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 ) {
271 for ( pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next )
273 if ( pBrush->undoId == undo->id ) {
277 //reset the undo IDs of all entities using thew new ID
278 for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next )
280 if ( pEntity->undoId == undo->id ) {
284 g_undoMemorySize += sizeof( undo_t );
286 //undo buffer is bound to a max
287 if ( g_undoSize > g_undoMaxSize ) {
288 Undo_FreeFirstUndo();
297 int Undo_BrushInUndo( undo_t *undo, brush_t *brush ){
300 for (b = undo->brushlist.next; b != &undo->brushlist; b = b->next)
302 // Arnout: NOTE - can't do a pointer compare as the brushes get cloned into the undo brushlist, and not just referenced from it
303 // 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.
304 if (b == brush) return true;
306 // Arnout: function is pointless right now, see above explanation
315 int Undo_EntityInUndo( undo_t *undo, entity_t *ent ){
318 for ( e = undo->entitylist.next; e != &undo->entitylist; e = e->next )
320 // Arnout: NOTE - can't do a pointer compare as the entities get cloned into the undo entitylist, and not just referenced from it
321 //if (e == ent) return true;
322 if ( e->entityId == ent->entityId ) {
334 void Undo_Start( const char *operation ){
335 // spog - disable undo if undo levels = 0
336 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
338 Sys_Printf( "Undo_Start: undo is disabled.\n" );
344 Undo_GeneralStart( operation );
352 void Undo_AddBrush( brush_t *pBrush ){
353 // spog - disable undo if undo levels = 0
354 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
356 Sys_Printf( "Undo_AddBrush: undo is disabled.\n" );
362 Sys_Printf( "Undo_AddBrushList: no last undo.\n" );
365 if ( g_lastundo->entitylist.next != &g_lastundo->entitylist ) {
366 Sys_Printf( "Undo_AddBrushList: WARNING adding brushes after entity.\n" );
368 //if the brush is already in the undo
369 if ( Undo_BrushInUndo( g_lastundo, pBrush ) ) {
373 brush_t* pClone = Brush_FullClone( pBrush );
374 //save the ID of the owner entity
375 pClone->ownerId = pBrush->owner->entityId;
376 //save the old undo ID for previous undos
377 pClone->undoId = pBrush->undoId;
378 Brush_AddToList( pClone, &g_lastundo->brushlist );
380 g_undoMemorySize += Brush_MemorySize( pClone );
386 TTimo: some brushes are just there for UI, and the information is somewhere else
387 for patches it's in the patchMesh_t structure, so when we clone the brush we get that information (brush_t::pPatch)
388 but: models are stored in pBrush->owner->md3Class, and owner epairs and origin parameters are important
389 so, we detect models and push the entity in the undo session (as well as it's BBox brush)
390 same for other items like weapons and ammo etc.
393 void Undo_AddBrushList( brush_t *brushlist ){
394 // spog - disable undo if undo levels = 0
395 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
397 Sys_Printf( "Undo_AddBrushList: undo is disabled.\n" );
405 Sys_Printf( "Undo_AddBrushList: no last undo.\n" );
408 if ( g_lastundo->entitylist.next != &g_lastundo->entitylist ) {
409 Sys_Printf( "Undo_AddBrushList: WARNING adding brushes after entity.\n" );
411 //copy the brushes to the undo
412 for ( pBrush = brushlist->next ; pBrush != NULL && pBrush != brushlist; pBrush = pBrush->next )
414 //if the brush is already in the undo
415 //++timo FIXME: when does this happen?
416 if ( Undo_BrushInUndo( g_lastundo, pBrush ) ) {
419 // do we need to store this brush's entity in the undo?
420 // if it's a fixed size entity, the brush that reprents it is not really relevant, it's used for selecting and moving around
421 // what we want to store for undo is the owner entity, epairs and origin/angle stuff
422 //++timo FIXME: if the entity is not fixed size I don't know, so I don't do it yet
423 if ( pBrush->owner->eclass->fixedsize == 1 ) {
424 Undo_AddEntity( pBrush->owner );
427 brush_t* pClone = Brush_FullClone( pBrush );
428 // save the ID of the owner entity
429 pClone->ownerId = pBrush->owner->entityId;
430 // save the old undo ID from previous undos
431 pClone->undoId = pBrush->undoId;
432 Brush_AddToList( pClone, &g_lastundo->brushlist );
433 // track memory size used by undo
434 g_undoMemorySize += Brush_MemorySize( pClone );
443 void Undo_EndBrush( brush_t *pBrush ){
444 // spog - disable undo if undo levels = 0
445 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
447 Sys_Printf( "Undo_EndBrush: undo is disabled.\n" );
454 //Sys_Printf("Undo_End: no last undo.\n");
457 if ( g_lastundo->done ) {
458 //Sys_Printf("Undo_End: last undo already finished.\n");
461 pBrush->undoId = g_lastundo->id;
469 void Undo_EndBrushList( brush_t *brushlist ){
470 // spog - disable undo if undo levels = 0
471 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
473 Sys_Printf( "Undo_EndBrushList: undo is disabled.\n" );
480 //Sys_Printf("Undo_End: no last undo.\n");
483 if ( g_lastundo->done ) {
484 //Sys_Printf("Undo_End: last undo already finished.\n");
487 for ( brush_t* pBrush = brushlist->next; pBrush != NULL && pBrush != brushlist; pBrush = pBrush->next )
489 pBrush->undoId = g_lastundo->id;
490 // http://github.com/mfn/GtkRadiant/commit/ee1ef98536470d5680bd9bfecc5b5c9a62ffe9ab
491 if ( pBrush->owner->eclass->fixedsize == 1 ) {
492 pBrush->owner->undoId = pBrush->undoId;
502 void Undo_AddEntity( entity_t *entity ){
503 // spog - disable undo if undo levels = 0
504 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
506 Sys_Printf( "Undo_AddEntity: undo is disabled.\n" );
515 Sys_Printf( "Undo_AddEntity: no last undo.\n" );
518 //if the entity is already in the undo
519 if ( Undo_EntityInUndo( g_lastundo, entity ) ) {
523 pClone = Entity_Clone( entity );
524 //save the old undo ID for previous undos
525 pClone->undoId = entity->undoId;
526 //save the entity ID (we need a full clone)
527 pClone->entityId = entity->entityId;
529 Entity_AddToList( pClone, &g_lastundo->entitylist );
531 g_undoMemorySize += Entity_MemorySize( pClone );
539 void Undo_EndEntity( entity_t *entity ){
540 // spog - disable undo if undo levels = 0
541 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
543 Sys_Printf( "Undo_EndEntity: undo is disabled.\n" );
551 Sys_Printf( "Undo_End: no last undo.\n" );
555 if ( g_lastundo->done ) {
557 Sys_Printf( "Undo_End: last undo already finished.\n" );
561 if ( entity == world_entity ) {
562 //Sys_Printf("Undo_AddEntity: undo on world entity.\n");
563 //NOTE: we never delete the world entity when undoing an operation
564 // we only transfer the epairs
567 entity->undoId = g_lastundo->id;
575 void Undo_End( void ){
576 // spog - disable undo if undo levels = 0
577 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
579 Sys_Printf( "Undo_End: undo is disabled.\n" );
586 //Sys_Printf("Undo_End: no last undo.\n");
589 if ( g_lastundo->done ) {
590 //Sys_Printf("Undo_End: last undo already finished.\n");
593 g_lastundo->done = true;
595 //undo memory size is bound to a max
596 while ( g_undoMemorySize > g_undoMaxMemorySize )
598 //always keep one undo
599 if ( g_undolist == g_lastundo ) {
602 Undo_FreeFirstUndo();
605 //Sys_Printf("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);
613 void Undo_Undo( boolean bSilent ){
614 // spog - disable undo if undo levels = 0
615 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
616 Sys_Printf( "Undo_Undo: undo is disabled.\n" );
621 brush_t *pBrush, *pNextBrush;
622 entity_t *pEntity, *pNextEntity, *pUndoEntity;
625 Sys_Printf( "Nothing left to undo.\n" );
628 if ( !g_lastundo->done ) {
629 Sys_Printf( "Undo_Undo: WARNING: last undo not yet finished!\n" );
633 if ( g_lastundo->prev ) {
634 g_lastundo->prev->next = NULL;
636 else{g_undolist = NULL; }
637 g_lastundo = g_lastundo->prev;
639 //allocate a new redo
640 redo = (undo_t *) malloc( sizeof( undo_t ) );
644 memset( redo, 0, sizeof( undo_t ) );
645 redo->brushlist.next = &redo->brushlist;
646 redo->brushlist.prev = &redo->brushlist;
647 redo->entitylist.next = &redo->entitylist;
648 redo->entitylist.prev = &redo->entitylist;
650 g_lastredo->next = redo;
652 else{g_redolist = redo; }
653 redo->prev = g_lastredo;
656 redo->time = Sys_DoubleTime();
657 redo->id = g_redoId++;
659 redo->operation = undo->operation;
661 //reset the redo IDs of all brushes using the new ID
662 for ( pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next )
664 if ( pBrush->redoId == redo->id ) {
668 for ( pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next )
670 if ( pBrush->redoId == redo->id ) {
674 //reset the redo IDs of all entities using thew new ID
675 for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next )
677 if ( pEntity->redoId == redo->id ) {
682 // deselect current sutff
684 // move "created" brushes to the redo
685 for ( pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush )
687 pNextBrush = pBrush->next;
688 if ( pBrush->undoId == undo->id ) {
689 //Brush_Free(pBrush);
690 //move the brush to the redo
691 Brush_RemoveFromList( pBrush );
692 Brush_AddToList( pBrush, &redo->brushlist );
693 //make sure the ID of the owner is stored
694 pBrush->ownerId = pBrush->owner->entityId;
695 //unlink the brush from the owner entity
696 Entity_UnlinkBrush( pBrush );
699 // move "created" entities to the redo
700 for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity )
702 pNextEntity = pEntity->next;
703 if ( pEntity->undoId == undo->id ) {
704 // check if this entity is in the undo
705 for ( pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next )
707 // move brushes to the undo entity
708 if ( pUndoEntity->entityId == pEntity->entityId ) {
709 pUndoEntity->brushes.next = pEntity->brushes.next;
710 pUndoEntity->brushes.prev = pEntity->brushes.prev;
711 pEntity->brushes.next = &pEntity->brushes;
712 pEntity->brushes.prev = &pEntity->brushes;
716 //Entity_Free(pEntity);
717 //move the entity to the redo
718 Entity_RemoveFromList( pEntity );
719 Entity_AddToList( pEntity, &redo->entitylist );
722 // add the undo entities back into the entity list
723 for ( pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = undo->entitylist.next )
725 g_undoMemorySize -= Entity_MemorySize( pEntity );
726 //if this is the world entity
727 if ( pEntity->entityId == world_entity->entityId ) {
728 epair_t* tmp = world_entity->epairs;
729 world_entity->epairs = pEntity->epairs;
730 pEntity->epairs = tmp;
731 Entity_Free( pEntity );
735 Entity_RemoveFromList( pEntity );
736 Entity_AddToList( pEntity, &entities );
737 pEntity->redoId = redo->id;
740 // add the undo brushes back into the selected brushes
741 for ( pBrush = undo->brushlist.next; pBrush != NULL && pBrush != &undo->brushlist; pBrush = undo->brushlist.next )
743 //Sys_Printf("Owner ID: %i\n",pBrush->ownerId);
744 g_undoMemorySize -= Brush_MemorySize( pBrush );
745 Brush_RemoveFromList( pBrush );
746 Brush_AddToList( pBrush, &active_brushes );
747 for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next ) // fixes broken undo on entities
749 //Sys_Printf("Entity ID: %i\n",pEntity->entityId);
750 if ( pEntity->entityId == pBrush->ownerId ) {
751 Entity_LinkBrush( pEntity, pBrush );
755 //if the brush is not linked then it should be linked into the world entity
756 //++timo FIXME: maybe not, maybe we've lost this entity's owner!
757 if ( pEntity == NULL || pEntity == &entities ) {
758 Entity_LinkBrush( world_entity, pBrush );
761 //Brush_Build(pBrush);
762 Select_Brush( pBrush );
763 pBrush->redoId = redo->id;
766 Sys_Printf( "%s undone.\n", undo->operation );
769 g_undoMemorySize -= sizeof( undo_t );
773 if ( g_undoId <= 0 ) {
774 g_undoId = 2 * g_undoMaxSize;
777 g_bScreenUpdates = true;
778 UpdateSurfaceDialog();
779 Sys_UpdateWindows( W_ALL );
787 void Undo_Redo( void ){
788 // spog - disable undo if undo levels = 0
789 if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
790 Sys_Printf( "Undo_Redo: undo is disabled.\n" );
795 brush_t *pBrush, *pNextBrush;
796 entity_t *pEntity, *pNextEntity, *pRedoEntity;
799 Sys_Printf( "Nothing left to redo.\n" );
803 if ( !g_lastundo->done ) {
804 Sys_Printf( "WARNING: last undo not finished.\n" );
809 if ( g_lastredo->prev ) {
810 g_lastredo->prev->next = NULL;
812 else{g_redolist = NULL; }
813 g_lastredo = g_lastredo->prev;
815 Undo_GeneralStart( redo->operation );
816 // remove current selection
818 // move "created" brushes back to the last undo
819 for ( pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush )
821 pNextBrush = pBrush->next;
822 if ( pBrush->redoId == redo->id ) {
823 //move the brush to the undo
824 Brush_RemoveFromList( pBrush );
825 Brush_AddToList( pBrush, &g_lastundo->brushlist );
826 g_undoMemorySize += Brush_MemorySize( pBrush );
827 pBrush->ownerId = pBrush->owner->entityId;
828 Entity_UnlinkBrush( pBrush );
831 // move "created" entities back to the last undo
832 for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity )
834 pNextEntity = pEntity->next;
835 if ( pEntity->redoId == redo->id ) {
836 // check if this entity is in the redo
837 for ( pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next )
839 // move brushes to the redo entity
840 if ( pRedoEntity->entityId == pEntity->entityId ) {
841 pRedoEntity->brushes.next = pEntity->brushes.next;
842 pRedoEntity->brushes.prev = pEntity->brushes.prev;
843 pEntity->brushes.next = &pEntity->brushes;
844 pEntity->brushes.prev = &pEntity->brushes;
848 //Entity_Free(pEntity);
849 //move the entity to the redo
850 Entity_RemoveFromList( pEntity );
851 Entity_AddToList( pEntity, &g_lastundo->entitylist );
852 g_undoMemorySize += Entity_MemorySize( pEntity );
855 // add the undo entities back into the entity list
856 for ( pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = redo->entitylist.next )
858 //if this is the world entity
859 if ( pEntity->entityId == world_entity->entityId ) {
860 epair_t* tmp = world_entity->epairs;
861 world_entity->epairs = pEntity->epairs;
862 pEntity->epairs = tmp;
863 Entity_Free( pEntity );
867 Entity_RemoveFromList( pEntity );
868 Entity_AddToList( pEntity, &entities );
871 // add the redo brushes back into the selected brushes
872 for ( pBrush = redo->brushlist.next; pBrush != NULL && pBrush != &redo->brushlist; pBrush = redo->brushlist.next )
874 Brush_RemoveFromList( pBrush );
875 Brush_AddToList( pBrush, &active_brushes );
876 for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next ) // fixes broken undo on entities
878 if ( pEntity->entityId == pBrush->ownerId ) {
879 Entity_LinkBrush( pEntity, pBrush );
883 //if the brush is not linked then it should be linked into the world entity
884 if ( pEntity == NULL || pEntity == &entities ) {
885 Entity_LinkBrush( world_entity, pBrush );
888 //Brush_Build(pBrush);
889 Select_Brush( pBrush );
894 Sys_Printf( "%s redone.\n", redo->operation );
900 g_bScreenUpdates = true;
901 UpdateSurfaceDialog();
902 Sys_UpdateWindows( W_ALL );
910 int Undo_RedoAvailable( void ){
917 int Undo_GetUndoId( void ){
919 return g_lastundo->id;
929 int Undo_UndoAvailable( void ){
931 if ( g_lastundo->done ) {