]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/undo.cpp
apply patch from Martin Gerhardy - more quake2 related modules compiled and misc...
[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         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 {
71         return g_undoMemorySize;
72 }
73
74 /*
75 =============
76 Undo_ClearRedo
77 =============
78 */
79 void Undo_ClearRedo(void)
80 {
81         undo_t *redo, *nextredo;
82         brush_t *pBrush, *pNextBrush;
83         entity_t *pEntity, *pNextEntity;
84
85         for (redo = g_redolist; redo; redo = nextredo)
86         {
87                 nextredo = redo->next;
88                 for (pBrush = redo->brushlist.next ; pBrush != NULL && pBrush != &redo->brushlist ; pBrush = pNextBrush)
89                 {
90                         pNextBrush = pBrush->next;
91                         Brush_Free(pBrush);
92                 }
93                 for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = pNextEntity)
94                 {
95                         pNextEntity = pEntity->next;
96                         Entity_Free(pEntity);
97                 }
98                 free(redo);
99         }
100         g_redolist = NULL;
101         g_lastredo = NULL;
102         g_redoId = 1;
103 }
104
105 /*
106 =============
107 Undo_Clear
108
109   Clears the undo buffer.
110 =============
111 */
112 void Undo_Clear(void)
113 {
114         undo_t *undo, *nextundo;
115         brush_t *pBrush, *pNextBrush;
116         entity_t *pEntity, *pNextEntity;
117
118         Undo_ClearRedo();
119         for (undo = g_undolist; undo; undo = nextundo)
120         {
121                 nextundo = undo->next;
122                 for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)
123                 {
124                         pNextBrush = pBrush->next;
125                         g_undoMemorySize -= Brush_MemorySize(pBrush);
126                         Brush_Free(pBrush);
127                 }
128                 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)
129                 {
130                         pNextEntity = pEntity->next;
131                         g_undoMemorySize -= Entity_MemorySize(pEntity);
132                         Entity_Free(pEntity);
133                 }
134                 g_undoMemorySize -= sizeof(undo_t);
135                 free(undo);
136         }
137         g_undolist = NULL;
138         g_lastundo = NULL;
139         g_undoSize = 0;
140         g_undoMemorySize = 0;
141         g_undoId = 1;
142 }
143
144 /*
145 =============
146 Undo_SetMaxSize
147 =============
148 */
149 void Undo_SetMaxSize(int size)
150 {
151         Undo_Clear();
152         if (size < 1) g_undoMaxSize = 1;
153         else g_undoMaxSize = size;
154 }
155
156 /*
157 =============
158 Undo_GetMaxSize
159 =============
160 */
161 int Undo_GetMaxSize(void)
162 {
163         return g_undoMaxSize;
164 }
165
166 /*
167 =============
168 Undo_SetMaxMemorySize
169 =============
170 */
171 void Undo_SetMaxMemorySize(int size)
172 {
173         Undo_Clear();
174         if (size < 1024) g_undoMaxMemorySize = 1024;
175         else g_undoMaxMemorySize = size;
176 }
177
178 /*
179 =============
180 Undo_GetMaxMemorySize
181 =============
182 */
183 int Undo_GetMaxMemorySize(void)
184 {
185         return g_undoMaxMemorySize;
186 }
187
188 /*
189 =============
190 Undo_FreeFirstUndo
191 =============
192 */
193 void Undo_FreeFirstUndo(void)
194 {
195         undo_t *undo;
196         brush_t *pBrush, *pNextBrush;
197         entity_t *pEntity, *pNextEntity;
198
199         //remove the oldest undo from the undo buffer
200         undo = g_undolist;
201         g_undolist = g_undolist->next;
202         g_undolist->prev = NULL;
203         //
204         for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)
205         {
206                 pNextBrush = pBrush->next;
207                 g_undoMemorySize -= Brush_MemorySize(pBrush);
208                 Brush_Free(pBrush);
209         }
210         for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)
211         {
212                 pNextEntity = pEntity->next;
213                 g_undoMemorySize -= Entity_MemorySize(pEntity);
214                 Entity_Free(pEntity);
215         }
216         g_undoMemorySize -= sizeof(undo_t);
217         free(undo);
218         g_undoSize--;
219 }
220
221 /*
222 =============
223 Undo_GeneralStart
224 =============
225 */
226 void Undo_GeneralStart(char *operation)
227 {
228         undo_t *undo;
229         brush_t *pBrush;
230         entity_t *pEntity;
231
232
233         if (g_lastundo)
234         {
235                 if (!g_lastundo->done)
236                 {
237                         Sys_Printf("Undo_Start: WARNING last undo not finished.\n");
238                 }
239         }
240
241         undo = (undo_t *) malloc(sizeof(undo_t));
242         if (!undo) 
243     return;
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;
249         if (g_lastundo)
250     g_lastundo->next = undo;
251         else
252     g_undolist = undo;
253         undo->prev = g_lastundo;
254         undo->next = NULL;
255         g_lastundo = undo;
256         
257         undo->time = Sys_DoubleTime();
258         //
259         if (g_undoId > g_undoMaxSize * 2) g_undoId = 1;
260         if (g_undoId <= 0) g_undoId = 1;
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                 {
269                         pBrush->undoId = 0;
270                 }
271         }
272         for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
273         {
274                 if (pBrush->undoId == undo->id)
275                 {
276                         pBrush->undoId = 0;
277                 }
278         }
279         //reset the undo IDs of all entities using thew new ID
280         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
281         {
282                 if (pEntity->undoId == undo->id)
283                 {
284                         pEntity->undoId = 0;
285                 }
286         }
287         g_undoMemorySize += sizeof(undo_t);
288         g_undoSize++;
289         //undo buffer is bound to a max
290         if (g_undoSize > g_undoMaxSize)
291         {
292                 Undo_FreeFirstUndo();
293         }
294 }
295
296 /*
297 =============
298 Undo_BrushInUndo
299 =============
300 */
301 int Undo_BrushInUndo(undo_t *undo, brush_t *brush)
302 {
303 /*      brush_t *b;
304
305         for (b = undo->brushlist.next; b != &undo->brushlist; b = b->next)
306         {
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;
310         }*/
311   // Arnout: function is pointless right now, see above explanation
312         return false;
313 }
314
315 /*
316 =============
317 Undo_EntityInUndo
318 =============
319 */
320 int Undo_EntityInUndo(undo_t *undo, entity_t *ent)
321 {
322         entity_t *e;
323
324         for (e = undo->entitylist.next; e != &undo->entitylist; e = e->next)
325         {
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;
329         }
330         return false;
331 }
332
333 /*
334 =============
335 Undo_Start
336 =============
337 */
338 void Undo_Start(char *operation)
339 {
340         // spog - disable undo if undo levels = 0
341         if (g_PrefsDlg.m_nUndoLevels == 0)
342         {
343 #ifdef DBG_UNDO
344                 Sys_Printf("Undo_Start: undo is disabled.\n");
345 #endif
346                 return;
347         }
348         
349         Undo_ClearRedo();
350         Undo_GeneralStart(operation);
351 }
352
353 /*
354 =============
355 Undo_AddBrush
356 =============
357 */
358 void Undo_AddBrush(brush_t *pBrush)
359 {
360         // spog - disable undo if undo levels = 0
361         if (g_PrefsDlg.m_nUndoLevels == 0)
362         {
363 #ifdef DBG_UNDO
364                 Sys_Printf("Undo_AddBrush: undo is disabled.\n");
365 #endif
366                 return;
367         }
368         
369         if (!g_lastundo)
370         {
371                 Sys_Printf("Undo_AddBrushList: no last undo.\n");
372                 return;
373         }
374         if (g_lastundo->entitylist.next != &g_lastundo->entitylist)
375         {
376                 Sys_Printf("Undo_AddBrushList: WARNING adding brushes after entity.\n");
377         }
378         //if the brush is already in the undo
379         if (Undo_BrushInUndo(g_lastundo, pBrush))
380                 return;
381         //clone the brush
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);
388         //
389         g_undoMemorySize += Brush_MemorySize(pClone);
390 }
391
392 /*
393 =============
394 Undo_AddBrushList
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.
400 =============
401 */
402 void Undo_AddBrushList(brush_t *brushlist)
403 {
404         // spog - disable undo if undo levels = 0
405         if (g_PrefsDlg.m_nUndoLevels == 0)
406         {
407 #ifdef DBG_UNDO
408                 Sys_Printf("Undo_AddBrushList: undo is disabled.\n");
409 #endif
410                 return;
411         }
412         
413         brush_t *pBrush;
414
415         if (!g_lastundo)
416         {
417                 Sys_Printf("Undo_AddBrushList: no last undo.\n");
418                 return;
419         }
420         if (g_lastundo->entitylist.next != &g_lastundo->entitylist)
421         {
422                 Sys_Printf("Undo_AddBrushList: WARNING adding brushes after entity.\n");
423         }
424         //copy the brushes to the undo
425         for (pBrush = brushlist->next ; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)
426         {
427                 //if the brush is already in the undo
428                 //++timo FIXME: when does this happen?
429                 if (Undo_BrushInUndo(g_lastundo, pBrush))
430                         continue;
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 );
437                 // clone the brush
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);
446         }
447 }
448
449 /*
450 =============
451 Undo_EndBrush
452 =============
453 */
454 void Undo_EndBrush(brush_t *pBrush)
455 {
456         // spog - disable undo if undo levels = 0
457         if (g_PrefsDlg.m_nUndoLevels == 0)
458         {
459 #ifdef DBG_UNDO
460                 Sys_Printf("Undo_EndBrush: undo is disabled.\n");
461 #endif
462                 return;
463         }
464         
465
466         if (!g_lastundo)
467         {
468                 //Sys_Printf("Undo_End: no last undo.\n");
469                 return;
470         }
471         if (g_lastundo->done)
472         {
473                 //Sys_Printf("Undo_End: last undo already finished.\n");
474                 return;
475         }
476         pBrush->undoId = g_lastundo->id;
477 }
478
479 /*
480 =============
481 Undo_EndBrushList
482 =============
483 */
484 void Undo_EndBrushList(brush_t *brushlist)
485 {
486         // spog - disable undo if undo levels = 0
487         if (g_PrefsDlg.m_nUndoLevels == 0)
488         {
489 #ifdef DBG_UNDO
490                 Sys_Printf("Undo_EndBrushList: undo is disabled.\n");
491 #endif
492                 return;
493         }
494         
495
496         if (!g_lastundo)
497         {
498                 //Sys_Printf("Undo_End: no last undo.\n");
499                 return;
500         }
501         if (g_lastundo->done)
502         {
503                 //Sys_Printf("Undo_End: last undo already finished.\n");
504                 return;
505         }
506         for (brush_t* pBrush = brushlist->next; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)
507         {
508                 pBrush->undoId = g_lastundo->id;
509         }
510 }
511
512 /*
513 =============
514 Undo_AddEntity
515 =============
516 */
517 void Undo_AddEntity(entity_t *entity)
518 {
519         // spog - disable undo if undo levels = 0
520         if (g_PrefsDlg.m_nUndoLevels == 0)
521         {
522 #ifdef DBG_UNDO
523                 Sys_Printf("Undo_AddEntity: undo is disabled.\n");
524 #endif
525                 return;
526         }
527         
528
529         entity_t* pClone;
530
531         if (!g_lastundo)
532         {
533                 Sys_Printf("Undo_AddEntity: no last undo.\n");
534                 return;
535         }
536         //if the entity is already in the undo
537         if (Undo_EntityInUndo(g_lastundo, entity))
538                 return;
539         //clone the 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;
545         //
546         Entity_AddToList(pClone, &g_lastundo->entitylist);
547         //
548         g_undoMemorySize += Entity_MemorySize(pClone);
549 }
550
551 /*
552 =============
553 Undo_EndEntity
554 =============
555 */
556 void Undo_EndEntity(entity_t *entity)
557 {
558         // spog - disable undo if undo levels = 0
559         if (g_PrefsDlg.m_nUndoLevels == 0)
560         {
561 #ifdef DBG_UNDO
562                 Sys_Printf("Undo_EndEntity: undo is disabled.\n");
563 #endif
564                 return;
565         }
566
567
568         if (!g_lastundo)
569         {
570 #ifdef _DEBUG
571                 Sys_Printf("Undo_End: no last undo.\n");
572 #endif
573                 return;
574         }
575         if (g_lastundo->done)
576         {
577 #ifdef _DEBUG
578                 Sys_Printf("Undo_End: last undo already finished.\n");
579 #endif
580                 return;
581         }
582         if (entity == world_entity)
583         {
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
587                 return;
588         }
589         entity->undoId = g_lastundo->id;
590 }
591
592 /*
593 =============
594 Undo_End
595 =============
596 */
597 void Undo_End(void)
598 {
599         // spog - disable undo if undo levels = 0
600         if (g_PrefsDlg.m_nUndoLevels == 0)
601         {
602 #ifdef DBG_UNDO
603                 Sys_Printf("Undo_End: undo is disabled.\n");
604 #endif
605                 return;
606         }
607         
608
609         if (!g_lastundo)
610         {
611                 //Sys_Printf("Undo_End: no last undo.\n");
612                 return;
613         }
614         if (g_lastundo->done)
615         {
616                 //Sys_Printf("Undo_End: last undo already finished.\n");
617                 return;
618         }
619         g_lastundo->done = true;
620
621         //undo memory size is bound to a max
622         while (g_undoMemorySize > g_undoMaxMemorySize)
623         {
624                 //always keep one undo
625                 if (g_undolist == g_lastundo) break;
626                 Undo_FreeFirstUndo();
627         }
628         //
629         //Sys_Printf("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);
630 }
631
632 /*
633 =============
634 Undo_Undo
635 =============
636 */
637 void Undo_Undo(boolean bSilent)
638 {
639         // spog - disable undo if undo levels = 0
640         if (g_PrefsDlg.m_nUndoLevels == 0)
641         {
642                 Sys_Printf("Undo_Undo: undo is disabled.\n");
643                 return;
644         }
645         
646         undo_t *undo, *redo;
647         brush_t *pBrush, *pNextBrush;
648         entity_t *pEntity, *pNextEntity, *pUndoEntity;
649
650         if (!g_lastundo)
651         {
652                 Sys_Printf("Nothing left to undo.\n");
653                 return;
654         }
655         if (!g_lastundo->done)
656         {
657                 Sys_Printf("Undo_Undo: WARNING: last undo not yet finished!\n");
658         }
659         // get the last undo
660         undo = g_lastundo;
661         if (g_lastundo->prev) g_lastundo->prev->next = NULL;
662         else g_undolist = NULL;
663         g_lastundo = g_lastundo->prev;
664
665         //allocate a new redo
666         redo = (undo_t *) malloc(sizeof(undo_t));
667         if (!redo) return;
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;
676         redo->next = NULL;
677         g_lastredo = redo;
678         redo->time = Sys_DoubleTime();
679         redo->id = g_redoId++;
680         redo->done = true;
681         redo->operation = undo->operation;
682
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)
685         {
686                 if (pBrush->redoId == redo->id)
687                 {
688                         pBrush->redoId = 0;
689                 }
690         }
691         for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
692         {
693                 if (pBrush->redoId == redo->id)
694                 {
695                         pBrush->redoId = 0;
696                 }
697         }
698         //reset the redo IDs of all entities using thew new ID
699         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
700         {
701                 if (pEntity->redoId == redo->id)
702                 {
703                         pEntity->redoId = 0;
704                 }
705         }
706
707         // deselect current sutff
708         Select_Deselect();
709         // move "created" brushes to the redo
710         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush=pNextBrush)
711         {
712                 pNextBrush = pBrush->next;
713                 if (pBrush->undoId == undo->id)
714                 {
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);
723                 }
724         }
725         // move "created" entities to the redo
726         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
727         {
728                 pNextEntity = pEntity->next;
729                 if (pEntity->undoId == undo->id)
730                 {
731                         // check if this entity is in the undo
732                         for (pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next)
733                         {
734                                 // move brushes to the undo entity
735                                 if (pUndoEntity->entityId == pEntity->entityId)
736                                 {
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;
741                                 }
742                         }
743                         //
744                         //Entity_Free(pEntity);
745                         //move the entity to the redo
746                         Entity_RemoveFromList(pEntity);
747                         Entity_AddToList(pEntity, &redo->entitylist);
748                 }
749         }
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)
752         {
753                 g_undoMemorySize -= Entity_MemorySize(pEntity);
754                 //if this is the world entity
755                 if (pEntity->entityId == world_entity->entityId)
756                 {
757                         epair_t* tmp = world_entity->epairs;
758                         world_entity->epairs = pEntity->epairs;
759       pEntity->epairs = tmp;
760                         Entity_Free(pEntity);
761                 }
762                 else
763                 {
764                         Entity_RemoveFromList(pEntity);
765                         Entity_AddToList(pEntity, &entities);
766                         pEntity->redoId = redo->id;
767                 }
768         }
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)
771         {
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
777                 {
778                         //Sys_Printf("Entity ID: %i\n",pEntity->entityId);
779                         if (pEntity->entityId == pBrush->ownerId)
780                         {
781                                 Entity_LinkBrush(pEntity, pBrush);
782                                 break;
783                         }
784                 }
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)
788                 {
789                         Entity_LinkBrush(world_entity, pBrush);
790                 }
791                 //build the brush
792                 //Brush_Build(pBrush);
793                 Select_Brush(pBrush);
794                 pBrush->redoId = redo->id;
795     }
796         if (!bSilent)
797                 Sys_Printf("%s undone.\n", undo->operation);
798         // free the undo
799         g_undoMemorySize -= sizeof(undo_t);
800         free(undo);
801         g_undoSize--;
802         g_undoId--;
803         if (g_undoId <= 0) g_undoId = 2 * g_undoMaxSize;
804         //
805     g_bScreenUpdates = true; 
806     UpdateSurfaceDialog();
807     Sys_UpdateWindows(W_ALL);
808 }
809
810 /*
811 =============
812 Undo_Redo
813 =============
814 */
815 void Undo_Redo(void)
816 {
817         // spog - disable undo if undo levels = 0
818         if (g_PrefsDlg.m_nUndoLevels == 0)
819         {
820                 Sys_Printf("Undo_Redo: undo is disabled.\n");
821                 return;
822         }
823
824         undo_t *redo;
825         brush_t *pBrush, *pNextBrush;
826         entity_t *pEntity, *pNextEntity, *pRedoEntity;
827
828         if (!g_lastredo)
829         {
830                 Sys_Printf("Nothing left to redo.\n");
831                 return;
832         }
833         if (g_lastundo)
834         {
835                 if (!g_lastundo->done)
836                 {
837                         Sys_Printf("WARNING: last undo not finished.\n");
838                 }
839         }
840         // get the last redo
841         redo = g_lastredo;
842         if (g_lastredo->prev) g_lastredo->prev->next = NULL;
843         else g_redolist = NULL;
844         g_lastredo = g_lastredo->prev;
845         //
846         Undo_GeneralStart(redo->operation);
847         // remove current selection
848         Select_Deselect();
849         // move "created" brushes back to the last undo
850         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush)
851         {
852                 pNextBrush = pBrush->next;
853                 if (pBrush->redoId == redo->id)
854                 {
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);
861                 }
862         }
863         // move "created" entities back to the last undo
864         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
865         {
866                 pNextEntity = pEntity->next;
867                 if (pEntity->redoId == redo->id)
868                 {
869                         // check if this entity is in the redo
870                         for (pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next)
871                         {
872                                 // move brushes to the redo entity
873                                 if (pRedoEntity->entityId == pEntity->entityId)
874                                 {
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;
879                                 }
880                         }
881                         //
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);
887                 }
888         }
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)
891         {
892                 //if this is the world entity
893                 if (pEntity->entityId == world_entity->entityId)
894                 {
895       epair_t* tmp = world_entity->epairs;
896                         world_entity->epairs = pEntity->epairs;
897       pEntity->epairs = tmp;
898                         Entity_Free(pEntity);
899                 }
900                 else
901                 {
902                         Entity_RemoveFromList(pEntity);
903                         Entity_AddToList(pEntity, &entities);
904                 }
905         }
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)
908         {
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
912                 {
913                         if (pEntity->entityId == pBrush->ownerId)
914                         {
915                                 Entity_LinkBrush(pEntity, pBrush);
916                                 break;
917                         }
918                 }
919                 //if the brush is not linked then it should be linked into the world entity
920                 if (pEntity == NULL || pEntity == &entities)
921                 {
922                         Entity_LinkBrush(world_entity, pBrush);
923                 }
924                 //build the brush
925                 //Brush_Build(pBrush);
926                 Select_Brush(pBrush);
927     }
928         //
929         Undo_End();
930         //
931         Sys_Printf("%s redone.\n", redo->operation);
932         //
933         g_redoId--;
934         // free the undo
935         free(redo);
936         //
937     g_bScreenUpdates = true; 
938     UpdateSurfaceDialog();
939     Sys_UpdateWindows(W_ALL);
940 }
941
942 /*
943 =============
944 Undo_RedoAvailable
945 =============
946 */
947 int Undo_RedoAvailable(void)
948 {
949         if (g_lastredo) return true;
950         return false;
951 }
952
953 int Undo_GetUndoId(void)
954 {
955         if (g_lastundo)
956                 return g_lastundo->id;
957         return 0;
958 }
959
960 /*
961 =============
962 Undo_UndoAvailable
963 =============
964 */
965 int Undo_UndoAvailable(void)
966 {
967         if (g_lastundo)
968         {
969                 if (g_lastundo->done)
970                         return true;
971         }
972         return false;
973 }