#include #include #include #include #include namespace rack { namespace history { ComplexAction::~ComplexAction() { for (Action* action : actions) { delete action; } } void ComplexAction::undo() { for (auto it = actions.rbegin(); it != actions.rend(); it++) { Action* action = *it; action->undo(); } } void ComplexAction::redo() { for (Action* action : actions) { action->redo(); } } void ComplexAction::push(Action* action) { actions.push_back(action); } bool ComplexAction::isEmpty() { return actions.empty(); } ModuleAdd::~ModuleAdd() { json_decref(moduleJ); } void ModuleAdd::setModule(app::ModuleWidget* mw) { assert(mw); model = mw->getModel(); assert(mw->getModule()); moduleId = mw->getModule()->id; pos = mw->box.pos; // ModuleAdd doesn't *really* need the state to be serialized, although ModuleRemove certainly does. // However, creating a module may give it a nondeterministic initial state for whatever reason, so serialize anyway. moduleJ = APP->engine->moduleToJson(mw->getModule()); } void ModuleAdd::undo() { app::ModuleWidget* mw = APP->scene->rack->getModule(moduleId); if (!mw) return; APP->scene->rack->removeModule(mw); delete mw; } void ModuleAdd::redo() { engine::Module* module = model->createModule(); module->id = moduleId; try { module->fromJson(moduleJ); } catch (Exception& e) { WARN("%s", e.what()); } APP->engine->addModule(module); app::ModuleWidget* mw = model->createModuleWidget(module); mw->box.pos = pos; APP->scene->rack->addModule(mw); } void ModuleMove::undo() { app::ModuleWidget* mw = APP->scene->rack->getModule(moduleId); if (!mw) return; APP->scene->rack->requestModulePos(mw, oldPos); } void ModuleMove::redo() { app::ModuleWidget* mw = APP->scene->rack->getModule(moduleId); if (!mw) return; APP->scene->rack->requestModulePos(mw, newPos); } void ModuleBypass::undo() { engine::Module* module = APP->engine->getModule(moduleId); if (!module) return; APP->engine->bypassModule(module, !bypassed); } void ModuleBypass::redo() { engine::Module* module = APP->engine->getModule(moduleId); if (!module) return; APP->engine->bypassModule(module, bypassed); } ModuleChange::~ModuleChange() { json_decref(oldModuleJ); json_decref(newModuleJ); } void ModuleChange::undo() { engine::Module* module = APP->engine->getModule(moduleId); if (!module) return; APP->engine->moduleFromJson(module, oldModuleJ); } void ModuleChange::redo() { engine::Module* module = APP->engine->getModule(moduleId); if (!module) return; APP->engine->moduleFromJson(module, newModuleJ); } void ParamChange::undo() { engine::Module* module = APP->engine->getModule(moduleId); if (!module) return; APP->engine->setParamValue(module, paramId, oldValue); } void ParamChange::redo() { engine::Module* module = APP->engine->getModule(moduleId); if (!module) return; APP->engine->setParamValue(module, paramId, newValue); } void CableAdd::setCable(app::CableWidget* cw) { assert(cw); assert(cw->cable); assert(cw->cable->id >= 0); cableId = cw->cable->id; assert(cw->cable->outputModule); outputModuleId = cw->cable->outputModule->id; outputId = cw->cable->outputId; assert(cw->cable->inputModule); inputModuleId = cw->cable->inputModule->id; inputId = cw->cable->inputId; color = cw->color; } void CableAdd::undo() { app::CableWidget* cw = APP->scene->rack->getCable(cableId); if (!cw) return; APP->scene->rack->removeCable(cw); delete cw; } void CableAdd::redo() { engine::Cable* cable = new engine::Cable; cable->id = cableId; cable->inputModule = APP->engine->getModule(inputModuleId); if (!cable->inputModule) { delete cable; return; } cable->inputId = inputId; cable->outputModule = APP->engine->getModule(outputModuleId); if (!cable->outputModule) { delete cable; return; } cable->outputId = outputId; APP->engine->addCable(cable); app::CableWidget* cw = new app::CableWidget; cw->setCable(cable); cw->color = color; APP->scene->rack->addCable(cw); } State::State() { clear(); } State::~State() { clear(); } void State::clear() { for (Action* action : actions) { delete action; } actions.clear(); actionIndex = 0; savedIndex = -1; } void State::push(Action* action) { // Delete all future actions (if we have undone some actions) for (int i = actionIndex; i < (int) actions.size(); i++) { delete actions[i]; } actions.resize(actionIndex); // Delete actions from beginning if limit is reached static const int limit = 500; int n = (int) actions.size() - limit + 1; if (n > 0) { for (int i = 0; i < n; i++) { delete actions[i]; } actions.erase(actions.begin(), actions.begin() + n); actionIndex -= n; savedIndex -= n; } // Push action actions.push_back(action); actionIndex++; // Unset the savedIndex if we just permanently overwrote the saved state if (actionIndex == savedIndex) { savedIndex = -1; } } void State::undo() { if (canUndo()) { actionIndex--; actions[actionIndex]->undo(); } } void State::redo() { if (canRedo()) { actions[actionIndex]->redo(); actionIndex++; } } bool State::canUndo() { return actionIndex > 0; } bool State::canRedo() { return actionIndex < (int) actions.size(); } std::string State::getUndoName() { if (!canUndo()) return ""; return actions[actionIndex - 1]->name; } std::string State::getRedoName() { if (!canRedo()) return ""; return actions[actionIndex]->name; } void State::setSaved() { savedIndex = actionIndex; } bool State::isSaved() { return actionIndex == savedIndex; } } // namespace history } // namespace rack