Refactor BKE project interface & implementation
Mostly this is about more cleanly separating between `BlenderProject` and `ProjectSettings`. Overall this is a nice improvement I think, helps readability of interfaces and implementation a lot.
This commit is contained in:
parent
30b08fbec5
commit
eafc9cb446
|
@ -20,7 +20,7 @@ typedef struct BlenderProject BlenderProject;
|
|||
/** See #bke::ProjectSettings::create_settings_directory(). */
|
||||
bool BKE_project_create_settings_directory(const char *project_root_path) ATTR_NONNULL();
|
||||
/** See #bke::ProjectSettings::delete_settings_directory(). */
|
||||
bool BKE_project_delete_settings_directory(const BlenderProject *project) ATTR_NONNULL();
|
||||
bool BKE_project_delete_settings_directory(BlenderProject *project) ATTR_NONNULL();
|
||||
|
||||
BlenderProject *BKE_project_active_get(void) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
|
@ -38,18 +38,6 @@ bool BKE_project_is_path_project_root(const char *path) ATTR_WARN_UNUSED_RESULT
|
|||
* referenced file/directory is a project root directory).
|
||||
*/
|
||||
bool BKE_project_contains_path(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
/**
|
||||
* Attempt to load project based on the given path and return it. This should never become the
|
||||
* active project, which should be loaded with #BKE_project_active_load_from_path() instead
|
||||
* (because the active project uses unique_ptr without the guarded allocator, unlike this C-API
|
||||
* function). The returned project pointer is owning and needs freeing with #BKE_project_free().
|
||||
*/
|
||||
BlenderProject *BKE_project_load_from_path(const char *path) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
/**
|
||||
* Free a project allocated by #BKE_project_load_from_path() and null the pointer to it.
|
||||
*/
|
||||
void BKE_project_free(BlenderProject **project) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Attempt to load and activate a project based on the given path. If the path doesn't lead
|
||||
|
|
|
@ -24,24 +24,67 @@ class ProjectSettings;
|
|||
struct CustomAssetLibraries;
|
||||
|
||||
/**
|
||||
* The active project (if there is one) is stored in static memory here too, and can be queried
|
||||
* using #BlenderProject::get_active().
|
||||
* Entry point / API for core Blender project management.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Own and give access to the active project.
|
||||
* - Manage the .blender_project/ directory.
|
||||
* - Store and manage (including reading & writing) of the .blender_project/settings.json file. The
|
||||
* implementation of this can be found in the internal #ProjectSettings class.
|
||||
* - Tag for unsaved changes as needed.
|
||||
*/
|
||||
class BlenderProject {
|
||||
friend class ProjectSettings;
|
||||
|
||||
/* Path to the project root using native slashes plus a trailing slash. */
|
||||
std::string root_path_;
|
||||
std::unique_ptr<ProjectSettings> settings_;
|
||||
|
||||
public:
|
||||
inline static const StringRefNull SETTINGS_DIRNAME = ".blender_project";
|
||||
inline static const StringRefNull SETTINGS_FILENAME = "settings.json";
|
||||
|
||||
public:
|
||||
static auto get_active [[nodiscard]] () -> BlenderProject *;
|
||||
static auto set_active_from_settings(std::unique_ptr<ProjectSettings> settings)
|
||||
-> BlenderProject *;
|
||||
static auto set_active(std::unique_ptr<BlenderProject> settings) -> BlenderProject *;
|
||||
|
||||
/**
|
||||
* Read project settings from the given \a path, which may point to some directory or file inside
|
||||
* of the project directory. Both Unix and Windows style slashes are allowed. Path is expected to
|
||||
* be normalized.
|
||||
*
|
||||
* Attempt to read project data from the given \a project_path, which may be either a project
|
||||
* root directory or the .blender_project directory, and load it into runtime data. Letting the
|
||||
* returned #unique_pointer run out of scope cleanly destructs the runtime project data.
|
||||
*
|
||||
* \note Does NOT set the loaded project active.
|
||||
*
|
||||
* \return The loaded project or null on failure.
|
||||
*/
|
||||
static auto load_from_path(StringRef project_path) -> std::unique_ptr<BlenderProject>;
|
||||
|
||||
/**
|
||||
* Initializes a blender project by creating a .blender_project directory at the given \a
|
||||
* project_root_path.
|
||||
* Both Unix and Windows style slashes are allowed.
|
||||
*
|
||||
* \return True if the settings directory was created, or already existed. False on failure.
|
||||
*/
|
||||
static auto create_settings_directory(StringRef project_root_path) -> bool;
|
||||
/**
|
||||
* Remove the .blender_project directory with all of its contents at the given \a
|
||||
* project_root_path. If this is the path of the active project, it is marked as having changed
|
||||
* but it is not unloaded. Runtime project data is still valid at this point.
|
||||
*
|
||||
* \return True on success.
|
||||
*/
|
||||
static auto delete_settings_directory(StringRef project_root_path) -> bool;
|
||||
|
||||
/**
|
||||
* Check if the directory given by \a path contains a .blender_project directory and should thus
|
||||
* be considered a project root directory.
|
||||
*/
|
||||
static bool path_is_project_root(StringRef path);
|
||||
static auto path_is_project_root(StringRef path) -> bool;
|
||||
|
||||
/**
|
||||
* Check if \a path points into a project and return the root directory path of that project (the
|
||||
|
@ -49,51 +92,60 @@ class BlenderProject {
|
|||
* the first project found, so if a project is nested inside another one, the nested project is
|
||||
* used.
|
||||
* Both Unix and Windows style slashes are allowed.
|
||||
* \return the project root path or an empty path if not found.
|
||||
*
|
||||
* \return The project root path or an empty path if not found. The referenced string points into
|
||||
* the input \a path, so slashes are not converted in the returned value.
|
||||
*/
|
||||
static auto project_root_path_find_from_path [[nodiscard]] (StringRef path) -> StringRef;
|
||||
|
||||
explicit BlenderProject(std::unique_ptr<ProjectSettings> settings);
|
||||
/* --- Non-static member functions. --- */
|
||||
|
||||
BlenderProject(StringRef project_root_path, std::unique_ptr<ProjectSettings> settings);
|
||||
|
||||
/**
|
||||
* Version of the static #delete_settings_directory() that deletes the settings directory of this
|
||||
* project. Always tags as having unsaved changes after successful deletion.
|
||||
*/
|
||||
auto delete_settings_directory() -> bool;
|
||||
|
||||
auto root_path [[nodiscard]] () const -> StringRefNull;
|
||||
auto get_settings [[nodiscard]] () const -> ProjectSettings &;
|
||||
|
||||
private:
|
||||
static std::unique_ptr<BlenderProject> &active_project_ptr();
|
||||
};
|
||||
|
||||
struct CustomAssetLibraries : NonCopyable {
|
||||
ListBase asset_libraries = {nullptr, nullptr}; /* CustomAssetLibraryDefinition */
|
||||
|
||||
CustomAssetLibraries() = default;
|
||||
CustomAssetLibraries(ListBase asset_libraries);
|
||||
CustomAssetLibraries(CustomAssetLibraries &&);
|
||||
~CustomAssetLibraries();
|
||||
auto operator=(CustomAssetLibraries &&) -> CustomAssetLibraries &;
|
||||
static auto active_project_ptr() -> std::unique_ptr<BlenderProject> &;
|
||||
/**
|
||||
* Get the project root path from a path that is either already the project root, or the
|
||||
* .blender_project directory. Returns the path with native slashes plus a trailing slash.
|
||||
*/
|
||||
static auto project_path_to_native_project_root_path(StringRef project_path) -> std::string;
|
||||
/**
|
||||
* Get the .blender_project directory path from a project root path. Returns the path with native
|
||||
* slashes plus a trailing slash. Assumes the path already ends with a native trailing slash.
|
||||
*/
|
||||
static auto project_root_path_to_settings_path(StringRef project_root_path) -> std::string;
|
||||
/**
|
||||
* Returns the path with native slashes.
|
||||
* Assumes the path already ends with a native trailing slash.
|
||||
*/
|
||||
static auto project_root_path_to_settings_filepath(StringRef project_root_path) -> std::string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runtime representation of the project settings (`.blender_project/settings.json`) with IO
|
||||
* functionality.
|
||||
*/
|
||||
class ProjectSettings {
|
||||
/* Path to the project root using slashes in the OS native format. */
|
||||
std::string project_root_path_;
|
||||
std::string project_name_;
|
||||
CustomAssetLibraries asset_libraries_ = {};
|
||||
std::unique_ptr<CustomAssetLibraries> asset_libraries_;
|
||||
|
||||
bool has_unsaved_changes_ = false;
|
||||
|
||||
public:
|
||||
inline static const StringRefNull SETTINGS_DIRNAME = ".blender_project";
|
||||
inline static const StringRefNull SETTINGS_FILENAME = "settings.json";
|
||||
|
||||
/**
|
||||
* Initializes a blender project by creating a .blender_project directory at the given \a
|
||||
* project_root_path.
|
||||
* Both Unix and Windows style slashes are allowed.
|
||||
* \return True if the settings directory was created, or already existed. False on failure.
|
||||
*/
|
||||
static auto create_settings_directory(StringRef project_root_path) -> bool;
|
||||
|
||||
/**
|
||||
* Read project settings from the given \a project_path, which may be either a project root
|
||||
* directory or the .blender_project directory.
|
||||
* Both Unix and Windows style slashes are allowed. Path is expected to be normalized.
|
||||
*
|
||||
* \return The read project settings or null in case of failure.
|
||||
*/
|
||||
static auto load_from_disk [[nodiscard]] (StringRef project_path)
|
||||
|
@ -102,27 +154,26 @@ class ProjectSettings {
|
|||
* Read project settings from the given \a path, which may point to some directory or file inside
|
||||
* of the project directory. Both Unix and Windows style slashes are allowed. Path is expected to
|
||||
* be normalized.
|
||||
*
|
||||
* \return The read project settings or null in case of failure.
|
||||
*/
|
||||
static auto load_from_path [[nodiscard]] (StringRef path) -> std::unique_ptr<ProjectSettings>;
|
||||
|
||||
/** Explicit constructor and destructor needed to manage the CustomAssetLibraries unique_ptr. */
|
||||
ProjectSettings();
|
||||
/* Implementation defaulted. */
|
||||
~ProjectSettings();
|
||||
|
||||
/**
|
||||
* Write project settings to the given \a project_path, which may be either a project root
|
||||
* directory or the .blender_project directory. The .blender_project directory must exist.
|
||||
* Both Unix and Windows style slashes are allowed. Path is expected to be normalized.
|
||||
* \return True on success. If the .blender_project directory doesn't exist, that's treated as
|
||||
* failure.
|
||||
*
|
||||
* \return True on success. If the .blender_project directory doesn't exist, that's treated
|
||||
* as failure.
|
||||
*/
|
||||
auto save_to_disk(StringRef project_path) -> bool;
|
||||
/**
|
||||
* Remove the .blender_project directory with all of its contents. Does not unload the active
|
||||
* project but marks it as having unsaved changes. Runtime project data is still valid.
|
||||
* \return True on success.
|
||||
*/
|
||||
auto delete_settings_directory() -> bool;
|
||||
|
||||
explicit ProjectSettings(StringRef project_root_path);
|
||||
|
||||
auto project_root_path [[nodiscard]] () const -> StringRefNull;
|
||||
void project_name(StringRef new_name);
|
||||
auto project_name [[nodiscard]] () const -> StringRefNull;
|
||||
auto asset_library_definitions() const -> const ListBase &;
|
||||
|
@ -146,3 +197,8 @@ class ProjectSettings {
|
|||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
inline ::BlenderProject *BKE_project_c_handle(blender::bke::BlenderProject *project)
|
||||
{
|
||||
return reinterpret_cast<::BlenderProject *>(project);
|
||||
}
|
||||
|
|
|
@ -18,11 +18,18 @@
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
BlenderProject::BlenderProject(std::unique_ptr<ProjectSettings> settings)
|
||||
BlenderProject::BlenderProject(const StringRef project_root_path,
|
||||
std::unique_ptr<ProjectSettings> settings)
|
||||
: settings_(std::move(settings))
|
||||
{
|
||||
root_path_ = BlenderProject::project_path_to_native_project_root_path(project_root_path);
|
||||
BLI_assert(root_path_.back() == SEP);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Active project management (static storage)
|
||||
* \{ */
|
||||
|
||||
/* Construct on First Use idiom. */
|
||||
std::unique_ptr<BlenderProject> &BlenderProject::active_project_ptr()
|
||||
{
|
||||
|
@ -30,11 +37,11 @@ std::unique_ptr<BlenderProject> &BlenderProject::active_project_ptr()
|
|||
return active_;
|
||||
}
|
||||
|
||||
BlenderProject *BlenderProject::set_active_from_settings(std::unique_ptr<ProjectSettings> settings)
|
||||
BlenderProject *BlenderProject::set_active(std::unique_ptr<BlenderProject> project)
|
||||
{
|
||||
std::unique_ptr<BlenderProject> &active = active_project_ptr();
|
||||
if (settings) {
|
||||
active = std::make_unique<BlenderProject>(BlenderProject(std::move(settings)));
|
||||
if (project) {
|
||||
active = std::move(project);
|
||||
}
|
||||
else {
|
||||
active = nullptr;
|
||||
|
@ -49,28 +56,70 @@ BlenderProject *BlenderProject::get_active()
|
|||
return active.get();
|
||||
}
|
||||
|
||||
StringRef BlenderProject::project_root_path_find_from_path(StringRef path)
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Project and project settings management.
|
||||
* \{ */
|
||||
|
||||
std::unique_ptr<BlenderProject> BlenderProject::load_from_path(StringRef project_path)
|
||||
{
|
||||
std::string path_native = path;
|
||||
BLI_path_slash_native(path_native.data());
|
||||
const StringRef project_root_path = project_root_path_find_from_path(project_path);
|
||||
|
||||
StringRef cur_path = path;
|
||||
|
||||
while (cur_path.size()) {
|
||||
std::string pa = StringRef(path_native.c_str(), cur_path.size());
|
||||
if (path_is_project_root(pa)) {
|
||||
return path.substr(0, cur_path.size());
|
||||
}
|
||||
|
||||
/* Walk "up the path" (check the parent next). */
|
||||
const int64_t pos_last_slash = cur_path.find_last_of(SEP);
|
||||
if (pos_last_slash == StringRef::not_found) {
|
||||
break;
|
||||
}
|
||||
cur_path = cur_path.substr(0, pos_last_slash);
|
||||
std::unique_ptr<bke::ProjectSettings> project_settings = bke::ProjectSettings::load_from_path(
|
||||
project_root_path);
|
||||
if (!project_settings) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return "";
|
||||
return std::make_unique<BlenderProject>(project_root_path, std::move(project_settings));
|
||||
}
|
||||
|
||||
bool BlenderProject::create_settings_directory(StringRef project_path)
|
||||
{
|
||||
std::string project_root_path = project_path_to_native_project_root_path(project_path);
|
||||
std::string settings_path = project_root_path_to_settings_path(project_root_path);
|
||||
|
||||
return BLI_dir_create_recursive(settings_path.c_str());
|
||||
}
|
||||
|
||||
bool BlenderProject::delete_settings_directory(StringRef project_path)
|
||||
{
|
||||
std::string project_root_path = project_path_to_native_project_root_path(project_path);
|
||||
std::string settings_path = project_root_path_to_settings_path(project_root_path);
|
||||
|
||||
/* Returns 0 on success. */
|
||||
if (BLI_delete(settings_path.c_str(), true, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlenderProject *active_project = get_active();
|
||||
if (active_project &&
|
||||
BLI_path_cmp_normalized(project_root_path.c_str(), active_project->root_path().c_str())) {
|
||||
active_project->settings_->tag_has_unsaved_changes();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlenderProject::delete_settings_directory()
|
||||
{
|
||||
if (!delete_settings_directory(root_path_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
settings_->tag_has_unsaved_changes();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Simple getters & setters
|
||||
* \{ */
|
||||
|
||||
StringRefNull BlenderProject::root_path() const
|
||||
{
|
||||
return root_path_;
|
||||
}
|
||||
|
||||
ProjectSettings &BlenderProject::get_settings() const
|
||||
|
@ -79,7 +128,38 @@ ProjectSettings &BlenderProject::get_settings() const
|
|||
return *settings_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Path stuff
|
||||
* \{ */
|
||||
|
||||
StringRef BlenderProject::project_root_path_find_from_path(StringRef path)
|
||||
{
|
||||
/* There are two versions of the path used here: One copy that is converted to native slashes,
|
||||
* and the unmodified original path from the input. */
|
||||
|
||||
std::string path_native = path;
|
||||
BLI_path_slash_native(path_native.data());
|
||||
|
||||
StringRef cur_path = path;
|
||||
|
||||
while (cur_path.size()) {
|
||||
std::string cur_path_native = StringRef(path_native.c_str(), cur_path.size());
|
||||
if (path_is_project_root(cur_path_native)) {
|
||||
return path.substr(0, cur_path.size());
|
||||
}
|
||||
|
||||
/* Walk "up the path" (check the parent next). */
|
||||
const int64_t pos_last_slash = cur_path_native.find_last_of(SEP);
|
||||
if (pos_last_slash == StringRef::not_found) {
|
||||
break;
|
||||
}
|
||||
cur_path = cur_path.substr(0, pos_last_slash);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static StringRef path_strip_trailing_native_slash(StringRef path)
|
||||
{
|
||||
|
@ -92,26 +172,53 @@ static StringRef path_strip_trailing_native_slash(StringRef path)
|
|||
bool BlenderProject::path_is_project_root(StringRef path)
|
||||
{
|
||||
path = path_strip_trailing_native_slash(path);
|
||||
return BLI_exists(std::string(path + SEP_STR + ProjectSettings::SETTINGS_DIRNAME).c_str());
|
||||
return BLI_exists(std::string(path + SEP_STR + SETTINGS_DIRNAME).c_str());
|
||||
}
|
||||
|
||||
std::string BlenderProject::project_path_to_native_project_root_path(StringRef project_path)
|
||||
{
|
||||
std::string project_path_native = project_path;
|
||||
BLI_path_slash_native(project_path_native.data());
|
||||
|
||||
const StringRef path_no_trailing_slashes = path_strip_trailing_native_slash(project_path_native);
|
||||
if (path_no_trailing_slashes.endswith(SETTINGS_DIRNAME)) {
|
||||
return StringRef(path_no_trailing_slashes).drop_suffix(SETTINGS_DIRNAME.size());
|
||||
}
|
||||
|
||||
return std::string(path_no_trailing_slashes) + SEP;
|
||||
}
|
||||
|
||||
std::string BlenderProject::project_root_path_to_settings_path(StringRef project_root_path)
|
||||
{
|
||||
BLI_assert(project_root_path.back() == SEP);
|
||||
return project_root_path + SETTINGS_DIRNAME + SEP;
|
||||
}
|
||||
|
||||
std::string BlenderProject::project_root_path_to_settings_filepath(StringRef project_root_path)
|
||||
{
|
||||
BLI_assert(project_root_path.back() == SEP);
|
||||
return project_root_path_to_settings_path(project_root_path) + SETTINGS_FILENAME;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name C-API
|
||||
* \{ */
|
||||
|
||||
using namespace blender;
|
||||
|
||||
bool BKE_project_create_settings_directory(const char *project_root_path)
|
||||
{
|
||||
return bke::ProjectSettings::create_settings_directory(project_root_path);
|
||||
return bke::BlenderProject::create_settings_directory(project_root_path);
|
||||
}
|
||||
|
||||
bool BKE_project_delete_settings_directory(const BlenderProject *project_handle)
|
||||
bool BKE_project_delete_settings_directory(BlenderProject *project_handle)
|
||||
{
|
||||
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
|
||||
project_handle);
|
||||
bke::ProjectSettings &settings = project->get_settings();
|
||||
return settings.delete_settings_directory();
|
||||
bke::BlenderProject *project = reinterpret_cast<bke::BlenderProject *>(project_handle);
|
||||
return project->delete_settings_directory();
|
||||
}
|
||||
|
||||
BlenderProject *BKE_project_active_get(void)
|
||||
|
@ -121,7 +228,7 @@ BlenderProject *BKE_project_active_get(void)
|
|||
|
||||
void BKE_project_active_unset(void)
|
||||
{
|
||||
bke::BlenderProject::set_active_from_settings(nullptr);
|
||||
bke::BlenderProject::set_active(nullptr);
|
||||
}
|
||||
|
||||
bool BKE_project_is_path_project_root(const char *path)
|
||||
|
@ -135,39 +242,15 @@ bool BKE_project_contains_path(const char *path)
|
|||
return !found_root_path.is_empty();
|
||||
}
|
||||
|
||||
BlenderProject *BKE_project_load_from_path(const char *path)
|
||||
{
|
||||
std::unique_ptr<bke::ProjectSettings> project_settings = bke::ProjectSettings::load_from_path(
|
||||
path);
|
||||
if (!project_settings) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<BlenderProject *>(
|
||||
MEM_new<bke::BlenderProject>(__func__, std::move(project_settings)));
|
||||
}
|
||||
|
||||
void BKE_project_free(BlenderProject **project_handle)
|
||||
{
|
||||
bke::BlenderProject *project = reinterpret_cast<bke::BlenderProject *>(*project_handle);
|
||||
BLI_assert_msg(project != bke::BlenderProject::get_active(),
|
||||
"Projects loaded with #BKE_project_load_from_path() must never be set active.");
|
||||
|
||||
MEM_delete(project);
|
||||
*project_handle = nullptr;
|
||||
}
|
||||
|
||||
BlenderProject *BKE_project_active_load_from_path(const char *path)
|
||||
{
|
||||
/* Project should be unset if the path doesn't contain a project root. Unset in the beginning so
|
||||
* early exiting behaves correctly. */
|
||||
BKE_project_active_unset();
|
||||
|
||||
std::unique_ptr<bke::ProjectSettings> project_settings = bke::ProjectSettings::load_from_path(
|
||||
path);
|
||||
bke::BlenderProject::set_active_from_settings(std::move(project_settings));
|
||||
std::unique_ptr<bke::BlenderProject> project = bke::BlenderProject::load_from_path(path);
|
||||
|
||||
return BKE_project_active_get();
|
||||
return reinterpret_cast<::BlenderProject *>(bke::BlenderProject::set_active(std::move(project)));
|
||||
}
|
||||
|
||||
bool BKE_project_settings_save(const BlenderProject *project_handle)
|
||||
|
@ -175,14 +258,14 @@ bool BKE_project_settings_save(const BlenderProject *project_handle)
|
|||
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
|
||||
project_handle);
|
||||
bke::ProjectSettings &settings = project->get_settings();
|
||||
return settings.save_to_disk(settings.project_root_path());
|
||||
return settings.save_to_disk(project->root_path());
|
||||
}
|
||||
|
||||
const char *BKE_project_root_path_get(const BlenderProject *project_handle)
|
||||
{
|
||||
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
|
||||
project_handle);
|
||||
return project->get_settings().project_root_path().c_str();
|
||||
return project->root_path().c_str();
|
||||
}
|
||||
|
||||
void BKE_project_name_set(const BlenderProject *project_handle, const char *name)
|
||||
|
@ -222,3 +305,5 @@ bool BKE_project_has_unsaved_changes(const BlenderProject *project_handle)
|
|||
const bke::ProjectSettings &settings = project->get_settings();
|
||||
return settings.has_unsaved_changes();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -24,35 +24,15 @@ namespace blender::bke {
|
|||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
ProjectSettings::ProjectSettings(StringRef project_root_path)
|
||||
: project_root_path_(project_root_path)
|
||||
{
|
||||
}
|
||||
struct CustomAssetLibraries : NonCopyable {
|
||||
ListBase asset_libraries = {nullptr, nullptr}; /* CustomAssetLibraryDefinition */
|
||||
|
||||
bool ProjectSettings::create_settings_directory(StringRef project_root_path)
|
||||
{
|
||||
std::string project_root_path_native = project_root_path;
|
||||
BLI_path_slash_native(project_root_path_native.data());
|
||||
|
||||
return BLI_dir_create_recursive(
|
||||
std::string(project_root_path_native + SEP + SETTINGS_DIRNAME).c_str());
|
||||
}
|
||||
|
||||
bool ProjectSettings::delete_settings_directory()
|
||||
{
|
||||
BLI_assert(project_root_path_[0] == SEP);
|
||||
std::string dot_blender_project_dir_path = project_root_path_ + SETTINGS_DIRNAME;
|
||||
|
||||
/* Returns 0 on success. */
|
||||
if (BLI_delete(dot_blender_project_dir_path.c_str(), true, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
has_unsaved_changes_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
CustomAssetLibraries() = default;
|
||||
CustomAssetLibraries(ListBase asset_libraries);
|
||||
CustomAssetLibraries(CustomAssetLibraries &&);
|
||||
~CustomAssetLibraries();
|
||||
auto operator=(CustomAssetLibraries &&) -> CustomAssetLibraries &;
|
||||
};
|
||||
|
||||
CustomAssetLibraries::CustomAssetLibraries(ListBase asset_libraries)
|
||||
: asset_libraries(asset_libraries)
|
||||
|
@ -196,11 +176,11 @@ std::unique_ptr<serialize::DictionaryValue> ProjectSettings::to_dictionary() con
|
|||
root_attributes.append_as("project", std::move(project_dict));
|
||||
}
|
||||
/* "asset_libraries": */ {
|
||||
if (!BLI_listbase_is_empty(&asset_libraries_.asset_libraries)) {
|
||||
if (!BLI_listbase_is_empty(&asset_libraries_->asset_libraries)) {
|
||||
std::unique_ptr<ArrayValue> asset_libs_array = std::make_unique<ArrayValue>();
|
||||
ArrayValue::Items &asset_libs_elements = asset_libs_array->elements();
|
||||
LISTBASE_FOREACH (
|
||||
const CustomAssetLibraryDefinition *, library, &asset_libraries_.asset_libraries) {
|
||||
const CustomAssetLibraryDefinition *, library, &asset_libraries_->asset_libraries) {
|
||||
std::unique_ptr<DictionaryValue> library_dict = std::make_unique<DictionaryValue>();
|
||||
DictionaryValue::Items &library_attributes = library_dict->elements();
|
||||
|
||||
|
@ -232,67 +212,33 @@ static void write_settings_file(StringRef settings_filepath,
|
|||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
struct ResolvedPaths {
|
||||
std::string settings_filepath;
|
||||
std::string project_root_path;
|
||||
};
|
||||
|
||||
static StringRef path_strip_trailing_native_slash(StringRef path)
|
||||
{
|
||||
const int64_t pos_before_trailing_slash = path.find_last_not_of(SEP);
|
||||
return (pos_before_trailing_slash == StringRef::not_found) ?
|
||||
path :
|
||||
path.substr(0, pos_before_trailing_slash + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned paths can be assumed to use native slashes.
|
||||
*/
|
||||
static ResolvedPaths resolve_paths_from_project_path(StringRef project_path)
|
||||
{
|
||||
std::string project_path_native = project_path;
|
||||
BLI_path_slash_native(project_path_native.data());
|
||||
|
||||
ResolvedPaths resolved_paths{};
|
||||
|
||||
const StringRef path_no_trailing_slashes = path_strip_trailing_native_slash(project_path_native);
|
||||
if (path_no_trailing_slashes.endswith(ProjectSettings::SETTINGS_DIRNAME)) {
|
||||
resolved_paths.project_root_path =
|
||||
StringRef(path_no_trailing_slashes).drop_suffix(ProjectSettings::SETTINGS_DIRNAME.size());
|
||||
}
|
||||
else {
|
||||
resolved_paths.project_root_path = std::string(path_no_trailing_slashes) + SEP;
|
||||
}
|
||||
resolved_paths.settings_filepath = resolved_paths.project_root_path +
|
||||
ProjectSettings::SETTINGS_DIRNAME + SEP +
|
||||
ProjectSettings::SETTINGS_FILENAME;
|
||||
|
||||
return resolved_paths;
|
||||
}
|
||||
|
||||
std::unique_ptr<ProjectSettings> ProjectSettings::load_from_disk(StringRef project_path)
|
||||
{
|
||||
ResolvedPaths paths = resolve_paths_from_project_path(project_path);
|
||||
const std::string project_root_path = BlenderProject::project_path_to_native_project_root_path(
|
||||
project_path);
|
||||
|
||||
if (!BLI_exists(paths.project_root_path.c_str())) {
|
||||
if (!BLI_exists(project_root_path.c_str())) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!BlenderProject::path_is_project_root(paths.project_root_path.c_str())) {
|
||||
if (!BlenderProject::path_is_project_root(project_root_path.c_str())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<serialize::Value> values = read_settings_file(paths.settings_filepath);
|
||||
const std::string settings_filepath = BlenderProject::project_root_path_to_settings_filepath(
|
||||
project_root_path);
|
||||
std::unique_ptr<serialize::Value> values = read_settings_file(settings_filepath);
|
||||
std::unique_ptr<ExtractedSettings> extracted_settings = nullptr;
|
||||
if (values) {
|
||||
BLI_assert(values->as_dictionary_value() != nullptr);
|
||||
extracted_settings = extract_settings(*values->as_dictionary_value());
|
||||
}
|
||||
|
||||
std::unique_ptr loaded_settings = std::make_unique<ProjectSettings>(paths.project_root_path);
|
||||
std::unique_ptr loaded_settings = std::make_unique<ProjectSettings>();
|
||||
if (extracted_settings) {
|
||||
loaded_settings->project_name_ = extracted_settings->project_name;
|
||||
/* Moves ownership. */
|
||||
loaded_settings->asset_libraries_ = CustomAssetLibraries(extracted_settings->asset_libraries);
|
||||
loaded_settings->asset_libraries_ = std::make_unique<CustomAssetLibraries>(
|
||||
extracted_settings->asset_libraries);
|
||||
}
|
||||
|
||||
return loaded_settings;
|
||||
|
@ -310,17 +256,20 @@ std::unique_ptr<ProjectSettings> ProjectSettings::load_from_path(StringRef path)
|
|||
|
||||
bool ProjectSettings::save_to_disk(StringRef project_path)
|
||||
{
|
||||
ResolvedPaths paths = resolve_paths_from_project_path(project_path);
|
||||
const std::string project_root_path = BlenderProject::project_path_to_native_project_root_path(
|
||||
project_path);
|
||||
|
||||
if (!BLI_exists(paths.project_root_path.c_str())) {
|
||||
if (!BLI_exists(project_root_path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
if (!BlenderProject::path_is_project_root(paths.project_root_path.c_str())) {
|
||||
if (!BlenderProject::path_is_project_root(project_root_path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string settings_filepath = BlenderProject::project_root_path_to_settings_filepath(
|
||||
project_root_path);
|
||||
std::unique_ptr settings_as_dict = to_dictionary();
|
||||
write_settings_file(paths.settings_filepath, std::move(settings_as_dict));
|
||||
write_settings_file(settings_filepath, std::move(settings_as_dict));
|
||||
|
||||
has_unsaved_changes_ = false;
|
||||
|
||||
|
@ -329,11 +278,12 @@ bool ProjectSettings::save_to_disk(StringRef project_path)
|
|||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
StringRefNull ProjectSettings::project_root_path() const
|
||||
ProjectSettings::ProjectSettings() : asset_libraries_(std::make_unique<CustomAssetLibraries>())
|
||||
{
|
||||
return project_root_path_;
|
||||
}
|
||||
|
||||
ProjectSettings::~ProjectSettings() = default;
|
||||
|
||||
void ProjectSettings::project_name(StringRef new_name)
|
||||
{
|
||||
project_name_ = new_name;
|
||||
|
@ -347,12 +297,12 @@ StringRefNull ProjectSettings::project_name() const
|
|||
|
||||
const ListBase &ProjectSettings::asset_library_definitions() const
|
||||
{
|
||||
return asset_libraries_.asset_libraries;
|
||||
return asset_libraries_->asset_libraries;
|
||||
}
|
||||
|
||||
ListBase &ProjectSettings::asset_library_definitions()
|
||||
{
|
||||
return asset_libraries_.asset_libraries;
|
||||
return asset_libraries_->asset_libraries;
|
||||
}
|
||||
|
||||
void ProjectSettings::tag_has_unsaved_changes()
|
||||
|
|
|
@ -118,13 +118,13 @@ class ProjectTest : public testing::Test {
|
|||
TEST_F(ProjectTest, settings_create)
|
||||
{
|
||||
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
|
||||
if (!ProjectSettings::create_settings_directory(project_path)) {
|
||||
if (!BlenderProject::create_settings_directory(project_path)) {
|
||||
/* Not a regular test failure, this may fail if there is a permission issue for example. */
|
||||
FAIL() << "Failed to create project directory in '" << project_path
|
||||
<< "', check permissions";
|
||||
}
|
||||
std::string project_settings_dir = project_path_native + SEP_STR +
|
||||
ProjectSettings::SETTINGS_DIRNAME;
|
||||
BlenderProject::SETTINGS_DIRNAME;
|
||||
EXPECT_TRUE(BLI_exists(project_settings_dir.c_str()) &&
|
||||
BLI_is_dir(project_settings_dir.c_str()))
|
||||
<< project_settings_dir + " was not created";
|
||||
|
@ -133,31 +133,31 @@ TEST_F(ProjectTest, settings_create)
|
|||
|
||||
/* Load the project by pointing to the project root directory (as opposed to the .blender_project
|
||||
* directory). */
|
||||
TEST_F(ProjectTest, settings_load_from_project_root_path)
|
||||
TEST_F(ProjectTest, load_from_project_root_path)
|
||||
{
|
||||
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
|
||||
ProjectSettings::create_settings_directory(project_path);
|
||||
BlenderProject::create_settings_directory(project_path);
|
||||
|
||||
std::unique_ptr project_settings = ProjectSettings::load_from_disk(project_path);
|
||||
EXPECT_NE(project_settings, nullptr);
|
||||
EXPECT_EQ(project_settings->project_root_path(), project_path_native);
|
||||
EXPECT_EQ(project_settings->project_name(), "");
|
||||
std::unique_ptr project = BlenderProject::load_from_path(project_path);
|
||||
EXPECT_NE(project, nullptr);
|
||||
EXPECT_EQ(project->root_path(), project_path_native);
|
||||
EXPECT_EQ(project->get_settings().project_name(), "");
|
||||
});
|
||||
}
|
||||
|
||||
/* Load the project by pointing to the .blender_project directory (as opposed to the project root
|
||||
* directory). */
|
||||
TEST_F(ProjectTest, settings_load_from_project_settings_path)
|
||||
TEST_F(ProjectTest, load_from_project_settings_path)
|
||||
{
|
||||
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
|
||||
ProjectSettings::create_settings_directory(project_path);
|
||||
BlenderProject::create_settings_directory(project_path);
|
||||
|
||||
std::unique_ptr project_settings = ProjectSettings::load_from_disk(
|
||||
std::unique_ptr project = BlenderProject::load_from_path(
|
||||
project_path + (ELEM(project_path.back(), SEP, ALTSEP) ? "" : SEP_STR) +
|
||||
ProjectSettings::SETTINGS_DIRNAME);
|
||||
EXPECT_NE(project_settings, nullptr);
|
||||
EXPECT_EQ(project_settings->project_root_path(), project_path_native);
|
||||
EXPECT_EQ(project_settings->project_name(), "");
|
||||
BlenderProject::SETTINGS_DIRNAME);
|
||||
EXPECT_NE(project, nullptr);
|
||||
EXPECT_EQ(project->root_path(), project_path_native);
|
||||
EXPECT_EQ(project->get_settings().project_name(), "");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ TEST_F(ProjectTest, settings_json_write)
|
|||
/* Take the settings read from the SVN files and write it to /tmp/ projects. */
|
||||
test_foreach_project_path(
|
||||
[&from_project_settings](StringRefNull to_project_path, StringRefNull) {
|
||||
ProjectSettings::create_settings_directory(to_project_path);
|
||||
BlenderProject::create_settings_directory(to_project_path);
|
||||
|
||||
if (!from_project_settings->save_to_disk(to_project_path)) {
|
||||
FAIL();
|
||||
|
@ -215,7 +215,7 @@ TEST_F(ProjectTest, settings_read_change_write)
|
|||
/* Take the settings read from the SVN files and write it to /tmp/ projects. */
|
||||
test_foreach_project_path(
|
||||
[&from_project_settings](StringRefNull to_project_path, StringRefNull) {
|
||||
ProjectSettings::create_settings_directory(to_project_path);
|
||||
BlenderProject::create_settings_directory(to_project_path);
|
||||
|
||||
from_project_settings->project_name("новый");
|
||||
EXPECT_TRUE(from_project_settings->has_unsaved_changes());
|
||||
|
@ -240,7 +240,7 @@ TEST_F(ProjectTest, project_root_path_find_from_path)
|
|||
/* First test without a .blender_project directory present. */
|
||||
EXPECT_EQ(BlenderProject::project_root_path_find_from_path(project_path), "");
|
||||
|
||||
ProjectSettings::create_settings_directory(project_path);
|
||||
BlenderProject::create_settings_directory(project_path);
|
||||
EXPECT_EQ(BlenderProject::project_root_path_find_from_path(project_path), project_path);
|
||||
});
|
||||
|
||||
|
@ -302,14 +302,14 @@ TEST_F(BlendfileProjectLoadingTest, load_blend_file)
|
|||
TEST_F(ProjectTest, project_load_and_delete)
|
||||
{
|
||||
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
|
||||
ProjectSettings::create_settings_directory(project_path);
|
||||
BlenderProject::create_settings_directory(project_path);
|
||||
|
||||
::BlenderProject *project = BKE_project_active_load_from_path(project_path.c_str());
|
||||
EXPECT_NE(project, nullptr);
|
||||
EXPECT_FALSE(BKE_project_has_unsaved_changes(project));
|
||||
|
||||
std::string project_settings_dir = project_path_native + SEP_STR +
|
||||
ProjectSettings::SETTINGS_DIRNAME;
|
||||
BlenderProject::SETTINGS_DIRNAME;
|
||||
|
||||
EXPECT_TRUE(BLI_exists(project_settings_dir.c_str()));
|
||||
if (!BKE_project_delete_settings_directory(project)) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "BKE_asset_library_custom.h"
|
||||
#include "BKE_blender_project.h"
|
||||
#include "BKE_blender_project.hh"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -52,15 +53,25 @@ bool ED_project_new(const Main *bmain, const char *project_root_dir, ReportList
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<bke::BlenderProject> loaded_project = bke::BlenderProject::load_from_path(
|
||||
project_root_dir);
|
||||
|
||||
/* Some default settings for the project. */
|
||||
if (loaded_project) {
|
||||
BlenderProject *loaded_project_c = BKE_project_c_handle(loaded_project.get());
|
||||
|
||||
ED_project_set_defaults(loaded_project_c);
|
||||
/* Write defaults to the hard drive. */
|
||||
BKE_project_settings_save(loaded_project_c);
|
||||
}
|
||||
|
||||
BKE_reportf(reports, RPT_INFO, "Project created and loaded successfully");
|
||||
|
||||
const char *blendfile_path = BKE_main_blendfile_path(bmain);
|
||||
|
||||
bool activated_new_project = false;
|
||||
if (blendfile_path[0] && BLI_path_contains(project_root_dir, blendfile_path)) {
|
||||
if (BKE_project_active_load_from_path(blendfile_path)) {
|
||||
activated_new_project = true;
|
||||
}
|
||||
const char *blend_path = BKE_main_blendfile_path(bmain);
|
||||
const bool blend_is_in_project = blend_path[0] &&
|
||||
BLI_path_contains(project_root_dir, blend_path);
|
||||
if (blend_is_in_project) {
|
||||
bke::BlenderProject::set_active(std::move(loaded_project));
|
||||
}
|
||||
else {
|
||||
BKE_reportf(reports,
|
||||
|
@ -69,21 +80,5 @@ bool ED_project_new(const Main *bmain, const char *project_root_dir, ReportList
|
|||
"project is not active");
|
||||
}
|
||||
|
||||
/* Some default settings for the project. It has to be loaded for that. */
|
||||
BlenderProject *new_project = activated_new_project ?
|
||||
CTX_wm_project() :
|
||||
BKE_project_load_from_path(project_root_dir);
|
||||
if (new_project) {
|
||||
ED_project_set_defaults(new_project);
|
||||
|
||||
/* Write defaults to the hard drive. */
|
||||
BKE_project_settings_save(new_project);
|
||||
|
||||
/* We just temporary loaded the project, free it again. */
|
||||
if (!activated_new_project) {
|
||||
BKE_project_free(&new_project);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue