1 //-----------------------------------------------------------------------------
2 // The user-visible undo/redo operation; whenever they change something, we
3 // record our state and push it on a stack, and we pop the stack when they
4 // select undo.
5 //
6 // Copyright 2008-2013 Jonathan Westhues.
7 //-----------------------------------------------------------------------------
8 #include "solvespace.h"
9 
UndoRemember(void)10 void SolveSpaceUI::UndoRemember(void) {
11     unsaved = true;
12     PushFromCurrentOnto(&undo);
13     UndoClearStack(&redo);
14     UndoEnableMenus();
15 }
16 
UndoUndo(void)17 void SolveSpaceUI::UndoUndo(void) {
18     if(undo.cnt <= 0) return;
19 
20     PushFromCurrentOnto(&redo);
21     PopOntoCurrentFrom(&undo);
22     UndoEnableMenus();
23 }
24 
UndoRedo(void)25 void SolveSpaceUI::UndoRedo(void) {
26     if(redo.cnt <= 0) return;
27 
28     PushFromCurrentOnto(&undo);
29     PopOntoCurrentFrom(&redo);
30     UndoEnableMenus();
31 }
32 
UndoEnableMenus(void)33 void SolveSpaceUI::UndoEnableMenus(void) {
34     EnableMenuById(GraphicsWindow::MNU_UNDO, undo.cnt > 0);
35     EnableMenuById(GraphicsWindow::MNU_REDO, redo.cnt > 0);
36 }
37 
PushFromCurrentOnto(UndoStack * uk)38 void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
39     int i;
40 
41     if(uk->cnt == MAX_UNDO) {
42         UndoClearState(&(uk->d[uk->write]));
43         // And then write in to this one again
44     } else {
45         (uk->cnt)++;
46     }
47 
48     UndoState *ut = &(uk->d[uk->write]);
49     *ut = {};
50     for(i = 0; i < SK.group.n; i++) {
51         Group *src = &(SK.group.elem[i]);
52         Group dest = *src;
53         // And then clean up all the stuff that needs to be a deep copy,
54         // and zero out all the dynamic stuff that will get regenerated.
55         dest.clean = false;
56         dest.solved = {};
57         dest.polyLoops = {};
58         dest.bezierLoops = {};
59         dest.bezierOpens = {};
60         dest.polyError = {};
61         dest.thisMesh = {};
62         dest.runningMesh = {};
63         dest.thisShell = {};
64         dest.runningShell = {};
65         dest.displayMesh = {};
66         dest.displayEdges = {};
67         dest.displayOutlines = {};
68 
69         dest.remap = {};
70         src->remap.DeepCopyInto(&(dest.remap));
71 
72         dest.impMesh = {};
73         dest.impShell = {};
74         dest.impEntity = {};
75         ut->group.Add(&dest);
76     }
77     for(i = 0; i < SK.groupOrder.n; i++) {
78         ut->groupOrder.Add(&(SK.groupOrder.elem[i]));
79     }
80     for(i = 0; i < SK.request.n; i++) {
81         ut->request.Add(&(SK.request.elem[i]));
82     }
83     for(i = 0; i < SK.constraint.n; i++) {
84         Constraint *src = &(SK.constraint.elem[i]);
85         Constraint dest = *src;
86         dest.dogd = {};
87         ut->constraint.Add(&dest);
88     }
89     for(i = 0; i < SK.param.n; i++) {
90         ut->param.Add(&(SK.param.elem[i]));
91     }
92     for(i = 0; i < SK.style.n; i++) {
93         ut->style.Add(&(SK.style.elem[i]));
94     }
95     ut->activeGroup = SS.GW.activeGroup;
96 
97     uk->write = WRAP(uk->write + 1, MAX_UNDO);
98 }
99 
PopOntoCurrentFrom(UndoStack * uk)100 void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
101     int i;
102 
103     if(uk->cnt <= 0) oops();
104     (uk->cnt)--;
105     uk->write = WRAP(uk->write - 1, MAX_UNDO);
106 
107     UndoState *ut = &(uk->d[uk->write]);
108 
109     // Free everything in the main copy of the program before replacing it
110     for(i = 0; i < SK.groupOrder.n; i++) {
111         Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
112         g->Clear();
113     }
114     SK.group.Clear();
115     SK.groupOrder.Clear();
116     SK.request.Clear();
117     SK.constraint.Clear();
118     SK.param.Clear();
119     SK.style.Clear();
120 
121     // And then do a shallow copy of the state from the undo list
122     ut->group.MoveSelfInto(&(SK.group));
123     for(i = 0; i < ut->groupOrder.n; i++)
124         SK.groupOrder.Add(&ut->groupOrder.elem[i]);
125     ut->request.MoveSelfInto(&(SK.request));
126     ut->constraint.MoveSelfInto(&(SK.constraint));
127     ut->param.MoveSelfInto(&(SK.param));
128     ut->style.MoveSelfInto(&(SK.style));
129     SS.GW.activeGroup = ut->activeGroup;
130 
131     // No need to free it, since a shallow copy was made above
132     *ut = {};
133 
134     // And reset the state everywhere else in the program, since the
135     // sketch just changed a lot.
136     SS.GW.ClearSuper();
137     SS.TW.ClearSuper();
138     SS.ReloadAllImported();
139     SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
140     SS.ScheduleShowTW();
141 
142     // Activate the group that was active before.
143     Group *activeGroup = SK.GetGroup(SS.GW.activeGroup);
144     activeGroup->Activate();
145 }
146 
UndoClearStack(UndoStack * uk)147 void SolveSpaceUI::UndoClearStack(UndoStack *uk) {
148     while(uk->cnt > 0) {
149         uk->write = WRAP(uk->write - 1, MAX_UNDO);
150         (uk->cnt)--;
151         UndoClearState(&(uk->d[uk->write]));
152     }
153     *uk = {}; // for good measure
154 }
155 
UndoClearState(UndoState * ut)156 void SolveSpaceUI::UndoClearState(UndoState *ut) {
157     int i;
158     for(i = 0; i < ut->group.n; i++) {
159         Group *g = &(ut->group.elem[i]);
160 
161         g->remap.Clear();
162     }
163     ut->group.Clear();
164     ut->request.Clear();
165     ut->constraint.Clear();
166     ut->param.Clear();
167     ut->style.Clear();
168     *ut = {};
169 }
170 
171