]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/undo.cpp
fix warning: format not a string literal and no format arguments
[xonotic/netradiant.git] / radiant / undo.cpp
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22
23 /*
24
25    QERadiant Undo/Redo
26
27
28    basic setup:
29
30    <-g_undolist---------g_lastundo> <---map data---> <-g_lastredo---------g_redolist->
31
32
33    undo/redo on the world_entity is special, only the epair changes are remembered
34    and the world entity never gets deleted.
35
36    FIXME: maybe reset the Undo system at map load
37          maybe also reset the entityId at map load
38  */
39
40 #include "stdafx.h"
41
42 typedef struct undo_s
43 {
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
51 } undo_t;
52
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)
63
64 /*
65    =============
66    Undo_MemorySize
67    =============
68  */
69 int Undo_MemorySize( void ){
70         return g_undoMemorySize;
71 }
72
73 /*
74    =============
75    Undo_ClearRedo
76    =============
77  */
78 void Undo_ClearRedo( void ){
79         undo_t *redo, *nextredo;
80         brush_t *pBrush, *pNextBrush;
81         entity_t *pEntity, *pNextEntity;
82
83         for ( redo = g_redolist; redo; redo = nextredo )
84         {
85                 nextredo = redo->next;
86                 for ( pBrush = redo->brushlist.next ; pBrush != NULL && pBrush != &redo->brushlist ; pBrush = pNextBrush )
87                 {
88                         pNextBrush = pBrush->next;
89                         Brush_Free( pBrush );
90                 }
91                 for ( pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = pNextEntity )
92                 {
93                         pNextEntity = pEntity->next;
94                         Entity_Free( pEntity );
95                 }
96                 free( redo );
97         }
98         g_redolist = NULL;
99         g_lastredo = NULL;
100         g_redoId = 1;
101 }
102
103 /*
104    =============
105    Undo_Clear
106
107    Clears the undo buffer.
108    =============
109  */
110 void Undo_Clear( void ){
111         undo_t *undo, *nextundo;
112         brush_t *pBrush, *pNextBrush;
113         entity_t *pEntity, *pNextEntity;
114
115         Undo_ClearRedo();
116         for ( undo = g_undolist; undo; undo = nextundo )
117         {
118                 nextundo = undo->next;
119                 for ( pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush )
120                 {
121                         pNextBrush = pBrush->next;
122                         g_undoMemorySize -= Brush_MemorySize( pBrush );
123                         Brush_Free( pBrush );
124                 }
125                 for ( pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity )
126                 {
127                         pNextEntity = pEntity->next;
128                         g_undoMemorySize -= Entity_MemorySize( pEntity );
129                         Entity_Free( pEntity );
130                 }
131                 g_undoMemorySize -= sizeof( undo_t );
132                 free( undo );
133         }
134         g_undolist = NULL;
135         g_lastundo = NULL;
136         g_undoSize = 0;
137         g_undoMemorySize = 0;
138         g_undoId = 1;
139 }
140
141 /*
142    =============
143    Undo_SetMaxSize
144    =============
145  */
146 void Undo_SetMaxSize( int size ){
147         Undo_Clear();
148         if ( size < 1 ) {
149                 g_undoMaxSize = 1;
150         }
151         else{g_undoMaxSize = size; }
152 }
153
154 /*
155    =============
156    Undo_GetMaxSize
157    =============
158  */
159 int Undo_GetMaxSize( void ){
160         return g_undoMaxSize;
161 }
162
163 /*
164    =============
165    Undo_SetMaxMemorySize
166    =============
167  */
168 void Undo_SetMaxMemorySize( int size ){
169         Undo_Clear();
170         if ( size < 1024 ) {
171                 g_undoMaxMemorySize = 1024;
172         }
173         else{g_undoMaxMemorySize = size; }
174 }
175
176 /*
177    =============
178    Undo_GetMaxMemorySize
179    =============
180  */
181 int Undo_GetMaxMemorySize( void ){
182         return g_undoMaxMemorySize;
183 }
184
185 /*
186    =============
187    Undo_FreeFirstUndo
188    =============
189  */
190 void Undo_FreeFirstUndo( void ){
191         undo_t *undo;
192         brush_t *pBrush, *pNextBrush;
193         entity_t *pEntity, *pNextEntity;
194
195         //remove the oldest undo from the undo buffer
196         undo = g_undolist;
197         g_undolist = g_undolist->next;
198         g_undolist->prev = NULL;
199         //
200         for ( pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush )
201         {
202                 pNextBrush = pBrush->next;
203                 g_undoMemorySize -= Brush_MemorySize( pBrush );
204                 Brush_Free( pBrush );
205         }
206         for ( pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity )
207         {
208                 pNextEntity = pEntity->next;
209                 g_undoMemorySize -= Entity_MemorySize( pEntity );
210                 Entity_Free( pEntity );
211         }
212         g_undoMemorySize -= sizeof( undo_t );
213         free( undo );
214         g_undoSize--;
215 }
216
217 /*
218    =============
219    Undo_GeneralStart
220    =============
221  */
222 void Undo_GeneralStart( const char *operation ){
223         undo_t *undo;
224         brush_t *pBrush;
225         entity_t *pEntity;
226
227
228         if ( g_lastundo ) {
229                 if ( !g_lastundo->done ) {
230                         Sys_Printf( "Undo_Start: WARNING last undo not finished.\n" );
231                 }
232         }
233
234         undo = (undo_t *) malloc( sizeof( undo_t ) );
235         if ( !undo ) {
236                 return;
237         }
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;
243         if ( g_lastundo ) {
244                 g_lastundo->next = undo;
245         }
246         else{
247                 g_undolist = undo;
248         }
249         undo->prev = g_lastundo;
250         undo->next = NULL;
251         g_lastundo = undo;
252
253         undo->time = Sys_DoubleTime();
254         //
255         if ( g_undoId > g_undoMaxSize * 2 ) {
256                 g_undoId = 1;
257         }
258         if ( g_undoId <= 0 ) {
259                 g_undoId = 1;
260         }
261         undo->id = g_undoId++;
262         undo->done = false;
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 )
266         {
267                 if ( pBrush->undoId == undo->id ) {
268                         pBrush->undoId = 0;
269                 }
270         }
271         for ( pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next )
272         {
273                 if ( pBrush->undoId == undo->id ) {
274                         pBrush->undoId = 0;
275                 }
276         }
277         //reset the undo IDs of all entities using thew new ID
278         for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next )
279         {
280                 if ( pEntity->undoId == undo->id ) {
281                         pEntity->undoId = 0;
282                 }
283         }
284         g_undoMemorySize += sizeof( undo_t );
285         g_undoSize++;
286         //undo buffer is bound to a max
287         if ( g_undoSize > g_undoMaxSize ) {
288                 Undo_FreeFirstUndo();
289         }
290 }
291
292 /*
293    =============
294    Undo_BrushInUndo
295    =============
296  */
297 int Undo_BrushInUndo( undo_t *undo, brush_t *brush ){
298 /*      brush_t *b;
299
300     for (b = undo->brushlist.next; b != &undo->brushlist; b = b->next)
301     {
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;
305     }*/
306         // Arnout: function is pointless right now, see above explanation
307         return false;
308 }
309
310 /*
311    =============
312    Undo_EntityInUndo
313    =============
314  */
315 int Undo_EntityInUndo( undo_t *undo, entity_t *ent ){
316         entity_t *e;
317
318         for ( e = undo->entitylist.next; e != &undo->entitylist; e = e->next )
319         {
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 ) {
323                         return true;
324                 }
325         }
326         return false;
327 }
328
329 /*
330    =============
331    Undo_Start
332    =============
333  */
334 void Undo_Start( const char *operation ){
335         // spog - disable undo if undo levels = 0
336         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
337 #ifdef DBG_UNDO
338                 Sys_Printf( "Undo_Start: undo is disabled.\n" );
339 #endif
340                 return;
341         }
342
343         Undo_ClearRedo();
344         Undo_GeneralStart( operation );
345 }
346
347 /*
348    =============
349    Undo_AddBrush
350    =============
351  */
352 void Undo_AddBrush( brush_t *pBrush ){
353         // spog - disable undo if undo levels = 0
354         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
355 #ifdef DBG_UNDO
356                 Sys_Printf( "Undo_AddBrush: undo is disabled.\n" );
357 #endif
358                 return;
359         }
360
361         if ( !g_lastundo ) {
362                 Sys_Printf( "Undo_AddBrushList: no last undo.\n" );
363                 return;
364         }
365         if ( g_lastundo->entitylist.next != &g_lastundo->entitylist ) {
366                 Sys_Printf( "Undo_AddBrushList: WARNING adding brushes after entity.\n" );
367         }
368         //if the brush is already in the undo
369         if ( Undo_BrushInUndo( g_lastundo, pBrush ) ) {
370                 return;
371         }
372         //clone the brush
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 );
379         //
380         g_undoMemorySize += Brush_MemorySize( pClone );
381 }
382
383 /*
384    =============
385    Undo_AddBrushList
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.
391    =============
392  */
393 void Undo_AddBrushList( brush_t *brushlist ){
394         // spog - disable undo if undo levels = 0
395         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
396 #ifdef DBG_UNDO
397                 Sys_Printf( "Undo_AddBrushList: undo is disabled.\n" );
398 #endif
399                 return;
400         }
401
402         brush_t *pBrush;
403
404         if ( !g_lastundo ) {
405                 Sys_Printf( "Undo_AddBrushList: no last undo.\n" );
406                 return;
407         }
408         if ( g_lastundo->entitylist.next != &g_lastundo->entitylist ) {
409                 Sys_Printf( "Undo_AddBrushList: WARNING adding brushes after entity.\n" );
410         }
411         //copy the brushes to the undo
412         for ( pBrush = brushlist->next ; pBrush != NULL && pBrush != brushlist; pBrush = pBrush->next )
413         {
414                 //if the brush is already in the undo
415                 //++timo FIXME: when does this happen?
416                 if ( Undo_BrushInUndo( g_lastundo, pBrush ) ) {
417                         continue;
418                 }
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 );
425                 }
426                 // clone the brush
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 );
435         }
436 }
437
438 /*
439    =============
440    Undo_EndBrush
441    =============
442  */
443 void Undo_EndBrush( brush_t *pBrush ){
444         // spog - disable undo if undo levels = 0
445         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
446 #ifdef DBG_UNDO
447                 Sys_Printf( "Undo_EndBrush: undo is disabled.\n" );
448 #endif
449                 return;
450         }
451
452
453         if ( !g_lastundo ) {
454                 //Sys_Printf("Undo_End: no last undo.\n");
455                 return;
456         }
457         if ( g_lastundo->done ) {
458                 //Sys_Printf("Undo_End: last undo already finished.\n");
459                 return;
460         }
461         pBrush->undoId = g_lastundo->id;
462 }
463
464 /*
465    =============
466    Undo_EndBrushList
467    =============
468  */
469 void Undo_EndBrushList( brush_t *brushlist ){
470         // spog - disable undo if undo levels = 0
471         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
472 #ifdef DBG_UNDO
473                 Sys_Printf( "Undo_EndBrushList: undo is disabled.\n" );
474 #endif
475                 return;
476         }
477
478
479         if ( !g_lastundo ) {
480                 //Sys_Printf("Undo_End: no last undo.\n");
481                 return;
482         }
483         if ( g_lastundo->done ) {
484                 //Sys_Printf("Undo_End: last undo already finished.\n");
485                 return;
486         }
487         for ( brush_t* pBrush = brushlist->next; pBrush != NULL && pBrush != brushlist; pBrush = pBrush->next )
488         {
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;
493                 }
494         }
495 }
496
497 /*
498    =============
499    Undo_AddEntity
500    =============
501  */
502 void Undo_AddEntity( entity_t *entity ){
503         // spog - disable undo if undo levels = 0
504         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
505 #ifdef DBG_UNDO
506                 Sys_Printf( "Undo_AddEntity: undo is disabled.\n" );
507 #endif
508                 return;
509         }
510
511
512         entity_t* pClone;
513
514         if ( !g_lastundo ) {
515                 Sys_Printf( "Undo_AddEntity: no last undo.\n" );
516                 return;
517         }
518         //if the entity is already in the undo
519         if ( Undo_EntityInUndo( g_lastundo, entity ) ) {
520                 return;
521         }
522         //clone the 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;
528         //
529         Entity_AddToList( pClone, &g_lastundo->entitylist );
530         //
531         g_undoMemorySize += Entity_MemorySize( pClone );
532 }
533
534 /*
535    =============
536    Undo_EndEntity
537    =============
538  */
539 void Undo_EndEntity( entity_t *entity ){
540         // spog - disable undo if undo levels = 0
541         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
542 #ifdef DBG_UNDO
543                 Sys_Printf( "Undo_EndEntity: undo is disabled.\n" );
544 #endif
545                 return;
546         }
547
548
549         if ( !g_lastundo ) {
550 #ifdef _DEBUG
551                 Sys_Printf( "Undo_End: no last undo.\n" );
552 #endif
553                 return;
554         }
555         if ( g_lastundo->done ) {
556 #ifdef _DEBUG
557                 Sys_Printf( "Undo_End: last undo already finished.\n" );
558 #endif
559                 return;
560         }
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
565                 return;
566         }
567         entity->undoId = g_lastundo->id;
568 }
569
570 /*
571    =============
572    Undo_End
573    =============
574  */
575 void Undo_End( void ){
576         // spog - disable undo if undo levels = 0
577         if ( g_PrefsDlg.m_nUndoLevels == 0 ) {
578 #ifdef DBG_UNDO
579                 Sys_Printf( "Undo_End: undo is disabled.\n" );
580 #endif
581                 return;
582         }
583
584
585         if ( !g_lastundo ) {
586                 //Sys_Printf("Undo_End: no last undo.\n");
587                 return;
588         }
589         if ( g_lastundo->done ) {
590                 //Sys_Printf("Undo_End: last undo already finished.\n");
591                 return;
592         }
593         g_lastundo->done = true;
594
595         //undo memory size is bound to a max
596         while ( g_undoMemorySize > g_undoMaxMemorySize )
597         {
598                 //always keep one undo
599                 if ( g_undolist == g_lastundo ) {
600                         break;
601                 }
602                 Undo_FreeFirstUndo();
603         }
604         //
605         //Sys_Printf("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);
606 }
607
608 /*
609    =============
610    Undo_Undo
611    =============
612  */
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" );
617                 return;
618         }
619
620         undo_t *undo, *redo;
621         brush_t *pBrush, *pNextBrush;
622         entity_t *pEntity, *pNextEntity, *pUndoEntity;
623
624         if ( !g_lastundo ) {
625                 Sys_Printf( "Nothing left to undo.\n" );
626                 return;
627         }
628         if ( !g_lastundo->done ) {
629                 Sys_Printf( "Undo_Undo: WARNING: last undo not yet finished!\n" );
630         }
631         // get the last undo
632         undo = g_lastundo;
633         if ( g_lastundo->prev ) {
634                 g_lastundo->prev->next = NULL;
635         }
636         else{g_undolist = NULL; }
637         g_lastundo = g_lastundo->prev;
638
639         //allocate a new redo
640         redo = (undo_t *) malloc( sizeof( undo_t ) );
641         if ( !redo ) {
642                 return;
643         }
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;
649         if ( g_lastredo ) {
650                 g_lastredo->next = redo;
651         }
652         else{g_redolist = redo; }
653         redo->prev = g_lastredo;
654         redo->next = NULL;
655         g_lastredo = redo;
656         redo->time = Sys_DoubleTime();
657         redo->id = g_redoId++;
658         redo->done = true;
659         redo->operation = undo->operation;
660
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 )
663         {
664                 if ( pBrush->redoId == redo->id ) {
665                         pBrush->redoId = 0;
666                 }
667         }
668         for ( pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next )
669         {
670                 if ( pBrush->redoId == redo->id ) {
671                         pBrush->redoId = 0;
672                 }
673         }
674         //reset the redo IDs of all entities using thew new ID
675         for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next )
676         {
677                 if ( pEntity->redoId == redo->id ) {
678                         pEntity->redoId = 0;
679                 }
680         }
681
682         // deselect current sutff
683         Select_Deselect();
684         // move "created" brushes to the redo
685         for ( pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush )
686         {
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 );
697                 }
698         }
699         // move "created" entities to the redo
700         for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity )
701         {
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 )
706                         {
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;
713                                 }
714                         }
715                         //
716                         //Entity_Free(pEntity);
717                         //move the entity to the redo
718                         Entity_RemoveFromList( pEntity );
719                         Entity_AddToList( pEntity, &redo->entitylist );
720                 }
721         }
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 )
724         {
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 );
732                 }
733                 else
734                 {
735                         Entity_RemoveFromList( pEntity );
736                         Entity_AddToList( pEntity, &entities );
737                         pEntity->redoId = redo->id;
738                 }
739         }
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 )
742         {
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
748                 {
749                         //Sys_Printf("Entity ID: %i\n",pEntity->entityId);
750                         if ( pEntity->entityId == pBrush->ownerId ) {
751                                 Entity_LinkBrush( pEntity, pBrush );
752                                 break;
753                         }
754                 }
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 );
759                 }
760                 //build the brush
761                 //Brush_Build(pBrush);
762                 Select_Brush( pBrush );
763                 pBrush->redoId = redo->id;
764         }
765         if ( !bSilent ) {
766                 Sys_Printf( "%s undone.\n", undo->operation );
767         }
768         // free the undo
769         g_undoMemorySize -= sizeof( undo_t );
770         free( undo );
771         g_undoSize--;
772         g_undoId--;
773         if ( g_undoId <= 0 ) {
774                 g_undoId = 2 * g_undoMaxSize;
775         }
776         //
777         g_bScreenUpdates = true;
778         UpdateSurfaceDialog();
779         Sys_UpdateWindows( W_ALL );
780 }
781
782 /*
783    =============
784    Undo_Redo
785    =============
786  */
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" );
791                 return;
792         }
793
794         undo_t *redo;
795         brush_t *pBrush, *pNextBrush;
796         entity_t *pEntity, *pNextEntity, *pRedoEntity;
797
798         if ( !g_lastredo ) {
799                 Sys_Printf( "Nothing left to redo.\n" );
800                 return;
801         }
802         if ( g_lastundo ) {
803                 if ( !g_lastundo->done ) {
804                         Sys_Printf( "WARNING: last undo not finished.\n" );
805                 }
806         }
807         // get the last redo
808         redo = g_lastredo;
809         if ( g_lastredo->prev ) {
810                 g_lastredo->prev->next = NULL;
811         }
812         else{g_redolist = NULL; }
813         g_lastredo = g_lastredo->prev;
814         //
815         Undo_GeneralStart( redo->operation );
816         // remove current selection
817         Select_Deselect();
818         // move "created" brushes back to the last undo
819         for ( pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush )
820         {
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 );
829                 }
830         }
831         // move "created" entities back to the last undo
832         for ( pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity )
833         {
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 )
838                         {
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;
845                                 }
846                         }
847                         //
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 );
853                 }
854         }
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 )
857         {
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 );
864                 }
865                 else
866                 {
867                         Entity_RemoveFromList( pEntity );
868                         Entity_AddToList( pEntity, &entities );
869                 }
870         }
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 )
873         {
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
877                 {
878                         if ( pEntity->entityId == pBrush->ownerId ) {
879                                 Entity_LinkBrush( pEntity, pBrush );
880                                 break;
881                         }
882                 }
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 );
886                 }
887                 //build the brush
888                 //Brush_Build(pBrush);
889                 Select_Brush( pBrush );
890         }
891         //
892         Undo_End();
893         //
894         Sys_Printf( "%s redone.\n", redo->operation );
895         //
896         g_redoId--;
897         // free the undo
898         free( redo );
899         //
900         g_bScreenUpdates = true;
901         UpdateSurfaceDialog();
902         Sys_UpdateWindows( W_ALL );
903 }
904
905 /*
906    =============
907    Undo_RedoAvailable
908    =============
909  */
910 int Undo_RedoAvailable( void ){
911         if ( g_lastredo ) {
912                 return true;
913         }
914         return false;
915 }
916
917 int Undo_GetUndoId( void ){
918         if ( g_lastundo ) {
919                 return g_lastundo->id;
920         }
921         return 0;
922 }
923
924 /*
925    =============
926    Undo_UndoAvailable
927    =============
928  */
929 int Undo_UndoAvailable( void ){
930         if ( g_lastundo ) {
931                 if ( g_lastundo->done ) {
932                         return true;
933                 }
934         }
935         return false;
936 }