]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/undo.cpp
get the basics of a new scons build system together
[xonotic/netradiant.git] / radiant / undo.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 \r
23 /*\r
24 \r
25   QERadiant Undo/Redo\r
26 \r
27 \r
28 basic setup:\r
29 \r
30 <-g_undolist---------g_lastundo> <---map data---> <-g_lastredo---------g_redolist->\r
31 \r
32 \r
33   undo/redo on the world_entity is special, only the epair changes are remembered\r
34   and the world entity never gets deleted.\r
35 \r
36   FIXME: maybe reset the Undo system at map load\r
37                  maybe also reset the entityId at map load\r
38 */\r
39 \r
40 #include "stdafx.h"\r
41 \r
42 typedef struct undo_s\r
43 {\r
44         double time;                            //time operation was performed\r
45         int id;                                         //every undo has an unique id\r
46         int done;                                       //true when undo is build\r
47         char *operation;                        //name of the operation\r
48         brush_t brushlist;                      //deleted brushes\r
49         entity_t entitylist;            //deleted entities\r
50         struct undo_s *prev, *next;     //next and prev undo in list\r
51 } undo_t;\r
52 \r
53 undo_t *g_undolist;                                             //first undo in the list\r
54 undo_t *g_lastundo;                                             //last undo in the list\r
55 undo_t *g_redolist;                                             //first redo in the list\r
56 undo_t *g_lastredo;                                             //last undo in list\r
57 int g_undoMaxSize = 64;                                 //maximum number of undos\r
58 int g_undoSize = 0;                                             //number of undos in the list\r
59 int g_undoMaxMemorySize = 2*1024*1024;  //maximum undo memory (default 2 MB)\r
60 int g_undoMemorySize = 0;                               //memory size of undo buffer\r
61 int g_undoId = 1;                                               //current undo ID (zero is invalid id)\r
62 int g_redoId = 1;                                               //current redo ID (zero is invalid id)\r
63 \r
64 /*\r
65 =============\r
66 Undo_MemorySize\r
67 =============\r
68 */\r
69 int Undo_MemorySize(void)\r
70 {\r
71         return g_undoMemorySize;\r
72 }\r
73 \r
74 /*\r
75 =============\r
76 Undo_ClearRedo\r
77 =============\r
78 */\r
79 void Undo_ClearRedo(void)\r
80 {\r
81         undo_t *redo, *nextredo;\r
82         brush_t *pBrush, *pNextBrush;\r
83         entity_t *pEntity, *pNextEntity;\r
84 \r
85         for (redo = g_redolist; redo; redo = nextredo)\r
86         {\r
87                 nextredo = redo->next;\r
88                 for (pBrush = redo->brushlist.next ; pBrush != NULL && pBrush != &redo->brushlist ; pBrush = pNextBrush)\r
89                 {\r
90                         pNextBrush = pBrush->next;\r
91                         Brush_Free(pBrush);\r
92                 }\r
93                 for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = pNextEntity)\r
94                 {\r
95                         pNextEntity = pEntity->next;\r
96                         Entity_Free(pEntity);\r
97                 }\r
98                 free(redo);\r
99         }\r
100         g_redolist = NULL;\r
101         g_lastredo = NULL;\r
102         g_redoId = 1;\r
103 }\r
104 \r
105 /*\r
106 =============\r
107 Undo_Clear\r
108 \r
109   Clears the undo buffer.\r
110 =============\r
111 */\r
112 void Undo_Clear(void)\r
113 {\r
114         undo_t *undo, *nextundo;\r
115         brush_t *pBrush, *pNextBrush;\r
116         entity_t *pEntity, *pNextEntity;\r
117 \r
118         Undo_ClearRedo();\r
119         for (undo = g_undolist; undo; undo = nextundo)\r
120         {\r
121                 nextundo = undo->next;\r
122                 for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)\r
123                 {\r
124                         pNextBrush = pBrush->next;\r
125                         g_undoMemorySize -= Brush_MemorySize(pBrush);\r
126                         Brush_Free(pBrush);\r
127                 }\r
128                 for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)\r
129                 {\r
130                         pNextEntity = pEntity->next;\r
131                         g_undoMemorySize -= Entity_MemorySize(pEntity);\r
132                         Entity_Free(pEntity);\r
133                 }\r
134                 g_undoMemorySize -= sizeof(undo_t);\r
135                 free(undo);\r
136         }\r
137         g_undolist = NULL;\r
138         g_lastundo = NULL;\r
139         g_undoSize = 0;\r
140         g_undoMemorySize = 0;\r
141         g_undoId = 1;\r
142 }\r
143 \r
144 /*\r
145 =============\r
146 Undo_SetMaxSize\r
147 =============\r
148 */\r
149 void Undo_SetMaxSize(int size)\r
150 {\r
151         Undo_Clear();\r
152         if (size < 1) g_undoMaxSize = 1;\r
153         else g_undoMaxSize = size;\r
154 }\r
155 \r
156 /*\r
157 =============\r
158 Undo_GetMaxSize\r
159 =============\r
160 */\r
161 int Undo_GetMaxSize(void)\r
162 {\r
163         return g_undoMaxSize;\r
164 }\r
165 \r
166 /*\r
167 =============\r
168 Undo_SetMaxMemorySize\r
169 =============\r
170 */\r
171 void Undo_SetMaxMemorySize(int size)\r
172 {\r
173         Undo_Clear();\r
174         if (size < 1024) g_undoMaxMemorySize = 1024;\r
175         else g_undoMaxMemorySize = size;\r
176 }\r
177 \r
178 /*\r
179 =============\r
180 Undo_GetMaxMemorySize\r
181 =============\r
182 */\r
183 int Undo_GetMaxMemorySize(void)\r
184 {\r
185         return g_undoMaxMemorySize;\r
186 }\r
187 \r
188 /*\r
189 =============\r
190 Undo_FreeFirstUndo\r
191 =============\r
192 */\r
193 void Undo_FreeFirstUndo(void)\r
194 {\r
195         undo_t *undo;\r
196         brush_t *pBrush, *pNextBrush;\r
197         entity_t *pEntity, *pNextEntity;\r
198 \r
199         //remove the oldest undo from the undo buffer\r
200         undo = g_undolist;\r
201         g_undolist = g_undolist->next;\r
202         g_undolist->prev = NULL;\r
203         //\r
204         for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush)\r
205         {\r
206                 pNextBrush = pBrush->next;\r
207                 g_undoMemorySize -= Brush_MemorySize(pBrush);\r
208                 Brush_Free(pBrush);\r
209         }\r
210         for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity)\r
211         {\r
212                 pNextEntity = pEntity->next;\r
213                 g_undoMemorySize -= Entity_MemorySize(pEntity);\r
214                 Entity_Free(pEntity);\r
215         }\r
216         g_undoMemorySize -= sizeof(undo_t);\r
217         free(undo);\r
218         g_undoSize--;\r
219 }\r
220 \r
221 /*\r
222 =============\r
223 Undo_GeneralStart\r
224 =============\r
225 */\r
226 void Undo_GeneralStart(char *operation)\r
227 {\r
228         undo_t *undo;\r
229         brush_t *pBrush;\r
230         entity_t *pEntity;\r
231 \r
232 \r
233         if (g_lastundo)\r
234         {\r
235                 if (!g_lastundo->done)\r
236                 {\r
237                         Sys_Printf("Undo_Start: WARNING last undo not finished.\n");\r
238                 }\r
239         }\r
240 \r
241         undo = (undo_t *) malloc(sizeof(undo_t));\r
242         if (!undo) \r
243     return;\r
244         memset(undo, 0, sizeof(undo_t));\r
245         undo->brushlist.next = &undo->brushlist;\r
246         undo->brushlist.prev = &undo->brushlist;\r
247         undo->entitylist.next = &undo->entitylist;\r
248         undo->entitylist.prev = &undo->entitylist;\r
249         if (g_lastundo)\r
250     g_lastundo->next = undo;\r
251         else\r
252     g_undolist = undo;\r
253         undo->prev = g_lastundo;\r
254         undo->next = NULL;\r
255         g_lastundo = undo;\r
256         \r
257         undo->time = Sys_DoubleTime();\r
258         //\r
259         if (g_undoId > g_undoMaxSize * 2) g_undoId = 1;\r
260         if (g_undoId <= 0) g_undoId = 1;\r
261         undo->id = g_undoId++;\r
262         undo->done = false;\r
263         undo->operation = operation;\r
264         //reset the undo IDs of all brushes using the new ID\r
265         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next)\r
266         {\r
267                 if (pBrush->undoId == undo->id)\r
268                 {\r
269                         pBrush->undoId = 0;\r
270                 }\r
271         }\r
272         for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)\r
273         {\r
274                 if (pBrush->undoId == undo->id)\r
275                 {\r
276                         pBrush->undoId = 0;\r
277                 }\r
278         }\r
279         //reset the undo IDs of all entities using thew new ID\r
280         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)\r
281         {\r
282                 if (pEntity->undoId == undo->id)\r
283                 {\r
284                         pEntity->undoId = 0;\r
285                 }\r
286         }\r
287         g_undoMemorySize += sizeof(undo_t);\r
288         g_undoSize++;\r
289         //undo buffer is bound to a max\r
290         if (g_undoSize > g_undoMaxSize)\r
291         {\r
292                 Undo_FreeFirstUndo();\r
293         }\r
294 }\r
295 \r
296 /*\r
297 =============\r
298 Undo_BrushInUndo\r
299 =============\r
300 */\r
301 int Undo_BrushInUndo(undo_t *undo, brush_t *brush)\r
302 {\r
303 /*      brush_t *b;\r
304 \r
305         for (b = undo->brushlist.next; b != &undo->brushlist; b = b->next)\r
306         {\r
307     // Arnout: NOTE - can't do a pointer compare as the brushes get cloned into the undo brushlist, and not just referenced from it\r
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.\r
309                 if (b == brush) return true;\r
310         }*/\r
311   // Arnout: function is pointless right now, see above explanation\r
312         return false;\r
313 }\r
314 \r
315 /*\r
316 =============\r
317 Undo_EntityInUndo\r
318 =============\r
319 */\r
320 int Undo_EntityInUndo(undo_t *undo, entity_t *ent)\r
321 {\r
322         entity_t *e;\r
323 \r
324         for (e = undo->entitylist.next; e != &undo->entitylist; e = e->next)\r
325         {\r
326     // Arnout: NOTE - can't do a pointer compare as the entities get cloned into the undo entitylist, and not just referenced from it\r
327                 //if (e == ent) return true;\r
328     if( e->entityId == ent->entityId ) return true;\r
329         }\r
330         return false;\r
331 }\r
332 \r
333 /*\r
334 =============\r
335 Undo_Start\r
336 =============\r
337 */\r
338 void Undo_Start(char *operation)\r
339 {\r
340         // spog - disable undo if undo levels = 0\r
341         if (g_PrefsDlg.m_nUndoLevels == 0)\r
342         {\r
343 #ifdef DBG_UNDO\r
344                 Sys_Printf("Undo_Start: undo is disabled.\n");\r
345 #endif\r
346                 return;\r
347         }\r
348         \r
349         Undo_ClearRedo();\r
350         Undo_GeneralStart(operation);\r
351 }\r
352 \r
353 /*\r
354 =============\r
355 Undo_AddBrush\r
356 =============\r
357 */\r
358 void Undo_AddBrush(brush_t *pBrush)\r
359 {\r
360         // spog - disable undo if undo levels = 0\r
361         if (g_PrefsDlg.m_nUndoLevels == 0)\r
362         {\r
363 #ifdef DBG_UNDO\r
364                 Sys_Printf("Undo_AddBrush: undo is disabled.\n");\r
365 #endif\r
366                 return;\r
367         }\r
368         \r
369         if (!g_lastundo)\r
370         {\r
371                 Sys_Printf("Undo_AddBrushList: no last undo.\n");\r
372                 return;\r
373         }\r
374         if (g_lastundo->entitylist.next != &g_lastundo->entitylist)\r
375         {\r
376                 Sys_Printf("Undo_AddBrushList: WARNING adding brushes after entity.\n");\r
377         }\r
378         //if the brush is already in the undo\r
379         if (Undo_BrushInUndo(g_lastundo, pBrush))\r
380                 return;\r
381         //clone the brush\r
382         brush_t* pClone = Brush_FullClone(pBrush);\r
383         //save the ID of the owner entity\r
384         pClone->ownerId = pBrush->owner->entityId;\r
385         //save the old undo ID for previous undos\r
386         pClone->undoId = pBrush->undoId;\r
387         Brush_AddToList (pClone, &g_lastundo->brushlist);\r
388         //\r
389         g_undoMemorySize += Brush_MemorySize(pClone);\r
390 }\r
391 \r
392 /*\r
393 =============\r
394 Undo_AddBrushList\r
395 TTimo: some brushes are just there for UI, and the information is somewhere else\r
396 for patches it's in the patchMesh_t structure, so when we clone the brush we get that information (brush_t::pPatch)\r
397 but: models are stored in pBrush->owner->md3Class, and owner epairs and origin parameters are important\r
398   so, we detect models and push the entity in the undo session (as well as it's BBox brush)\r
399         same for other items like weapons and ammo etc.\r
400 =============\r
401 */\r
402 void Undo_AddBrushList(brush_t *brushlist)\r
403 {\r
404         // spog - disable undo if undo levels = 0\r
405         if (g_PrefsDlg.m_nUndoLevels == 0)\r
406         {\r
407 #ifdef DBG_UNDO\r
408                 Sys_Printf("Undo_AddBrushList: undo is disabled.\n");\r
409 #endif\r
410                 return;\r
411         }\r
412         \r
413         brush_t *pBrush;\r
414 \r
415         if (!g_lastundo)\r
416         {\r
417                 Sys_Printf("Undo_AddBrushList: no last undo.\n");\r
418                 return;\r
419         }\r
420         if (g_lastundo->entitylist.next != &g_lastundo->entitylist)\r
421         {\r
422                 Sys_Printf("Undo_AddBrushList: WARNING adding brushes after entity.\n");\r
423         }\r
424         //copy the brushes to the undo\r
425         for (pBrush = brushlist->next ; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)\r
426         {\r
427                 //if the brush is already in the undo\r
428                 //++timo FIXME: when does this happen?\r
429                 if (Undo_BrushInUndo(g_lastundo, pBrush))\r
430                         continue;\r
431                 // do we need to store this brush's entity in the undo?\r
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\r
433                 // what we want to store for undo is the owner entity, epairs and origin/angle stuff\r
434                 //++timo FIXME: if the entity is not fixed size I don't know, so I don't do it yet\r
435                 if (pBrush->owner->eclass->fixedsize == 1)\r
436                         Undo_AddEntity( pBrush->owner );\r
437                 // clone the brush\r
438                 brush_t* pClone = Brush_FullClone(pBrush);\r
439                 // save the ID of the owner entity\r
440                 pClone->ownerId = pBrush->owner->entityId;\r
441                 // save the old undo ID from previous undos\r
442                 pClone->undoId = pBrush->undoId;\r
443                 Brush_AddToList (pClone, &g_lastundo->brushlist);\r
444                 // track memory size used by undo\r
445                 g_undoMemorySize += Brush_MemorySize(pClone);\r
446         }\r
447 }\r
448 \r
449 /*\r
450 =============\r
451 Undo_EndBrush\r
452 =============\r
453 */\r
454 void Undo_EndBrush(brush_t *pBrush)\r
455 {\r
456         // spog - disable undo if undo levels = 0\r
457         if (g_PrefsDlg.m_nUndoLevels == 0)\r
458         {\r
459 #ifdef DBG_UNDO\r
460                 Sys_Printf("Undo_EndBrush: undo is disabled.\n");\r
461 #endif\r
462                 return;\r
463         }\r
464         \r
465 \r
466         if (!g_lastundo)\r
467         {\r
468                 //Sys_Printf("Undo_End: no last undo.\n");\r
469                 return;\r
470         }\r
471         if (g_lastundo->done)\r
472         {\r
473                 //Sys_Printf("Undo_End: last undo already finished.\n");\r
474                 return;\r
475         }\r
476         pBrush->undoId = g_lastundo->id;\r
477 }\r
478 \r
479 /*\r
480 =============\r
481 Undo_EndBrushList\r
482 =============\r
483 */\r
484 void Undo_EndBrushList(brush_t *brushlist)\r
485 {\r
486         // spog - disable undo if undo levels = 0\r
487         if (g_PrefsDlg.m_nUndoLevels == 0)\r
488         {\r
489 #ifdef DBG_UNDO\r
490                 Sys_Printf("Undo_EndBrushList: undo is disabled.\n");\r
491 #endif\r
492                 return;\r
493         }\r
494         \r
495 \r
496         if (!g_lastundo)\r
497         {\r
498                 //Sys_Printf("Undo_End: no last undo.\n");\r
499                 return;\r
500         }\r
501         if (g_lastundo->done)\r
502         {\r
503                 //Sys_Printf("Undo_End: last undo already finished.\n");\r
504                 return;\r
505         }\r
506         for (brush_t* pBrush = brushlist->next; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next)\r
507         {\r
508                 pBrush->undoId = g_lastundo->id;\r
509         }\r
510 }\r
511 \r
512 /*\r
513 =============\r
514 Undo_AddEntity\r
515 =============\r
516 */\r
517 void Undo_AddEntity(entity_t *entity)\r
518 {\r
519         // spog - disable undo if undo levels = 0\r
520         if (g_PrefsDlg.m_nUndoLevels == 0)\r
521         {\r
522 #ifdef DBG_UNDO\r
523                 Sys_Printf("Undo_AddEntity: undo is disabled.\n");\r
524 #endif\r
525                 return;\r
526         }\r
527         \r
528 \r
529         entity_t* pClone;\r
530 \r
531         if (!g_lastundo)\r
532         {\r
533                 Sys_Printf("Undo_AddEntity: no last undo.\n");\r
534                 return;\r
535         }\r
536         //if the entity is already in the undo\r
537         if (Undo_EntityInUndo(g_lastundo, entity))\r
538                 return;\r
539         //clone the entity\r
540         pClone = Entity_Clone(entity);\r
541         //save the old undo ID for previous undos\r
542         pClone->undoId = entity->undoId;\r
543         //save the entity ID (we need a full clone)\r
544         pClone->entityId = entity->entityId;\r
545         //\r
546         Entity_AddToList(pClone, &g_lastundo->entitylist);\r
547         //\r
548         g_undoMemorySize += Entity_MemorySize(pClone);\r
549 }\r
550 \r
551 /*\r
552 =============\r
553 Undo_EndEntity\r
554 =============\r
555 */\r
556 void Undo_EndEntity(entity_t *entity)\r
557 {\r
558         // spog - disable undo if undo levels = 0\r
559         if (g_PrefsDlg.m_nUndoLevels == 0)\r
560         {\r
561 #ifdef DBG_UNDO\r
562                 Sys_Printf("Undo_EndEntity: undo is disabled.\n");\r
563 #endif\r
564                 return;\r
565         }\r
566 \r
567 \r
568         if (!g_lastundo)\r
569         {\r
570 #ifdef _DEBUG\r
571                 Sys_Printf("Undo_End: no last undo.\n");\r
572 #endif\r
573                 return;\r
574         }\r
575         if (g_lastundo->done)\r
576         {\r
577 #ifdef _DEBUG\r
578                 Sys_Printf("Undo_End: last undo already finished.\n");\r
579 #endif\r
580                 return;\r
581         }\r
582         if (entity == world_entity)\r
583         {\r
584                 //Sys_Printf("Undo_AddEntity: undo on world entity.\n");\r
585                 //NOTE: we never delete the world entity when undoing an operation\r
586                 //              we only transfer the epairs\r
587                 return;\r
588         }\r
589         entity->undoId = g_lastundo->id;\r
590 }\r
591 \r
592 /*\r
593 =============\r
594 Undo_End\r
595 =============\r
596 */\r
597 void Undo_End(void)\r
598 {\r
599         // spog - disable undo if undo levels = 0\r
600         if (g_PrefsDlg.m_nUndoLevels == 0)\r
601         {\r
602 #ifdef DBG_UNDO\r
603                 Sys_Printf("Undo_End: undo is disabled.\n");\r
604 #endif\r
605                 return;\r
606         }\r
607         \r
608 \r
609         if (!g_lastundo)\r
610         {\r
611                 //Sys_Printf("Undo_End: no last undo.\n");\r
612                 return;\r
613         }\r
614         if (g_lastundo->done)\r
615         {\r
616                 //Sys_Printf("Undo_End: last undo already finished.\n");\r
617                 return;\r
618         }\r
619         g_lastundo->done = true;\r
620 \r
621         //undo memory size is bound to a max\r
622         while (g_undoMemorySize > g_undoMaxMemorySize)\r
623         {\r
624                 //always keep one undo\r
625                 if (g_undolist == g_lastundo) break;\r
626                 Undo_FreeFirstUndo();\r
627         }\r
628         //\r
629         //Sys_Printf("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize);\r
630 }\r
631 \r
632 /*\r
633 =============\r
634 Undo_Undo\r
635 =============\r
636 */\r
637 void Undo_Undo(boolean bSilent)\r
638 {\r
639         // spog - disable undo if undo levels = 0\r
640         if (g_PrefsDlg.m_nUndoLevels == 0)\r
641         {\r
642                 Sys_Printf("Undo_Undo: undo is disabled.\n");\r
643                 return;\r
644         }\r
645         \r
646         undo_t *undo, *redo;\r
647         brush_t *pBrush, *pNextBrush;\r
648         entity_t *pEntity, *pNextEntity, *pUndoEntity;\r
649 \r
650         if (!g_lastundo)\r
651         {\r
652                 Sys_Printf("Nothing left to undo.\n");\r
653                 return;\r
654         }\r
655         if (!g_lastundo->done)\r
656         {\r
657                 Sys_Printf("Undo_Undo: WARNING: last undo not yet finished!\n");\r
658         }\r
659         // get the last undo\r
660         undo = g_lastundo;\r
661         if (g_lastundo->prev) g_lastundo->prev->next = NULL;\r
662         else g_undolist = NULL;\r
663         g_lastundo = g_lastundo->prev;\r
664 \r
665         //allocate a new redo\r
666         redo = (undo_t *) malloc(sizeof(undo_t));\r
667         if (!redo) return;\r
668         memset(redo, 0, sizeof(undo_t));\r
669         redo->brushlist.next = &redo->brushlist;\r
670         redo->brushlist.prev = &redo->brushlist;\r
671         redo->entitylist.next = &redo->entitylist;\r
672         redo->entitylist.prev = &redo->entitylist;\r
673         if (g_lastredo) g_lastredo->next = redo;\r
674         else g_redolist = redo;\r
675         redo->prev = g_lastredo;\r
676         redo->next = NULL;\r
677         g_lastredo = redo;\r
678         redo->time = Sys_DoubleTime();\r
679         redo->id = g_redoId++;\r
680         redo->done = true;\r
681         redo->operation = undo->operation;\r
682 \r
683         //reset the redo IDs of all brushes using the new ID\r
684         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next)\r
685         {\r
686                 if (pBrush->redoId == redo->id)\r
687                 {\r
688                         pBrush->redoId = 0;\r
689                 }\r
690         }\r
691         for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next)\r
692         {\r
693                 if (pBrush->redoId == redo->id)\r
694                 {\r
695                         pBrush->redoId = 0;\r
696                 }\r
697         }\r
698         //reset the redo IDs of all entities using thew new ID\r
699         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next)\r
700         {\r
701                 if (pEntity->redoId == redo->id)\r
702                 {\r
703                         pEntity->redoId = 0;\r
704                 }\r
705         }\r
706 \r
707         // deselect current sutff\r
708         Select_Deselect();\r
709         // move "created" brushes to the redo\r
710         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush=pNextBrush)\r
711         {\r
712                 pNextBrush = pBrush->next;\r
713                 if (pBrush->undoId == undo->id)\r
714                 {\r
715                         //Brush_Free(pBrush);\r
716                         //move the brush to the redo\r
717                         Brush_RemoveFromList(pBrush);\r
718                         Brush_AddToList(pBrush, &redo->brushlist);\r
719                         //make sure the ID of the owner is stored\r
720                         pBrush->ownerId = pBrush->owner->entityId;\r
721                         //unlink the brush from the owner entity\r
722                         Entity_UnlinkBrush(pBrush);\r
723                 }\r
724         }\r
725         // move "created" entities to the redo\r
726         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)\r
727         {\r
728                 pNextEntity = pEntity->next;\r
729                 if (pEntity->undoId == undo->id)\r
730                 {\r
731                         // check if this entity is in the undo\r
732                         for (pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next)\r
733                         {\r
734                                 // move brushes to the undo entity\r
735                                 if (pUndoEntity->entityId == pEntity->entityId)\r
736                                 {\r
737                                         pUndoEntity->brushes.next = pEntity->brushes.next;\r
738                                         pUndoEntity->brushes.prev = pEntity->brushes.prev;\r
739                                         pEntity->brushes.next = &pEntity->brushes;\r
740                                         pEntity->brushes.prev = &pEntity->brushes;\r
741                                 }\r
742                         }\r
743                         //\r
744                         //Entity_Free(pEntity);\r
745                         //move the entity to the redo\r
746                         Entity_RemoveFromList(pEntity);\r
747                         Entity_AddToList(pEntity, &redo->entitylist);\r
748                 }\r
749         }\r
750         // add the undo entities back into the entity list\r
751         for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = undo->entitylist.next)\r
752         {\r
753                 g_undoMemorySize -= Entity_MemorySize(pEntity);\r
754                 //if this is the world entity\r
755                 if (pEntity->entityId == world_entity->entityId)\r
756                 {\r
757                         epair_t* tmp = world_entity->epairs;\r
758                         world_entity->epairs = pEntity->epairs;\r
759       pEntity->epairs = tmp;\r
760                         Entity_Free(pEntity);\r
761                 }\r
762                 else\r
763                 {\r
764                         Entity_RemoveFromList(pEntity);\r
765                         Entity_AddToList(pEntity, &entities);\r
766                         pEntity->redoId = redo->id;\r
767                 }\r
768         }\r
769         // add the undo brushes back into the selected brushes\r
770         for (pBrush = undo->brushlist.next; pBrush != NULL && pBrush != &undo->brushlist; pBrush = undo->brushlist.next)\r
771         {\r
772                 //Sys_Printf("Owner ID: %i\n",pBrush->ownerId);\r
773                 g_undoMemorySize -= Brush_MemorySize(pBrush);\r
774                 Brush_RemoveFromList(pBrush);\r
775         Brush_AddToList(pBrush, &active_brushes);\r
776                 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) // fixes broken undo on entities\r
777                 {\r
778                         //Sys_Printf("Entity ID: %i\n",pEntity->entityId);\r
779                         if (pEntity->entityId == pBrush->ownerId)\r
780                         {\r
781                                 Entity_LinkBrush(pEntity, pBrush);\r
782                                 break;\r
783                         }\r
784                 }\r
785                 //if the brush is not linked then it should be linked into the world entity\r
786                 //++timo FIXME: maybe not, maybe we've lost this entity's owner!\r
787                 if (pEntity == NULL || pEntity == &entities)\r
788                 {\r
789                         Entity_LinkBrush(world_entity, pBrush);\r
790                 }\r
791                 //build the brush\r
792                 //Brush_Build(pBrush);\r
793                 Select_Brush(pBrush);\r
794                 pBrush->redoId = redo->id;\r
795     }\r
796         if (!bSilent)\r
797                 Sys_Printf("%s undone.\n", undo->operation);\r
798         // free the undo\r
799         g_undoMemorySize -= sizeof(undo_t);\r
800         free(undo);\r
801         g_undoSize--;\r
802         g_undoId--;\r
803         if (g_undoId <= 0) g_undoId = 2 * g_undoMaxSize;\r
804         //\r
805     g_bScreenUpdates = true; \r
806     UpdateSurfaceDialog();\r
807     Sys_UpdateWindows(W_ALL);\r
808 }\r
809 \r
810 /*\r
811 =============\r
812 Undo_Redo\r
813 =============\r
814 */\r
815 void Undo_Redo(void)\r
816 {\r
817         // spog - disable undo if undo levels = 0\r
818         if (g_PrefsDlg.m_nUndoLevels == 0)\r
819         {\r
820                 Sys_Printf("Undo_Redo: undo is disabled.\n");\r
821                 return;\r
822         }\r
823 \r
824         undo_t *redo;\r
825         brush_t *pBrush, *pNextBrush;\r
826         entity_t *pEntity, *pNextEntity, *pRedoEntity;\r
827 \r
828         if (!g_lastredo)\r
829         {\r
830                 Sys_Printf("Nothing left to redo.\n");\r
831                 return;\r
832         }\r
833         if (g_lastundo)\r
834         {\r
835                 if (!g_lastundo->done)\r
836                 {\r
837                         Sys_Printf("WARNING: last undo not finished.\n");\r
838                 }\r
839         }\r
840         // get the last redo\r
841         redo = g_lastredo;\r
842         if (g_lastredo->prev) g_lastredo->prev->next = NULL;\r
843         else g_redolist = NULL;\r
844         g_lastredo = g_lastredo->prev;\r
845         //\r
846         Undo_GeneralStart(redo->operation);\r
847         // remove current selection\r
848         Select_Deselect();\r
849         // move "created" brushes back to the last undo\r
850         for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush)\r
851         {\r
852                 pNextBrush = pBrush->next;\r
853                 if (pBrush->redoId == redo->id)\r
854                 {\r
855                         //move the brush to the undo\r
856                         Brush_RemoveFromList(pBrush);\r
857                         Brush_AddToList(pBrush, &g_lastundo->brushlist);\r
858                         g_undoMemorySize += Brush_MemorySize(pBrush);\r
859                         pBrush->ownerId = pBrush->owner->entityId;\r
860                         Entity_UnlinkBrush(pBrush);\r
861                 }\r
862         }\r
863         // move "created" entities back to the last undo\r
864         for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity)\r
865         {\r
866                 pNextEntity = pEntity->next;\r
867                 if (pEntity->redoId == redo->id)\r
868                 {\r
869                         // check if this entity is in the redo\r
870                         for (pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next)\r
871                         {\r
872                                 // move brushes to the redo entity\r
873                                 if (pRedoEntity->entityId == pEntity->entityId)\r
874                                 {\r
875                                         pRedoEntity->brushes.next = pEntity->brushes.next;\r
876                                         pRedoEntity->brushes.prev = pEntity->brushes.prev;\r
877                                         pEntity->brushes.next = &pEntity->brushes;\r
878                                         pEntity->brushes.prev = &pEntity->brushes;\r
879                                 }\r
880                         }\r
881                         //\r
882                         //Entity_Free(pEntity);\r
883                         //move the entity to the redo\r
884                         Entity_RemoveFromList(pEntity);\r
885                         Entity_AddToList(pEntity, &g_lastundo->entitylist);\r
886                         g_undoMemorySize += Entity_MemorySize(pEntity);\r
887                 }\r
888         }\r
889         // add the undo entities back into the entity list\r
890         for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = redo->entitylist.next)\r
891         {\r
892                 //if this is the world entity\r
893                 if (pEntity->entityId == world_entity->entityId)\r
894                 {\r
895       epair_t* tmp = world_entity->epairs;\r
896                         world_entity->epairs = pEntity->epairs;\r
897       pEntity->epairs = tmp;\r
898                         Entity_Free(pEntity);\r
899                 }\r
900                 else\r
901                 {\r
902                         Entity_RemoveFromList(pEntity);\r
903                         Entity_AddToList(pEntity, &entities);\r
904                 }\r
905         }\r
906         // add the redo brushes back into the selected brushes\r
907         for (pBrush = redo->brushlist.next; pBrush != NULL && pBrush != &redo->brushlist; pBrush = redo->brushlist.next)\r
908         {\r
909                 Brush_RemoveFromList(pBrush);\r
910     Brush_AddToList(pBrush, &active_brushes);\r
911                 for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) // fixes broken undo on entities\r
912                 {\r
913                         if (pEntity->entityId == pBrush->ownerId)\r
914                         {\r
915                                 Entity_LinkBrush(pEntity, pBrush);\r
916                                 break;\r
917                         }\r
918                 }\r
919                 //if the brush is not linked then it should be linked into the world entity\r
920                 if (pEntity == NULL || pEntity == &entities)\r
921                 {\r
922                         Entity_LinkBrush(world_entity, pBrush);\r
923                 }\r
924                 //build the brush\r
925                 //Brush_Build(pBrush);\r
926                 Select_Brush(pBrush);\r
927     }\r
928         //\r
929         Undo_End();\r
930         //\r
931         Sys_Printf("%s redone.\n", redo->operation);\r
932         //\r
933         g_redoId--;\r
934         // free the undo\r
935         free(redo);\r
936         //\r
937     g_bScreenUpdates = true; \r
938     UpdateSurfaceDialog();\r
939     Sys_UpdateWindows(W_ALL);\r
940 }\r
941 \r
942 /*\r
943 =============\r
944 Undo_RedoAvailable\r
945 =============\r
946 */\r
947 int Undo_RedoAvailable(void)\r
948 {\r
949         if (g_lastredo) return true;\r
950         return false;\r
951 }\r
952 \r
953 int Undo_GetUndoId(void)\r
954 {\r
955         if (g_lastundo)\r
956                 return g_lastundo->id;\r
957         return 0;\r
958 }\r
959 \r
960 /*\r
961 =============\r
962 Undo_UndoAvailable\r
963 =============\r
964 */\r
965 int Undo_UndoAvailable(void)\r
966 {\r
967         if (g_lastundo)\r
968         {\r
969                 if (g_lastundo->done)\r
970                         return true;\r
971         }\r
972         return false;\r
973 }\r