Browse Source

Improve clarity and modularity of PatchManager methods.

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
68a875bbca
6 changed files with 125 additions and 105 deletions
  1. +17
    -4
      include/patch.hpp
  2. +1
    -1
      src/app/MenuBar.cpp
  3. +3
    -3
      src/app/Scene.cpp
  4. +17
    -2
      src/main.cpp
  5. +86
    -94
      src/patch.cpp
  6. +1
    -1
      src/window.cpp

+ 17
- 4
include/patch.hpp View File

@@ -15,19 +15,32 @@ struct PatchManager {


PatchManager(); PatchManager();
~PatchManager(); ~PatchManager();
void init(std::string path);
/** Resets the history and scroll position to prepare for a new patch. */
void reset(); void reset();
void resetDialog();
/** Clears the patch. */
void clear();
/** Saves the patch and nothing else. */
void save(std::string path); void save(std::string path);
void saveDialog(); void saveDialog();
void saveAsDialog(); void saveAsDialog();
void saveTemplateDialog(); void saveTemplateDialog();
void saveAutosave();
/** Loads a patch and nothing else.
Returns whether the patch was loaded successfully.
*/
bool load(std::string path); bool load(std::string path);
/** Loads the template patch. */
void loadTemplate();
void loadTemplateDialog();
void loadAutosave();
/** Loads a patch, sets the current path, and updates the recent patches. */
void loadAction(std::string path);
void loadDialog(); void loadDialog();
void loadPathDialog(std::string path); void loadPathDialog(std::string path);
/** If `lastPath` is defined, ask the user to reload it */
/** Asks the user to reload the current patch. */
void revertDialog(); void revertDialog();
/** Disconnects all cables */
void pushRecentPath(std::string path);
/** Disconnects all cables. */
void disconnectDialog(); void disconnectDialog();


json_t* toJson(); json_t* toJson();


+ 1
- 1
src/app/MenuBar.cpp View File

@@ -80,7 +80,7 @@ struct FolderItem : ui::MenuItem {


struct NewItem : ui::MenuItem { struct NewItem : ui::MenuItem {
void onAction(const event::Action& e) override { void onAction(const event::Action& e) override {
APP->patch->resetDialog();
APP->patch->loadTemplateDialog();
} }
}; };




+ 3
- 3
src/app/Scene.cpp View File

@@ -62,12 +62,12 @@ void Scene::step() {
menuBar->box.size.x = box.size.x; menuBar->box.size.x = box.size.x;
rackScroll->box.size = box.size.minus(rackScroll->box.pos); rackScroll->box.size = box.size.minus(rackScroll->box.pos);


// Autosave every 15 seconds
// Autosave periodically
if (settings::autosavePeriod > 0.0) { if (settings::autosavePeriod > 0.0) {
double time = glfwGetTime(); double time = glfwGetTime();
if (time - lastAutosaveTime >= settings::autosavePeriod) { if (time - lastAutosaveTime >= settings::autosavePeriod) {
lastAutosaveTime = time; lastAutosaveTime = time;
APP->patch->save(asset::autosavePath);
APP->patch->saveAutosave();
settings::save(asset::settingsPath); settings::save(asset::settingsPath);
} }
} }
@@ -96,7 +96,7 @@ void Scene::onHoverKey(const event::HoverKey& e) {


if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
if (e.key == GLFW_KEY_N && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { if (e.key == GLFW_KEY_N && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
APP->patch->resetDialog();
APP->patch->loadTemplateDialog();
e.consume(this); e.consume(this);
} }
else if (e.key == GLFW_KEY_Q && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { else if (e.key == GLFW_KEY_Q && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {


+ 17
- 2
src/main.cpp View File

@@ -106,6 +106,7 @@ int main(int argc, char* argv[]) {


// Initialize environment // Initialize environment
asset::init(); asset::init();
bool loggerWasTruncated = logger::isTruncated();
logger::init(); logger::init();


// We can now install a signal handler and log the output // We can now install a signal handler and log the output
@@ -185,8 +186,20 @@ int main(int argc, char* argv[]) {
} }
#endif #endif


APP->patch->init(patchPath);
// Initialize patch
if (loggerWasTruncated && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack crashed during the last session, possibly due to a buggy module in your patch. Clear your patch and start over?")) {
// This is the default state, but just call clear() to be explicit.
APP->patch->clear();
}
else if (patchPath != "") {
APP->patch->loadAction(patchPath);
}
else {
APP->patch->path = settings::patchPath;
APP->patch->loadAutosave();
}


// Run context
if (settings::headless) { if (settings::headless) {
printf("Press enter to exit.\n"); printf("Press enter to exit.\n");
getchar(); getchar();
@@ -203,7 +216,9 @@ int main(int argc, char* argv[]) {


// Destroy context // Destroy context
if (!settings::headless) { if (!settings::headless) {
APP->patch->save(asset::autosavePath);
APP->patch->saveAutosave();
// TODO If Rack crashes, the settings' patch path won't be changed although the autosave will. This could possibly cause the user to overwrite their old patch with the unrelated autosave.
settings::patchPath = APP->patch->path;
} }
INFO("Destroying context"); INFO("Destroying context");
contextDestroy(); contextDestroy();


+ 86
- 94
src/patch.cpp View File

@@ -20,57 +20,26 @@ static const char PATCH_FILTERS[] = "VCV Rack patch (.vcv):vcv";




PatchManager::PatchManager() { PatchManager::PatchManager() {
path = settings::patchPath;
} }


PatchManager::~PatchManager() { PatchManager::~PatchManager() {
settings::patchPath = path;
} }


void PatchManager::init(std::string path) {
if (!path.empty()) {
// Load patch
load(path);
this->path = path;
return;
}

if (!settings::devMode) {
// To prevent launch crashes, if Rack crashes between now and 15 seconds from now, the "skipAutosaveOnLaunch" property will remain in settings.json, so that in the next launch, the broken autosave will not be loaded.
bool oldSkipLoadOnLaunch = settings::skipLoadOnLaunch;
settings::skipLoadOnLaunch = true;
settings::save(asset::settingsPath);
settings::skipLoadOnLaunch = false;
if (oldSkipLoadOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Clear your patch and start over?")) {
this->path = "";
return;
}
void PatchManager::reset() {
if (APP->history) {
APP->history->clear();
} }

// Load autosave
if (load("")) {
return;
if (APP->scene) {
APP->scene->rackScroll->reset();
} }

reset();
} }


void PatchManager::reset() {
if (!settings::headless) {
APP->history->clear();
void PatchManager::clear() {
if (APP->scene) {
APP->scene->rack->clear(); APP->scene->rack->clear();
APP->scene->rackScroll->reset();
} }
APP->engine->clear(); APP->engine->clear();

path = "";
if (load(asset::templatePath)) {
return;
}

if (load(asset::system("template.vcv"))) {
return;
}
reset();
} }


static bool promptClear(std::string text) { static bool promptClear(std::string text) {
@@ -81,12 +50,6 @@ static bool promptClear(std::string text) {
return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str()); return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str());
} }


void PatchManager::resetDialog() {
if (!promptClear("The current patch is unsaved. Clear it and start a new patch?"))
return;
reset();
}

void PatchManager::save(std::string path) { void PatchManager::save(std::string path) {
INFO("Saving patch %s", path.c_str()); INFO("Saving patch %s", path.c_str());
json_t* rootJ = toJson(); json_t* rootJ = toJson();
@@ -110,25 +73,25 @@ void PatchManager::save(std::string path) {
} }


void PatchManager::saveDialog() { void PatchManager::saveDialog() {
if (!path.empty()) {
save(path);
APP->history->setSaved();
if (path == "") {
saveAsDialog();
} }
else { else {
saveAsDialog();
save(path);
APP->history->setSaved();
} }
} }


void PatchManager::saveAsDialog() { void PatchManager::saveAsDialog() {
std::string dir; std::string dir;
std::string filename; std::string filename;
if (path.empty()) {
if (this->path == "") {
dir = asset::user("patches"); dir = asset::user("patches");
system::createDirectory(dir); system::createDirectory(dir);
} }
else { else {
dir = string::directory(path);
filename = string::filename(path);
dir = string::directory(this->path);
filename = string::filename(this->path);
} }


osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS); osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS);
@@ -146,14 +109,15 @@ void PatchManager::saveAsDialog() {
}); });


// Append .vcv extension if no extension was given. // Append .vcv extension if no extension was given.
std::string pathStr = pathC;
if (string::filenameExtension(string::filename(pathStr)) == "") {
pathStr += ".vcv";
std::string path = pathC;
if (string::filenameExtension(string::filename(path)) == "") {
path += ".vcv";
} }


save(pathStr);
path = pathStr;
save(path);
this->path = path;
APP->history->setSaved(); APP->history->setSaved();
pushRecentPath(path);
} }


void PatchManager::saveTemplateDialog() { void PatchManager::saveTemplateDialog() {
@@ -164,11 +128,13 @@ void PatchManager::saveTemplateDialog() {
save(asset::templatePath); save(asset::templatePath);
} }


bool PatchManager::load(std::string path) {
std::string actualPath = (path != "") ? path : asset::autosavePath;
void PatchManager::saveAutosave() {
save(asset::autosavePath);
}


INFO("Loading patch %s", actualPath.c_str());
FILE* file = std::fopen(actualPath.c_str(), "r");
bool PatchManager::load(std::string path) {
INFO("Loading patch %s", path.c_str());
FILE* file = std::fopen(path.c_str(), "r");
if (!file) { if (!file) {
// Exit silently // Exit silently
return false; return false;
@@ -180,7 +146,7 @@ bool PatchManager::load(std::string path) {
json_error_t error; json_error_t error;
json_t* rootJ = json_loadf(file, 0, &error); json_t* rootJ = json_loadf(file, 0, &error);
if (!rootJ) { if (!rootJ) {
std::string message = string::f("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
std::string message = string::f("Failed to load patch. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
return false; return false;
} }
@@ -188,26 +154,47 @@ bool PatchManager::load(std::string path) {
json_decref(rootJ); json_decref(rootJ);
}); });


if (!settings::headless) {
APP->history->clear();
APP->scene->rack->clear();
APP->scene->rackScroll->reset();
}
APP->engine->clear();
clear();
fromJson(rootJ); fromJson(rootJ);
return true;
}


// Update recent patches
if (path != "") {
auto& recent = settings::recentPatchPaths;
// Remove path from recent patches (if exists)
recent.erase(std::remove(recent.begin(), recent.end(), path), recent.end());
// Add path to top of recent patches
recent.push_front(path);
// Limit recent patches size
recent.resize(std::min((int) recent.size(), 10));
void PatchManager::loadTemplate() {
this->path = "";
APP->history->setSaved();

if (load(asset::templatePath)) {
return;
} }


return true;
if (load(asset::system("template.vcv"))) {
return;
}

clear();
}

void PatchManager::loadTemplateDialog() {
if (!promptClear("The current patch is unsaved. Clear it and start a new patch?")) {
return;
}
loadTemplate();
}

void PatchManager::loadAutosave() {
if (load(asset::autosavePath)) {
return;
}
loadTemplate();
}

void PatchManager::loadAction(std::string path) {
if (!load(path)) {
return;
}
this->path = path;
APP->history->setSaved();
pushRecentPath(path);
} }


void PatchManager::loadDialog() { void PatchManager::loadDialog() {
@@ -215,12 +202,12 @@ void PatchManager::loadDialog() {
return; return;


std::string dir; std::string dir;
if (path.empty()) {
if (this->path == "") {
dir = asset::user("patches"); dir = asset::user("patches");
system::createDirectory(dir); system::createDirectory(dir);
} }
else { else {
dir = string::directory(path);
dir = string::directory(this->path);
} }


osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS); osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS);
@@ -233,26 +220,21 @@ void PatchManager::loadDialog() {
// Fail silently // Fail silently
return; return;
} }
DEFER({
std::free(pathC);
});
std::string path = pathC;
std::free(pathC);


load(pathC);
path = pathC;
APP->history->setSaved();
loadAction(path);
} }


void PatchManager::loadPathDialog(std::string path) { void PatchManager::loadPathDialog(std::string path) {
if (!promptClear("The current patch is unsaved. Clear it and open the new patch?")) if (!promptClear("The current patch is unsaved. Clear it and open the new patch?"))
return; return;


load(path);
this->path = path;
APP->history->setSaved();
loadAction(path);
} }


void PatchManager::revertDialog() { void PatchManager::revertDialog() {
if (path.empty())
if (path == "")
return; return;
if (!promptClear("Revert patch to the last saved state?")) if (!promptClear("Revert patch to the last saved state?"))
return; return;
@@ -261,6 +243,16 @@ void PatchManager::revertDialog() {
APP->history->setSaved(); APP->history->setSaved();
} }


void PatchManager::pushRecentPath(std::string path) {
auto& recent = settings::recentPatchPaths;
// Remove path from recent patches (if exists)
recent.erase(std::remove(recent.begin(), recent.end(), path), recent.end());
// Add path to top of recent patches
recent.push_front(path);
// Limit recent patches size
recent.resize(std::min((int) recent.size(), 10));
}

void PatchManager::disconnectDialog() { void PatchManager::disconnectDialog() {
APP->scene->rack->clearCablesAction(); APP->scene->rack->clearCablesAction();
} }
@@ -274,7 +266,7 @@ json_t* PatchManager::toJson() {
json_object_set_new(rootJ, "version", versionJ); json_object_set_new(rootJ, "version", versionJ);


json_t* engineJ = APP->engine->toJson(); json_t* engineJ = APP->engine->toJson();
if (!settings::headless) {
if (APP->scene) {
APP->scene->rack->mergeJson(engineJ); APP->scene->rack->mergeJson(engineJ);
} }


@@ -310,14 +302,14 @@ void PatchManager::fromJson(json_t* rootJ) {
} }


APP->engine->fromJson(rootJ); APP->engine->fromJson(rootJ);
if (!settings::headless) {
if (APP->scene) {
APP->scene->rack->fromJson(rootJ); APP->scene->rack->fromJson(rootJ);
} }
// At this point, ModuleWidgets and CableWidgets should own all Modules and Cables. // At this point, ModuleWidgets and CableWidgets should own all Modules and Cables.
// TODO Assert this // TODO Assert this


// Display a message if we have something to say
if (!warningLog.empty()) {
// Display a message if we have something to say.
if (warningLog != "") {
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, warningLog.c_str()); osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, warningLog.c_str());
} }
warningLog = ""; warningLog = "";


+ 1
- 1
src/window.cpp View File

@@ -362,7 +362,7 @@ void Window::run() {


// Set window title // Set window title
std::string windowTitle = APP_NAME + " v" + APP_VERSION; std::string windowTitle = APP_NAME + " v" + APP_VERSION;
if (!APP->patch->path.empty()) {
if (APP->patch->path != "") {
windowTitle += " - "; windowTitle += " - ";
if (!APP->history->isSaved()) if (!APP->history->isSaved())
windowTitle += "*"; windowTitle += "*";


Loading…
Cancel
Save