From 8f0dda5eab181b0f14f2652b4e984aaaae3f258c Mon Sep 17 00:00:00 2001 From: rtk0c Date: Mon, 27 Jun 2022 18:27:13 -0700 Subject: Start from a clean slate --- core/src/Model/Assets.cpp | 306 -------- core/src/Model/Assets.hpp | 130 ---- core/src/Model/Database.cpp | 163 ----- core/src/Model/Database.hpp | 79 -- core/src/Model/Filter.cpp | 1 - core/src/Model/Filter.hpp | 6 - core/src/Model/GlobalStates.cpp | 163 ----- core/src/Model/GlobalStates.hpp | 55 -- core/src/Model/Items.cpp | 114 --- core/src/Model/Items.hpp | 253 ------- core/src/Model/Project.cpp | 168 ----- core/src/Model/Project.hpp | 57 -- core/src/Model/Template/TableTemplate.cpp | 591 --------------- core/src/Model/Template/TableTemplate.hpp | 223 ------ core/src/Model/Template/TableTemplateIterator.cpp | 52 -- core/src/Model/Template/TableTemplateIterator.hpp | 35 - core/src/Model/Template/Template.hpp | 68 -- core/src/Model/Template/Template_Main.cpp | 214 ------ core/src/Model/Template/Template_RTTI.cpp | 29 - core/src/Model/Template/fwd.hpp | 11 - core/src/Model/Workflow/Evaluation.cpp | 174 ----- core/src/Model/Workflow/Evaluation.hpp | 67 -- core/src/Model/Workflow/Nodes/DocumentNodes.cpp | 18 - core/src/Model/Workflow/Nodes/DocumentNodes.hpp | 13 - core/src/Model/Workflow/Nodes/NumericNodes.cpp | 94 --- core/src/Model/Workflow/Nodes/NumericNodes.hpp | 44 -- core/src/Model/Workflow/Nodes/TextNodes.cpp | 231 ------ core/src/Model/Workflow/Nodes/TextNodes.hpp | 53 -- core/src/Model/Workflow/Nodes/UserInputNodes.cpp | 32 - core/src/Model/Workflow/Nodes/UserInputNodes.hpp | 23 - core/src/Model/Workflow/Nodes/fwd.hpp | 15 - core/src/Model/Workflow/Value.hpp | 94 --- core/src/Model/Workflow/ValueInternals.hpp | 21 - core/src/Model/Workflow/Value_Main.cpp | 35 - core/src/Model/Workflow/Value_RTTI.cpp | 174 ----- core/src/Model/Workflow/Values/Basic.cpp | 111 --- core/src/Model/Workflow/Values/Basic.hpp | 67 -- core/src/Model/Workflow/Values/Database.cpp | 88 --- core/src/Model/Workflow/Values/Database.hpp | 51 -- core/src/Model/Workflow/Values/Dictionary.cpp | 49 -- core/src/Model/Workflow/Values/Dictionary.hpp | 25 - core/src/Model/Workflow/Values/List.cpp | 100 --- core/src/Model/Workflow/Values/List.hpp | 50 -- core/src/Model/Workflow/Values/fwd.hpp | 17 - core/src/Model/Workflow/Workflow.hpp | 316 -------- core/src/Model/Workflow/Workflow_Main.cpp | 846 ---------------------- core/src/Model/Workflow/Workflow_RTTI.cpp | 143 ---- core/src/Model/Workflow/fwd.hpp | 22 - core/src/Model/fwd.hpp | 35 - 49 files changed, 5726 deletions(-) delete mode 100644 core/src/Model/Assets.cpp delete mode 100644 core/src/Model/Assets.hpp delete mode 100644 core/src/Model/Database.cpp delete mode 100644 core/src/Model/Database.hpp delete mode 100644 core/src/Model/Filter.cpp delete mode 100644 core/src/Model/Filter.hpp delete mode 100644 core/src/Model/GlobalStates.cpp delete mode 100644 core/src/Model/GlobalStates.hpp delete mode 100644 core/src/Model/Items.cpp delete mode 100644 core/src/Model/Items.hpp delete mode 100644 core/src/Model/Project.cpp delete mode 100644 core/src/Model/Project.hpp delete mode 100644 core/src/Model/Template/TableTemplate.cpp delete mode 100644 core/src/Model/Template/TableTemplate.hpp delete mode 100644 core/src/Model/Template/TableTemplateIterator.cpp delete mode 100644 core/src/Model/Template/TableTemplateIterator.hpp delete mode 100644 core/src/Model/Template/Template.hpp delete mode 100644 core/src/Model/Template/Template_Main.cpp delete mode 100644 core/src/Model/Template/Template_RTTI.cpp delete mode 100644 core/src/Model/Template/fwd.hpp delete mode 100644 core/src/Model/Workflow/Evaluation.cpp delete mode 100644 core/src/Model/Workflow/Evaluation.hpp delete mode 100644 core/src/Model/Workflow/Nodes/DocumentNodes.cpp delete mode 100644 core/src/Model/Workflow/Nodes/DocumentNodes.hpp delete mode 100644 core/src/Model/Workflow/Nodes/NumericNodes.cpp delete mode 100644 core/src/Model/Workflow/Nodes/NumericNodes.hpp delete mode 100644 core/src/Model/Workflow/Nodes/TextNodes.cpp delete mode 100644 core/src/Model/Workflow/Nodes/TextNodes.hpp delete mode 100644 core/src/Model/Workflow/Nodes/UserInputNodes.cpp delete mode 100644 core/src/Model/Workflow/Nodes/UserInputNodes.hpp delete mode 100644 core/src/Model/Workflow/Nodes/fwd.hpp delete mode 100644 core/src/Model/Workflow/Value.hpp delete mode 100644 core/src/Model/Workflow/ValueInternals.hpp delete mode 100644 core/src/Model/Workflow/Value_Main.cpp delete mode 100644 core/src/Model/Workflow/Value_RTTI.cpp delete mode 100644 core/src/Model/Workflow/Values/Basic.cpp delete mode 100644 core/src/Model/Workflow/Values/Basic.hpp delete mode 100644 core/src/Model/Workflow/Values/Database.cpp delete mode 100644 core/src/Model/Workflow/Values/Database.hpp delete mode 100644 core/src/Model/Workflow/Values/Dictionary.cpp delete mode 100644 core/src/Model/Workflow/Values/Dictionary.hpp delete mode 100644 core/src/Model/Workflow/Values/List.cpp delete mode 100644 core/src/Model/Workflow/Values/List.hpp delete mode 100644 core/src/Model/Workflow/Values/fwd.hpp delete mode 100644 core/src/Model/Workflow/Workflow.hpp delete mode 100644 core/src/Model/Workflow/Workflow_Main.cpp delete mode 100644 core/src/Model/Workflow/Workflow_RTTI.cpp delete mode 100644 core/src/Model/Workflow/fwd.hpp delete mode 100644 core/src/Model/fwd.hpp (limited to 'core/src/Model') 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 -#include -#include -#include -#include -#include - -using namespace std::literals::string_view_literals; -namespace fs = std::filesystem; - -template -void OperateStreamForSavedAsset(TSavedAsset& cell, TStream& proxy) -{ - proxy.template ObjectAdapted(cell.Name); - proxy.template ObjectAdapted(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 Assets; - tsl::array_map> 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() } -{ - 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& 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 AssetList::CreateAndLoad(SavedAsset assetIn) -{ - auto& savedAsset = Create(std::move(assetIn)); - auto asset = std::unique_ptr(CreateInstance(savedAsset)); - return asset; -} - -std::unique_ptr AssetList::Load(std::string_view name) const -{ - if (auto savedAsset = FindByName(name)) { - auto asset = Load(*savedAsset); - return asset; - } else { - return nullptr; - } -} - -std::unique_ptr AssetList::Load(const SavedAsset& asset) const -{ - return std::unique_ptr(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& 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& callback, const fs::path& containerDir, const std::function& 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 -#include -#include -#include -#include - -/// 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 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& GetAssets() const; - - const SavedAsset* FindByName(std::string_view name) const; - const SavedAsset& Create(SavedAsset asset); - std::unique_ptr CreateAndLoad(SavedAsset asset); - /// Load the asset on disk by its name. - std::unique_ptr 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 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& callback) const = 0; - - // Helper - void DiscoverFilesByExtension(const std::function& callback, const std::filesystem::path& containerDir, std::string_view extension) const; - void DiscoverFilesByHeader(const std::function& callback, const std::filesystem::path& containerDir, const std::function& 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 AssetListTyped : public AssetList -{ -public: - using AssetList::AssetList; - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "HidingNonVirtualFunction" - std::unique_ptr CreateAndLoad(SavedAsset asset) - { - return std::unique_ptr(static_cast(AssetList::CreateAndLoad(asset).release())); - } - - std::unique_ptr Load(std::string_view name) const - { - return std::unique_ptr(static_cast(AssetList::Load(name).release())); - } - - std::unique_ptr Load(const SavedAsset& asset) const - { - return std::unique_ptr(static_cast(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 -#include - -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 -#include -#include - -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 -#include -#include -#include -#include -#include -#include - -namespace fs = std::filesystem; - -static std::unique_ptr globalStateInstance; -static fs::path globalDataPath; - -void GlobalStates::Init() -{ - Init(StandardDirectories::UserData() / "cplt"); -} - -void GlobalStates::Init(std::filesystem::path userDataDir) -{ - globalStateInstance = std::make_unique(); - 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::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) -{ - 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 -#include -#include - -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 mRecentProjects; - std::unique_ptr mCurrentProject; - mutable bool mDirty = false; - -public: - const std::vector& 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); - - // 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -template -class ItemList -{ -private: - std::vector mStorage; - tsl::array_map mNameLookup; - -public: - template - 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)...); - 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)...); - 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 - friend class ItemBase; - - void UpdateItemName(const T& item, const std::string& newName) - { - mNameLookup.erase(item.GetName()); - mNameLookup.insert(newName, item.GetId()); - } -}; - -template -class ItemBase -{ -private: - ItemList* mList; - size_t mId; - std::string mName; - -public: - ItemBase(ItemList& list, size_t id = std::numeric_limits::max(), std::string name = "") - : mList{ &list } - , mId{ id } - , mName{ std::move(name) } - { - } - - bool IsInvalid() const - { - return mId == std::numeric_limits::max(); - } - - ItemList& 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(*this), name); - mName = std::move(name); - } -}; - -class ProductItem : public ItemBase -{ -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 -{ -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 -{ -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 -#include -#include -#include -#include -#include -#include - -namespace fs = std::filesystem; - -template -static void ReadItemList(ItemList& list, const fs::path& filePath) -{ - std::ifstream ifs(filePath); - if (ifs) { - Json::Value root; - ifs >> root; - - list = ItemList(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 -static void WriteItemList(ItemList& 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 -#include -#include -#include -#include - -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 Products; - ItemList Factories; - ItemList 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 -#include -#include -#include -#include -#include -#include - -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 -void OperateStreamForTableCell(TTableCell& cell, TStream& proxy) -{ - proxy.template ObjectAdapted(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 -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 -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 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(const_cast(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 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 - 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 - static void OperateStream(TTableTemplate& table, TProxy& proxy) - { - proxy.template ObjectAdapted>(table.mColumnWidths); - proxy.template ObjectAdapted>(table.mRowHeights); - proxy.template ObjectAdapted>(table.mCells); - proxy.template ObjectAdapted>(table.mArrayGroups); - proxy.template ObjectAdapted>(table.mName2Parameters); - proxy.template ObjectAdapted>(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 -#include -#include -#include -#include - -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 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 SingularCells; - - using ArrayGroupRow = std::vector; - using ArrayGroupData = std::vector; - std::vector 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 mName2Parameters; - /// Map from array group name to the index of the array group (stored in mArrayGroups). - tsl::array_map mName2ArrayGroups; - std::vector mCells; - std::vector mArrayGroups; - std::vector mRowHeights; - std::vector 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); - ///
    - ///
  • In case of becoming a SingularParametricCell: the parameter name is filled with TableCell::Content. - ///
  • 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. - ///
- 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 - -class TableSingleParamsIter -{ -private: - TableTemplate* mTemplate; - tsl::array_map::iterator mIter; - -public: - TableSingleParamsIter(TableTemplate& tmpl); - - bool HasNext() const; - TableCell& Next(); -}; - -class TableArrayGroupsIter -{ -private: - TableTemplate* mTemplate; - tsl::array_map::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 -#include -#include -#include - -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