//----------------------------------------------------------------------------- // The user-visible undo/redo operation; whenever they change something, we // record our state and push it on a stack, and we pop the stack when they // select undo. // // Copyright 2008-2013 Jonathan Westhues. //----------------------------------------------------------------------------- #include "solvespace.h" void SolveSpaceUI::UndoRemember() { unsaved = true; PushFromCurrentOnto(&undo); UndoClearStack(&redo); UndoEnableMenus(); } void SolveSpaceUI::UndoUndo() { if(undo.cnt <= 0) return; PushFromCurrentOnto(&redo); PopOntoCurrentFrom(&undo); UndoEnableMenus(); } void SolveSpaceUI::UndoRedo() { if(redo.cnt <= 0) return; PushFromCurrentOnto(&undo); PopOntoCurrentFrom(&redo); UndoEnableMenus(); } void SolveSpaceUI::UndoEnableMenus() { SS.GW.undoMenuItem->SetEnabled(undo.cnt > 0); SS.GW.redoMenuItem->SetEnabled(redo.cnt > 0); } void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) { if(uk->cnt == MAX_UNDO) { UndoClearState(&(uk->d[uk->write])); // And then write in to this one again } else { (uk->cnt)++; } UndoState *ut = &(uk->d[uk->write]); *ut = {}; ut->group.ReserveMore(SK.group.n); for(Group &src : SK.group) { // Shallow copy Group dest(src); // And then clean up all the stuff that needs to be a deep copy, // and zero out all the dynamic stuff that will get regenerated. dest.clean = false; dest.solved = {}; dest.polyLoops = {}; dest.bezierLoops = {}; dest.bezierOpens = {}; dest.polyError = {}; dest.thisMesh = {}; dest.runningMesh = {}; dest.thisShell = {}; dest.runningShell = {}; dest.displayMesh = {}; dest.displayOutlines = {}; dest.remap = src.remap; dest.impMesh = {}; dest.impShell = {}; dest.impEntity = {}; ut->group.Add(&dest); } for(auto &src : SK.groupOrder) { ut->groupOrder.Add(&src); } ut->request.ReserveMore(SK.request.n); for(auto &src : SK.request) { ut->request.Add(&src); } ut->constraint.ReserveMore(SK.constraint.n); for(auto &src : SK.constraint) { // Shallow copy Constraint dest(src); ut->constraint.Add(&dest); } ut->param.ReserveMore(SK.param.n); for(auto &src : SK.param) { ut->param.Add(&src); } ut->style.ReserveMore(SK.style.n); for(auto &src : SK.style) { ut->style.Add(&src); } ut->activeGroup = SS.GW.activeGroup; uk->write = WRAP(uk->write + 1, MAX_UNDO); } void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) { ssassert(uk->cnt > 0, "Cannot pop from empty undo stack"); (uk->cnt)--; uk->write = WRAP(uk->write - 1, MAX_UNDO); UndoState *ut = &(uk->d[uk->write]); // Free everything in the main copy of the program before replacing it for(hGroup hg : SK.groupOrder) { Group *g = SK.GetGroup(hg); g->Clear(); } SK.group.Clear(); SK.groupOrder.Clear(); SK.request.Clear(); SK.constraint.Clear(); SK.param.Clear(); SK.style.Clear(); // And then do a shallow copy of the state from the undo list ut->group.MoveSelfInto(&(SK.group)); for(auto &gh : ut->groupOrder) { SK.groupOrder.Add(&gh); } ut->request.MoveSelfInto(&(SK.request)); ut->constraint.MoveSelfInto(&(SK.constraint)); ut->param.MoveSelfInto(&(SK.param)); ut->style.MoveSelfInto(&(SK.style)); SS.GW.activeGroup = ut->activeGroup; // No need to free it, since a shallow copy was made above *ut = {}; // And reset the state everywhere else in the program, since the // sketch just changed a lot. SS.GW.ClearSuper(); SS.TW.ClearSuper(); SS.ReloadAllLinked(SS.saveFile); SS.GenerateAll(SolveSpaceUI::Generate::ALL); SS.ScheduleShowTW(); // Activate the group that was active before. Group *activeGroup = SK.GetGroup(SS.GW.activeGroup); activeGroup->Activate(); } void SolveSpaceUI::UndoClearStack(UndoStack *uk) { while(uk->cnt > 0) { uk->write = WRAP(uk->write - 1, MAX_UNDO); (uk->cnt)--; UndoClearState(&(uk->d[uk->write])); } *uk = {}; // for good measure } void SolveSpaceUI::UndoClearState(UndoState *ut) { for(auto &g : ut->group) { g.remap.clear(); } ut->group.Clear(); ut->request.Clear(); ut->constraint.Clear(); ut->param.Clear(); ut->style.Clear(); *ut = {}; }