diff options
author | rtk0c <[email protected]> | 2022-06-30 21:38:53 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2022-06-30 21:38:53 -0700 |
commit | 7fe47a9d5b1727a61dc724523b530762f6d6ba19 (patch) | |
tree | e95be6e66db504ed06d00b72c579565bab873277 /core/src/Model | |
parent | 2cf952088d375ac8b2f45b144462af0953436cff (diff) |
Restructure project
Diffstat (limited to 'core/src/Model')
49 files changed, 0 insertions, 5726 deletions
diff --git a/core/src/Model/Assets.cpp b/core/src/Model/Assets.cpp deleted file mode 100644 index fa29523..0000000 --- a/core/src/Model/Assets.cpp +++ /dev/null @@ -1,306 +0,0 @@ -#include "Assets.hpp" - -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" -#include "Utils/IO/DataStream.hpp" -#include "Utils/IO/StringIntegration.hpp" -#include "Utils/IO/UuidIntegration.hpp" - -#include <IconsFontAwesome.h> -#include <imgui.h> -#include <imgui_stdlib.h> -#include <tsl/array_map.h> -#include <string> -#include <utility> - -using namespace std::literals::string_view_literals; -namespace fs = std::filesystem; - -template <class TSavedAsset, class TStream> -void OperateStreamForSavedAsset(TSavedAsset& cell, TStream& proxy) -{ - proxy.template ObjectAdapted<DataStreamAdapters::String>(cell.Name); - proxy.template ObjectAdapted<DataStreamAdapters::Uuid>(cell.Uuid); - proxy.Value(cell.Payload); -} - -void SavedAsset::ReadFromDataStream(InputDataStream& stream) -{ - ::OperateStreamForSavedAsset(*this, stream); -} - -void SavedAsset::WriteToDataStream(OutputDataStream& stream) const -{ - ::OperateStreamForSavedAsset(*this, stream); -} - -Asset::Asset() = default; - -class AssetList::Private -{ -public: - Project* ConnectedProject; - tsl::array_map<char, SavedAsset> Assets; - tsl::array_map<char, std::unique_ptr<Asset>> Cache; - int CacheSizeLimit = 0; - - struct - { - std::string NewName; - NameSelectionError NewNameError = NameSelectionError::Empty; - - void ShowErrors() const - { - switch (NewNameError) { - case NameSelectionError::None: break; - case NameSelectionError::Duplicated: - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - break; - case NameSelectionError::Empty: - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - break; - } - } - - bool HasErrors() const - { - return NewNameError != NameSelectionError::None; - } - - void Validate(const AssetList& self) - { - if (NewName.empty()) { - NewNameError = NameSelectionError::Empty; - return; - } - - if (self.FindByName(NewName)) { - NewNameError = NameSelectionError::Duplicated; - return; - } - - NewNameError = NameSelectionError::None; - } - } PopupPrivateState; -}; - -AssetList::AssetList(Project& project) - : mPrivate{ std::make_unique<Private>() } -{ - mPrivate->ConnectedProject = &project; -} - -// Write an empty destructor here so std::unique_ptr's destructor can see AssetList::Private's implementation -AssetList::~AssetList() -{ -} - -Project& AssetList::GetConnectedProject() const -{ - return *mPrivate->ConnectedProject; -} - -void AssetList::Reload() -{ - // TODO fix asset dicovery loading - mPrivate->Assets.clear(); - mPrivate->Cache.clear(); - DiscoverFiles([this](SavedAsset asset) -> void { - mPrivate->Assets.insert(asset.Name, std::move(asset)); - }); -} - -int AssetList::GetCount() const -{ - return mPrivate->Assets.size(); -} - -const tsl::array_map<char, SavedAsset>& AssetList::GetAssets() const -{ - return mPrivate->Assets; -} - -const SavedAsset* AssetList::FindByName(std::string_view name) const -{ - auto iter = mPrivate->Assets.find(name); - if (iter != mPrivate->Assets.end()) { - return &iter.value(); - } else { - return nullptr; - } -} - -const SavedAsset& AssetList::Create(SavedAsset asset) -{ - auto [iter, DISCARD] = mPrivate->Assets.insert(asset.Name, SavedAsset{}); - auto& savedAsset = iter.value(); - - savedAsset = std::move(asset); - if (savedAsset.Uuid.is_nil()) { - savedAsset.Uuid = uuids::uuid_random_generator{}(); - } - - SaveInstance(savedAsset, nullptr); - - return savedAsset; -} - -std::unique_ptr<Asset> AssetList::CreateAndLoad(SavedAsset assetIn) -{ - auto& savedAsset = Create(std::move(assetIn)); - auto asset = std::unique_ptr<Asset>(CreateInstance(savedAsset)); - return asset; -} - -std::unique_ptr<Asset> AssetList::Load(std::string_view name) const -{ - if (auto savedAsset = FindByName(name)) { - auto asset = Load(*savedAsset); - return asset; - } else { - return nullptr; - } -} - -std::unique_ptr<Asset> AssetList::Load(const SavedAsset& asset) const -{ - return std::unique_ptr<Asset>(LoadInstance(asset)); -} - -const SavedAsset* AssetList::Rename(std::string_view oldName, std::string_view newName) -{ - auto iter = mPrivate->Assets.find(oldName); - if (iter == mPrivate->Assets.end()) return nullptr; - - auto info = std::move(iter.value()); - info.Name = newName; - - RenameInstanceOnDisk(info, oldName); - - mPrivate->Assets.erase(iter); - auto [newIter, DISCARD] = mPrivate->Assets.insert(newName, std::move(info)); - - return &newIter.value(); -} - -bool AssetList::Remove(std::string_view name) -{ - auto iter = mPrivate->Assets.find(name); - if (iter == mPrivate->Assets.end()) { - return false; - } - auto& asset = iter.value(); - - fs::remove(RetrievePathFromAsset(asset)); - mPrivate->Assets.erase(iter); - - return true; -} - -int AssetList::GetCacheSizeLimit() const -{ - return mPrivate->CacheSizeLimit; -} - -void AssetList::SetCacheSizeLimit(int limit) -{ - mPrivate->CacheSizeLimit = limit; -} - -void AssetList::DisplayIconsList(ListState& state) -{ - // TODO -} - -void AssetList::DisplayDetailsList(ListState& state) -{ - // Note: stub function remained here in case any state processing needs to be done before issuing to implementers - DisplayDetailsTable(state); -} - -void AssetList::DisplayControls(ListState& state) -{ - auto& ps = mPrivate->PopupPrivateState; - bool openedDummy = true; - - if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { - ImGui::OpenPopup(I18N_TEXT("Add asset wizard", L10N_ADD_ASSET_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Add asset wizard", L10N_ADD_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - DisplayAssetCreator(state); - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_I_CURSOR " " I18N_TEXT("Rename", L10N_RENAME), state.SelectedAsset == nullptr)) { - ImGui::OpenPopup(I18N_TEXT("Rename asset wizard", L10N_RENAME_ASSET_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Rename asset wizard", L10N_RENAME_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::InputText(I18N_TEXT("Name", L10N_NAME), &ps.NewName)) { - ps.Validate(*this); - } - - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM), ps.HasErrors())) { - ImGui::CloseCurrentPopup(); - - auto movedAsset = Rename(state.SelectedAsset->Name, ps.NewName); - // Update the selected pointer to the new location (we mutated the map, the pointer may be invalid now) - state.SelectedAsset = movedAsset; - - ps = {}; - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - - ps.ShowErrors(); - - ImGui::EndPopup(); - } - - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), state.SelectedAsset == nullptr)) { - ImGui::OpenPopup(I18N_TEXT("Delete asset", L10N_DELETE_ASSET_DIALOG_TITLE)); - } - if (ImGui::BeginPopupModal(I18N_TEXT("Delete asset", L10N_DELETE_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM))) { - ImGui::CloseCurrentPopup(); - - auto& assetName = state.SelectedAsset->Name; - Remove(assetName); - - state.SelectedAsset = nullptr; - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } -} - -void AssetList::DiscoverFilesByExtension(const std::function<void(SavedAsset)>& callback, const fs::path& containerDir, std::string_view extension) const -{ - for (auto entry : fs::directory_iterator(containerDir)) { - if (!entry.is_regular_file()) continue; - - // If the caller provided an extension to match against, and it doesn't equal to current file extension, skip - if (!extension.empty() && - entry.path().extension() != extension) - { - continue; - } - - callback(SavedAsset{ - .Name = RetrieveNameFromFile(entry.path()), - .Uuid = RetrieveUuidFromFile(entry.path()), - // TODO load payload - }); - } -} - -void AssetList::DiscoverFilesByHeader(const std::function<void(SavedAsset)>& callback, const fs::path& containerDir, const std::function<bool(std::istream&)>& validater) const -{ - // TODO -} diff --git a/core/src/Model/Assets.hpp b/core/src/Model/Assets.hpp deleted file mode 100644 index 3d90d3f..0000000 --- a/core/src/Model/Assets.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include "Utils/UUID.hpp" -#include "cplt_fwd.hpp" - -#include <tsl/array_map.h> -#include <filesystem> -#include <iosfwd> -#include <memory> -#include <string_view> - -/// A structure representing a ready-to-be-loaded asset, locating on the disk. -/// Each asset should be identified by a unique uuid within the asset category (i.e. a workflow and a template can share the same uuid), -/// generated on insertion to an asset list if not given by the caller. -struct SavedAsset -{ - std::string Name; - /// UUID of this asset. This field is generated as a random UUID v4 upon insertion into an AssetList, if not already provided by the caller (indicated by !is_nil()). - uuids::uuid Uuid; - /// Extra data to be used by the AssetList/Asset implementation. - uint64_t Payload; - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; -}; - -class Asset -{ -public: - Asset(); - virtual ~Asset() = default; -}; - -enum class NameSelectionError -{ - None, - Duplicated, - Empty, -}; - -class AssetList -{ -private: - class Private; - std::unique_ptr<Private> mPrivate; - -public: - AssetList(Project& project); - virtual ~AssetList(); - - Project& GetConnectedProject() const; - - // TODO support file watches - void Reload(); - - int GetCount() const; - // TODO convert to custom iterable - const tsl::array_map<char, SavedAsset>& GetAssets() const; - - const SavedAsset* FindByName(std::string_view name) const; - const SavedAsset& Create(SavedAsset asset); - std::unique_ptr<Asset> CreateAndLoad(SavedAsset asset); - /// Load the asset on disk by its name. - std::unique_ptr<Asset> Load(std::string_view name) const; - /// Load the asset on disk by a reference to its SavedAsset instance. This function assumes that the given SavedAsset - /// is stored in AssetList, otherwise the behavior is undefined. - std::unique_ptr<Asset> Load(const SavedAsset& asset) const; - const SavedAsset* Rename(std::string_view oldName, std::string_view newName); - bool Remove(std::string_view name); - - int GetCacheSizeLimit() const; - void SetCacheSizeLimit(int limit); - - struct ListState - { - const SavedAsset* SelectedAsset = nullptr; - }; - void DisplayIconsList(ListState& state); - void DisplayDetailsList(ListState& state); - void DisplayControls(ListState& state); - -protected: - virtual void DiscoverFiles(const std::function<void(SavedAsset)>& callback) const = 0; - - // Helper - void DiscoverFilesByExtension(const std::function<void(SavedAsset)>& callback, const std::filesystem::path& containerDir, std::string_view extension) const; - void DiscoverFilesByHeader(const std::function<void(SavedAsset)>& callback, const std::filesystem::path& containerDir, const std::function<bool(std::istream&)>& validater) const; - - /// Create an empty/default instance of this asset type on disk, potentially qualified by SavedAsset::Payload. - /// Return `true` on success and `false` on failure. - virtual bool SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const = 0; - /// The returned pointer indicate ownership to the object. - virtual Asset* LoadInstance(const SavedAsset& assetInfo) const = 0; - /// Create an empty/default instance of this asset type, potentially qualified by SavedAsset::Payload. - /// The returned pointer indicate ownership to the object. - virtual Asset* CreateInstance(const SavedAsset& assetInfo) const = 0; - virtual bool RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const = 0; - - virtual std::string RetrieveNameFromFile(const std::filesystem::path& file) const = 0; - virtual uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const = 0; - virtual std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const = 0; - - virtual void DisplayAssetCreator(ListState& state) = 0; - virtual void DisplayDetailsTable(ListState& state) const = 0; -}; - -template <class T> -class AssetListTyped : public AssetList -{ -public: - using AssetList::AssetList; - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "HidingNonVirtualFunction" - std::unique_ptr<T> CreateAndLoad(SavedAsset asset) - { - return std::unique_ptr<T>(static_cast<T*>(AssetList::CreateAndLoad(asset).release())); - } - - std::unique_ptr<T> Load(std::string_view name) const - { - return std::unique_ptr<T>(static_cast<T*>(AssetList::Load(name).release())); - } - - std::unique_ptr<T> Load(const SavedAsset& asset) const - { - return std::unique_ptr<T>(static_cast<T*>(AssetList::Load(asset).release())); - } -#pragma clang diagnostic pop -}; diff --git a/core/src/Model/Database.cpp b/core/src/Model/Database.cpp deleted file mode 100644 index 569b949..0000000 --- a/core/src/Model/Database.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "Database.hpp" - -#include "Model/Project.hpp" - -#include <filesystem> -#include <stdexcept> - -namespace fs = std::filesystem; - -SalesTable::SalesTable(MainDatabase& db) - // language=SQLite - : GetRowCount(db.GetSQLite(), "SELECT Count(*) FROM Sales") - // language=SQLite - , GetRows(db.GetSQLite(), "SELECT * FROM Sales LIMIT ? OFFSET ?") - // language=SQLite - , GetItems(db.GetSQLite(), "SELECT * FROM SalesItems WHERE SaleId == ?") -{ -} - -PurchasesTable::PurchasesTable(MainDatabase& db) - // language=SQLite - : GetRowCount(db.GetSQLite(), "SELECT Count(*) FROM Purchases") - // language=SQLite - , GetRows(db.GetSQLite(), "SELECT * FROM Purchases LIMIT ? OFFSET ?") - // language=SQLite - , GetItems(db.GetSQLite(), "SELECT * FROM PurchasesItems WHERE PurchaseId == ?") -{ -} - -DeliveryTable::DeliveryTable(MainDatabase& db) - // language=SQLite - : FilterByTypeAndId(db.GetSQLite(), "SELECT * FROM Deliveries WHERE AssociatedOrder == ? AND Outgoing = ?") - // language=SQLite - , GetItems(db.GetSQLite(), "SELECT * FROM DeliveriesItems WHERE DeliveryId == ?") -{ -} - -static std::string GetDatabaseFilePath(const Project& project) -{ - auto dbsDir = project.GetPath() / "databases"; - fs::create_directories(dbsDir); - - auto dbFile = dbsDir / "transactions.sqlite3"; - return dbFile.string(); -} - -/// Wrapper for SQLite::Database that creates the default tables -MainDatabase::DatabaseWrapper::DatabaseWrapper(MainDatabase& self) - : mSqlite(GetDatabaseFilePath(*self.mProject), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE) -{ - // If this table doesn't exist, the database probably just got initialized - if (mSqlite.tableExists("Sales")) { - return; - } - - // 'Sales' schema - // - Customer: the customer item ID - // - Deadline: unix epoch time of order deadline - // - DeliveryTime: the time this order was completed (through a set of deliveries) - - // 'Purchases' schema - // - Factory: the factory id, - // - OrderTime: the time this order was made - // - DeliveryTime: the time this order was completed (through a set of deliveries) - - // 'Deliveries' schema - // - ShipmentTime: unix epoch time stamp of sending to delivery - // - ArrivalTime: unix epoch time stamp of delivery arrived at warehouse; 0 if not arrived yet - // - AssociatedOrder: Id of the order that this delivery is completing (which table: Outgoing=true -> Sales, Outgoing=false -> Purchases) - // - Outgoing: true if the delivery is from warehouse to customer; false if the delivery is from factory to warehouse - - // Note: the 'Id' key would be unique (not recycled after row deletion) because it's explicit - // https://www.sqlite.org/rowidtable.html - - // language=SQLite - mSqlite.exec(R"""( -CREATE TABLE IF NOT EXISTS Sales( - Id INT PRIMARY KEY, - Customer INT, - Deadline DATETIME, - DeliveryTime DATETIME -); -CREATE TABLE IF NOT EXISTS SalesItems( - SaleId INT, - ItemId INT, - Count INT -); - -CREATE TABLE IF NOT EXISTS Purchases( - Id INT PRIMARY KEY, - Factory INT, - OrderTime DATETIME, - DeliveryTime DATETIME -); -CREATE TABLE IF NOT EXISTS PurchasesItems( - PurchaseId INT, - ItemId INT, - Count INT -); - -CREATE TABLE IF NOT EXISTS Deliveries( - Id INT PRIMARY KEY, - ShipmentTime DATETIME, - ArrivalTime DATETIME, - AssociatedOrder INT, - Outgoing BOOLEAN -); -CREATE TABLE IF NOT EXISTS DeliveriesItems( - DeliveryId INT, - ItemId INT, - Count INT -); -)"""); -} - -MainDatabase::MainDatabase(Project& project) - : mProject{ &project } - , mDbWrapper(*this) - , mSales(*this) - , mPurchases(*this) - , mDeliveries(*this) -{ -} - -const SQLite::Database& MainDatabase::GetSQLite() const -{ - return mDbWrapper.mSqlite; -} - -SQLite::Database& MainDatabase::GetSQLite() -{ - return mDbWrapper.mSqlite; -} - -const SalesTable& MainDatabase::GetSales() const -{ - return mSales; -} - -SalesTable& MainDatabase::GetSales() -{ - return mSales; -} - -const PurchasesTable& MainDatabase::GetPurchases() const -{ - return mPurchases; -} - -PurchasesTable& MainDatabase::GetPurchases() -{ - return mPurchases; -} - -const DeliveryTable& MainDatabase::GetDeliveries() const -{ - return mDeliveries; -} - -DeliveryTable& MainDatabase::GetDeliveries() -{ - return mDeliveries; -} diff --git a/core/src/Model/Database.hpp b/core/src/Model/Database.hpp deleted file mode 100644 index 107baf6..0000000 --- a/core/src/Model/Database.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "cplt_fwd.hpp" - -#include <SQLiteCpp/Database.h> -#include <SQLiteCpp/Statement.h> -#include <cstdint> - -enum class TableKind -{ - Sales, - SalesItems, - Purchases, - PurchasesItems, - Deliveries, - DeliveriesItems, -}; - -class SalesTable -{ -public: - SQLite::Statement GetRowCount; - SQLite::Statement GetRows; - SQLite::Statement GetItems; - -public: - SalesTable(MainDatabase& db); -}; - -class PurchasesTable -{ -public: - SQLite::Statement GetRowCount; - SQLite::Statement GetRows; - SQLite::Statement GetItems; - -public: - PurchasesTable(MainDatabase& db); -}; - -class DeliveryTable -{ -public: - SQLite::Statement FilterByTypeAndId; - SQLite::Statement GetItems; - -public: - DeliveryTable(MainDatabase& db); -}; - -class MainDatabase -{ -private: - class DatabaseWrapper - { - public: - SQLite::Database mSqlite; - DatabaseWrapper(MainDatabase& self); - }; - - Project* mProject; - DatabaseWrapper mDbWrapper; - SalesTable mSales; - PurchasesTable mPurchases; - DeliveryTable mDeliveries; - -public: - MainDatabase(Project& project); - - const SQLite::Database& GetSQLite() const; - SQLite::Database& GetSQLite(); - - const SalesTable& GetSales() const; - SalesTable& GetSales(); - const PurchasesTable& GetPurchases() const; - PurchasesTable& GetPurchases(); - const DeliveryTable& GetDeliveries() const; - DeliveryTable& GetDeliveries(); -}; diff --git a/core/src/Model/Filter.cpp b/core/src/Model/Filter.cpp deleted file mode 100644 index 1e4b31b..0000000 --- a/core/src/Model/Filter.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Filter.hpp" diff --git a/core/src/Model/Filter.hpp b/core/src/Model/Filter.hpp deleted file mode 100644 index 1b923e1..0000000 --- a/core/src/Model/Filter.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -class TableRowsFilter -{ - // TODO -}; diff --git a/core/src/Model/GlobalStates.cpp b/core/src/Model/GlobalStates.cpp deleted file mode 100644 index a449afb..0000000 --- a/core/src/Model/GlobalStates.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#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() -{ - Init(StandardDirectories::UserData() / "cplt"); -} - -void GlobalStates::Init(std::filesystem::path userDataDir) -{ - globalStateInstance = std::make_unique<GlobalStates>(); - globalDataPath = userDataDir; - 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; - - globalStateInstance->SetCurrentProject(nullptr); - - 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::MoveProjectToTop(const Project& project) -{ - for (auto it = mRecentProjects.begin(); it != mRecentProjects.end(); ++it) { - if (it->Path == project.GetPath()) { - std::rotate(it, it + 1, mRecentProjects.end()); - MarkDirty(); - return; - } - } - AddRecentProject(project); -} - -void GlobalStates::RemoveRecentProject(int idx) -{ - assert(idx >= 0 && idx < mRecentProjects.size()); - - mRecentProjects.erase(mRecentProjects.begin() + idx); - MarkDirty(); -} - -bool GlobalStates::HasCurrentProject() const -{ - return mCurrentProject != nullptr; -} - -Project* GlobalStates::GetCurrentProject() const -{ - return mCurrentProject.get(); -} - -void GlobalStates::SetCurrentProject(std::unique_ptr<Project> project) -{ - if (mCurrentProject) { - mCurrentProject->WriteToDisk(); - mCurrentProject = nullptr; - } - if (project) { - MoveProjectToTop(*project); - } - mCurrentProject = std::move(project); -} - -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 deleted file mode 100644 index cc41bd5..0000000 --- a/core/src/Model/GlobalStates.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "Utils/Sigslot.hpp" -#include "cplt_fwd.hpp" - -#include <filesystem> -#include <string> -#include <vector> - -class GlobalStates -{ -public: - static void Init(); - static void Init(std::filesystem::path userDataDir); - 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; - std::unique_ptr<Project> mCurrentProject; - mutable bool mDirty = false; - -public: - const std::vector<RecentProject>& GetRecentProjects() const; - void ClearRecentProjects(); - void AddRecentProject(const Project& project); - /// Move or add the project to end of the recent projects list. - /// If the project is not in the list of recently used projects, it will be appended, otherwise - /// it will be moved to the end. - void MoveProjectToTop(const Project& project); - void RemoveRecentProject(int idx); - - bool HasCurrentProject() const; - Project* GetCurrentProject() const; - void SetCurrentProject(std::unique_ptr<Project> project); - - // TODO async autosaving to prevent data loss on crash - void WriteToDisk() const; - - bool IsDirty() const; - -private: - void MarkDirty(); -}; diff --git a/core/src/Model/Items.cpp b/core/src/Model/Items.cpp deleted file mode 100644 index 9d2abc6..0000000 --- a/core/src/Model/Items.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "Items.hpp" - -const std::string& ProductItem::GetDescription() const -{ - return mDescription; -} - -void ProductItem::SetDescription(std::string description) -{ - mDescription = std::move(description); -} - -int ProductItem::GetPrice() const -{ - return mPrice; -} -void ProductItem::SetPrice(int price) -{ - mPrice = price; -} - -int ProductItem::GetStock() const -{ - return mStock; -} - -void ProductItem::SetStock(int stock) -{ - mStock = stock; -} - -Json::Value ProductItem::Serialize() const -{ - Json::Value elm; - elm["Description"] = mDescription; - elm["Price"] = mPrice; - elm["Stock"] = mStock; - return elm; -} - -void ProductItem::Deserialize(const Json::Value& elm) -{ - mDescription = elm["Description"].asString(); - mPrice = elm["Price"].asInt(); - mStock = elm["Stock"].asInt(); -} - -const std::string& FactoryItem::GetDescription() const -{ - return mDescription; -} - -void FactoryItem::SetDescription(std::string description) -{ - mDescription = std::move(description); -} - -const std::string& FactoryItem::GetEmail() const -{ - return mEmail; -} - -void FactoryItem::SetEmail(std::string email) -{ - mEmail = std::move(email); -} - -Json::Value FactoryItem::Serialize() const -{ - Json::Value elm; - elm["Description"] = mDescription; - elm["Email"] = mEmail; - return elm; -} - -void FactoryItem::Deserialize(const Json::Value& elm) -{ - mDescription = elm["Description"].asString(); - mEmail = elm["Email"].asString(); -} - -const std::string& CustomerItem::GetDescription() const -{ - return mDescription; -} - -void CustomerItem::SetDescription(std::string description) -{ - mDescription = std::move(description); -} - -const std::string& CustomerItem::GetEmail() const -{ - return mEmail; -} - -void CustomerItem::SetEmail(std::string email) -{ - mEmail = std::move(email); -} - -Json::Value CustomerItem::Serialize() const -{ - Json::Value elm; - elm["Description"] = mDescription; - elm["Email"] = mEmail; - return elm; -} - -void CustomerItem::Deserialize(const Json::Value& elm) -{ - mDescription = elm["Description"].asString(); - mEmail = elm["Email"].asString(); -} diff --git a/core/src/Model/Items.hpp b/core/src/Model/Items.hpp deleted file mode 100644 index 859aedf..0000000 --- a/core/src/Model/Items.hpp +++ /dev/null @@ -1,253 +0,0 @@ -#pragma once - -#include "cplt_fwd.hpp" - -#include <json/reader.h> -#include <json/value.h> -#include <json/writer.h> -#include <tsl/array_map.h> -#include <cstddef> -#include <limits> -#include <stdexcept> -#include <string> -#include <string_view> -#include <utility> -#include <vector> - -template <class T> -class ItemList -{ -private: - std::vector<T> mStorage; - tsl::array_map<char, size_t> mNameLookup; - -public: - template <class... Args> - T& Insert(std::string name, Args... args) - { - auto iter = mNameLookup.find(name); - if (iter != mNameLookup.end()) { - throw std::runtime_error("Duplicate key."); - } - - for (size_t i = 0; i < mStorage.size(); ++i) { - if (mStorage[i].IsInvalid()) { - mStorage[i] = T(*this, i, std::move(name), std::forward<Args>(args)...); - mNameLookup.insert(name, i); - return mStorage[i]; - } - } - - size_t id = mStorage.size(); - mNameLookup.insert(name, id); - mStorage.emplace_back(*this, id, std::move(name), std::forward<Args>(args)...); - return mStorage[id]; - } - - void Remove(size_t index) - { - auto& item = mStorage[index]; - mNameLookup.erase(item.GetName()); - mStorage[index] = T(*this); - } - - T* Find(size_t id) - { - return &mStorage[id]; - } - - const T* Find(size_t id) const - { - return &mStorage[id]; - } - - const T* Find(std::string_view name) const - { - auto iter = mNameLookup.find(name); - if (iter != mNameLookup.end()) { - return &mStorage[iter.value()]; - } else { - return nullptr; - } - } - - Json::Value Serialize() const - { - Json::Value items(Json::arrayValue); - for (auto& item : mStorage) { - if (!item.IsInvalid()) { - auto elm = item.Serialize(); - elm["Id"] = item.GetId(); - elm["Name"] = item.GetName(); - items.append(elm); - } - } - - Json::Value root; - root["MaxItemId"] = mStorage.size(); - root["Items"] = std::move(items); - - return root; - } - - ItemList() = default; - - ItemList(const Json::Value& root) - { - constexpr const char* kMessage = "Failed to load item list from JSON."; - - auto& itemCount = root["MaxItemId"]; - if (!itemCount.isIntegral()) throw std::runtime_error(kMessage); - - mStorage.resize(itemCount.asInt64(), T(*this)); - - auto& items = root["Items"]; - if (!items.isArray()) throw std::runtime_error(kMessage); - - for (auto& elm : items) { - if (!elm.isObject()) throw std::runtime_error(kMessage); - - auto& id = elm["Id"]; - if (!id.isIntegral()) throw std::runtime_error(kMessage); - auto& name = elm["Name"]; - if (!name.isString()) throw std::runtime_error(kMessage); - - size_t iid = id.asInt64(); - mStorage[iid] = T(*this, iid, name.asString()); - mStorage[iid].Deserialize(elm); - } - } - - typename decltype(mStorage)::iterator begin() - { - return mStorage.begin(); - } - - typename decltype(mStorage)::iterator end() - { - return mStorage.end(); - } - - typename decltype(mStorage)::const_iterator begin() const - { - return mStorage.begin(); - } - - typename decltype(mStorage)::const_iterator end() const - { - return mStorage.end(); - } - -private: - template <class TSelf> - friend class ItemBase; - - void UpdateItemName(const T& item, const std::string& newName) - { - mNameLookup.erase(item.GetName()); - mNameLookup.insert(newName, item.GetId()); - } -}; - -template <class TSelf> -class ItemBase -{ -private: - ItemList<TSelf>* mList; - size_t mId; - std::string mName; - -public: - ItemBase(ItemList<TSelf>& list, size_t id = std::numeric_limits<size_t>::max(), std::string name = "") - : mList{ &list } - , mId{ id } - , mName{ std::move(name) } - { - } - - bool IsInvalid() const - { - return mId == std::numeric_limits<size_t>::max(); - } - - ItemList<TSelf>& GetList() const - { - return *mList; - } - - size_t GetId() const - { - return mId; - } - - const std::string& GetName() const - { - return mName; - } - - void SetName(std::string name) - { - mList->UpdateItemName(static_cast<TSelf&>(*this), name); - mName = std::move(name); - } -}; - -class ProductItem : public ItemBase<ProductItem> -{ -private: - std::string mDescription; - int mPrice = 0; - int mStock = 0; - -public: - using ItemBase::ItemBase; - - const std::string& GetDescription() const; - void SetDescription(std::string description); - /// Get the price of this item in US cents. - int GetPrice() const; - void SetPrice(int price); - /// Get the current number of this product in warehouse. - /// This is a housekeeping field and shouldn't be editable by the user from the UI. - int GetStock() const; - void SetStock(int stock); - - Json::Value Serialize() const; - void Deserialize(const Json::Value& elm); -}; - -class FactoryItem : public ItemBase<FactoryItem> -{ -private: - std::string mDescription; - std::string mEmail; - -public: - using ItemBase::ItemBase; - - const std::string& GetDescription() const; - void SetDescription(std::string description); - const std::string& GetEmail() const; - void SetEmail(std::string email); - - Json::Value Serialize() const; - void Deserialize(const Json::Value& elm); -}; - -class CustomerItem : public ItemBase<CustomerItem> -{ -private: - std::string mDescription; - std::string mEmail; - -public: - using ItemBase::ItemBase; - - const std::string& GetDescription() const; - void SetDescription(std::string description); - const std::string& GetEmail() const; - void SetEmail(std::string email); - - Json::Value Serialize() const; - void Deserialize(const Json::Value& elm); -}; diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp deleted file mode 100644 index 523ee9b..0000000 --- a/core/src/Model/Project.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include "Project.hpp" - -#include "Model/Workflow/Workflow.hpp" -#include "Utils/Macros.hpp" - -#include <json/reader.h> -#include <json/value.h> -#include <json/writer.h> -#include <filesystem> -#include <fstream> -#include <stdexcept> -#include <utility> - -namespace fs = std::filesystem; - -template <class T> -static void ReadItemList(ItemList<T>& list, const fs::path& filePath) -{ - std::ifstream ifs(filePath); - if (ifs) { - Json::Value root; - ifs >> root; - - list = ItemList<T>(root); - } -} - -static void CreateProjectSubfolders(const Project& project) -{ - fs::create_directory(project.GetDatabasesDirectory()); - fs::create_directory(project.GetItemsDirectory()); - fs::create_directory(project.GetWorkflowsDirectory()); - fs::create_directory(project.GetTemplatesDirectory()); -} - -Project::Project(fs::path rootPath) - : mRootPath{ std::move(rootPath) } - , mRootPathString{ mRootPath.string() } - , Workflows(*this) - , Templates(*this) - , Database(*this) -{ - // TODO better diagnostic - const char* kInvalidFormatErr = "Failed to load project: invalid format."; - - std::ifstream ifs(mRootPath / "cplt_project.json"); - if (!ifs) { - std::string message; - message += "Failed to load project file at '"; - message += mRootPath.string(); - message += "'."; - throw std::runtime_error(message); - } - - { - 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 silently creating new elements - if (!croot.isObject()) { - throw std::runtime_error(kInvalidFormatErr); - } - - if (auto& name = croot["Name"]; name.isString()) { - mName = name.asString(); - } else { - throw std::runtime_error(kInvalidFormatErr); - } - } - - CreateProjectSubfolders(*this); - - auto itemsDir = mRootPath / "items"; - ReadItemList(Products, itemsDir / "products.json"); - ReadItemList(Factories, itemsDir / "factories.json"); - ReadItemList(Customers, itemsDir / "customers.json"); - - Workflows.Reload(); - Templates.Reload(); -} - -Project::Project(fs::path rootPath, std::string name) - : mRootPath{ std::move(rootPath) } - , mRootPathString{ mRootPath.string() } - , mName{ std::move(name) } - , Workflows(*this) - , Templates(*this) - , Database(*this) -{ - CreateProjectSubfolders(*this); -} - -const fs::path& Project::GetPath() const -{ - return mRootPath; -} - -const std::string& Project::GetPathString() const -{ - return mRootPathString; -} - -fs::path Project::GetDatabasesDirectory() const -{ - return mRootPath / "databases"; -} - -fs::path Project::GetItemsDirectory() const -{ - return mRootPath / "items"; -} - -fs::path Project::GetWorkflowsDirectory() const -{ - return mRootPath / "workflows"; -} - -fs::path Project::GetWorkflowPath(std::string_view name) const -{ - return (mRootPath / "workflows" / name).concat(".cplt-workflow"); -} - -fs::path Project::GetTemplatesDirectory() const -{ - return mRootPath / "templates"; -} - -fs::path Project::GetTemplatePath(std::string_view name) const -{ - return (mRootPath / "templates" / name).concat(".cplt-template"); -} - -const std::string& Project::GetName() const -{ - return mName; -} - -void Project::SetName(std::string name) -{ - mName = std::move(name); -} - -Json::Value Project::Serialize() -{ - Json::Value root(Json::objectValue); - - root["Name"] = mName; - - return root; -} - -template <class T> -static void WriteItemList(ItemList<T>& list, const fs::path& filePath) -{ - std::ofstream ofs(filePath); - ofs << list.Serialize(); -} - -void Project::WriteToDisk() -{ - std::ofstream ofs(mRootPath / "cplt_project.json"); - ofs << this->Serialize(); - - auto itemsDir = GetItemsDirectory(); - WriteItemList(Products, itemsDir / "products.json"); - WriteItemList(Factories, itemsDir / "factories.json"); - WriteItemList(Customers, itemsDir / "customers.json"); -} diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp deleted file mode 100644 index 17d9acb..0000000 --- a/core/src/Model/Project.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "Model/Assets.hpp" -#include "Model/Database.hpp" -#include "Model/Items.hpp" -#include "Model/Template/Template.hpp" -#include "Model/Workflow/Workflow.hpp" - -#include <json/forwards.h> -#include <tsl/array_map.h> -#include <filesystem> -#include <string> -#include <string_view> - -class Project -{ -private: - std::filesystem::path mRootPath; - std::string mRootPathString; - std::string mName; - - // (Exception to style guidelines) - // This is put after the private fields, so that when XxxDatabase's constructor runs, all of them will be initialized -public: - WorkflowAssetList Workflows; - TemplateAssetList Templates; - ItemList<ProductItem> Products; - ItemList<FactoryItem> Factories; - ItemList<CustomerItem> Customers; - MainDatabase Database; - -public: - /// Load the project from a directory containing the cplt_project.json file. - /// This only loads the main project file, the caller needs to - Project(std::filesystem::path rootPath); - - /// 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. - Project(std::filesystem::path rootPath, std::string name); - - /// Path to a *directory* that contains the project file. - const std::filesystem::path& GetPath() const; - const std::string& GetPathString() const; - - std::filesystem::path GetDatabasesDirectory() const; - std::filesystem::path GetItemsDirectory() const; - std::filesystem::path GetWorkflowsDirectory() const; - std::filesystem::path GetWorkflowPath(std::string_view name) const; - std::filesystem::path GetTemplatesDirectory() const; - std::filesystem::path GetTemplatePath(std::string_view name) const; - - const std::string& GetName() const; - void SetName(std::string name); - - Json::Value Serialize(); - void WriteToDisk(); -}; diff --git a/core/src/Model/Template/TableTemplate.cpp b/core/src/Model/Template/TableTemplate.cpp deleted file mode 100644 index 57caac4..0000000 --- a/core/src/Model/Template/TableTemplate.cpp +++ /dev/null @@ -1,591 +0,0 @@ -#include "TableTemplate.hpp" - -#include "Utils/IO/StringIntegration.hpp" -#include "Utils/IO/TslArrayIntegration.hpp" -#include "Utils/IO/VectorIntegration.hpp" - -#include <xlsxwriter.h> -#include <algorithm> -#include <charconv> -#include <cstddef> -#include <cstdint> -#include <iostream> -#include <map> - -bool TableCell::IsDataHoldingCell() const -{ - return IsPrimaryCell() || !IsMergedCell(); -} - -bool TableCell::IsPrimaryCell() const -{ - return PrimaryCellLocation == Location; -} - -bool TableCell::IsMergedCell() const -{ - return PrimaryCellLocation.x == -1 || PrimaryCellLocation.y == -1; -} - -template <class TTableCell, class TStream> -void OperateStreamForTableCell(TTableCell& cell, TStream& proxy) -{ - proxy.template ObjectAdapted<DataStreamAdapters::String>(cell.Content); - proxy.Object(cell.Location); - proxy.Object(cell.PrimaryCellLocation); - proxy.Value(cell.SpanX); - proxy.Value(cell.SpanY); - proxy.Enum(cell.HorizontalAlignment); - proxy.Enum(cell.VerticalAlignment); - proxy.Enum(cell.Type); - proxy.Value(cell.DataId); -} - -void TableCell::ReadFromDataStream(InputDataStream& stream) -{ - ::OperateStreamForTableCell(*this, stream); -} - -void TableCell::WriteToDataStream(OutputDataStream& stream) const -{ - ::OperateStreamForTableCell(*this, stream); -} - -Vec2i TableArrayGroup::GetLeftCell() const -{ - return { Row, LeftCell }; -} - -Vec2i TableArrayGroup::GetRightCell() const -{ - return { Row, RightCell }; -} - -int TableArrayGroup::GetCount() const -{ - return RightCell - LeftCell + 1; -} - -Vec2i TableArrayGroup::FindCell(std::string_view name) -{ - // TODO - return Vec2i{}; -} - -template <class TMap> -static bool UpdateElementName(TMap& map, std::string_view oldName, std::string_view newName) -{ - auto iter = map.find(oldName); - if (iter == map.end()) { - return false; - } - - auto elm = iter.value(); - auto [DISCARD, inserted] = map.insert(newName, elm); - if (!inserted) { - return false; - } - - map.erase(iter); - return true; -} - -bool TableArrayGroup::UpdateCellName(std::string_view oldName, std::string_view newName) -{ - return ::UpdateElementName(mName2Cell, oldName, newName); -} - -template <class TTableArrayGroup, class TStream> -void OperateStreamForTableArrayGroup(TTableArrayGroup& group, TStream& stream) -{ - stream.Value(group.Row); - stream.Value(group.LeftCell); - stream.Value(group.RightCell); -} - -void TableArrayGroup::ReadFromDataStream(InputDataStream& stream) -{ - ::OperateStreamForTableArrayGroup(*this, stream); -} - -void TableArrayGroup::WriteToDataStream(OutputDataStream& stream) const -{ - ::OperateStreamForTableArrayGroup(*this, stream); -} - -TableInstantiationParameters::TableInstantiationParameters(const TableTemplate& table) - : mTable{ &table } -{ -} - -TableInstantiationParameters& TableInstantiationParameters::ResetTable(const TableTemplate& newTable) -{ - mTable = &newTable; - return *this; -} - -TableInstantiationParameters TableInstantiationParameters::RebindTable(const TableTemplate& newTable) const -{ - TableInstantiationParameters result(newTable); - result.SingularCells = this->SingularCells; - result.ArrayGroups = this->ArrayGroups; - return result; -} - -const TableTemplate& TableInstantiationParameters::GetTable() const -{ - return *mTable; -} - -bool TableTemplate::IsInstance(const Template* tmpl) -{ - return tmpl->GetKind() == KD_Table; -} - -TableTemplate::TableTemplate() - : Template(KD_Table) -{ -} - -int TableTemplate::GetTableWidth() const -{ - return mColumnWidths.size(); -} - -int TableTemplate::GetTableHeight() const -{ - return mRowHeights.size(); -} - -void TableTemplate::Resize(int newWidth, int newHeight) -{ - // TODO this doesn't gracefully handle resizing to a smaller size which trims some merged cells - - std::vector<TableCell> cells; - cells.reserve(newWidth * newHeight); - - int tableWidth = GetTableWidth(); - int tableHeight = GetTableHeight(); - - for (int y = 0; y < newHeight; ++y) { - if (y >= tableHeight) { - for (int x = 0; x < newWidth; ++x) { - cells.push_back(TableCell{}); - } - continue; - } - - for (int x = 0; x < newWidth; ++x) { - if (x >= tableWidth) { - cells.push_back(TableCell{}); - } else { - auto& cell = GetCell({ x, y }); - cells.push_back(std::move(cell)); - } - } - } - - mCells = std::move(cells); - mColumnWidths.resize(newWidth, 80); - mRowHeights.resize(newHeight, 20); -} - -int TableTemplate::GetRowHeight(int row) const -{ - return mRowHeights[row]; -} - -void TableTemplate::SetRowHeight(int row, int height) -{ - mRowHeights[row] = height; -} - -int TableTemplate::GetColumnWidth(int column) const -{ - return mColumnWidths[column]; -} - -void TableTemplate::SetColumnWidth(int column, int width) -{ - mColumnWidths[column] = width; -} - -const TableCell& TableTemplate::GetCell(Vec2i pos) const -{ - int tableWidth = GetTableWidth(); - return mCells[pos.y * tableWidth + pos.x]; -} - -TableCell& TableTemplate::GetCell(Vec2i pos) -{ - return const_cast<TableCell&>(const_cast<const TableTemplate*>(this)->GetCell(pos)); -} - -void TableTemplate::SetCellType(Vec2i pos, TableCell::CellType type) -{ - auto& cell = GetCell(pos); - if (cell.Type == type) { - return; - } - - switch (cell.Type) { - // Nothing to change - case TableCell::ConstantCell: break; - - case TableCell::SingularParametricCell: - mName2Parameters.erase(cell.Content); - break; - - case TableCell::ArrayParametricCell: { - auto& ag = mArrayGroups[cell.DataId]; - if (pos.x == ag.LeftCell) { - ag.LeftCell++; - } else if (pos.x == ag.RightCell) { - ag.RightCell--; - } else { - } - } break; - } - - switch (type) { - // Nothing to do - case TableCell::ConstantCell: break; - - case TableCell::SingularParametricCell: { - int idx = pos.y * GetTableWidth() + pos.x; - auto [DISCARD, inserted] = mName2Parameters.insert(cell.Content, idx); - - // Duplicate name - if (!inserted) { - return; - } - } break; - - case TableCell::ArrayParametricCell: { - auto ptr = AddArrayGroup(pos.y, pos.x, pos.x); - - // Duplicate name - if (ptr == nullptr) { - return; - } - } break; - } - - cell.Type = type; -} - -bool TableTemplate::UpdateParameterName(std::string_view oldName, std::string_view newName) -{ - return ::UpdateElementName(mName2Parameters, oldName, newName); -} - -int TableTemplate::GetArrayGroupCount() const -{ - return mArrayGroups.size(); -} - -const TableArrayGroup& TableTemplate::GetArrayGroup(int id) const -{ - return mArrayGroups[id]; -} - -TableArrayGroup& TableTemplate::GetArrayGroup(int id) -{ - return mArrayGroups[id]; -} - -TableArrayGroup* TableTemplate::AddArrayGroup(int row, int left, int right) -{ - // size_t max value: 18446744073709551615 - // ^~~~~~~~~~~~~~~~~~~~ 20 chars - char name[20]; - auto res = std::to_chars(std::begin(name), std::end(name), mArrayGroups.size()); - std::string_view nameStr(name, res.ptr - name); - - return AddArrayGroup(nameStr, row, left, right); -} - -TableArrayGroup* TableTemplate::AddArrayGroup(std::string_view name, int row, int left, int right) -{ - assert(row >= 0 && row < GetTableHeight()); - assert(left >= 0 && left < GetTableWidth()); - assert(right >= 0 && right < GetTableWidth()); - - // TODO check for overlap - - if (left > right) { - std::swap(left, right); - } - - auto [DISCARD, inserted] = mName2ArrayGroups.insert(name, (int)mArrayGroups.size()); - if (!inserted) { - return nullptr; - } - - mArrayGroups.push_back(TableArrayGroup{ - .Row = row, - .LeftCell = left, - .RightCell = right, - }); - auto& ag = mArrayGroups.back(); - - for (int x = left; x <= right; x++) { - auto& cell = GetCell({ x, row }); - - // Update type - cell.Type = TableCell::ArrayParametricCell; - - // Insert parameter name lookup - while (true) { - auto [DISCARD, inserted] = ag.mName2Cell.insert(cell.Content, x); - if (inserted) { - break; - } - - cell.Content += "-"; - } - } - - return &ag; -} - -bool TableTemplate::UpdateArrayGroupName(std::string_view oldName, std::string_view newName) -{ - return ::UpdateElementName(mName2ArrayGroups, oldName, newName); -} - -bool TableTemplate::ExtendArrayGroupLeft(int id, int n) -{ - assert(n > 0); - - auto& ag = mArrayGroups[id]; - ag.LeftCell -= n; - - return false; -} - -bool TableTemplate::ExtendArrayGroupRight(int id, int n) -{ - assert(n > 0); - - auto& ag = mArrayGroups[id]; - ag.RightCell += n; - - return false; -} - -TableCell* TableTemplate::FindCell(std::string_view name) -{ - auto iter = mName2Parameters.find(name); - if (iter != mName2Parameters.end()) { - return &mCells[iter.value()]; - } else { - return nullptr; - } -} - -TableArrayGroup* TableTemplate::FindArrayGroup(std::string_view name) -{ - auto iter = mName2ArrayGroups.find(name); - if (iter != mName2ArrayGroups.end()) { - return &mArrayGroups[iter.value()]; - } else { - return nullptr; - } -} - -TableTemplate::MergeCellsResult TableTemplate::MergeCells(Vec2i topLeft, Vec2i bottomRight) -{ - auto SortTwo = [](int& a, int& b) { - if (a > b) { - std::swap(a, b); - } - }; - SortTwo(topLeft.x, bottomRight.x); - SortTwo(topLeft.y, bottomRight.y); - - auto ResetProgress = [&]() { - for (int y = topLeft.y; y < bottomRight.y; ++y) { - for (int x = topLeft.x; x < bottomRight.x; ++x) { - auto& cell = GetCell({ x, y }); - cell.PrimaryCellLocation = { -1, -1 }; - } - } - }; - - for (int y = topLeft.y; y < bottomRight.y; ++y) { - for (int x = topLeft.x; x < bottomRight.x; ++x) { - auto& cell = GetCell({ x, y }); - if (cell.IsMergedCell()) { - ResetProgress(); - return MCR_CellAlreadyMerged; - } - - cell.PrimaryCellLocation = topLeft; - } - } - - auto& primaryCell = GetCell(topLeft); - primaryCell.SpanX = bottomRight.x - topLeft.x; - primaryCell.SpanY = bottomRight.y - topLeft.y; - - return MCR_Success; -} - -TableTemplate::BreakCellsResult TableTemplate::BreakCells(Vec2i topLeft) -{ - auto& primaryCell = GetCell(topLeft); - if (!primaryCell.IsMergedCell()) { - return BCR_CellNotMerged; - } - - for (int dy = 0; dy < primaryCell.SpanY; ++dy) { - for (int dx = 0; dx < primaryCell.SpanX; ++dx) { - auto& cell = GetCell({ topLeft.x + dx, topLeft.y + dy }); - cell.PrimaryCellLocation = { -1, -1 }; - } - } - - primaryCell.SpanX = 1; - primaryCell.SpanY = 1; - - return BCR_Success; -} - -lxw_workbook* TableTemplate::InstantiateToExcelWorkbook(const TableInstantiationParameters& params) const -{ - auto workbook = workbook_new("Table.xlsx"); - InstantiateToExcelWorksheet(workbook, params); - return workbook; -} - -lxw_worksheet* TableTemplate::InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstantiationParameters& params) const -{ - auto worksheet = workbook_add_worksheet(workbook, "CpltExport.xlsx"); - - // Map: row number -> length of generated ranges - std::map<int, int> generatedRanges; - - for (size_t i = 0; i < mArrayGroups.size(); ++i) { - auto& info = mArrayGroups[i]; - auto& param = params.ArrayGroups[i]; - - auto iter = generatedRanges.find(i); - if (iter != generatedRanges.end()) { - int available = iter->second; - if (available >= param.size()) { - // Current space is enough to fit in this array group, skip - continue; - } - } - - // Not enough space to fit in this array group, update (or insert) the appropriate amount of generated rows - int row = i; - int count = param.size(); - generatedRanges.try_emplace(row, count); - } - - auto GetOffset = [&](int y) -> int { - // std::find_if <values less than y> - int verticalOffset = 0; - for (auto it = generatedRanges.begin(); it != generatedRanges.end() && it->first < y; ++it) { - verticalOffset += it->second; - } - return verticalOffset; - }; - - auto WriteCell = [&](int row, int col, const TableCell& cell, const char* text) -> void { - if (cell.IsPrimaryCell()) { - int lastRow = row + cell.SpanY - 1; - int lastCol = col + cell.SpanX - 1; - // When both `string` and `format` are null, the top-left cell contents are untouched (what we just wrote in the above switch) - worksheet_merge_range(worksheet, row, col, lastRow, lastCol, text, nullptr); - } else { - worksheet_write_string(worksheet, row, col, text, nullptr); - } - }; - - // Write/instantiate all array groups - for (size_t i = 0; i < mArrayGroups.size(); ++i) { - auto& groupInfo = mArrayGroups[i]; - auto& groupParams = params.ArrayGroups[i]; - - int rowCellCount = groupInfo.GetCount(); - int rowCount = groupParams.size(); - int baseRowIdx = groupInfo.Row + GetOffset(groupInfo.Row); - - // For each row that would be generated - for (int rowIdx = 0; rowIdx < rowCount; ++rowIdx) { - auto& row = groupParams[rowIdx]; - - // For each cell in the row - for (int rowCellIdx = 0; rowCellIdx < rowCellCount; ++rowCellIdx) { - // TODO support merged cells in array groups - worksheet_write_string(worksheet, baseRowIdx + rowIdx, rowCellIdx, row[rowCellIdx].c_str(), nullptr); - } - } - } - - int tableWidth = GetTableWidth(); - int tableHeight = GetTableHeight(); - - // Write all regular and singular parameter cells - for (int y = 0; y < tableHeight; ++y) { - for (int x = 0; x < tableWidth; ++x) { - auto& cell = GetCell({ x, y }); - - if (!cell.IsDataHoldingCell()) { - continue; - } - - switch (cell.Type) { - case TableCell::ConstantCell: { - int row = y + GetOffset(y); - int col = x; - - WriteCell(row, col, cell, cell.Content.c_str()); - } break; - - case TableCell::SingularParametricCell: { - int row = y + GetOffset(y); - int col = x; - - auto iter = params.SingularCells.find({ x, y }); - if (iter != params.SingularCells.end()) { - WriteCell(row, col, cell, iter.value().c_str()); - } - } break; - - // See loop above that processes whole array groups at the same time - case TableCell::ArrayParametricCell: break; - } - } - } - - return worksheet; -} - -class TableTemplate::Private -{ -public: - template <class TTableTemplate, class TProxy> - static void OperateStream(TTableTemplate& table, TProxy& proxy) - { - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mColumnWidths); - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mRowHeights); - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mCells); - proxy.template ObjectAdapted<DataStreamAdapters::Vector<>>(table.mArrayGroups); - proxy.template ObjectAdapted<DataStreamAdapters::TslArrayMap<>>(table.mName2Parameters); - proxy.template ObjectAdapted<DataStreamAdapters::TslArrayMap<>>(table.mName2ArrayGroups); - } -}; - -void TableTemplate::ReadFromDataStream(InputDataStream& stream) -{ - Private::OperateStream(*this, stream); -} - -void TableTemplate::WriteToDataStream(OutputDataStream& stream) const -{ - Private::OperateStream(*this, stream); -} diff --git a/core/src/Model/Template/TableTemplate.hpp b/core/src/Model/Template/TableTemplate.hpp deleted file mode 100644 index 8771867..0000000 --- a/core/src/Model/Template/TableTemplate.hpp +++ /dev/null @@ -1,223 +0,0 @@ -#pragma once - -#include "Model/Template/Template.hpp" -#include "Utils/Vector.hpp" -#include "Utils/VectorHash.hpp" -#include "cplt_fwd.hpp" - -#include <tsl/array_map.h> -#include <tsl/robin_map.h> -#include <string> -#include <string_view> -#include <vector> - -class TableCell -{ -public: - enum TextAlignment - { - /// For horizontal alignment, this means align left. For vertical alignment, this means align top. - AlignAxisMin, - /// Align middle of the text to the middle of the axis. - AlignCenter, - /// For horizontal alignment, this means align right. For vertical alignment, this means align bottom. - AlignAxisMax, - }; - - enum CellType - { - ConstantCell, - SingularParametricCell, - ArrayParametricCell, - }; - -public: - /// Display content of this cell. This doesn't necessarily have to line up with the parameter name (if this cell is one). - std::string Content; - Vec2i Location; - /// Location of the primary (top left) cell, if this cell is a part of a merged group. - /// Otherwise, either component of this field shall be -1. - Vec2i PrimaryCellLocation{ -1, -1 }; - int SpanX = 0; - int SpanY = 0; - TextAlignment HorizontalAlignment = AlignCenter; - TextAlignment VerticalAlignment = AlignCenter; - CellType Type = ConstantCell; - /// The id of the group description object, if this cell isn't a constant or singular parameter cell. Otherwise, this value is -1. - int DataId = -1; - -public: - /// Return whether this cell holds meaningful data, i.e. true when this cell is either unmerged or the primary cell of a merged range. - bool IsDataHoldingCell() const; - /// Return whether this cell is the primary (i.e. top left) cell of a merged range or not. - bool IsPrimaryCell() const; - /// Return whether this cell is a part of a merged range or not. Includes the primary cell. - bool IsMergedCell() const; - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; -}; - -// TODO support reverse (bottom to top) filling order -// TODO support horizontal filling order - -/// Parameter group information for a grouped array of cells. When instantiated, an array of 0 or more -/// elements shall be provided by the user, which will replace the group of templated cells with a list -/// of rows, each instantiated with the n-th element in the provided array. -/// \code -/// [["foo", "bar", "foobar"], -/// ["a", "b", c"], -/// ["1", "2", "3"], -/// ["x", "y", "z"]] -/// // ... may be more -/// \endcode -/// This would create 4 rows of data in the place of the original parameter group. -/// -/// If more than one array parameter groups are on the same row, they would share space between each other: -/// \code -/// | 2 elements was fed to it -/// | | 1 element was fed to it -/// V V -/// {~~~~~~~~~~~~~~~~}{~~~~~~~~~~~~~~} -/// +------+---------+---------------+ -/// | Foo | Example | Another group | -/// +------+---------+---------------+ -/// | Cool | Example | | -/// +------+---------+---------------+ -/// \endcode -/// -/// \see TableCell -/// \see TableInstantiationParameters -/// \see TableTemplate -class TableArrayGroup -{ -public: - /// Parameter name mapped to cell location (index from LeftCell). - tsl::array_map<char, int> mName2Cell; - int Row; - /// Leftmost cell in this group - int LeftCell; - /// Rightmost cell in this group - int RightCell; - -public: - Vec2i GetLeftCell() const; - Vec2i GetRightCell() const; - int GetCount() const; - - /// Find the location of the cell within this array group that has the given name. - Vec2i FindCell(std::string_view name); - bool UpdateCellName(std::string_view oldName, std::string_view newName); - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; -}; - -// Forward declaration of libxlsxwriter structs -struct lxw_workbook; -struct lxw_worksheet; - -/// An object containing the necessary information to instantiate a table template. -/// \see TableTemplate -class TableInstantiationParameters -{ -private: - const TableTemplate* mTable; - -public: - tsl::robin_map<Vec2i, std::string> SingularCells; - - using ArrayGroupRow = std::vector<std::string>; - using ArrayGroupData = std::vector<ArrayGroupRow>; - std::vector<ArrayGroupData> ArrayGroups; - -public: - TableInstantiationParameters(const TableTemplate& table); - - TableInstantiationParameters& ResetTable(const TableTemplate& newTable); - TableInstantiationParameters RebindTable(const TableTemplate& newTable) const; - - const TableTemplate& GetTable() const; -}; - -/// A table template, where individual cells can be filled by workflows instantiating this template. Merged cells, -/// parametric rows/columns, and grids are also supported. -/// -/// This current supports exporting to xlsx files. -class TableTemplate : public Template -{ - friend class TableSingleParamsIter; - friend class TableArrayGroupsIter; - class Private; - -private: - /// Map from parameter name to index of the parameter cell (stored in mCells). - tsl::array_map<char, int> mName2Parameters; - /// Map from array group name to the index of the array group (stored in mArrayGroups). - tsl::array_map<char, int> mName2ArrayGroups; - std::vector<TableCell> mCells; - std::vector<TableArrayGroup> mArrayGroups; - std::vector<int> mRowHeights; - std::vector<int> mColumnWidths; - -public: - static bool IsInstance(const Template* tmpl); - TableTemplate(); - - int GetTableWidth() const; - int GetTableHeight() const; - void Resize(int newWidth, int newHeight); - - int GetRowHeight(int row) const; - void SetRowHeight(int row, int height); - int GetColumnWidth(int column) const; - void SetColumnWidth(int column, int width); - - const TableCell& GetCell(Vec2i pos) const; - TableCell& GetCell(Vec2i pos); - /// <ul> - /// <li> In case of becoming a SingularParametricCell: the parameter name is filled with TableCell::Content. - /// <li> In case of becoming a ArrayGroupParametricCell: the array group name is automatically generated as the nth group it would be come. - /// i.e., if there aRe currently 3 groups, the newly created group would be named "4". - /// If this name collides with an existing group, hyphens \c - will be append to the name until no collision happens. - /// </ul> - void SetCellType(Vec2i pos, TableCell::CellType type); - - /// Updates the parameter cell to a new name. Returns true on success and false on failure (param not found or name duplicates). - bool UpdateParameterName(std::string_view oldName, std::string_view newName); - - int GetArrayGroupCount() const; - const TableArrayGroup& GetArrayGroup(int id) const; - TableArrayGroup& GetArrayGroup(int id); - TableArrayGroup* AddArrayGroup(int row, int left, int right); - TableArrayGroup* AddArrayGroup(std::string_view name, int row, int left, int right); - bool UpdateArrayGroupName(std::string_view oldName, std::string_view newName); - bool ExtendArrayGroupLeft(int id, int n); - bool ExtendArrayGroupRight(int id, int n); - - /// Find a singular parameter cell by its name. This does not include cells within an array group. - TableCell* FindCell(std::string_view name); - - /// Find an array group by its name. - TableArrayGroup* FindArrayGroup(std::string_view name); - - enum MergeCellsResult - { - MCR_CellAlreadyMerged, - MCR_Success, - }; - MergeCellsResult MergeCells(Vec2i topLeft, Vec2i bottomRight); - - enum BreakCellsResult - { - BCR_CellNotMerged, - BCR_Success, - }; - BreakCellsResult BreakCells(Vec2i topLeft); - - lxw_workbook* InstantiateToExcelWorkbook(const TableInstantiationParameters& params) const; - lxw_worksheet* InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstantiationParameters& params) const; - - void ReadFromDataStream(InputDataStream& stream) override; - void WriteToDataStream(OutputDataStream& stream) const override; -}; diff --git a/core/src/Model/Template/TableTemplateIterator.cpp b/core/src/Model/Template/TableTemplateIterator.cpp deleted file mode 100644 index 19e30b9..0000000 --- a/core/src/Model/Template/TableTemplateIterator.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "TableTemplateIterator.hpp" - -TableSingleParamsIter::TableSingleParamsIter(TableTemplate& tmpl) - : mTemplate{ &tmpl } - , mIter{ tmpl.mName2Parameters.begin() } -{ -} - -bool TableSingleParamsIter::HasNext() const -{ - return mIter != mTemplate->mName2Parameters.end(); -} - -TableCell& TableSingleParamsIter::Next() -{ - int id = mIter.value(); - ++mIter; - - return mTemplate->mCells[id]; -} - -TableArrayGroupsIter::TableArrayGroupsIter(TableTemplate& tmpl) - : mTemplate{ &tmpl } - , mIter{ tmpl.mName2ArrayGroups.begin() } -{ -} - -bool TableArrayGroupsIter::HasNext() const -{ - return mIter != mTemplate->mName2ArrayGroups.end(); -} - -TableArrayGroup& TableArrayGroupsIter::Peek() const -{ - int id = mIter.value(); - return mTemplate->mArrayGroups[id]; -} - -std::string_view TableArrayGroupsIter::PeekName() const -{ - return mIter.key_sv(); -} - -const char* TableArrayGroupsIter::PeekNameCStr() const -{ - return mIter.key(); -} - -void TableArrayGroupsIter::Next() -{ - ++mIter; -} diff --git a/core/src/Model/Template/TableTemplateIterator.hpp b/core/src/Model/Template/TableTemplateIterator.hpp deleted file mode 100644 index bf7f517..0000000 --- a/core/src/Model/Template/TableTemplateIterator.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "Model/Template/TableTemplate.hpp" -#include "Model/Template/Template.hpp" - -#include <string_view> - -class TableSingleParamsIter -{ -private: - TableTemplate* mTemplate; - tsl::array_map<char, int>::iterator mIter; - -public: - TableSingleParamsIter(TableTemplate& tmpl); - - bool HasNext() const; - TableCell& Next(); -}; - -class TableArrayGroupsIter -{ -private: - TableTemplate* mTemplate; - tsl::array_map<char, int>::iterator mIter; - -public: - TableArrayGroupsIter(TableTemplate& tmpl); - - bool HasNext() const; - TableArrayGroup& Peek() const; - std::string_view PeekName() const; - const char* PeekNameCStr() const; - void Next(); -}; diff --git a/core/src/Model/Template/Template.hpp b/core/src/Model/Template/Template.hpp deleted file mode 100644 index 061cc07..0000000 --- a/core/src/Model/Template/Template.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "Model/Assets.hpp" -#include "cplt_fwd.hpp" - -#include <filesystem> -#include <iosfwd> -#include <memory> -#include <string> - -class Template : public Asset -{ -public: - enum Kind - { - KD_Table, - - InvalidKind, - KindCount = InvalidKind, - }; - - using CategoryType = TemplateAssetList; - -private: - Kind mKind; - -public: - static const char* FormatKind(Kind kind); - static std::unique_ptr<Template> CreateByKind(Kind kind); - - static bool IsInstance(const Template* tmpl); - - Template(Kind kind); - ~Template() override = default; - - Kind GetKind() const; - - virtual void ReadFromDataStream(InputDataStream& stream) = 0; - virtual void WriteToDataStream(OutputDataStream& stream) const = 0; -}; - -class TemplateAssetList final : public AssetListTyped<Template> -{ -private: - // AC = Asset Creator - std::string mACNewName; - NameSelectionError mACNewNameError = NameSelectionError::Empty; - Template::Kind mACNewKind = Template::InvalidKind; - -public: - // Inherit constructors - using AssetListTyped::AssetListTyped; - -protected: - void DiscoverFiles(const std::function<void(SavedAsset)>& callback) const override; - - std::string RetrieveNameFromFile(const std::filesystem::path& file) const override; - uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const override; - std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const override; - - bool SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const override; - Template* LoadInstance(const SavedAsset& assetInfo) const override; - Template* CreateInstance(const SavedAsset& assetInfo) const override; - bool RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const override; - - void DisplayAssetCreator(ListState& state) override; - void DisplayDetailsTable(ListState& state) const override; -}; diff --git a/core/src/Model/Template/Template_Main.cpp b/core/src/Model/Template/Template_Main.cpp deleted file mode 100644 index 4d6b67c..0000000 --- a/core/src/Model/Template/Template_Main.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "Template.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" -#include "Utils/IO/Archive.hpp" -#include "Utils/UUID.hpp" - -#include <imgui.h> -#include <imgui_stdlib.h> -#include <algorithm> -#include <cstdint> -#include <fstream> - -using namespace std::literals::string_view_literals; -namespace fs = std::filesystem; - -Template::Template(Kind kind) - : mKind{ kind } -{ -} - -Template::Kind Template::GetKind() const -{ - return mKind; -} - -void TemplateAssetList::DiscoverFiles(const std::function<void(SavedAsset)>& callback) const -{ - auto dir = GetConnectedProject().GetTemplatesDirectory(); - DiscoverFilesByExtension(callback, dir, ".cplt-template"sv); -} - -std::string TemplateAssetList::RetrieveNameFromFile(const fs::path& file) const -{ - auto res = DataArchive::LoadFile(file); - if (!res) return ""; - auto& stream = res.value(); - - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - return assetInfo.Name; -} - -uuids::uuid TemplateAssetList::RetrieveUuidFromFile(const fs::path& file) const -{ - return uuids::uuid::from_string(file.stem().string()); -} - -fs::path TemplateAssetList::RetrievePathFromAsset(const SavedAsset& asset) const -{ - auto fileName = uuids::to_string(asset.Uuid); - return GetConnectedProject().GetTemplatePath(fileName); -} - -bool TemplateAssetList::SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const -{ - auto path = RetrievePathFromAsset(assetInfo); - auto res = DataArchive::SaveFile(path); - if (!res) return false; - auto& stream = res.value(); - - stream.WriteObject(assetInfo); - // This cast is fine: calls to this class will always be wrapped in TypedAssetList<T>, which will ensure `asset` points to some Template - if (auto tmpl = static_cast<const Template*>(asset)) { // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) - stream.WriteObject(*tmpl); - } - - return true; -} - -static std::unique_ptr<Template> LoadTemplateFromFile(const fs::path& path) -{ - auto res = DataArchive::LoadFile(path); - if (!res) return nullptr; - auto& stream = res.value(); - - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - auto kind = static_cast<Template::Kind>(assetInfo.Payload); - auto tmpl = Template::CreateByKind(kind); - stream.ReadObject(*tmpl); - - return tmpl; -} - -Template* TemplateAssetList::LoadInstance(const SavedAsset& assetInfo) const -{ - return ::LoadTemplateFromFile(RetrievePathFromAsset(assetInfo)).release(); -} - -Template* TemplateAssetList::CreateInstance(const SavedAsset& assetInfo) const -{ - auto kind = static_cast<Template::Kind>(assetInfo.Payload); - return Template::CreateByKind(kind).release(); -} - -bool TemplateAssetList::RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const -{ - // Get asset path, which is only dependent on UUID - auto path = RetrievePathFromAsset(assetInfo); - - auto tmpl = ::LoadTemplateFromFile(path); - if (!tmpl) return false; - - // Rewrite the asset with the updated name (note the given assetInfo already has the update name) - SaveInstance(assetInfo, tmpl.get()); - - return true; -} - -void TemplateAssetList::DisplayAssetCreator(ListState& state) -{ - auto ValidateNewName = [&]() -> void { - if (mACNewName.empty()) { - mACNewNameError = NameSelectionError::Empty; - return; - } - - if (FindByName(mACNewName)) { - mACNewNameError = NameSelectionError::Duplicated; - return; - } - - mACNewNameError = NameSelectionError::None; - }; - - auto ShowNewNameErrors = [&]() -> void { - switch (mACNewNameError) { - case NameSelectionError::None: break; - case NameSelectionError::Duplicated: - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - break; - case NameSelectionError::Empty: - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - break; - } - }; - - auto ShowNewKindErrors = [&]() -> void { - if (mACNewKind == Template::InvalidKind) { - ImGui::ErrorMessage(I18N_TEXT("Invalid template type", L10N_TEMPLATE_INVALID_TYPE_ERROR)); - } - }; - - auto IsInputValid = [&]() -> bool { - return mACNewNameError == NameSelectionError::None && - mACNewKind != Template::InvalidKind; - }; - - auto ResetState = [&]() -> void { - mACNewName.clear(); - mACNewKind = Template::InvalidKind; - ValidateNewName(); - }; - - if (ImGui::InputText(I18N_TEXT("Name", L10N_NAME), &mACNewName)) { - ValidateNewName(); - } - - if (ImGui::BeginCombo(I18N_TEXT("Type", L10N_TYPE), Template::FormatKind(mACNewKind))) { - for (int i = 0; i < Template::KindCount; ++i) { - auto kind = static_cast<Template::Kind>(i); - if (ImGui::Selectable(Template::FormatKind(kind), mACNewKind == kind)) { - mACNewKind = kind; - } - } - ImGui::EndCombo(); - } - - ShowNewNameErrors(); - ShowNewKindErrors(); - - if (ImGui::Button(I18N_TEXT("OK", L10N_CONFIRM), !IsInputValid())) { - ImGui::CloseCurrentPopup(); - - Create(SavedAsset{ - .Name = mACNewName, - .Payload = static_cast<uint64_t>(mACNewKind), - }); - ResetState(); - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } -} - -void TemplateAssetList::DisplayDetailsTable(ListState& state) const -{ - ImGui::BeginTable("AssetDetailsTable", 2, ImGuiTableFlags_Borders); - - ImGui::TableSetupColumn(I18N_TEXT("Name", L10N_NAME)); - ImGui::TableSetupColumn(I18N_TEXT("Type", L10N_TYPE)); - ImGui::TableHeadersRow(); - - for (auto& asset : this->GetAssets()) { - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups)) { - state.SelectedAsset = &asset; - } - - ImGui::TableNextColumn(); - auto kind = static_cast<Template::Kind>(asset.Payload); - ImGui::TextUnformatted(Template::FormatKind(kind)); - } - - ImGui::EndTable(); -} diff --git a/core/src/Model/Template/Template_RTTI.cpp b/core/src/Model/Template/Template_RTTI.cpp deleted file mode 100644 index d1affe7..0000000 --- a/core/src/Model/Template/Template_RTTI.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "Template.hpp" - -#include "Model/Template/TableTemplate.hpp" -#include "Utils/I18n.hpp" - -const char* Template::FormatKind(Kind kind) -{ - switch (kind) { - case KD_Table: return I18N_TEXT("Table template", L10N_TEMPLATE_TABLE); - - case InvalidKind: break; - } - return ""; -} - -std::unique_ptr<Template> Template::CreateByKind(Kind kind) -{ - switch (kind) { - case KD_Table: return std::make_unique<TableTemplate>(); - - case InvalidKind: break; - } - return nullptr; -} - -bool Template::IsInstance(const Template* tmpl) -{ - return true; -} diff --git a/core/src/Model/Template/fwd.hpp b/core/src/Model/Template/fwd.hpp deleted file mode 100644 index 8378871..0000000 --- a/core/src/Model/Template/fwd.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// TableTemplate.hpp -class TableCell; -class TableArrayGroup; -class TableInstantiationParameters; -class TableTemplate; - -// Template.hpp -class Template; -class TemplateAssetList; diff --git a/core/src/Model/Workflow/Evaluation.cpp b/core/src/Model/Workflow/Evaluation.cpp deleted file mode 100644 index 7035bf9..0000000 --- a/core/src/Model/Workflow/Evaluation.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "Evaluation.hpp" - -#include <queue> - -const char* WorkflowEvaluationError::FormatMessageType(enum MessageType messageType) -{ - switch (messageType) { - case Error: return "Error"; - case Warning: return "Warning"; - } -} - -const char* WorkflowEvaluationError::FormatPinType(enum PinType pinType) -{ - switch (pinType) { - case NoPin: return nullptr; - case InputPin: return "Input pin"; - case OutputPin: return "Output pin"; - } -} - -std::string WorkflowEvaluationError::Format() const -{ - // TODO convert to std::format - - std::string result; - result += FormatMessageType(this->Type); - result += " at "; - result += NodeId; - if (auto pinText = FormatPinType(this->PinType)) { - result += "/"; - result += pinText; - result += " "; - result += PinId; - } - result += ": "; - result += this->Message; - - return result; -} - -struct WorkflowEvaluationContext::RuntimeNode -{ - enum EvaluationStatus - { - ST_Unevaluated, - ST_Success, - ST_Failed, - }; - - EvaluationStatus Status = ST_Unevaluated; -}; - -struct WorkflowEvaluationContext::RuntimeConnection -{ - std::unique_ptr<BaseValue> Value; - - bool IsAvailableValue() const - { - return Value != nullptr; - } -}; - -WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow) - : mWorkflow{ &workflow } -{ - mRuntimeNodes.resize(workflow.mNodes.size()); - mRuntimeConnections.resize(workflow.mConnections.size()); -} - -BaseValue* WorkflowEvaluationContext::GetConnectionValue(size_t id, bool constant) -{ - if (constant) { - return mWorkflow->GetConstantById(id); - } else { - return mRuntimeConnections[id].Value.get(); - } -} - -BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::InputPin& inputPin) -{ - if (inputPin.IsConnected()) { - return GetConnectionValue(inputPin.Connection, inputPin.IsConstantConnection()); - } else { - return nullptr; - } -} - -void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value) -{ - mRuntimeConnections[id].Value = std::move(value); -} - -void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) -{ - if (outputPin.IsConnected()) { - SetConnectionValue(outputPin.Connection, std::move(value)); - } -} - -void WorkflowEvaluationContext::Run() -{ - int evaluatedCount = 0; - int erroredCount = 0; - - for (auto& depthGroup : mWorkflow->GetDepthGroups()) { - for (size_t idx : depthGroup) { - auto& rn = mRuntimeNodes[idx]; - auto& n = *mWorkflow->mNodes[idx]; - - // TODO - - int preEvalErrors = mErrors.size(); - n.Evaluate(*this); - if (preEvalErrors != mErrors.size()) { - erroredCount++; - } else { - evaluatedCount++; - } - } - } - - for (size_t i = 0; i < mRuntimeNodes.size(); ++i) { - auto& rn = mRuntimeNodes[i]; - auto& n = *mWorkflow->mNodes[i]; - if (n.GetType() == WorkflowNode::OutputType) { - // TODO record outputs - } - } -} - -void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = pinId, - .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin, - .Type = WorkflowEvaluationError::Error, - }); -} - -void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = -1, - .PinType = WorkflowEvaluationError::NoPin, - .Type = WorkflowEvaluationError::Error, - }); -} - -void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = pinId, - .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin, - .Type = WorkflowEvaluationError::Warning, - }); -} - -void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node) -{ - mErrors.push_back(WorkflowEvaluationError{ - .Message = std::move(message), - .NodeId = node.GetId(), - .PinId = -1, - .PinType = WorkflowEvaluationError::NoPin, - .Type = WorkflowEvaluationError::Warning, - }); -} diff --git a/core/src/Model/Workflow/Evaluation.hpp b/core/src/Model/Workflow/Evaluation.hpp deleted file mode 100644 index 4d78872..0000000 --- a/core/src/Model/Workflow/Evaluation.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -#include <cstddef> -#include <cstdint> -#include <string> -#include <vector> - -class WorkflowEvaluationError -{ -public: - enum MessageType : int16_t - { - Error, - Warning, - }; - - enum PinType : int16_t - { - NoPin, - InputPin, - OutputPin, - }; - -public: - std::string Message; - size_t NodeId; - int PinId; - PinType PinType; - MessageType Type; - -public: - static const char* FormatMessageType(enum MessageType messageType); - static const char* FormatPinType(enum PinType pinType); - - std::string Format() const; -}; - -class WorkflowEvaluationContext -{ -private: - struct RuntimeNode; - struct RuntimeConnection; - - Workflow* mWorkflow; - std::vector<RuntimeNode> mRuntimeNodes; - std::vector<RuntimeConnection> mRuntimeConnections; - std::vector<WorkflowEvaluationError> mErrors; - std::vector<WorkflowEvaluationError> mWarnings; - -public: - WorkflowEvaluationContext(Workflow& workflow); - - BaseValue* GetConnectionValue(size_t id, bool constant); - BaseValue* GetConnectionValue(const WorkflowNode::InputPin& inputPin); - void SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value); - void SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value); - - void ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin); - void ReportError(std::string message, const WorkflowNode& node); - void ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin); - void ReportWarning(std::string message, const WorkflowNode& node); - - /// Run until all possible paths have been evaluated. - void Run(); -}; diff --git a/core/src/Model/Workflow/Nodes/DocumentNodes.cpp b/core/src/Model/Workflow/Nodes/DocumentNodes.cpp deleted file mode 100644 index 6729c63..0000000 --- a/core/src/Model/Workflow/Nodes/DocumentNodes.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "DocumentNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" - -bool DocumentTemplateExpansionNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_DocumentTemplateExpansion; -} - -DocumentTemplateExpansionNode::DocumentTemplateExpansionNode() - : WorkflowNode(KD_DocumentTemplateExpansion, false) -{ -} - -void DocumentTemplateExpansionNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} diff --git a/core/src/Model/Workflow/Nodes/DocumentNodes.hpp b/core/src/Model/Workflow/Nodes/DocumentNodes.hpp deleted file mode 100644 index 85bba9e..0000000 --- a/core/src/Model/Workflow/Nodes/DocumentNodes.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -class DocumentTemplateExpansionNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - DocumentTemplateExpansionNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; diff --git a/core/src/Model/Workflow/Nodes/NumericNodes.cpp b/core/src/Model/Workflow/Nodes/NumericNodes.cpp deleted file mode 100644 index 3a81979..0000000 --- a/core/src/Model/Workflow/Nodes/NumericNodes.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "NumericNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" -#include "Utils/I18n.hpp" -#include "Utils/Macros.hpp" -#include "Utils/RTTI.hpp" - -#include <cassert> -#include <utility> - -WorkflowNode::Kind NumericOperationNode::OperationTypeToNodeKind(OperationType type) -{ - switch (type) { - case Addition: return KD_NumericAddition; - case Subtraction: return KD_NumericSubtraction; - case Multiplication: return KD_NumericMultiplication; - case Division: return KD_NumericDivision; - default: return InvalidKind; - } -} - -NumericOperationNode::OperationType NumericOperationNode::NodeKindToOperationType(Kind kind) -{ - switch (kind) { - case KD_NumericAddition: return Addition; - case KD_NumericSubtraction: return Subtraction; - case KD_NumericMultiplication: return Multiplication; - case KD_NumericDivision: return Division; - default: return InvalidType; - } -} - -bool NumericOperationNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() >= KD_NumericAddition && node->GetKind() <= KD_NumericDivision; -} - -NumericOperationNode::NumericOperationNode(OperationType type) - : WorkflowNode(OperationTypeToNodeKind(type), false) - , mType{ type } -{ - mInputs.resize(2); - mInputs[0].MatchingType = BaseValue::KD_Numeric; - mInputs[1].MatchingType = BaseValue::KD_Numeric; - - mOutputs.resize(1); - mOutputs[0].MatchingType = BaseValue::KD_Numeric; -} - -void NumericOperationNode::Evaluate(WorkflowEvaluationContext& ctx) -{ - auto lhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[0])); - if (!lhsVal) return; - double lhs = lhsVal->GetValue(); - - auto rhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[1])); - if (!rhsVal) return; - double rhs = rhsVal->GetValue(); - - double res; - switch (mType) { - case Addition: res = lhs + rhs; break; - case Subtraction: res = lhs - rhs; break; - case Multiplication: res = lhs * rhs; break; - case Division: { - if (rhs == 0.0) { - ctx.ReportError(I18N_TEXT("Error: division by 0", L10N_WORKFLOW_RTERROR_DIV_BY_0), *this); - return; - } - res = lhs / rhs; - } break; - - default: return; - } - - auto value = std::make_unique<NumericValue>(); - value->SetValue(res); - ctx.SetConnectionValue(mOutputs[0], std::move(value)); -} - -bool NumericExpressionNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_NumericExpression; -} - -NumericExpressionNode::NumericExpressionNode() - : WorkflowNode(KD_NumericExpression, false) -{ -} - -void NumericExpressionNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} diff --git a/core/src/Model/Workflow/Nodes/NumericNodes.hpp b/core/src/Model/Workflow/Nodes/NumericNodes.hpp deleted file mode 100644 index 56c0313..0000000 --- a/core/src/Model/Workflow/Nodes/NumericNodes.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -#include <cstddef> -#include <memory> -#include <variant> -#include <vector> - -class NumericOperationNode : public WorkflowNode -{ -public: - enum OperationType - { - Addition, - Subtraction, - Multiplication, - Division, - - InvalidType, - TypeCount = InvalidType, - }; - -private: - OperationType mType; - -public: - static Kind OperationTypeToNodeKind(OperationType type); - static OperationType NodeKindToOperationType(Kind kind); - static bool IsInstance(const WorkflowNode* node); - NumericOperationNode(OperationType type); - - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; - -class NumericExpressionNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - NumericExpressionNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -};
\ No newline at end of file diff --git a/core/src/Model/Workflow/Nodes/TextNodes.cpp b/core/src/Model/Workflow/Nodes/TextNodes.cpp deleted file mode 100644 index 4628dd3..0000000 --- a/core/src/Model/Workflow/Nodes/TextNodes.cpp +++ /dev/null @@ -1,231 +0,0 @@ -#include "TextNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" -#include "Utils/Macros.hpp" -#include "Utils/RTTI.hpp" -#include "Utils/Variant.hpp" - -#include <cassert> -#include <utility> -#include <variant> -#include <vector> - -class TextFormatterNode::Impl -{ -public: - template <class TFunction> - static void ForArguments(std::vector<Element>::iterator begin, std::vector<Element>::iterator end, const TFunction& func) - { - for (auto it = begin; it != end; ++it) { - auto& elm = *it; - if (auto arg = std::get_if<Argument>(&elm)) { - func(*arg); - } - } - } - - /// Find the pin index that the \c elmIdx -th element should have, based on the elements coming before it. - static int FindPinForElement(const std::vector<Element>& vec, int elmIdx) - { - for (int i = elmIdx; i >= 0; --i) { - auto& elm = vec[i]; - if (auto arg = std::get_if<Argument>(&elm)) { - return arg->PinIdx + 1; - } - } - return 0; - } -}; - -BaseValue::Kind TextFormatterNode::ArgumentTypeToValueKind(TextFormatterNode::ArgumentType arg) -{ - switch (arg) { - case NumericArgument: return BaseValue::KD_Numeric; - case TextArgument: return BaseValue::KD_Text; - case DateTimeArgument: return BaseValue::KD_DateTime; - } -} - -bool TextFormatterNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_TextFormatting; -} - -TextFormatterNode::TextFormatterNode() - : WorkflowNode(KD_TextFormatting, false) -{ -} - -int TextFormatterNode::GetElementCount() const -{ - return mElements.size(); -} - -const TextFormatterNode::Element& TextFormatterNode::GetElement(int idx) const -{ - return mElements[idx]; -} - -void TextFormatterNode::SetElement(int idx, std::string text) -{ - assert(idx >= 0 && idx < mElements.size()); - - std::visit( - Overloaded{ - [&](const std::string& original) { mMinOutputChars -= original.size(); }, - [&](const Argument& original) { PreRemoveElement(idx); }, - }, - mElements[idx]); - - mMinOutputChars += text.size(); - mElements[idx] = std::move(text); -} - -void TextFormatterNode::SetElement(int idx, ArgumentType argument) -{ - assert(idx >= 0 && idx < mElements.size()); - - std::visit( - Overloaded{ - [&](const std::string& original) { - mMinOutputChars -= original.size(); - - mElements[idx] = Argument{ - .Type = argument, - .PinIdx = Impl::FindPinForElement(mElements, idx), - }; - /* `original` is invalid from this point */ - }, - [&](const Argument& original) { - int pinIdx = original.PinIdx; - - // Create pin - auto& pin = mInputs[pinIdx]; - pin.MatchingType = ArgumentTypeToValueKind(argument); - - // Create element - mElements[idx] = Argument{ - .Type = argument, - .PinIdx = pinIdx, - }; - /* `original` is invalid from this point */ - }, - }, - mElements[idx]); -} - -void TextFormatterNode::InsertElement(int idx, std::string text) -{ - assert(idx >= 0); - if (idx >= mElements.size()) AppendElement(std::move(text)); - - mMinOutputChars += text.size(); - mElements.insert(mElements.begin() + idx, std::move(text)); -} - -void TextFormatterNode::InsertElement(int idx, ArgumentType argument) -{ - assert(idx >= 0); - if (idx >= mElements.size()) AppendElement(argument); - - int pinIdx = Impl::FindPinForElement(mElements, idx); - - // Create pin - auto& pin = InsertInputPin(pinIdx); - pin.MatchingType = ArgumentTypeToValueKind(argument); - - // Create element - mElements.insert( - mElements.begin() + idx, - Argument{ - .Type = argument, - .PinIdx = pinIdx, - }); -} - -void TextFormatterNode::AppendElement(std::string text) -{ - mMinOutputChars += text.size(); - mElements.push_back(std::move(text)); -} - -void TextFormatterNode::AppendElement(ArgumentType argument) -{ - int pinIdx = mInputs.size(); - // Create pin - mInputs.push_back(InputPin{}); - mInputs.back().MatchingType = ArgumentTypeToValueKind(argument); - // Creat eelement - mElements.push_back(Argument{ - .Type = argument, - .PinIdx = pinIdx, - }); -} - -void TextFormatterNode::RemoveElement(int idx) -{ - assert(idx >= 0 && idx < mElements.size()); - - PreRemoveElement(idx); - if (auto arg = std::get_if<Argument>(&mElements[idx])) { - RemoveInputPin(arg->PinIdx); - } - mElements.erase(mElements.begin() + idx); -} - -void TextFormatterNode::Evaluate(WorkflowEvaluationContext& ctx) -{ - std::string result; - result.reserve((size_t)(mMinOutputChars * 1.5f)); - - auto HandleText = [&](const std::string& str) { - result += str; - }; - auto HandleArgument = [&](const Argument& arg) { - switch (arg.Type) { - case NumericArgument: { - if (auto val = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { - result += val->GetString(); - } else { - // TODO localize - ctx.ReportError("Non-numeric value connected to a numeric text format parameter.", *this); - } - } break; - case TextArgument: { - if (auto val = dyn_cast<TextValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { - result += val->GetValue(); - } else { - // TODO localize - ctx.ReportError("Non-text value connected to a textual text format parameter.", *this); - } - } break; - case DateTimeArgument: { - if (auto val = dyn_cast<DateTimeValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { - result += val->GetString(); - } else { - // TODO localize - ctx.ReportError("Non-date/time value connected to a date/time text format parameter.", *this); - } - } break; - } - }; - - for (auto& elm : mElements) { - std::visit(Overloaded{ HandleText, HandleArgument }, elm); - } -} - -void TextFormatterNode::PreRemoveElement(int idx) -{ - auto& elm = mElements[idx]; - if (auto arg = std::get_if<Argument>(&elm)) { - RemoveInputPin(arg->PinIdx); - Impl::ForArguments( - mElements.begin() + idx + 1, - mElements.end(), - [&](Argument& arg) { - arg.PinIdx--; - }); - } -} diff --git a/core/src/Model/Workflow/Nodes/TextNodes.hpp b/core/src/Model/Workflow/Nodes/TextNodes.hpp deleted file mode 100644 index c33854c..0000000 --- a/core/src/Model/Workflow/Nodes/TextNodes.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -#include <cstddef> -#include <memory> -#include <variant> -#include <vector> - -class TextFormatterNode : public WorkflowNode -{ -public: - enum ArgumentType - { - NumericArgument, - TextArgument, - DateTimeArgument, - }; - -private: - class Impl; - - struct Argument - { - ArgumentType Type; - int PinIdx; - }; - using Element = std::variant<std::string, Argument>; - - std::vector<Element> mElements; - int mMinOutputChars; - -public: - static BaseValue::Kind ArgumentTypeToValueKind(ArgumentType arg); - static bool IsInstance(const WorkflowNode* node); - TextFormatterNode(); - - int GetElementCount() const; - const Element& GetElement(int idx) const; - - void SetElement(int idx, std::string text); - void SetElement(int idx, ArgumentType argument); - void InsertElement(int idx, std::string text); - void InsertElement(int idx, ArgumentType argument); - void AppendElement(std::string text); - void AppendElement(ArgumentType argument); - void RemoveElement(int idx); - - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; - -private: - void PreRemoveElement(int idx); -}; diff --git a/core/src/Model/Workflow/Nodes/UserInputNodes.cpp b/core/src/Model/Workflow/Nodes/UserInputNodes.cpp deleted file mode 100644 index 0b6d471..0000000 --- a/core/src/Model/Workflow/Nodes/UserInputNodes.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "UserInputNodes.hpp" - -#include "Model/Workflow/Evaluation.hpp" -#include "Model/Workflow/Values/Basic.hpp" - -bool FormInputNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_FormInput; -} - -FormInputNode::FormInputNode() - : WorkflowNode(KD_FormInput, false) -{ -} - -void FormInputNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} - -bool DatabaseRowsInputNode::IsInstance(const WorkflowNode* node) -{ - return node->GetKind() == KD_DatabaseRowsInput; -} - -DatabaseRowsInputNode::DatabaseRowsInputNode() - : WorkflowNode(KD_DatabaseRowsInput, false) -{ -} - -void DatabaseRowsInputNode::Evaluate(WorkflowEvaluationContext& ctx) -{ -} diff --git a/core/src/Model/Workflow/Nodes/UserInputNodes.hpp b/core/src/Model/Workflow/Nodes/UserInputNodes.hpp deleted file mode 100644 index 10ea95d..0000000 --- a/core/src/Model/Workflow/Nodes/UserInputNodes.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Model/Workflow/Workflow.hpp" - -class FormInputNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - FormInputNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; - -class DatabaseRowsInputNode : public WorkflowNode -{ -public: - static bool IsInstance(const WorkflowNode* node); - DatabaseRowsInputNode(); - - // TODO - virtual void Evaluate(WorkflowEvaluationContext& ctx) override; -}; diff --git a/core/src/Model/Workflow/Nodes/fwd.hpp b/core/src/Model/Workflow/Nodes/fwd.hpp deleted file mode 100644 index 4153825..0000000 --- a/core/src/Model/Workflow/Nodes/fwd.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -// DocumentNodes.hpp -class DocumentTemplateExpansionNode; - -// InputNodes.hpp -class FormInputNode; -class DatabaseRowsInputNode; - -// NumericNodes.hpp -class NumericOperationNode; -class NumericExpressionNode; - -// TextNodes.hpp -class TextFormatterNode; diff --git a/core/src/Model/Workflow/Value.hpp b/core/src/Model/Workflow/Value.hpp deleted file mode 100644 index 2198674..0000000 --- a/core/src/Model/Workflow/Value.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "Utils/Color.hpp" -#include "cplt_fwd.hpp" - -#include <iosfwd> -#include <memory> -#include <string> -#include <vector> - -class BaseValue -{ -public: - enum Kind - { - KD_Numeric, - KD_Text, - KD_DateTime, - KD_DatabaseRowId, - KD_List, - KD_Dictionary, - - KD_BaseObject, - KD_SaleDatabaseRow, - KD_PurchaseDatabaseRow, - KD_BaseObjectLast = KD_PurchaseDatabaseRow, - - /// An unspecified type, otherwise known as "any" in some contexts. - InvalidKind, - KindCount = InvalidKind, - }; - - struct KindInfo - { - ImGui::IconType PinIcon; - RgbaColor PinColor; - }; - -private: - Kind mKind; - -public: - static const KindInfo& QueryInfo(Kind kind); - static const char* Format(Kind kind); - static std::unique_ptr<BaseValue> CreateByKind(Kind kind); - - static bool IsInstance(const BaseValue* value); - - BaseValue(Kind kind); - virtual ~BaseValue() = default; - - BaseValue(const BaseValue&) = delete; - BaseValue& operator=(const BaseValue&) = delete; - BaseValue(BaseValue&&) = default; - BaseValue& operator=(BaseValue&&) = default; - - Kind GetKind() const; - - // TODO get constant editor - - /// The functions \c ReadFrom, \c WriteTo will only be valid to call if this function returns true. - virtual bool SupportsConstant() const; - virtual void ReadFrom(std::istream& stream); - virtual void WriteTo(std::ostream& stream); -}; - -class BaseObjectDescription -{ -public: - struct Property - { - std::string Name; - BaseValue::Kind Kind; - bool Mutatable = true; - }; - -public: - std::vector<Property> Properties; -}; - -class BaseObjectValue : public BaseValue -{ -public: - /// \param kind A value kind enum, within the range of KD_BaseObject and KD_BaseObjectLast (both inclusive). - static const BaseObjectDescription& QueryObjectInfo(Kind kind); - - static bool IsInstance(const BaseValue* value); - BaseObjectValue(Kind kind); - - const BaseObjectDescription& GetObjectDescription() const; - - virtual const BaseValue* GetProperty(int idx) const = 0; - virtual bool SetProperty(int idx, std::unique_ptr<BaseValue> value) = 0; -}; diff --git a/core/src/Model/Workflow/ValueInternals.hpp b/core/src/Model/Workflow/ValueInternals.hpp deleted file mode 100644 index 49981f0..0000000 --- a/core/src/Model/Workflow/ValueInternals.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// This file contains utility classes and macros for implementing values -// As consumers, you should not include this header as it contains unnecessary symbols and can pollute your files -// for this reason, classes here aren't forward-declared in fwd.hpp either. - -#pragma once - -#include "Utils/RTTI.hpp" - -#include <utility> - -#define CHECK_VALUE_TYPE(Type, value) \ - if (!is_a<Type>(value)) { \ - return false; \ - } - -#define CHECK_VALUE_TYPE_AND_MOVE(Type, dest, value) \ - if (auto ptr = dyn_cast<Type>(value)) { \ - dest = std::move(*ptr); \ - } else { \ - return false; \ - } diff --git a/core/src/Model/Workflow/Value_Main.cpp b/core/src/Model/Workflow/Value_Main.cpp deleted file mode 100644 index ca972c4..0000000 --- a/core/src/Model/Workflow/Value_Main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "Value.hpp" - -BaseValue::BaseValue(Kind kind) - : mKind{ kind } -{ -} - -BaseValue::Kind BaseValue::GetKind() const -{ - return mKind; -} - -bool BaseValue::SupportsConstant() const -{ - return false; -} - -void BaseValue::ReadFrom(std::istream& stream) -{ -} - -void BaseValue::WriteTo(std::ostream& stream) -{ -} - -BaseObjectValue::BaseObjectValue(Kind kind) - : BaseValue(kind) -{ - assert(kind >= KD_BaseObject && kind <= KD_BaseObjectLast); -} - -const BaseObjectDescription& BaseObjectValue::GetObjectDescription() const -{ - return QueryObjectInfo(this->GetKind()); -} diff --git a/core/src/Model/Workflow/Value_RTTI.cpp b/core/src/Model/Workflow/Value_RTTI.cpp deleted file mode 100644 index 0561239..0000000 --- a/core/src/Model/Workflow/Value_RTTI.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "Value.hpp" - -#include "Model/Workflow/Values/Basic.hpp" -#include "Model/Workflow/Values/Database.hpp" -#include "Model/Workflow/Values/Dictionary.hpp" -#include "Model/Workflow/Values/List.hpp" -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" - -constexpr BaseValue::KindInfo kEmptyInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(), -}; - -constexpr BaseValue::KindInfo kNumericInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(147, 226, 74), -}; - -constexpr BaseValue::KindInfo kTextInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(124, 21, 153), -}; - -constexpr BaseValue::KindInfo kDateTimeInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(147, 226, 74), -}; - -constexpr BaseValue::KindInfo kDatabaseRowIdInfo{ - .PinIcon = ImGui::IconType::Circle, - .PinColor = RgbaColor(216, 42, 221), -}; - -constexpr BaseValue::KindInfo kListInfo{ - .PinIcon = ImGui::IconType::Diamond, - .PinColor = RgbaColor(58, 154, 214), -}; - -constexpr BaseValue::KindInfo kDictionaryInfo{ - .PinIcon = ImGui::IconType::Diamond, - .PinColor = RgbaColor(240, 240, 34), -}; - -constexpr BaseValue::KindInfo kDatabaseRowInfo{ - .PinIcon = ImGui::IconType::Square, - .PinColor = RgbaColor(15, 124, 196), -}; - -constexpr BaseValue::KindInfo kObjectInfo{ - .PinIcon = ImGui::IconType::Square, - .PinColor = RgbaColor(161, 161, 161), -}; - -const BaseValue::KindInfo& BaseValue::QueryInfo(BaseValue::Kind kind) -{ - switch (kind) { - case KD_Numeric: return kNumericInfo; - case KD_Text: return kTextInfo; - case KD_DateTime: return kDateTimeInfo; - case KD_DatabaseRowId: return kDatabaseRowIdInfo; - case KD_List: return kListInfo; - case KD_Dictionary: return kDictionaryInfo; - - case KD_BaseObject: return kObjectInfo; - case KD_SaleDatabaseRow: - case KD_PurchaseDatabaseRow: - return kDatabaseRowInfo; - - case InvalidKind: break; - } - return kEmptyInfo; -} - -const char* BaseValue::Format(Kind kind) -{ - switch (kind) { - case KD_Numeric: return I18N_TEXT("Numeric", L10N_VALUE_NUMERIC); - case KD_Text: return I18N_TEXT("Text", L10N_VALUE_TEXT); - case KD_DateTime: return I18N_TEXT("Date/time", L10N_VALUE_DATE_TIME); - case KD_DatabaseRowId: return I18N_TEXT("Row id", L10N_VALUE_ROW_ID); - case KD_List: return I18N_TEXT("List", L10N_VALUE_LIST); - case KD_Dictionary: return I18N_TEXT("Dictionary", L10N_VALUE_DICT); - - case KD_BaseObject: return I18N_TEXT("Object", L10N_VALUE_OBJECT); - case KD_SaleDatabaseRow: return I18N_TEXT("Sale record", L10N_VALUE_SALE_RECORD); - case KD_PurchaseDatabaseRow: return I18N_TEXT("Purchase record", L10N_VALUE_PURCHASE_RECORD); - - case InvalidKind: break; - } - return ""; -} - -std::unique_ptr<BaseValue> BaseValue::CreateByKind(BaseValue::Kind kind) -{ - switch (kind) { - case KD_Numeric: return std::make_unique<NumericValue>(); - case KD_Text: return std::make_unique<TextValue>(); - case KD_DateTime: return std::make_unique<DateTimeValue>(); - case KD_DatabaseRowId: return std::make_unique<DatabaseRowIdValue>(); - case KD_List: return std::make_unique<ListValue>(); - case KD_Dictionary: return std::make_unique<DictionaryValue>(); - - case KD_BaseObject: return nullptr; - case KD_SaleDatabaseRow: return std::make_unique<SaleDatabaseRowValue>(); - case KD_PurchaseDatabaseRow: return std::make_unique<PurchaseDatabaseRowValue>(); - - case InvalidKind: break; - } - return nullptr; -} - -bool BaseValue::IsInstance(const BaseValue* value) -{ - return true; -} - -const BaseObjectDescription kEmptyObjectInfo{ - .Properties = {}, -}; - -const BaseObjectDescription kSaleDbRowObject{ - .Properties = { - { - .Name = I18N_TEXT("Customer", L10N_VALUE_PROPERTY_CUSTOMER), - .Kind = BaseValue::KD_Text, - .Mutatable = false, - }, - { - .Name = I18N_TEXT("Deadline", L10N_VALUE_PROPERTY_DEADLINE), - .Kind = BaseValue::KD_DateTime, - }, - { - .Name = I18N_TEXT("Delivery time", L10N_VALUE_PROPERTY_DELIVERY_TIME), - .Kind = BaseValue::KD_DateTime, - }, - }, -}; - -const BaseObjectDescription kPurchaseDbRowObject{ - .Properties = { - { - .Name = I18N_TEXT("Factory", L10N_VALUE_PROPERTY_FACTORY), - .Kind = BaseValue::KD_Text, - .Mutatable = false, - }, - { - .Name = I18N_TEXT("Order time", L10N_VALUE_PROPERTY_ORDER_TIME), - .Kind = BaseValue::KD_DateTime, - }, - { - .Name = I18N_TEXT("Delivery time", L10N_VALUE_PROPERTY_DELIVERY_TIME), - .Kind = BaseValue::KD_DateTime, - }, - }, -}; - -const BaseObjectDescription& BaseObjectValue::QueryObjectInfo(Kind kind) -{ - switch (kind) { - case KD_BaseObject: return kEmptyObjectInfo; - case KD_SaleDatabaseRow: return kSaleDbRowObject; - case KD_PurchaseDatabaseRow: return kPurchaseDbRowObject; - - default: break; - } - return kEmptyObjectInfo; -} - -bool BaseObjectValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() >= KD_BaseObject && - value->GetKind() <= KD_BaseObjectLast; -} diff --git a/core/src/Model/Workflow/Values/Basic.cpp b/core/src/Model/Workflow/Values/Basic.cpp deleted file mode 100644 index 198387c..0000000 --- a/core/src/Model/Workflow/Values/Basic.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "Basic.hpp" - -#include <charconv> -#include <cmath> -#include <limits> - -bool NumericValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_Numeric; -} - -NumericValue::NumericValue() - : BaseValue(BaseValue::KD_Numeric) -{ -} - -template <class T, int kMaxSize> -static std::string NumberToString(T value) -{ - char buf[kMaxSize]; - auto res = std::to_chars(buf, buf + kMaxSize, value); - if (res.ec == std::errc()) { - return std::string(buf, res.ptr); - } else { - return "<err>"; - } -} - -std::string NumericValue::GetTruncatedString() const -{ - constexpr auto kMaxSize = std::numeric_limits<int64_t>::digits10; - return ::NumberToString<int64_t, kMaxSize>((int64_t)mValue); -} - -std::string NumericValue::GetRoundedString() const -{ - constexpr auto kMaxSize = std::numeric_limits<int64_t>::digits10; - return ::NumberToString<int64_t, kMaxSize>((int64_t)std::round(mValue)); -} - -std::string NumericValue::GetString() const -{ - constexpr auto kMaxSize = std::numeric_limits<double>::max_digits10; - return ::NumberToString<double, kMaxSize>(mValue); -} - -int64_t NumericValue::GetInt() const -{ - return static_cast<int64_t>(mValue); -} - -double NumericValue::GetValue() const -{ - return mValue; -} - -void NumericValue::SetValue(double value) -{ - mValue = value; -} - -bool TextValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_Text; -} - -TextValue::TextValue() - : BaseValue(BaseValue::KD_Text) -{ -} - -const std::string& TextValue::GetValue() const -{ - return mValue; -} - -void TextValue::SetValue(const std::string& value) -{ - mValue = value; -} - -bool DateTimeValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_DateTime; -} - -DateTimeValue::DateTimeValue() - : BaseValue(BaseValue::KD_DateTime) -{ -} - -std::string DateTimeValue::GetString() const -{ - namespace chrono = std::chrono; - auto t = chrono::system_clock::to_time_t(mValue); - - char data[32]; - std::strftime(data, sizeof(data), "%Y-%m-%d %H:%M:%S", std::localtime(&t)); - - return std::string(data); -} - -const std::chrono::time_point<std::chrono::system_clock>& DateTimeValue::GetValue() const -{ - return mValue; -} - -void DateTimeValue::SetValue(const std::chrono::time_point<std::chrono::system_clock>& value) -{ - mValue = value; -} diff --git a/core/src/Model/Workflow/Values/Basic.hpp b/core/src/Model/Workflow/Values/Basic.hpp deleted file mode 100644 index 38e0531..0000000 --- a/core/src/Model/Workflow/Values/Basic.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" - -#include <chrono> -#include <cstdint> -#include <string> - -class NumericValue : public BaseValue -{ -private: - double mValue; - -public: - static bool IsInstance(const BaseValue* value); - NumericValue(); - - NumericValue(const NumericValue&) = delete; - NumericValue& operator=(const NumericValue&) = delete; - NumericValue(NumericValue&&) = default; - NumericValue& operator=(NumericValue&&) = default; - - std::string GetTruncatedString() const; - std::string GetRoundedString() const; - std::string GetString() const; - - int64_t GetInt() const; - double GetValue() const; - void SetValue(double value); -}; - -class TextValue : public BaseValue -{ -private: - std::string mValue; - -public: - static bool IsInstance(const BaseValue* value); - TextValue(); - - TextValue(const TextValue&) = delete; - TextValue& operator=(const TextValue&) = delete; - TextValue(TextValue&&) = default; - TextValue& operator=(TextValue&&) = default; - - const std::string& GetValue() const; - void SetValue(const std::string& value); -}; - -class DateTimeValue : public BaseValue -{ -private: - std::chrono::time_point<std::chrono::system_clock> mValue; - -public: - static bool IsInstance(const BaseValue* value); - DateTimeValue(); - - DateTimeValue(const DateTimeValue&) = delete; - DateTimeValue& operator=(const DateTimeValue&) = delete; - DateTimeValue(DateTimeValue&&) = default; - DateTimeValue& operator=(DateTimeValue&&) = default; - - std::string GetString() const; - const std::chrono::time_point<std::chrono::system_clock>& GetValue() const; - void SetValue(const std::chrono::time_point<std::chrono::system_clock>& value); -}; diff --git a/core/src/Model/Workflow/Values/Database.cpp b/core/src/Model/Workflow/Values/Database.cpp deleted file mode 100644 index cdc2b4f..0000000 --- a/core/src/Model/Workflow/Values/Database.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Database.hpp" - -#include "Model/Database.hpp" -#include "Model/Workflow/ValueInternals.hpp" - -#include <limits> - -TableKind DatabaseRowIdValue::GetTable() const -{ - return mTable; -} - -int64_t DatabaseRowIdValue::GetRowId() const -{ - return mRowId; -} - -bool DatabaseRowIdValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_DatabaseRowId; -} - -DatabaseRowIdValue::DatabaseRowIdValue() - : BaseValue(KD_DatabaseRowId) - , mTable{ TableKind::Sales } - , mRowId{ std::numeric_limits<int64_t>::max() } -{ -} - -bool SaleDatabaseRowValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_SaleDatabaseRow; -} - -SaleDatabaseRowValue::SaleDatabaseRowValue() - : BaseObjectValue(KD_SaleDatabaseRow) -{ -} - -const BaseValue* SaleDatabaseRowValue::GetProperty(int idx) const -{ - switch (idx) { - case 0: return &mCustomerName; - case 1: return &mDeadline; - case 2: return &mDeliveryTime; - default: return nullptr; - } -} - -bool SaleDatabaseRowValue::SetProperty(int idx, std::unique_ptr<BaseValue> value) -{ - switch (idx) { - case 0: return false; - case 1: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mDeadline, value.get()); break; - case 2: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mDeliveryTime, value.get()); break; - } - return true; -} - -bool PurchaseDatabaseRowValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_PurchaseDatabaseRow; -} - -PurchaseDatabaseRowValue::PurchaseDatabaseRowValue() - : BaseObjectValue(KD_PurchaseDatabaseRow) -{ -} - -const BaseValue* PurchaseDatabaseRowValue::GetProperty(int idx) const -{ - switch (idx) { - case 0: return &mFactoryName; - case 1: return &mOrderTime; - case 2: return &mDeliveryTime; - default: return nullptr; - } -} - -bool PurchaseDatabaseRowValue::SetProperty(int idx, std::unique_ptr<BaseValue> value) -{ - switch (idx) { - case 0: return false; - case 1: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mOrderTime, value.get()); break; - case 2: CHECK_VALUE_TYPE_AND_MOVE(DateTimeValue, mDeliveryTime, value.get()); break; - } - return true; -} diff --git a/core/src/Model/Workflow/Values/Database.hpp b/core/src/Model/Workflow/Values/Database.hpp deleted file mode 100644 index e8a4f83..0000000 --- a/core/src/Model/Workflow/Values/Database.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" -#include "Model/Workflow/Values/Basic.hpp" -#include "cplt_fwd.hpp" - -class DatabaseRowIdValue : public BaseValue -{ -private: - TableKind mTable; - int64_t mRowId; - -public: - static bool IsInstance(const BaseValue* value); - DatabaseRowIdValue(); - - TableKind GetTable() const; - int64_t GetRowId() const; -}; - -class SaleDatabaseRowValue : public BaseObjectValue -{ -private: - int mCustomerId; - TextValue mCustomerName; - DateTimeValue mDeadline; - DateTimeValue mDeliveryTime; - -public: - static bool IsInstance(const BaseValue* value); - SaleDatabaseRowValue(); - - virtual const BaseValue* GetProperty(int idx) const; - virtual bool SetProperty(int idx, std::unique_ptr<BaseValue> value); -}; - -class PurchaseDatabaseRowValue : public BaseObjectValue -{ -private: - int mFactoryId; - TextValue mFactoryName; - DateTimeValue mOrderTime; - DateTimeValue mDeliveryTime; - -public: - static bool IsInstance(const BaseValue* value); - PurchaseDatabaseRowValue(); - - virtual const BaseValue* GetProperty(int idx) const; - virtual bool SetProperty(int idx, std::unique_ptr<BaseValue> value); -}; diff --git a/core/src/Model/Workflow/Values/Dictionary.cpp b/core/src/Model/Workflow/Values/Dictionary.cpp deleted file mode 100644 index 106e48d..0000000 --- a/core/src/Model/Workflow/Values/Dictionary.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "Dictionary.hpp" - -#include "Utils/Macros.hpp" - -bool DictionaryValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_Dictionary; -} - -DictionaryValue::DictionaryValue() - : BaseValue(KD_Dictionary) -{ -} - -int DictionaryValue::GetCount() const -{ - return mElements.size(); -} - -BaseValue* DictionaryValue::Find(std::string_view key) -{ - auto iter = mElements.find(key); - if (iter != mElements.end()) { - return iter.value().get(); - } else { - return nullptr; - } -} - -BaseValue* DictionaryValue::Insert(std::string_view key, std::unique_ptr<BaseValue>& value) -{ - auto [iter, success] = mElements.insert(key, std::move(value)); - if (success) { - return iter.value().get(); - } else { - return nullptr; - } -} - -BaseValue& DictionaryValue::InsertOrReplace(std::string_view key, std::unique_ptr<BaseValue> value) -{ - auto [iter, DISCARD] = mElements.emplace(key, std::move(value)); - return *iter.value(); -} - -void DictionaryValue::Remove(std::string_view key) -{ - mElements.erase(mElements.find(key)); -} diff --git a/core/src/Model/Workflow/Values/Dictionary.hpp b/core/src/Model/Workflow/Values/Dictionary.hpp deleted file mode 100644 index 65ea82f..0000000 --- a/core/src/Model/Workflow/Values/Dictionary.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" - -#include <tsl/array_map.h> -#include <memory> -#include <string> -#include <string_view> - -class DictionaryValue : public BaseValue -{ -private: - tsl::array_map<char, std::unique_ptr<BaseValue>> mElements; - -public: - static bool IsInstance(const BaseValue* value); - DictionaryValue(); - - int GetCount() const; - BaseValue* Find(std::string_view key); - - BaseValue* Insert(std::string_view key, std::unique_ptr<BaseValue>& value); - BaseValue& InsertOrReplace(std::string_view key, std::unique_ptr<BaseValue> value); - void Remove(std::string_view key); -}; diff --git a/core/src/Model/Workflow/Values/List.cpp b/core/src/Model/Workflow/Values/List.cpp deleted file mode 100644 index 9fd6bfd..0000000 --- a/core/src/Model/Workflow/Values/List.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "List.hpp" - -#include <utility> - -BaseValue* ListValue::Iterator::operator*() const -{ - return mIter->get(); -} - -BaseValue* ListValue::Iterator::operator->() const -{ - return mIter->get(); -} - -ListValue::Iterator& ListValue::Iterator::operator++() -{ - ++mIter; - return *this; -} - -ListValue::Iterator ListValue::Iterator::operator++(int) const -{ - return Iterator(mIter + 1); -} - -ListValue::Iterator& ListValue::Iterator::operator--() -{ - --mIter; - return *this; -} - -ListValue::Iterator ListValue::Iterator::operator--(int) const -{ - return Iterator(mIter - 1); -} - -bool operator==(const ListValue::Iterator& a, const ListValue::Iterator& b) -{ - return a.mIter == b.mIter; -} - -ListValue::Iterator::Iterator(decltype(mIter) iter) - : mIter{ iter } -{ -} - -bool ListValue::IsInstance(const BaseValue* value) -{ - return value->GetKind() == KD_List; -} - -ListValue::ListValue() - : BaseValue(KD_List) -{ -} - -int ListValue::GetCount() const -{ - return mElements.size(); -} - -BaseValue* ListValue::GetElement(int i) const -{ - return mElements[i].get(); -} - -void ListValue::Append(std::unique_ptr<BaseValue> element) -{ - mElements.push_back(std::move(element)); -} - -void ListValue::Insert(int i, std::unique_ptr<BaseValue> element) -{ - mElements.insert(mElements.begin() + i, std::move(element)); -} - -void ListValue::Insert(Iterator iter, std::unique_ptr<BaseValue> element) -{ - mElements.insert(iter.mIter, std::move(element)); -} - -void ListValue::Remove(int i) -{ - mElements.erase(mElements.begin() + i); -} - -void ListValue::Remove(Iterator iter) -{ - mElements.erase(iter.mIter); -} - -ListValue::Iterator ListValue::begin() -{ - return Iterator(mElements.begin()); -} - -ListValue::Iterator ListValue::end() -{ - return Iterator(mElements.end()); -} diff --git a/core/src/Model/Workflow/Values/List.hpp b/core/src/Model/Workflow/Values/List.hpp deleted file mode 100644 index 706a95c..0000000 --- a/core/src/Model/Workflow/Values/List.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "Model/Workflow/Value.hpp" - -#include <memory> -#include <vector> - -class ListValue : public BaseValue -{ -public: - class Iterator - { - private: - std::vector<std::unique_ptr<BaseValue>>::iterator mIter; - - public: - BaseValue* operator*() const; - BaseValue* operator->() const; - - Iterator& operator++(); - Iterator operator++(int) const; - Iterator& operator--(); - Iterator operator--(int) const; - - friend bool operator==(const Iterator& a, const Iterator& b); - - private: - friend class ListValue; - Iterator(decltype(mIter) iter); - }; - -private: - std::vector<std::unique_ptr<BaseValue>> mElements; - -public: - static bool IsInstance(const BaseValue* value); - ListValue(); - - int GetCount() const; - BaseValue* GetElement(int i) const; - - void Append(std::unique_ptr<BaseValue> element); - void Insert(int i, std::unique_ptr<BaseValue> element); - void Insert(Iterator iter, std::unique_ptr<BaseValue> element); - void Remove(int i); - void Remove(Iterator iter); - - Iterator begin(); - Iterator end(); -}; diff --git a/core/src/Model/Workflow/Values/fwd.hpp b/core/src/Model/Workflow/Values/fwd.hpp deleted file mode 100644 index 51a04e9..0000000 --- a/core/src/Model/Workflow/Values/fwd.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -// Basic.hpp -class NumericValue; -class TextValue; -class DateTimeValue; - -// Database.hpp -class DatabaseRowIdValue; -class SaleDatabaseRowValue; -class PurchaseDatabaseRowValue; - -// Dictionary.hpp -class DictionaryValue; - -// List.hpp -class ListValue; diff --git a/core/src/Model/Workflow/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp deleted file mode 100644 index 3c4d320..0000000 --- a/core/src/Model/Workflow/Workflow.hpp +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once - -#include "Model/Assets.hpp" -#include "Model/Workflow/Value.hpp" -#include "Utils/Vector.hpp" -#include "cplt_fwd.hpp" - -#include <imgui_node_editor.h> -#include <cstddef> -#include <cstdint> -#include <filesystem> -#include <functional> -#include <iosfwd> -#include <limits> -#include <memory> -#include <span> -#include <string> -#include <variant> -#include <vector> - -namespace ImNodes = ax::NodeEditor; - -class WorkflowConnection -{ -public: - static constexpr auto kInvalidId = std::numeric_limits<uint32_t>::max(); - - uint32_t Id; - uint32_t SourceNode; - uint32_t SourcePin; - uint32_t DestinationNode; - uint32_t DestinationPin; - -public: - WorkflowConnection(); - - bool IsValid() const; - - /// Used for `LinkId` when interfacing with imgui node editor. Runtime only (not saved to disk and generated when loading). - ImNodes::LinkId GetLinkId() const; - - void DrawDebugInfo() const; - void ReadFrom(std::istream& stream); - void WriteTo(std::ostream& stream) const; -}; - -class WorkflowNode -{ -public: - static constexpr auto kInvalidId = std::numeric_limits<uint32_t>::max(); - static constexpr auto kInvalidPinId = std::numeric_limits<uint32_t>::max(); - - enum Type - { - InputType, - TransformType, - OutputType, - }; - - enum Kind - { - KD_NumericAddition, - KD_NumericSubtraction, - KD_NumericMultiplication, - KD_NumericDivision, - KD_NumericExpression, - KD_TextFormatting, - KD_DocumentTemplateExpansion, - KD_FormInput, - KD_DatabaseRowsInput, - - InvalidKind, - KindCount = InvalidKind, - }; - - enum Category - { - CG_Numeric, - CG_Text, - CG_Document, - CG_UserInput, - CG_SystemInput, - CG_Output, - - InvalidCategory, - CategoryCount = InvalidCategory, - }; - - struct InputPin - { - uint32_t Connection = WorkflowConnection::kInvalidId; - BaseValue::Kind MatchingType = BaseValue::InvalidKind; - bool ConnectionToConst = false; - - /// A constant connection connects from a user-specified constant value, feeding to a valid \c DestinationNode and \c DestinationPin (i.e. input pins). - bool IsConstantConnection() const; - bool IsConnected() const; - BaseValue::Kind GetMatchingType() const; - }; - - struct OutputPin - { - uint32_t Connection = WorkflowConnection::kInvalidId; - BaseValue::Kind MatchingType = BaseValue::InvalidKind; - - bool IsConnected() const; - BaseValue::Kind GetMatchingType() const; - }; - -protected: - friend class Workflow; - friend class WorkflowEvaluationContext; - - Workflow* mWorkflow; - std::vector<InputPin> mInputs; - std::vector<OutputPin> mOutputs; - Vec2i mPosition; - uint32_t mId; - Kind mKind; - int mDepth; - bool mLocked; - -public: - static const char* FormatKind(Kind kind); - static const char* FormatCategory(Category category); - static const char* FormatType(Type type); - static Category QueryCategory(Kind kind); - static std::span<const Kind> QueryCategoryMembers(Category category); - static std::unique_ptr<WorkflowNode> CreateByKind(Kind kind); - - static bool IsInstance(const WorkflowNode* node); - - WorkflowNode(Kind kind, bool locked); - virtual ~WorkflowNode() = default; - - WorkflowNode(const WorkflowNode&) = delete; - WorkflowNode& operator=(const WorkflowNode&) = delete; - WorkflowNode(WorkflowNode&&) = default; - WorkflowNode& operator=(WorkflowNode&&) = default; - - void SetPosition(const Vec2i& position); - Vec2i GetPosition() const; - - uint32_t GetId() const; - /// Used for `NodeId` when interfacing with imgui node editor. Runtime only (not saved to disk and generated when loading). - ImNodes::NodeId GetNodeId() const; - Kind GetKind() const; - int GetDepth() const; - bool IsLocked() const; - - Type GetType() const; - bool IsInputNode() const; - bool IsOutputNode() const; - - void ConnectInput(uint32_t pinId, WorkflowNode& srcNode, uint32_t srcPinId); - void DisconnectInput(uint32_t pinId); - - void DrawInputPinDebugInfo(uint32_t pinId) const; - const InputPin& GetInputPin(uint32_t pinId) const; - ImNodes::PinId GetInputPinUniqueId(uint32_t pinId) const; - - void ConnectOutput(uint32_t pinId, WorkflowNode& dstNode, uint32_t dstPinId); - void DisconnectOutput(uint32_t pinId); - - void DrawOutputPinDebugInfo(uint32_t pinId) const; - const OutputPin& GetOutputPin(uint32_t pinId) const; - ImNodes::PinId GetOutputPinUniqueId(uint32_t pinId) const; - - virtual void Evaluate(WorkflowEvaluationContext& ctx) = 0; - - void Draw(); - virtual void DrawExtra() {} - - void DrawDebugInfo() const; - virtual void DrawExtraDebugInfo() const {} - - virtual void ReadFrom(std::istream& istream); - virtual void WriteTo(std::ostream& ostream); - -protected: - InputPin& InsertInputPin(int atIdx); - void RemoveInputPin(int pin); - void SwapInputPin(int a, int b); - OutputPin& InsertOutputPin(int atIdx); - void RemoveOutputPin(int pin); - void SwapOutputPin(int a, int b); - - /* For \c Workflow to invoke, override by implementations */ - - void OnAttach(Workflow& workflow, uint32_t newId); - void OnDetach(); -}; - -class Workflow : public Asset -{ - friend class WorkflowNode; - friend class WorkflowEvaluationContext; - class Private; - -public: - using CategoryType = WorkflowAssetList; - static constinit const WorkflowAssetList Category; - -private: - std::vector<WorkflowConnection> mConnections; - std::vector<std::unique_ptr<WorkflowNode>> mNodes; - std::vector<std::unique_ptr<BaseValue>> mConstants; - std::vector<std::vector<uint32_t>> mDepthGroups; - int mConnectionCount; - int mNodeCount; - int mConstantCount; - bool mDepthsDirty = true; - -public: - /* Graph access */ - - const std::vector<WorkflowConnection>& GetConnections() const; - std::vector<WorkflowConnection>& GetConnections(); - const std::vector<std::unique_ptr<WorkflowNode>>& GetNodes() const; - std::vector<std::unique_ptr<WorkflowNode>>& GetNodes(); - const std::vector<std::unique_ptr<BaseValue>>& GetConstants() const; - std::vector<std::unique_ptr<BaseValue>>& GetConstants(); - - WorkflowConnection* GetConnectionById(uint32_t id); - WorkflowConnection* GetConnectionByLinkId(ImNodes::LinkId linkId); - WorkflowNode* GetNodeById(uint32_t id); - WorkflowNode* GetNodeByNodeId(ImNodes::NodeId nodeId); - BaseValue* GetConstantById(uint32_t id); - - struct GlobalPinId - { - WorkflowNode* Node; - uint32_t PinId; - /// true => input pin - /// false => output pin - bool IsOutput; - }; - - /// `pinId` should be the `UniqueId` of a pin from a node that's within this workflow. - GlobalPinId DisassembleGlobalPinId(ImNodes::PinId id); - ImNodes::PinId FabricateGlobalPinId(const WorkflowNode& node, uint32_t pinId, bool isOutput) const; - - const std::vector<std::vector<uint32_t>>& GetDepthGroups() const; - bool DoesDepthNeedsUpdate() const; - - /* Graph mutation */ - - void AddNode(std::unique_ptr<WorkflowNode> step); - void RemoveNode(uint32_t id); - - void RemoveConnection(uint32_t id); - - bool Connect(WorkflowNode& sourceNode, uint32_t sourcePin, WorkflowNode& destinationNode, uint32_t destinationPin); - bool DisconnectBySource(WorkflowNode& sourceNode, uint32_t sourcePin); - bool DisconnectByDestination(WorkflowNode& destinationNode, uint32_t destinationPin); - - /* Graph rebuild */ - - enum GraphUpdateResult - { - /// Successfully rebuilt graph dependent data. - /// Details: nothing is written. - GUR_Success, - /// Nothing has changed since last time UpdateGraph() was called. - /// Details: nothing is written. - GUR_NoWorkToDo, - /// Details: list of nodes is written. - GUR_UnsatisfiedDependencies, - /// Details: list of nodes is written. - GUR_UnreachableNodes, - }; - - using GraphUpdateDetails = std::variant< - // Case: nothing - std::monostate, - // Case: list of nodes (ids) - std::vector<uint32_t>>; - - GraphUpdateResult UpdateGraph(GraphUpdateDetails* details = nullptr); - - /* Serialization */ - - void ReadFromDataStream(InputDataStream& stream); - void WriteToDataStream(OutputDataStream& stream) const; - -private: - std::pair<WorkflowConnection&, uint32_t> AllocWorkflowConnection(); - std::pair<std::unique_ptr<WorkflowNode>&, uint32_t> AllocWorkflowStep(); -}; - -class WorkflowAssetList final : public AssetListTyped<Workflow> -{ -private: - // AC = Asset Creator - std::string mACNewName; - NameSelectionError mACNewNameError = NameSelectionError::Empty; - -public: - // Inherit constructors - using AssetListTyped::AssetListTyped; - -protected: - void DiscoverFiles(const std::function<void(SavedAsset)>& callback) const override; - - std::string RetrieveNameFromFile(const std::filesystem::path& file) const override; - uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const override; - std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const override; - - bool SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const override; - Workflow* LoadInstance(const SavedAsset& assetInfo) const override; - Workflow* CreateInstance(const SavedAsset& assetInfo) const override; - bool RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const override; - - void DisplayAssetCreator(ListState& state) override; - void DisplayDetailsTable(ListState& state) const override; -}; diff --git a/core/src/Model/Workflow/Workflow_Main.cpp b/core/src/Model/Workflow/Workflow_Main.cpp deleted file mode 100644 index 3be2d4d..0000000 --- a/core/src/Model/Workflow/Workflow_Main.cpp +++ /dev/null @@ -1,846 +0,0 @@ -#include "Workflow.hpp" - -#include "Model/GlobalStates.hpp" -#include "Model/Project.hpp" -#include "UI/UI.hpp" -#include "Utils/I18n.hpp" -#include "Utils/IO/Archive.hpp" -#include "Utils/UUID.hpp" - -#include <imgui.h> -#include <imgui_node_editor.h> -#include <imgui_stdlib.h> -#include <tsl/robin_set.h> -#include <algorithm> -#include <cassert> -#include <cstdint> -#include <fstream> -#include <iostream> -#include <queue> -#include <utility> - -using namespace std::literals::string_view_literals; -namespace fs = std::filesystem; -namespace ImNodes = ax::NodeEditor; - -WorkflowConnection::WorkflowConnection() - : Id{ 0 } - , SourceNode{ WorkflowNode::kInvalidId } - , SourcePin{ WorkflowNode::kInvalidPinId } - , DestinationNode{ WorkflowNode::kInvalidId } - , DestinationPin{ WorkflowNode::kInvalidPinId } -{ -} - -bool WorkflowConnection::IsValid() const -{ - return Id != 0; -} - -ImNodes::LinkId WorkflowConnection::GetLinkId() const -{ - // Our id is 0-based (represents an index directly) - // but imgui-node-editor uses the value 0 to represent a null id, so we need to offset by 1 - return Id + 1; -} - -void WorkflowConnection::DrawDebugInfo() const -{ - ImGui::Text("Source (node with output pin):"); - ImGui::Text("{ Node = %u, Pin = %u }", SourceNode, SourcePin); - ImGui::Text("Destination (node with input pin):"); - ImGui::Text("{ Node = %u, Pin = %u }", DestinationNode, DestinationPin); -} - -void WorkflowConnection::ReadFrom(std::istream& stream) -{ - stream >> SourceNode >> SourcePin; - stream >> DestinationNode >> DestinationPin; -} - -void WorkflowConnection::WriteTo(std::ostream& stream) const -{ - stream << SourceNode << SourcePin; - stream << DestinationNode << DestinationPin; -} - -bool WorkflowNode::InputPin::IsConstantConnection() const -{ - return ConnectionToConst && IsConnected(); -} - -bool WorkflowNode::InputPin::IsConnected() const -{ - return Connection != WorkflowConnection::kInvalidId; -} - -BaseValue::Kind WorkflowNode::InputPin::GetMatchingType() const -{ - return MatchingType; -} - -bool WorkflowNode::OutputPin::IsConnected() const -{ - return Connection != WorkflowConnection::kInvalidId; -} - -BaseValue::Kind WorkflowNode::OutputPin::GetMatchingType() const -{ - return MatchingType; -} - -WorkflowNode::WorkflowNode(Kind kind, bool locked) - : mKind{ kind } - , mDepth{ -1 } - , mLocked(locked) -{ -} - -Vec2i WorkflowNode::GetPosition() const -{ - return mPosition; -} - -void WorkflowNode::SetPosition(const Vec2i& position) -{ - mPosition = position; -} - -uint32_t WorkflowNode::GetId() const -{ - return mId; -} - -ImNodes::NodeId WorkflowNode::GetNodeId() const -{ - // See WorkflowConnection::GetLinkId for the rationale - return mId + 1; -} - -WorkflowNode::Kind WorkflowNode::GetKind() const -{ - return mKind; -} - -int WorkflowNode::GetDepth() const -{ - return mDepth; -} - -bool WorkflowNode::IsLocked() const -{ - return mLocked; -} - -WorkflowNode::Type WorkflowNode::GetType() const -{ - if (IsInputNode()) { - return InputType; - } else if (IsOutputNode()) { - return OutputType; - } else { - return TransformType; - } -} - -bool WorkflowNode::IsInputNode() const -{ - return mInputs.size() == 0; -} - -bool WorkflowNode::IsOutputNode() const -{ - return mOutputs.size() == 0; -} - -void WorkflowNode::ConnectInput(uint32_t pinId, WorkflowNode& srcNode, uint32_t srcPinId) -{ - mWorkflow->Connect(*this, pinId, srcNode, srcPinId); -} - -void WorkflowNode::DisconnectInput(uint32_t pinId) -{ - mWorkflow->DisconnectByDestination(*this, pinId); -} - -void WorkflowNode::DrawInputPinDebugInfo(uint32_t pinId) const -{ - ImGui::Text("Node ID: %d", mId); - ImGui::Text("Pin ID: (input) %d", pinId); -} - -const WorkflowNode::InputPin& WorkflowNode::GetInputPin(uint32_t pinId) const -{ - return mInputs[pinId]; -} - -ImNodes::PinId WorkflowNode::GetInputPinUniqueId(uint32_t pinId) const -{ - return mWorkflow->FabricateGlobalPinId(*this, pinId, false); -} - -void WorkflowNode::ConnectOutput(uint32_t pinId, WorkflowNode& dstNode, uint32_t dstPinId) -{ - mWorkflow->Connect(dstNode, dstPinId, *this, pinId); -} - -void WorkflowNode::DisconnectOutput(uint32_t pinId) -{ - mWorkflow->DisconnectBySource(*this, pinId); -} - -void WorkflowNode::DrawOutputPinDebugInfo(uint32_t pinId) const -{ - ImGui::Text("Node ID: %d", mId); - ImGui::Text("Pin ID: (output) %d", pinId); -} - -const WorkflowNode::OutputPin& WorkflowNode::GetOutputPin(uint32_t pinId) const -{ - return mOutputs[pinId]; -} - -ImNodes::PinId WorkflowNode::GetOutputPinUniqueId(uint32_t pinId) const -{ - return mWorkflow->FabricateGlobalPinId(*this, pinId, true); -} - -void WorkflowNode::Draw() -{ - for (uint32_t i = 0; i < mInputs.size(); ++i) { - auto& pin = mInputs[i]; - auto& typeInfo = BaseValue::QueryInfo(pin.MatchingType); - ImNodes::BeginPin(GetInputPinUniqueId(i), ImNodes::PinKind::Input); - // TODO - ImNodes::EndPin(); - } - for (uint32_t i = 0; i < mOutputs.size(); ++i) { - auto& pin = mOutputs[i]; - auto& typeInfo = BaseValue::QueryInfo(pin.MatchingType); - ImNodes::BeginPin(GetOutputPinUniqueId(i), ImNodes::PinKind::Output); - // TODO - ImNodes::EndPin(); - } -} - -void WorkflowNode::DrawDebugInfo() const -{ - ImGui::Text("Node kind: %s", FormatKind(mKind)); - ImGui::Text("Node type: %s", FormatType(GetType())); - ImGui::Text("Node ID: %u", mId); - ImGui::Text("Depth: %d", mDepth); - DrawExtraDebugInfo(); -} - -void WorkflowNode::ReadFrom(std::istream& stream) -{ - stream >> mId; - stream >> mPosition.x >> mPosition.y; -} - -void WorkflowNode::WriteTo(std::ostream& stream) -{ - stream << mId; - stream << mPosition.x << mPosition.y; -} - -WorkflowNode::InputPin& WorkflowNode::InsertInputPin(int atIdx) -{ - assert(atIdx >= 0 && atIdx < mInputs.size()); - - mInputs.push_back(InputPin{}); - for (int i = (int)mInputs.size() - 1, end = atIdx + 1; i >= end; --i) { - SwapInputPin(i, i + 1); - } - - return mInputs[atIdx]; -} - -void WorkflowNode::RemoveInputPin(int pin) -{ - DisconnectInput(pin); - for (int i = 0, end = (int)mInputs.size() - 1; i < end; ++i) { - SwapInputPin(i, i + 1); - } - mInputs.resize(mInputs.size() - 1); -} - -void WorkflowNode::SwapInputPin(int a, int b) -{ - auto& pinA = mInputs[a]; - auto& pinB = mInputs[b]; - - if (mWorkflow) { - if (pinA.IsConnected() && !pinA.IsConstantConnection()) { - auto& conn = *mWorkflow->GetConnectionById(pinA.Connection); - conn.DestinationPin = b; - } - if (pinB.IsConnected() && !pinB.IsConstantConnection()) { - auto& conn = *mWorkflow->GetConnectionById(pinB.Connection); - conn.DestinationPin = a; - } - } - - std::swap(pinA, pinB); -} - -WorkflowNode::OutputPin& WorkflowNode::InsertOutputPin(int atIdx) -{ - assert(atIdx >= 0 && atIdx < mOutputs.size()); - - mOutputs.push_back(OutputPin{}); - for (int i = (int)mOutputs.size() - 1, end = atIdx + 1; i >= end; --i) { - SwapOutputPin(i, i + 1); - } - - return mOutputs[atIdx]; -} - -void WorkflowNode::RemoveOutputPin(int pin) -{ - DisconnectOutput(pin); - for (int i = 0, end = (int)mOutputs.size() - 1; i < end; ++i) { - SwapInputPin(i, i + 1); - } - mOutputs.resize(mOutputs.size() - 1); -} - -void WorkflowNode::SwapOutputPin(int a, int b) -{ - auto& pinA = mOutputs[a]; - auto& pinB = mOutputs[b]; - - if (mWorkflow) { - if (pinA.IsConnected()) { - auto& conn = *mWorkflow->GetConnectionById(pinA.Connection); - conn.SourcePin = b; - } - if (pinB.IsConnected()) { - auto& conn = *mWorkflow->GetConnectionById(pinB.Connection); - conn.SourcePin = a; - } - } - - std::swap(pinA, pinB); -} - -void WorkflowNode::OnAttach(Workflow& workflow, uint32_t newId) -{ -} - -void WorkflowNode::OnDetach() -{ -} - -const std::vector<WorkflowConnection>& Workflow::GetConnections() const -{ - return mConnections; -} - -std::vector<WorkflowConnection>& Workflow::GetConnections() -{ - return mConnections; -} - -const std::vector<std::unique_ptr<WorkflowNode>>& Workflow::GetNodes() const -{ - return mNodes; -} - -std::vector<std::unique_ptr<WorkflowNode>>& Workflow::GetNodes() -{ - return mNodes; -} - -const std::vector<std::unique_ptr<BaseValue>>& Workflow::GetConstants() const -{ - return mConstants; -} - -std::vector<std::unique_ptr<BaseValue>>& Workflow::GetConstants() -{ - return mConstants; -} - -WorkflowConnection* Workflow::GetConnectionById(uint32_t id) -{ - return &mConnections[id]; -} - -WorkflowConnection* Workflow::GetConnectionByLinkId(ImNodes::LinkId id) -{ - return &mConnections[(uint32_t)(size_t)id - 1]; -} - -WorkflowNode* Workflow::GetNodeById(uint32_t id) -{ - return mNodes[id].get(); -} - -WorkflowNode* Workflow::GetNodeByNodeId(ImNodes::NodeId id) -{ - return mNodes[(uint32_t)(size_t)id - 1].get(); -} - -BaseValue* Workflow::GetConstantById(uint32_t id) -{ - return mConstants[id].get(); -} - -Workflow::GlobalPinId Workflow::DisassembleGlobalPinId(ImNodes::PinId pinId) -{ - // imgui-node-editor requires all pins to have a global, unique id - // but in our model the pin are typed (input vs output) and associated with a node: there is no built-in global id - // Therefore we encode one ourselves - - // Global pin id format - // nnnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn Tppppppp ppppppppp pppppppp pppppppp - // <------- (32 bits) node id -------> ^<------ (31 bits) pin id --------> - // | (1 bit) input (false) vs output (true) - - // 1 is added to pin id to prevent the 0th node's 0th input pin resulting in a 0 global pin id - // (this is problematic because imgui-node-editor use 0 to represent null) - - auto id = static_cast<uint64_t>(pinId); - GlobalPinId result; - - result.Node = mNodes[id >> 32].get(); - result.PinId = (uint32_t)(id & 0x000000001FFFFFFF) - 1; - result.IsOutput = id >> 31; - - return result; -} - -ImNodes::PinId Workflow::FabricateGlobalPinId(const WorkflowNode& node, uint32_t pinId, bool isOutput) const -{ - // See this->DisassembleGlobalPinId for format details and rationale - - uint64_t id = 0; - id |= ((uint64_t)node.GetId() << 32); - id |= (isOutput << 31); - id |= ((pinId + 1) & 0x1FFFFFFF); - - return id; -} - -const std::vector<std::vector<uint32_t>>& Workflow::GetDepthGroups() const -{ - return mDepthGroups; -} - -bool Workflow::DoesDepthNeedsUpdate() const -{ - return mDepthsDirty; -} - -void Workflow::AddNode(std::unique_ptr<WorkflowNode> step) -{ - auto [storage, id] = AllocWorkflowStep(); - storage = std::move(step); - storage->OnAttach(*this, id); - storage->mWorkflow = this; - storage->mId = id; -} - -void Workflow::RemoveNode(uint32_t id) -{ - auto& step = mNodes[id]; - if (step == nullptr) return; - - step->OnDetach(); - step->mWorkflow = nullptr; - step->mId = WorkflowNode::kInvalidId; -} - -void Workflow::RemoveConnection(uint32_t id) -{ - auto& conn = mConnections[id]; - if (!conn.IsValid()) return; - - mNodes[conn.SourceNode]->mInputs[conn.SourcePin].Connection = WorkflowNode::kInvalidId; - mNodes[conn.DestinationNode]->mInputs[conn.DestinationPin].Connection = WorkflowNode::kInvalidId; - - conn = {}; - mDepthsDirty = true; -} - -bool Workflow::Connect(WorkflowNode& sourceNode, uint32_t sourcePin, WorkflowNode& destinationNode, uint32_t destinationPin) -{ - auto& src = sourceNode.mOutputs[sourcePin]; - auto& dst = destinationNode.mInputs[destinationPin]; - - // TODO report error to user? - if (src.GetMatchingType() != dst.GetMatchingType()) { - return false; - } - - if (src.IsConnected()) { - DisconnectBySource(sourceNode, sourcePin); - } - - auto [conn, id] = AllocWorkflowConnection(); - conn.SourceNode = sourceNode.GetId(); - conn.SourcePin = sourcePin; - conn.DestinationNode = destinationNode.GetId(); - conn.DestinationPin = destinationPin; - - src.Connection = id; - dst.Connection = id; - - mDepthsDirty = true; - return true; -} - -bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, uint32_t sourcePin) -{ - auto& sn = sourceNode.mOutputs[sourcePin]; - if (!sn.IsConnected()) return false; - - auto& conn = mConnections[sn.Connection]; - auto& dn = mNodes[conn.DestinationNode]->mInputs[conn.DestinationPin]; - - sn.Connection = WorkflowConnection::kInvalidId; - dn.Connection = WorkflowConnection::kInvalidId; - conn = {}; - - mDepthsDirty = true; - return true; -} - -bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, uint32_t destinationPin) -{ - auto& dn = destinationNode.mOutputs[destinationPin]; - if (!dn.IsConnected()) return false; - - auto& conn = mConnections[dn.Connection]; - auto& sn = mNodes[conn.SourceNode]->mInputs[conn.SourcePin]; - - sn.Connection = WorkflowConnection::kInvalidId; - dn.Connection = WorkflowConnection::kInvalidId; - conn = {}; - - mDepthsDirty = true; - return true; -} - -Workflow::GraphUpdateResult Workflow::UpdateGraph(GraphUpdateDetails* details) -{ - if (!mDepthsDirty) { - return GUR_NoWorkToDo; - } - - // Terminology: - // - Dependency = nodes its input pins are connected to - // - Dependents = nodes its output pins are connected to - - struct WorkingNode - { - // The max depth out of all dependency nodes, maintained during the traversal and committed as the actual depth - // when all dependencies of this node has been resolved. Add 1 to get the depth that will be assigned to the node. - int MaximumDepth = 0; - int FulfilledInputCount = 0; - }; - - std::vector<WorkingNode> workingNodes; - std::queue<uint32_t> q; - - // Check if all dependencies of this node is satisfied - auto CheckNodeDependencies = [&](WorkflowNode& node) -> bool { - for (auto& pin : node.mInputs) { - if (!pin.IsConnected()) { - return false; - } - } - return true; - }; - - workingNodes.reserve(mNodes.size()); - { - std::vector<uint32_t> unsatisfiedNodes; - for (uint32_t i = 0; i < mNodes.size(); ++i) { - auto& node = mNodes[i]; - workingNodes.push_back(WorkingNode{}); - - if (!node) continue; - - if (!CheckNodeDependencies(*node)) { - unsatisfiedNodes.push_back(i); - } - - node->mDepth = -1; - - // Start traversing with the input nodes - if (node->GetType() == WorkflowNode::InputType) { - q.push(i); - } - } - - if (!unsatisfiedNodes.empty()) { - if (details) { - details->emplace<decltype(unsatisfiedNodes)>(std::move(unsatisfiedNodes)); - } - return GUR_UnsatisfiedDependencies; - } - } - - auto ProcessNode = [&](WorkflowNode& node) -> void { - for (auto& pin : node.mOutputs) { - if (!pin.IsConnected()) continue; - auto& conn = mConnections[pin.Connection]; - - auto& wn = workingNodes[conn.DestinationNode]; - auto& n = *mNodes[conn.DestinationPin].get(); - - wn.FulfilledInputCount++; - wn.MaximumDepth = std::max(node.mDepth, wn.MaximumDepth); - - // Node's dependency is fulfilled, we can process its dependents next - // We use >= here because for a many-to-one pin, the dependency is an "or" relation ship, i.e. any of the nodes firing before this will fulfill the requirement - if (n.mInputs.size() >= wn.FulfilledInputCount) { - n.mDepth = wn.MaximumDepth + 1; - } - } - }; - - int processedNodes = 0; - while (!q.empty()) { - auto& wn = workingNodes[q.front()]; - auto& n = *mNodes[q.front()]; - q.pop(); - processedNodes++; - - ProcessNode(n); - } - - if (processedNodes < mNodes.size()) { - // There is unreachable nodes, collect them and report to the caller - - std::vector<uint32_t> unreachableNodes; - for (uint32_t i = 0; i < mNodes.size(); ++i) { - auto& wn = workingNodes[i]; - auto& n = *mNodes[i]; - - // This is a reachable node - if (n.mDepth != -1) continue; - - unreachableNodes.push_back(i); - } - - if (details) { - details->emplace<decltype(unreachableNodes)>(std::move(unreachableNodes)); - } - return GUR_UnreachableNodes; - } - - return GUR_Success; -} - -class Workflow::Private -{ -public: - template <class TSelf, class TProxy> - static void OperateStream(TSelf& self, TProxy& proxy) - { - // TODO - } -}; - -void Workflow::ReadFromDataStream(InputDataStream& stream) -{ - Private::OperateStream(*this, stream); -} - -void Workflow::WriteToDataStream(OutputDataStream& stream) const -{ - Private::OperateStream(*this, stream); -} - -std::pair<WorkflowConnection&, uint32_t> Workflow::AllocWorkflowConnection() -{ - for (size_t idx = 0; idx < mConnections.size(); ++idx) { - auto& elm = mConnections[idx]; - if (!elm.IsValid()) { - return { elm, (uint32_t)idx }; - } - } - - auto id = (uint32_t)mConnections.size(); - auto& conn = mConnections.emplace_back(WorkflowConnection{}); - conn.Id = id; - - return { conn, id }; -} - -std::pair<std::unique_ptr<WorkflowNode>&, uint32_t> Workflow::AllocWorkflowStep() -{ - for (size_t idx = 0; idx < mNodes.size(); ++idx) { - auto& elm = mNodes[idx]; - if (elm == nullptr) { - return { elm, (uint32_t)idx }; - } - } - - auto id = (uint32_t)mNodes.size(); - auto& node = mNodes.emplace_back(std::unique_ptr<WorkflowNode>()); - - return { node, id }; -} - -void WorkflowAssetList::DiscoverFiles(const std::function<void(SavedAsset)>& callback) const -{ - auto dir = GetConnectedProject().GetWorkflowsDirectory(); - DiscoverFilesByExtension(callback, dir, ".cplt-workflow"sv); -} - -std::string WorkflowAssetList::RetrieveNameFromFile(const fs::path& file) const -{ - auto res = DataArchive::LoadFile(file); - if (!res) return ""; - auto& stream = res.value(); - - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - return assetInfo.Name; -} - -uuids::uuid WorkflowAssetList::RetrieveUuidFromFile(const fs::path& file) const -{ - return uuids::uuid::from_string(file.stem().string()); -} - -fs::path WorkflowAssetList::RetrievePathFromAsset(const SavedAsset& asset) const -{ - auto fileName = uuids::to_string(asset.Uuid); - return GetConnectedProject().GetWorkflowPath(fileName); -} - -bool WorkflowAssetList::SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const -{ - auto path = RetrievePathFromAsset(assetInfo); - auto res = DataArchive::SaveFile(path); - if (!res) return false; - auto& stream = res.value(); - - stream.WriteObject(assetInfo); - // This cast is fine: calls to this class will always be wrapped in TypedAssetList<T>, which will ensure `asset` points to some Workflow - if (auto workflow = static_cast<const Workflow*>(asset)) { // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) - stream.WriteObject(*workflow); - } - - return true; -} - -static std::unique_ptr<Workflow> LoadWorkflowFromFile(const fs::path& path) -{ - auto res = DataArchive::LoadFile(path); - if (!res) return nullptr; - auto& stream = res.value(); - - // TODO this is currently unused - SavedAsset assetInfo; - stream.ReadObject(assetInfo); - - auto workflow = std::make_unique<Workflow>(); - stream.ReadObject(*workflow); - - return workflow; -} - -Workflow* WorkflowAssetList::LoadInstance(const SavedAsset& assetInfo) const -{ - return ::LoadWorkflowFromFile(RetrievePathFromAsset(assetInfo)).release(); -} - -Workflow* WorkflowAssetList::CreateInstance(const SavedAsset& assetInfo) const -{ - return new Workflow(); -} - -bool WorkflowAssetList::RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const -{ - auto path = RetrievePathFromAsset(assetInfo); - - auto workflow = ::LoadWorkflowFromFile(path); - if (!workflow) return false; - - SaveInstance(assetInfo, workflow.get()); - - return true; -} - -void WorkflowAssetList::DisplayAssetCreator(ListState& state) -{ - auto ValidateNewName = [&]() -> void { - if (mACNewName.empty()) { - mACNewNameError = NameSelectionError::Empty; - return; - } - - if (FindByName(mACNewName)) { - mACNewNameError = NameSelectionError::Duplicated; - return; - } - - mACNewNameError = NameSelectionError::None; - }; - - auto ShowNewNameErrors = [&]() -> void { - switch (mACNewNameError) { - case NameSelectionError::None: break; - case NameSelectionError::Duplicated: - ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); - break; - case NameSelectionError::Empty: - ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); - break; - } - }; - - auto IsInputValid = [&]() -> bool { - return mACNewNameError == NameSelectionError::None; - }; - - auto ResetState = [&]() -> void { - mACNewName.clear(); - ValidateNewName(); - }; - - if (ImGui::InputText(I18N_TEXT("Name", L10N_NAME), &mACNewName)) { - ValidateNewName(); - } - - ShowNewNameErrors(); - - if (ImGui::Button(I18N_TEXT("OK", L10N_CONFIRM), !IsInputValid())) { - ImGui::CloseCurrentPopup(); - - Create(SavedAsset{ - .Name = mACNewName, - }); - ResetState(); - } - ImGui::SameLine(); - if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { - ImGui::CloseCurrentPopup(); - } -} - -void WorkflowAssetList::DisplayDetailsTable(ListState& state) const -{ - ImGui::BeginTable("AssetDetailsTable", 1, ImGuiTableFlags_Borders); - - ImGui::TableSetupColumn(I18N_TEXT("Name", L10N_NAME)); - ImGui::TableHeadersRow(); - - for (auto& asset : this->GetAssets()) { - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups)) { - state.SelectedAsset = &asset; - } - } - - ImGui::EndTable(); -} diff --git a/core/src/Model/Workflow/Workflow_RTTI.cpp b/core/src/Model/Workflow/Workflow_RTTI.cpp deleted file mode 100644 index cb66c69..0000000 --- a/core/src/Model/Workflow/Workflow_RTTI.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "Workflow.hpp" - -#include "Model/Workflow/Nodes/DocumentNodes.hpp" -#include "Model/Workflow/Nodes/NumericNodes.hpp" -#include "Model/Workflow/Nodes/TextNodes.hpp" -#include "Model/Workflow/Nodes/UserInputNodes.hpp" -#include "Utils/I18n.hpp" -#include "Utils/Macros.hpp" - -#include <memory> - -const char* WorkflowNode::FormatKind(Kind kind) -{ - switch (kind) { - case KD_NumericAddition: return I18N_TEXT("Add", L10N_WORKFLOW_ADD); - case KD_NumericSubtraction: return I18N_TEXT("Subtract", L10N_WORKFLOW_SUB); - case KD_NumericMultiplication: return I18N_TEXT("Multiply", L10N_WORKFLOW_MUL); - case KD_NumericDivision: return I18N_TEXT("Divide", L10N_WORKFLOW_DIV); - case KD_NumericExpression: return I18N_TEXT("Evaluate expression", L10N_WORKFLOW_EVAL); - case KD_TextFormatting: return I18N_TEXT("Format text", L10N_WORKFLOW_FMT); - case KD_DocumentTemplateExpansion: return I18N_TEXT("Expand template", L10N_WORKFLOW_INSTANTIATE_TEMPLATE); - case KD_FormInput: return I18N_TEXT("Form input", L10N_WORKFLOW_FORM_INPUT); - case KD_DatabaseRowsInput: return I18N_TEXT("Database input", L10N_WORKFLOW_DB_INPUT); - - case InvalidKind: break; - } - return ""; -} - -const char* WorkflowNode::FormatCategory(WorkflowNode::Category category) -{ - switch (category) { - case CG_Numeric: return I18N_TEXT("Numeric", L10N_WORKFLOW_CATEGORY_NUMERIC); - case CG_Text: return I18N_TEXT("Text", L10N_WORKFLOW_CATEGORY_TEXT); - case CG_Document: return I18N_TEXT("Document", L10N_WORKFLOW_CATEGORY_DOCUMENT); - case CG_UserInput: return I18N_TEXT("User input", L10N_WORKFLOW_CATEGORY_USER_INPUT); - case CG_SystemInput: return I18N_TEXT("System input", L10N_WORKFLOW_CATEGORY_SYS_INPUT); - case CG_Output: return I18N_TEXT("Output", L10N_WORKFLOW_CATEGORY_OUTPUT); - - case InvalidCategory: break; - } - return ""; -} - -const char* WorkflowNode::FormatType(Type type) -{ - switch (type) { - case InputType: return I18N_TEXT("Input", L10N_WORKFLOW_KIND_INPUT); - case TransformType: return I18N_TEXT("Transform", L10N_WORKFLOW_KIND_TRANSFORM); - case OutputType: return I18N_TEXT("Output", L10N_WORKFLOW_KIND_OUTPUT); - } - return ""; -} - -WorkflowNode::Category WorkflowNode::QueryCategory(Kind kind) -{ - switch (kind) { - case KD_NumericAddition: - case KD_NumericSubtraction: - case KD_NumericMultiplication: - case KD_NumericDivision: - case KD_NumericExpression: - return CG_Numeric; - case KD_TextFormatting: - return CG_Text; - case KD_DocumentTemplateExpansion: - return CG_Document; - case KD_FormInput: - case KD_DatabaseRowsInput: - return CG_UserInput; - - case InvalidKind: break; - } - return InvalidCategory; -} - -std::span<const WorkflowNode::Kind> WorkflowNode::QueryCategoryMembers(Category category) -{ - constexpr WorkflowNode::Kind kNumeric[] = { - KD_NumericAddition, - KD_NumericSubtraction, - KD_NumericMultiplication, - KD_NumericDivision, - KD_NumericExpression, - }; - - constexpr WorkflowNode::Kind kText[] = { - KD_TextFormatting, - }; - - constexpr WorkflowNode::Kind kDocument[] = { - KD_DocumentTemplateExpansion, - }; - - constexpr WorkflowNode::Kind kUserInput[] = { - KD_FormInput, - KD_DatabaseRowsInput, - }; - - // TODO remove invalid kinds after we have nodes of these categories - constexpr WorkflowNode::Kind kSystemInput[] = { - InvalidKind, - }; - - constexpr WorkflowNode::Kind kOutput[] = { - InvalidKind, - }; - - switch (category) { - case CG_Numeric: return kNumeric; - case CG_Text: return kText; - case CG_Document: return kDocument; - case CG_UserInput: return kUserInput; - case CG_SystemInput: return kSystemInput; - case CG_Output: return kOutput; - - case InvalidCategory: break; - } - return {}; -} - -std::unique_ptr<WorkflowNode> WorkflowNode::CreateByKind(WorkflowNode::Kind kind) -{ - switch (kind) { - case KD_NumericAddition: return std::make_unique<NumericOperationNode>(NumericOperationNode::Addition); - case KD_NumericSubtraction: return std::make_unique<NumericOperationNode>(NumericOperationNode::Subtraction); - case KD_NumericMultiplication: return std::make_unique<NumericOperationNode>(NumericOperationNode::Multiplication); - case KD_NumericDivision: return std::make_unique<NumericOperationNode>(NumericOperationNode::Division); - case KD_NumericExpression: return std::make_unique<NumericExpressionNode>(); - case KD_TextFormatting: return std::make_unique<TextFormatterNode>(); - case KD_DocumentTemplateExpansion: return std::make_unique<DocumentTemplateExpansionNode>(); - case KD_FormInput: return std::make_unique<FormInputNode>(); - case KD_DatabaseRowsInput: return std::make_unique<DatabaseRowsInputNode>(); - - case InvalidKind: break; - } - return nullptr; -} - -bool WorkflowNode::IsInstance(const WorkflowNode* node) -{ - return true; -} diff --git a/core/src/Model/Workflow/fwd.hpp b/core/src/Model/Workflow/fwd.hpp deleted file mode 100644 index ed39bdb..0000000 --- a/core/src/Model/Workflow/fwd.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "Model/Workflow/Nodes/fwd.hpp" -#include "Model/Workflow/Values/fwd.hpp" - -// Evaluation.hpp -class WorkflowEvaluationError; -class WorkflowEvaluationContext; - -// SavedWorkflow.hpp -class SavedWorkflowCache; -class SavedWorkflow; - -// Value.hpp -class BaseValue; -class BaseObjectValue; - -// Workflow.hpp -class WorkflowConnection; -class WorkflowNode; -class Workflow; -class WorkflowAssetList; diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp deleted file mode 100644 index 358fc49..0000000 --- a/core/src/Model/fwd.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "Model/Template/fwd.hpp" -#include "Model/Workflow/fwd.hpp" - -// Database.hpp -enum class TableKind; -class SalesTable; -class PurchasesTable; -class DeliveryTable; -class MainDatabase; - -// Assets.hpp -struct SavedAsset; -class Asset; -enum class NameSelectionError; -class AssetList; - -// Filter.hpp -class TableRowsFilter; - -// GlobalStates.hpp -class GlobalStates; - -// Items.hpp -template <class T> -class ItemList; -template <class TSelf> -class ItemBase; -class ProductItem; -class FactoryItem; -class CustomerItem; - -// Project.hpp -class Project; |