]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/undo.cpp
fix undo bug with entities (lights) - Markus Fischer
[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 {
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(const 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(const 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                 // http://github.com/mfn/GtkRadiant/commit/ee1ef98536470d5680bd9bfecc5b5c9a62ffe9ab
510                 if ( pBrush->owner->eclass->fixedsize == 1 ) {
511                   pBrush->owner->undoId = pBrush->undoId;
512                 }
513         }
514 }
515
516 /*
517 =============
518 Undo_AddEntity
519 =============
520 */
521 void Undo_AddEntity(entity_t *entity)
522 {
523         // spog - disable undo if undo levels = 0
524         if (g_PrefsDlg.m_nUndoLevels == 0)
525         {
526 #ifdef DBG_UNDO
527                 Sys_Printf("Undo_AddEntity: undo is disabled.\n");
528 #endif
529                 return;
530         }
531
532
533         entity_t* pClone;
534
535         if (!g_lastundo)
536         {
537                 Sys_Printf("Undo_AddEntity: no last undo.\n");
538                 return;
539         }
540         //if the entity is already in the undo
541         if (Undo_EntityInUndo(g_lastundo, entity))
542                 return;
543         //clone the 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;
549         //
550         Entity_AddToList(pClone, &g_lastundo->entitylist);
551         //
552         g_undoMemorySize += Entity_MemorySize(pClone);
553 }
554
555 /*
556 =============
557 Undo_EndEntity
558 =============
559 */
560 void Undo_EndEntity(entity_t *entity)
561 {
562         // spog - disable undo if undo levels = 0
563         if (g_PrefsDlg.m_nUndoLevels == 0)
564         {
565 #ifdef DBG_UNDO
566                 Sys_Printf("Undo_EndEntity: undo is disabled.\n");
567 #endif
568                 return;
569         }
570
571
572         if (!g_lastundo)
573         {
574 #ifdef _DEBUG
575                 Sys_Printf("Undo_End: no last undo.\n");
576 #endif
577                 return;
578         }
579         if (g_lastundo->done)
580         {
581 #ifdef _DEBUG
582                 Sys_Printf("Undo_End: last undo already finished.\n");
583 #endif
584                 return;
585         }
586         if (entity == world_entity)
587         {
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
591                 return;
592         }
593         entity->undoId = g_lastundo->id;
594 }
595
596 /*
597 =============
598 Undo_End
599 =============
600 */
601 void Undo_End(void)
602 {
603         // spog - disable undo if undo levels = 0
604         if (g_PrefsDlg.m_nUndoLevels == 0)
605         {
606 #ifdef DBG_UNDO
607                 Sys_Printf("Undo_End: undo is disabled.\n");
608 #endif
609                 return;
610         }
611
612
613         if (!g_lastundo)
614         {
615                 //Sys_Printf("Undo_End: no last undo.\n");
616                 return;
617         }
618         if (g_lastundo->done)
619         {
620                 //Sys_Printf("Undo_End: last undo already finished.\n");
621                 return;
622         }
623         g_lastundo->done = true;
624
625         //undo memory size is bound to a max
626         while (g_undoMemorySize > g_undoMaxMemorySize)
627         {
628                 //always keep one undo
629                 if (g_undolist == g_lastundo) break;
630                 Undo_FreeFirstUndo();
631         }
632         //
633         //Sys_Printf("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);
634 }
635
636 /*
637 =============
638 Undo_Undo
639 =============
640 */
641 void Undo_Undo(boolean bSilent)
642 {
643         // spog - disable undo if undo levels = 0
644         if (g_PrefsDlg.m_nUndoLevels == 0)
645         {
646                 Sys_Printf("Undo_Undo: undo is disabled.\n");
647                 return;
648         }
649
650         undo_t *undo, *redo;
651         brush_t *pBrush, *pNextBrush;
652         entity_t *pEntity, *pNextEntity, *pUndoEntity;
653
654         if (!g_lastundo)
655         {
656                 Sys_Printf("Nothing left to undo.\n");
657                 return;
658         }
659         if (!g_lastundo->done)
660         {
661                 Sys_Printf("Undo_Undo: WARNING: last undo not yet finished!\n");
662         }
663         // get the last undo
664         undo = g_lastundo;
665         if (g_lastundo->prev) g_lastundo->prev->next = NULL;
666         else g_undolist = NULL;
667         g_lastundo = g_lastundo->prev;
668
669         //allocate a new redo
670         redo = (undo_t *) malloc(sizeof(undo_t));
671         if (!redo) return;
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;
680         redo->next = NULL;
681         g_lastredo = redo;
682         redo->time = Sys_DoubleTime();
683         redo->id = g_redoId++;
684         redo->done = true;
685         redo->operation = undo->operation;
686
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)
689         {
690                 if (pBrush->redoId == redo->id)
691                 {
692                         pBrush->redoId = 0;
693                 }
694         }
695         for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)
696         {
697                 if (pBrush->redoId == redo->id)
698                 {
699                         pBrush->redoId = 0;
700                 }
701         }
702         //reset the redo IDs of all entities using thew new ID
703         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)
704         {
705                 if (pEntity->redoId == redo->id)
706                 {
707                         pEntity->redoId = 0;
708                 }
709         }
710
711         // deselect current sutff
712         Select_Deselect();
713         // move "created" brushes to the redo
714         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush=pNextBrush)
715         {
716                 pNextBrush = pBrush->next;
717                 if (pBrush->undoId == undo->id)
718                 {
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);
727                 }
728         }
729         // move "created" entities to the redo
730         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
731         {
732                 pNextEntity = pEntity->next;
733                 if (pEntity->undoId == undo->id)
734                 {
735                         // check if this entity is in the undo
736                         for (pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next)
737                         {
738                                 // move brushes to the undo entity
739                                 if (pUndoEntity->entityId == pEntity->entityId)
740                                 {
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;
745                                 }
746                         }
747                         //
748                         //Entity_Free(pEntity);
749                         //move the entity to the redo
750                         Entity_RemoveFromList(pEntity);
751                         Entity_AddToList(pEntity, &redo->entitylist);
752                 }
753         }
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)
756         {
757                 g_undoMemorySize -= Entity_MemorySize(pEntity);
758                 //if this is the world entity
759                 if (pEntity->entityId == world_entity->entityId)
760                 {
761                         epair_t* tmp = world_entity->epairs;
762                         world_entity->epairs = pEntity->epairs;
763       pEntity->epairs = tmp;
764                         Entity_Free(pEntity);
765                 }
766                 else
767                 {
768                         Entity_RemoveFromList(pEntity);
769                         Entity_AddToList(pEntity, &entities);
770                         pEntity->redoId = redo->id;
771                 }
772         }
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)
775         {
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
781                 {
782                         //Sys_Printf("Entity ID: %i\n",pEntity->entityId);
783                         if (pEntity->entityId == pBrush->ownerId)
784                         {
785                                 Entity_LinkBrush(pEntity, pBrush);
786                                 break;
787                         }
788                 }
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)
792                 {
793                         Entity_LinkBrush(world_entity, pBrush);
794                 }
795                 //build the brush
796                 //Brush_Build(pBrush);
797                 Select_Brush(pBrush);
798                 pBrush->redoId = redo->id;
799     }
800         if (!bSilent)
801                 Sys_Printf("%s undone.\n", undo->operation);
802         // free the undo
803         g_undoMemorySize -= sizeof(undo_t);
804         free(undo);
805         g_undoSize--;
806         g_undoId--;
807         if (g_undoId <= 0) g_undoId = 2 * g_undoMaxSize;
808         //
809     g_bScreenUpdates = true;
810     UpdateSurfaceDialog();
811     Sys_UpdateWindows(W_ALL);
812 }
813
814 /*
815 =============
816 Undo_Redo
817 =============
818 */
819 void Undo_Redo(void)
820 {
821         // spog - disable undo if undo levels = 0
822         if (g_PrefsDlg.m_nUndoLevels == 0)
823         {
824                 Sys_Printf("Undo_Redo: undo is disabled.\n");
825                 return;
826         }
827
828         undo_t *redo;
829         brush_t *pBrush, *pNextBrush;
830         entity_t *pEntity, *pNextEntity, *pRedoEntity;
831
832         if (!g_lastredo)
833         {
834                 Sys_Printf("Nothing left to redo.\n");
835                 return;
836         }
837         if (g_lastundo)
838         {
839                 if (!g_lastundo->done)
840                 {
841                         Sys_Printf("WARNING: last undo not finished.\n");
842                 }
843         }
844         // get the last redo
845         redo = g_lastredo;
846         if (g_lastredo->prev) g_lastredo->prev->next = NULL;
847         else g_redolist = NULL;
848         g_lastredo = g_lastredo->prev;
849         //
850         Undo_GeneralStart(redo->operation);
851         // remove current selection
852         Select_Deselect();
853         // move "created" brushes back to the last undo
854         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush)
855         {
856                 pNextBrush = pBrush->next;
857                 if (pBrush->redoId == redo->id)
858                 {
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);
865                 }
866         }
867         // move "created" entities back to the last undo
868         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)
869         {
870                 pNextEntity = pEntity->next;
871                 if (pEntity->redoId == redo->id)
872                 {
873                         // check if this entity is in the redo
874                         for (pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next)
875                         {
876                                 // move brushes to the redo entity
877                                 if (pRedoEntity->entityId == pEntity->entityId)
878                                 {
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;
883                                 }
884                         }
885                         //
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);
891                 }
892         }
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)
895         {
896                 //if this is the world entity
897                 if (pEntity->entityId == world_entity->entityId)
898                 {
899       epair_t* tmp = world_entity->epairs;
900                         world_entity->epairs = pEntity->epairs;
901       pEntity->epairs = tmp;
902                         Entity_Free(pEntity);
903                 }
904                 else
905                 {
906                         Entity_RemoveFromList(pEntity);
907                         Entity_AddToList(pEntity, &entities);
908                 }
909         }
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)
912         {
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
916                 {
917                         if (pEntity->entityId == pBrush->ownerId)
918                         {
919                                 Entity_LinkBrush(pEntity, pBrush);
920                                 break;
921                         }
922                 }
923                 //if the brush is not linked then it should be linked into the world entity
924                 if (pEntity == NULL || pEntity == &entities)
925                 {
926                         Entity_LinkBrush(world_entity, pBrush);
927                 }
928                 //build the brush
929                 //Brush_Build(pBrush);
930                 Select_Brush(pBrush);
931     }
932         //
933         Undo_End();
934         //
935         Sys_Printf("%s redone.\n", redo->operation);
936         //
937         g_redoId--;
938         // free the undo
939         free(redo);
940         //
941     g_bScreenUpdates = true;
942     UpdateSurfaceDialog();
943     Sys_UpdateWindows(W_ALL);
944 }
945
946 /*
947 =============
948 Undo_RedoAvailable
949 =============
950 */
951 int Undo_RedoAvailable(void)
952 {
953         if (g_lastredo) return true;
954         return false;
955 }
956
957 int Undo_GetUndoId(void)
958 {
959         if (g_lastundo)
960                 return g_lastundo->id;
961         return 0;
962 }
963
964 /*
965 =============
966 Undo_UndoAvailable
967 =============
968 */
969 int Undo_UndoAvailable(void)
970 {
971         if (g_lastundo)
972         {
973                 if (g_lastundo->done)
974                         return true;
975         }
976         return false;
977 }