Browse Source

UI: Undo/Redo Scene Collections

Implements undo/redo for scene collections. This includes operations
including rename, delete, and addition.
Ford Smith 4 years ago
parent
commit
a374c023a1
2 changed files with 160 additions and 12 deletions
  1. 158 10
      UI/window-basic-main-scene-collections.cpp
  2. 2 2
      UI/window-basic-main.cpp

+ 158 - 10
UI/window-basic-main-scene-collections.cpp

@@ -151,17 +151,55 @@ bool OBSBasic::AddSceneCollection(bool create_new, const QString &qname)
 		}
 	}
 
-	SaveProjectNow();
+	auto new_collection = [this, create_new](const std::string &file,
+						 const std::string &name) {
+		SaveProjectNow();
 
-	config_set_string(App()->GlobalConfig(), "Basic", "SceneCollection",
-			  name.c_str());
-	config_set_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile",
-			  file.c_str());
-	if (create_new) {
-		CreateDefaultScene(false);
-	}
-	SaveProjectNow();
-	RefreshSceneCollections();
+		config_set_string(App()->GlobalConfig(), "Basic",
+				  "SceneCollection", name.c_str());
+		config_set_string(App()->GlobalConfig(), "Basic",
+				  "SceneCollectionFile", file.c_str());
+		if (create_new) {
+			CreateDefaultScene(false);
+		}
+		SaveProjectNow();
+		RefreshSceneCollections();
+	};
+
+	new_collection(file, name);
+
+	auto undo = [this, oldName = name](const std::string &data) {
+		std::string newPath;
+		auto cb = [&](const char *name, const char *filePath) {
+			if (strcmp(oldName.c_str(), name) != 0) {
+				newPath = filePath;
+				return false;
+			}
+			return true;
+		};
+
+		EnumSceneCollections(cb);
+		char path[512];
+		int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/");
+		if (ret <= 0) {
+			blog(LOG_WARNING,
+			     "Failed to get scene collection config path");
+			return;
+		}
+		std::string file = path + data + ".json";
+		os_unlink(file.c_str());
+		file += ".bak";
+		os_unlink(file.c_str());
+		Load(newPath.c_str());
+		RefreshSceneCollections();
+	};
+
+	auto redo = [new_collection, file, name](const std::string &) {
+		new_collection(file, name);
+	};
+
+	undo_s.add_action(QTStr("Undo.Add").arg(name.c_str()), undo, redo, file,
+			  "", NULL);
 
 	blog(LOG_INFO, "Added scene collection '%s' (%s, %s.json)",
 	     name.c_str(), create_new ? "clean" : "duplicate", file.c_str());
@@ -245,11 +283,13 @@ void OBSBasic::on_actionRenameSceneCollection_triggered()
 {
 	std::string name;
 	std::string file;
+	std::string oname;
 
 	std::string oldFile = config_get_string(App()->GlobalConfig(), "Basic",
 						"SceneCollectionFile");
 	const char *oldName = config_get_string(App()->GlobalConfig(), "Basic",
 						"SceneCollection");
+	oname = std::string(oldName);
 
 	bool success = GetSceneCollectionName(this, name, file, oldName);
 	if (!success)
@@ -268,6 +308,59 @@ void OBSBasic::on_actionRenameSceneCollection_triggered()
 		return;
 	}
 
+	auto undo = [name = oname, file = oldFile, of = file,
+		     this](const std::string &) {
+		config_set_string(App()->GlobalConfig(), "Basic",
+				  "SceneCollection", name.c_str());
+		config_set_string(App()->GlobalConfig(), "Basic",
+				  "SceneCollectionFile", file.c_str());
+		SaveProjectNow();
+
+		char path[512];
+		int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/");
+		if (ret <= 0) {
+			blog(LOG_WARNING,
+			     "Failed to get scene collection config path");
+			return;
+		}
+		std::string oldFile = of;
+
+		oldFile.insert(0, path);
+		oldFile += ".json";
+		os_unlink(oldFile.c_str());
+		oldFile += ".bak";
+		os_unlink(oldFile.c_str());
+
+		UpdateTitleBar();
+		RefreshSceneCollections();
+	};
+
+	auto redo = [of = oldFile, name, file, this](const std::string &) {
+		config_set_string(App()->GlobalConfig(), "Basic",
+				  "SceneCollection", name.c_str());
+		config_set_string(App()->GlobalConfig(), "Basic",
+				  "SceneCollectionFile", file.c_str());
+		SaveProjectNow();
+
+		char path[512];
+		int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/");
+		if (ret <= 0) {
+			blog(LOG_WARNING,
+			     "Failed to get scene collection config path");
+			return;
+		}
+		std::string oldFile = of;
+
+		oldFile.insert(0, path);
+		oldFile += ".json";
+		os_unlink(oldFile.c_str());
+		oldFile += ".bak";
+		os_unlink(oldFile.c_str());
+
+		UpdateTitleBar();
+		RefreshSceneCollections();
+	};
+
 	oldFile.insert(0, path);
 	oldFile += ".json";
 	os_unlink(oldFile.c_str());
@@ -282,6 +375,9 @@ void OBSBasic::on_actionRenameSceneCollection_triggered()
 	UpdateTitleBar();
 	RefreshSceneCollections();
 
+	undo_s.add_action(QTStr("Undo.Rename").arg(name.c_str()), undo, redo,
+			  "", "", NULL);
+
 	if (api) {
 		api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);
 		api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
@@ -331,6 +427,32 @@ void OBSBasic::on_actionRemoveSceneCollection_triggered()
 
 	oldFile.insert(0, path);
 	oldFile += ".json";
+	obs_data_t *data =
+		obs_data_create_from_json_file_safe(oldFile.c_str(), "bak");
+	obs_data_set_string(data, "undo_filename", oldFile.c_str());
+	auto undo = [this](const std::string &data) {
+		obs_data_t *dat = obs_data_create_from_json(data.c_str());
+		LoadData(dat, obs_data_get_string(dat, "undo_filename"));
+		SaveProjectNow();
+		RefreshSceneCollections();
+		obs_data_release(dat);
+	};
+
+	auto redo = [this, of = oldFile, newPath](const std::string &) {
+		std::string oldFile = of;
+		os_unlink(oldFile.c_str());
+		oldFile += ".bak";
+		os_unlink(oldFile.c_str());
+
+		Load(newPath.c_str());
+		RefreshSceneCollections();
+	};
+
+	std::string undo_data = std::string(obs_data_get_json(data));
+	undo_s.add_action(QTStr("Undo.Delete").arg(oldName.c_str()), undo, redo,
+			  undo_data, "", NULL);
+	obs_data_release(data);
+
 	os_unlink(oldFile.c_str());
 	oldFile += ".bak";
 	os_unlink(oldFile.c_str());
@@ -410,6 +532,9 @@ void OBSBasic::ChangeSceneCollection()
 
 	const char *oldName = config_get_string(App()->GlobalConfig(), "Basic",
 						"SceneCollection");
+	std::string oldFile = std::string(config_get_string(
+		App()->GlobalConfig(), "Basic", "SceneCollectionFile"));
+
 	if (action->text().compare(QT_UTF8(oldName)) == 0) {
 		action->setChecked(true);
 		return;
@@ -431,6 +556,29 @@ void OBSBasic::ChangeSceneCollection()
 
 	UpdateTitleBar();
 
+	auto undo = [this, fn = std::string(oldFile)](const std::string &) {
+		string fileName = fn;
+		char path[512];
+		int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/");
+		if (ret <= 0) {
+			blog(LOG_WARNING,
+			     "Failed to get scene collection config path");
+			return;
+		}
+		fileName.insert(0, path);
+		fileName += ".json";
+		Load(fileName.c_str());
+		RefreshSceneCollections();
+	};
+
+	auto redo = [this, fileName](const std::string &) {
+		Load(fileName.c_str());
+		RefreshSceneCollections();
+	};
+
+	undo_s.add_action(QTStr("Undo.SceneCollection.Switch").arg(newName),
+			  undo, redo, "", "", NULL);
+
 	if (api)
 		api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
 }

+ 2 - 2
UI/window-basic-main.cpp

@@ -925,6 +925,8 @@ void OBSBasic::LogScenes()
 
 void OBSBasic::Load(const char *file)
 {
+	disableSaving++;
+
 	obs_data_t *data = obs_data_create_from_json_file_safe(file, "bak");
 	if (!data) {
 		disableSaving--;
@@ -939,8 +941,6 @@ void OBSBasic::Load(const char *file)
 
 void OBSBasic::LoadData(obs_data_t *data, const char *file)
 {
-	disableSaving++;
-
 	ClearSceneData();
 	InitDefaultTransitions();
 	ClearContextBar();