summaryrefslogtreecommitdiff
path: root/core/src/Model
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-06-27 18:27:13 -0700
committerrtk0c <[email protected]>2022-06-27 18:27:13 -0700
commit8f0dda5eab181b0f14f2652b4e984aaaae3f258c (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /core/src/Model
parentfad6a88a13ab1f888ab25ad0aae19c1d63aa0623 (diff)
Start from a clean slate
Diffstat (limited to 'core/src/Model')
-rw-r--r--core/src/Model/Assets.cpp306
-rw-r--r--core/src/Model/Assets.hpp130
-rw-r--r--core/src/Model/Database.cpp163
-rw-r--r--core/src/Model/Database.hpp79
-rw-r--r--core/src/Model/Filter.cpp1
-rw-r--r--core/src/Model/Filter.hpp6
-rw-r--r--core/src/Model/GlobalStates.cpp163
-rw-r--r--core/src/Model/GlobalStates.hpp55
-rw-r--r--core/src/Model/Items.cpp114
-rw-r--r--core/src/Model/Items.hpp253
-rw-r--r--core/src/Model/Project.cpp168
-rw-r--r--core/src/Model/Project.hpp57
-rw-r--r--core/src/Model/Template/TableTemplate.cpp591
-rw-r--r--core/src/Model/Template/TableTemplate.hpp223
-rw-r--r--core/src/Model/Template/TableTemplateIterator.cpp52
-rw-r--r--core/src/Model/Template/TableTemplateIterator.hpp35
-rw-r--r--core/src/Model/Template/Template.hpp68
-rw-r--r--core/src/Model/Template/Template_Main.cpp214
-rw-r--r--core/src/Model/Template/Template_RTTI.cpp29
-rw-r--r--core/src/Model/Template/fwd.hpp11
-rw-r--r--core/src/Model/Workflow/Evaluation.cpp174
-rw-r--r--core/src/Model/Workflow/Evaluation.hpp67
-rw-r--r--core/src/Model/Workflow/Nodes/DocumentNodes.cpp18
-rw-r--r--core/src/Model/Workflow/Nodes/DocumentNodes.hpp13
-rw-r--r--core/src/Model/Workflow/Nodes/NumericNodes.cpp94
-rw-r--r--core/src/Model/Workflow/Nodes/NumericNodes.hpp44
-rw-r--r--core/src/Model/Workflow/Nodes/TextNodes.cpp231
-rw-r--r--core/src/Model/Workflow/Nodes/TextNodes.hpp53
-rw-r--r--core/src/Model/Workflow/Nodes/UserInputNodes.cpp32
-rw-r--r--core/src/Model/Workflow/Nodes/UserInputNodes.hpp23
-rw-r--r--core/src/Model/Workflow/Nodes/fwd.hpp15
-rw-r--r--core/src/Model/Workflow/Value.hpp94
-rw-r--r--core/src/Model/Workflow/ValueInternals.hpp21
-rw-r--r--core/src/Model/Workflow/Value_Main.cpp35
-rw-r--r--core/src/Model/Workflow/Value_RTTI.cpp174
-rw-r--r--core/src/Model/Workflow/Values/Basic.cpp111
-rw-r--r--core/src/Model/Workflow/Values/Basic.hpp67
-rw-r--r--core/src/Model/Workflow/Values/Database.cpp88
-rw-r--r--core/src/Model/Workflow/Values/Database.hpp51
-rw-r--r--core/src/Model/Workflow/Values/Dictionary.cpp49
-rw-r--r--core/src/Model/Workflow/Values/Dictionary.hpp25
-rw-r--r--core/src/Model/Workflow/Values/List.cpp100
-rw-r--r--core/src/Model/Workflow/Values/List.hpp50
-rw-r--r--core/src/Model/Workflow/Values/fwd.hpp17
-rw-r--r--core/src/Model/Workflow/Workflow.hpp316
-rw-r--r--core/src/Model/Workflow/Workflow_Main.cpp846
-rw-r--r--core/src/Model/Workflow/Workflow_RTTI.cpp143
-rw-r--r--core/src/Model/Workflow/fwd.hpp22
-rw-r--r--core/src/Model/fwd.hpp35
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;