summaryrefslogtreecommitdiff
path: root/core/src/Model
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/Model')
-rw-r--r--core/src/Model/GlobalStates.cpp110
-rw-r--r--core/src/Model/GlobalStates.hpp43
-rw-r--r--core/src/Model/Project.cpp50
-rw-r--r--core/src/Model/Project.hpp9
-rw-r--r--core/src/Model/fwd.hpp3
5 files changed, 214 insertions, 1 deletions
diff --git a/core/src/Model/GlobalStates.cpp b/core/src/Model/GlobalStates.cpp
new file mode 100644
index 0000000..cd076f4
--- /dev/null
+++ b/core/src/Model/GlobalStates.cpp
@@ -0,0 +1,110 @@
+#include "GlobalStates.hpp"
+
+#include "Model/Project.hpp"
+#include "Utils/StandardDirectories.hpp"
+
+#include <json/reader.h>
+#include <json/value.h>
+#include <json/writer.h>
+#include <cassert>
+#include <filesystem>
+#include <fstream>
+#include <memory>
+
+namespace fs = std::filesystem;
+
+static std::unique_ptr<GlobalStates> globalStateInstance;
+static fs::path globalDataPath;
+
+void GlobalStates::Init() {
+ globalStateInstance = std::make_unique<GlobalStates>();
+ globalDataPath = StandardDirectories::UserData() / "cplt";
+ fs::create_directories(globalDataPath);
+
+ // Reading recent projects
+ [&]() {
+ std::ifstream ifs(globalDataPath / "recents.json");
+ if (!ifs) return;
+
+ Json::Value root;
+ ifs >> root;
+
+ if (!root.isObject()) return;
+ if (auto& recents = root["RecentProjects"]; recents.isArray()) {
+ for (auto& elm : recents) {
+ if (!elm.isString()) continue;
+
+ fs::path path(elm.asCString());
+ if (!fs::exists(path)) continue;
+
+ auto utf8String = path.string();
+ globalStateInstance->mRecentProjects.push_back(RecentProject{
+ .path = std::move(path),
+ .cachedUtf8String = std::move(utf8String),
+ });
+ }
+ }
+ }();
+}
+
+void GlobalStates::Shutdown() {
+ if (!globalStateInstance) return;
+ if (globalStateInstance->mDirty) {
+ globalStateInstance->WriteToDisk();
+ }
+}
+
+GlobalStates& GlobalStates::GetInstance() {
+ return *globalStateInstance;
+}
+
+const std::filesystem::path& GlobalStates::GetUserDataPath() {
+ return globalDataPath;
+}
+
+const std::vector<GlobalStates::RecentProject>& GlobalStates::GetRecentProjects() const {
+ return mRecentProjects;
+}
+
+void GlobalStates::ClearRecentProjects() {
+ mRecentProjects.clear();
+ MarkDirty();
+}
+
+void GlobalStates::AddRecentProject(const Project& project) {
+ mRecentProjects.push_back(RecentProject{
+ .path = project.GetPath(),
+ .cachedUtf8String = project.GetPath().string(),
+ });
+ MarkDirty();
+}
+
+void GlobalStates::RemoveRecentProject(int idx) {
+ assert(idx >= 0 && idx < mRecentProjects.size());
+
+ mRecentProjects.erase(mRecentProjects.begin() + idx);
+ MarkDirty();
+}
+
+void GlobalStates::WriteToDisk() const {
+ Json::Value root;
+
+ auto& recentProjects = root["RecentProjects"] = Json::Value(Json::arrayValue);
+ for (auto& [path, _] : mRecentProjects) {
+ recentProjects.append(Json::Value(path.string()));
+ }
+
+ std::ofstream ofs(globalDataPath / "recents.json");
+ ofs << root;
+
+ mDirty = false;
+}
+
+bool GlobalStates::IsDirty() const {
+ return mDirty;
+}
+
+void GlobalStates::MarkDirty() {
+ mDirty = true;
+ OnModified();
+}
diff --git a/core/src/Model/GlobalStates.hpp b/core/src/Model/GlobalStates.hpp
new file mode 100644
index 0000000..e6d823b
--- /dev/null
+++ b/core/src/Model/GlobalStates.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "Utils/Sigslot.hpp"
+#include "cplt_fwd.hpp"
+
+#include <filesystem>
+#include <string>
+#include <vector>
+
+class GlobalStates {
+public:
+ static void Init();
+ static void Shutdown();
+
+ static GlobalStates& GetInstance();
+ static const std::filesystem::path& GetUserDataPath();
+
+ struct RecentProject {
+ std::filesystem::path path;
+ std::string cachedUtf8String;
+ };
+
+public:
+ Signal<> OnModified;
+
+private:
+ std::vector<RecentProject> mRecentProjects;
+ mutable bool mDirty = false;
+
+public:
+ const std::vector<RecentProject>& GetRecentProjects() const;
+ void ClearRecentProjects();
+ void AddRecentProject(const Project& project);
+ void RemoveRecentProject(int idx);
+
+ // TODO async autosaving to prevent data loss on crash
+ void WriteToDisk() const;
+
+ bool IsDirty() const;
+
+private:
+ void MarkDirty();
+};
diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp
index f301bb8..c54a02c 100644
--- a/core/src/Model/Project.cpp
+++ b/core/src/Model/Project.cpp
@@ -1,8 +1,56 @@
#include "Project.hpp"
+#include <json/reader.h>
+#include <json/value.h>
+#include <json/writer.h>
+#include <filesystem>
+#include <fstream>
+#include <stdexcept>
#include <utility>
-const std::filesystem::path& Project::GetPath() const {
+namespace fs = std::filesystem;
+
+Project Project::Load(const fs::path& path) {
+ // TODO better diagnostic
+ const char* kInvalidFormatErr = "Failed to load project: invalid format.";
+
+ std::ifstream ifs(path);
+ if (!ifs) {
+ std::string message;
+ message += "Failed to load project file at '";
+ message += path.string();
+ message += "'.";
+ throw std::runtime_error(message);
+ }
+
+ Project proj;
+ proj.mRootPath = path.parent_path();
+
+ Json::Value root;
+ ifs >> root;
+
+ const auto& croot = root; // Use const reference so that accessors default to returning a null if not found, instead of siliently creating new elements
+ if (!croot.isObject()) {
+ throw std::runtime_error(kInvalidFormatErr);
+ }
+
+ if (auto& name = croot["Name"]; name.isString()) {
+ proj.mName = name.asString();
+ } else {
+ throw std::runtime_error(kInvalidFormatErr);
+ }
+
+ return proj;
+}
+
+Project Project::Create(std::string name, const fs::path& path) {
+ Project proj;
+ proj.mRootPath = path;
+ proj.mName = std::move(name);
+ return proj;
+}
+
+const fs::path& Project::GetPath() const {
return mRootPath;
}
diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp
index 34ae1d7..7b5c7e3 100644
--- a/core/src/Model/Project.hpp
+++ b/core/src/Model/Project.hpp
@@ -9,9 +9,18 @@ public:
std::string mName;
public:
+ /// Load the project from a cplt_project.json file.
+ static Project Load(const std::filesystem::path& path);
+ /// Create a project with the given name in the given path. Note that the path should be a directory that will contain the project files once created.
+ /// This function assumes the given directory will exist and is empty.
+ static Project Create(std::string name, const std::filesystem::path& path);
+
// Path to a *directory* that contains the project file.
const std::filesystem::path& GetPath() const;
const std::string& GetName() const;
void SetName(std::string name);
+
+private:
+ Project() = default;
};
diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp
index f5a5818..6bbc0b7 100644
--- a/core/src/Model/fwd.hpp
+++ b/core/src/Model/fwd.hpp
@@ -1,4 +1,7 @@
#pragma once
+// GlobalStates.hpp
+class GlobalStates;
+
// Project.hpp
class Project;