void OBSBasic::ChangeSceneCollection()
{
	QAction *action = reinterpret_cast<QAction*>(sender());
	std::string fileName;

	if (!action)
		return;

	fileName = QT_TO_UTF8(action->property("fileName").value<QString>());
	if (fileName.empty())
		return;

	const char *oldName = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollection");
	if (action->text().compare(QT_UTF8(oldName)) == 0) {
		action->setChecked(true);
		return;
	}

	SaveProjectNow();

	Load(fileName.c_str());
	RefreshSceneCollections();

	const char *newName = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollection");
	const char *newFile = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollectionFile");

	blog(LOG_INFO, "Switched to scene collection '%s' (%s.json)",
			newName, newFile);
	blog(LOG_INFO, "------------------------------------------------");

	UpdateTitleBar();
}
void OBSBasic::on_actionRemoveSceneCollection_triggered()
{
	std::string newName;
	std::string newPath;

	std::string oldFile = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollectionFile");
	std::string oldName = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollection");

	auto cb = [&](const char *name, const char *filePath)
	{
		if (strcmp(oldName.c_str(), name) != 0) {
			newName = name;
			newPath = filePath;
			return false;
		}

		return true;
	};

	EnumSceneCollections(cb);

	/* this should never be true due to menu item being grayed out */
	if (newPath.empty())
		return;

	QString text = QTStr("ConfirmRemove.Text");
	text.replace("$1", QT_UTF8(oldName.c_str()));

	QMessageBox::StandardButton button = QMessageBox::question(this,
			QTStr("ConfirmRemove.Title"), text);
	if (button == QMessageBox::No)
		return;

	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;
	}

	oldFile.insert(0, path);
	oldFile += ".json";
	os_unlink(oldFile.c_str());

	Load(newPath.c_str());
	RefreshSceneCollections();

	const char *newFile = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollectionFile");

	blog(LOG_INFO, "Removed scene collection '%s' (%s.json), "
			"switched to '%s' (%s.json)",
			oldName.c_str(), oldFile.c_str(),
			newName.c_str(), newFile);
	blog(LOG_INFO, "------------------------------------------------");

	UpdateTitleBar();
}
void OBSBasic::AddSceneCollection(bool create_new)
{
	std::string name;
	std::string file;

	if (!GetSceneCollectionName(this, name, file))
		return;

	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();
	}
	SaveProjectNow();
	RefreshSceneCollections();

	blog(LOG_INFO, "Added scene collection '%s' (%s, %s.json)",
			name.c_str(), create_new ? "clean" : "duplicate",
			file.c_str());
	blog(LOG_INFO, "------------------------------------------------");

	UpdateTitleBar();
}
void OBSBasic::ChangeProfile()
{
	QAction *action = reinterpret_cast<QAction*>(sender());
	ConfigFile config;
	std::string path;

	if (!action)
		return;

	path = QT_TO_UTF8(action->property("file_name").value<QString>());
	if (path.empty())
		return;

	const char *oldName = config_get_string(App()->GlobalConfig(),
			"Basic", "Profile");
	if (action->text().compare(QT_UTF8(oldName)) == 0) {
		action->setChecked(true);
		return;
	}

	size_t path_len = path.size();
	path += "/basic.ini";

	if (config.Open(path.c_str(), CONFIG_OPEN_ALWAYS) != 0) {
		blog(LOG_ERROR, "ChangeProfile: Failed to load file '%s'",
				path.c_str());
		return;
	}

	path.resize(path_len);

	const char *newName = config_get_string(config, "General", "Name");
	const char *newDir = strrchr(path.c_str(), '/') + 1;

	config_set_string(App()->GlobalConfig(), "Basic", "Profile", newName);
	config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir",
			newDir);

	config.Swap(basicConfig);
	InitBasicConfigDefaults();
	ResetProfileData();
	RefreshProfiles();
	config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
	UpdateTitleBar();

	blog(LOG_INFO, "Switched to profile '%s' (%s)",
			newName, newDir);
	blog(LOG_INFO, "------------------------------------------------");

	if (api)
		api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED);
}
void OBSBasic::on_actionRenameSceneCollection_triggered()
{
	std::string name;
	std::string file;

	std::string oldFile = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollectionFile");
	const char *oldName = config_get_string(App()->GlobalConfig(),
			"Basic", "SceneCollection");

	bool success = GetSceneCollectionName(this, name, file, oldName);
	if (!success)
		return;

	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;
	}

	oldFile.insert(0, path);
	oldFile += ".json";
	os_unlink(oldFile.c_str());

	blog(LOG_INFO, "------------------------------------------------");
	blog(LOG_INFO, "Renamed scene collection to '%s' (%s.json)",
			name.c_str(), file.c_str());
	blog(LOG_INFO, "------------------------------------------------");

	UpdateTitleBar();
	RefreshSceneCollections();
}
void OBSBasic::on_actionRemoveProfile_triggered()
{
	std::string newName;
	std::string newPath;
	ConfigFile config;

	std::string oldDir = config_get_string(App()->GlobalConfig(),
			"Basic", "ProfileDir");
	std::string oldName = config_get_string(App()->GlobalConfig(),
			"Basic", "Profile");

	auto cb = [&](const char *name, const char *filePath)
	{
		if (strcmp(oldName.c_str(), name) != 0) {
			newName = name;
			newPath = filePath;
			return false;
		}

		return true;
	};

	EnumProfiles(cb);

	/* this should never be true due to menu item being grayed out */
	if (newPath.empty())
		return;

	QString text = QTStr("ConfirmRemove.Text");
	text.replace("$1", QT_UTF8(oldName.c_str()));

	QMessageBox::StandardButton button = QMessageBox::question(this,
			QTStr("ConfirmRemove.Title"), text);
	if (button == QMessageBox::No)
		return;

	size_t newPath_len = newPath.size();
	newPath += "/basic.ini";

	if (config.Open(newPath.c_str(), CONFIG_OPEN_ALWAYS) != 0) {
		blog(LOG_ERROR, "ChangeProfile: Failed to load file '%s'",
				newPath.c_str());
		return;
	}

	newPath.resize(newPath_len);

	const char *newDir = strrchr(newPath.c_str(), '/') + 1;

	config_set_string(App()->GlobalConfig(), "Basic", "Profile",
			newName.c_str());
	config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir",
			newDir);

	config.Swap(basicConfig);
	InitBasicConfigDefaults();
	ResetProfileData();
	DeleteProfile(oldName.c_str(), oldDir.c_str());
	RefreshProfiles();
	config_save_safe(App()->GlobalConfig(), "tmp", nullptr);

	blog(LOG_INFO, "Switched to profile '%s' (%s)",
			newName.c_str(), newDir);
	blog(LOG_INFO, "------------------------------------------------");

	UpdateTitleBar();
}
bool OBSBasic::AddProfile(bool create_new, const char *title, const char *text,
		const char *init_text)
{
	std::string newName;
	std::string newDir;
	ConfigFile config;

	if (!GetProfileName(this, newName, newDir, title, text, init_text))
		return false;

	std::string curDir = config_get_string(App()->GlobalConfig(),
			"Basic", "ProfileDir");

	char newPath[512];
	int ret = GetConfigPath(newPath, 512, "obs-studio/basic/profiles/");
	if (ret <= 0) {
		blog(LOG_WARNING, "Failed to get profiles config path");
		return false;
	}

	strcat(newPath, newDir.c_str());

	if (os_mkdir(newPath) < 0) {
		blog(LOG_WARNING, "Failed to create profile directory '%s'",
				newDir.c_str());
		return false;
	}

	if (!create_new)
		CopyProfile(curDir.c_str(), newPath);

	strcat(newPath, "/basic.ini");

	if (config.Open(newPath, CONFIG_OPEN_ALWAYS) != 0) {
		blog(LOG_ERROR, "Failed to open new config file '%s'",
				newDir.c_str());
		return false;
	}

	config_set_string(App()->GlobalConfig(), "Basic", "Profile",
			newName.c_str());
	config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir",
			newDir.c_str());

	config_set_string(config, "General", "Name", newName.c_str());
	config.SaveSafe("tmp");
	config.Swap(basicConfig);
	InitBasicConfigDefaults();
	RefreshProfiles();

	if (create_new)
		ResetProfileData();

	blog(LOG_INFO, "Created profile '%s' (%s, %s)", newName.c_str(),
			create_new ? "clean" : "duplicate", newDir.c_str());
	blog(LOG_INFO, "------------------------------------------------");

	config_save_safe(App()->GlobalConfig(), "tmp", nullptr);
	UpdateTitleBar();
	return true;
}
bool PerigeeMove::DoIt()
{
	file_list::size_type num = 0, cnt = m_files.size();
	m_progress.m_Progress.SetRange32(0, cnt);
	m_progress.m_Progress.SetPos(0);
	m_progress.m_Progress.SetStep(1);
	for(file_list::iterator it = m_files.begin(); it != m_files.end(); ++it)
	{
		CString dest_name = PsAppendPath(it->dest, PathFindFileName(it->source));
		m_progress.SetSourceDest(it->source, dest_name);
		
		// first make sure the destination isn't the source; otherwise, there's nothing to do
		if (0 != it->source.CompareNoCase(dest_name))
		{
			// check whether the destination is a subfolder of the source, and skip
			if ( PathIsDirectory(it->source) && DestIsSubfolderOfSource(dest_name, it->source) )
			{
				file_info fi;
				fi.source = it->source;
				fi.dest = dest_name;
				fi.error_code = ERROR_INVALID_PARAMETER;
				fi.error_type = warn_move_dest_is_subfolder_of_source;
				m_error_files.push_back(fi);
				continue;
			}

			TargetState state = CheckTarget(dest_name, it->source);
			if ( targetCanBeOverwritten == state )
			{
				if ( m_options.overwrite_read_only )
					PrepareOverwrite(dest_name);

				if ( m_options.recycle )
					PsRecycle(dest_name);
				else
					DeleteFile(dest_name);
			}
			else if ( targetCannotBeOverwritten == state )
			{
				if (m_options.overwrite == CPerigeeCopyOptions::overwrite_postpone) {
					// put the file in the error list for now
					file_info fi;
					fi.source = it->source;
					fi.dest   = it->dest;
					fi.error_code = ERROR_FILE_EXISTS;
					fi.error_type = err_move;
					m_error_files.push_back(fi);
					continue;
				} else {
					// exclude the file from the job
					--cnt;
					m_progress.m_Progress.SetRange32(0, cnt);
					ProcessMessages();
					m_cancel = m_cancel || m_progress.m_CancelRequest;
					if ( m_cancel )
						break;
					continue;
				}
			}

			while ( !MoveFile(it->source, dest_name) )
			{
				file_info fi;
				fi.source = it->source;
				fi.dest   = it->dest;
				fi.error_code = GetLastError();
				fi.error_type = err_move;
				if ( !HandleError(fi) )
					break;
			}
		}

		m_progress.m_Progress.StepIt();
		UpdateTitleBar(m_progress, IDS_MOVING_FILES, num++, cnt);
		ProcessMessages();
		m_cancel = m_cancel || m_progress.m_CancelRequest;

		if ( m_cancel )
			break;
	}

	return m_error_files.empty();
}