diff --git a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.cpp b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.cpp index 36cbee2..af6658d 100644 --- a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.cpp +++ b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.cpp @@ -3,6 +3,7 @@ #include "Active/Utility/Memory.h" #include "Active/Utility/String.h" #include "Speckle/Environment/Addon.h" +#include "Speckle/Event/Type/DocStoreMergeEvent.h" #include "Speckle/Utility/Guid.h" #include "Speckle/Utility/String.h" @@ -14,6 +15,7 @@ using namespace active::setting; using namespace speckle::database; using namespace speckle::environment; +using namespace speckle::event; using namespace speckle::utility; using enum DocumentStoreCore::Status; @@ -55,16 +57,27 @@ namespace { if (storeID.id) return true; //We must have a store if the ID is populated bool isStoreFound = false; -#ifdef ARCHICAD API_Guid acID; if (auto statusCode = convertArchicadError(ACAPI_AddOnObject_GetObjectGuidFromName(String{storeID.name}, &acID)); statusCode != nominal) throw std::system_error(DocumentStoreCore::makeError(statusCode)); storeID.id = Guid{acID}; isStoreFound = true; -#endif return isStoreFound; } //isExistingStore + + /*-------------------------------------------------------------------- + Copy a GS handle to a Memory object + + handle: The GS handle + memory: The Memory object to receive the data + --------------------------------------------------------------------*/ + void copyHandleToMemory(const GSHandle& handle, active::utility::Memory& memory) { + auto storeSize = BMGetHandleSize(handle); + memory.resize(storeSize); + active::utility::Memory::copy(memory.data(), *handle, storeSize, storeSize); + } //copyHandleToMemory + #endif ///Category for DocumentStore processing errors @@ -111,6 +124,30 @@ std::error_code DocumentStoreCore::makeError(DocumentStoreCore::Status code) { } //DocumentStoreCore::makeError +/*-------------------------------------------------------------------- + Handle a document merge operation + + event: The merge event + + return: True if the event should be closed + --------------------------------------------------------------------*/ +bool DocumentStoreCore::handle(const DocStoreMergeEvent& event) { +#ifdef ARCHICAD + if (event.objects == nullptr) + return false; + for (const auto& object : *event.objects) { + if (*object.name != String{m_id.name}) + continue; + active::utility::Memory toMerge; + copyHandleToMemory(object.data, toMerge); + mergeStore(toMerge); + writeStore(); + } +#endif + return false; +} //DocumentStoreCore::handle + + /*-------------------------------------------------------------------- Read the data stored in the document @@ -128,9 +165,7 @@ active::utility::Memory DocumentStoreCore::readStore() const { if (auto statusCode = convertArchicadError(ACAPI_AddOnObject_GetObjectContent(Guid{m_id.id}, &storeName, &storedData)); statusCode != nominal) throw std::system_error(makeError(statusCode)); //Copy the stored data into the result - auto storeSize = BMGetHandleSize(storedData); - result.resize(storeSize); - active::utility::Memory::copy(result.data(), *storedData, storeSize, storeSize); + copyHandleToMemory(storedData, result); BMKillHandle(&storedData); #endif return result; diff --git a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.h b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.h index 7a81aa1..57933d1 100644 --- a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.h +++ b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreCore.h @@ -5,6 +5,7 @@ #include "Active/Setting/SettingList.h" #include "Active/Database/Storage/DBaseSchema.h" #include "Active/Utility/NameID.h" +#include "Speckle/Event/Subscriber/DocStoreSubscriber.h" namespace speckle::database { @@ -13,7 +14,7 @@ namespace speckle::database { Currently implement for Archicad Add-On Objects */ - class DocumentStoreCore { + class DocumentStoreCore : public event::DocStoreSubscriber { public: // MARK: - Types @@ -54,6 +55,15 @@ namespace speckle::database { */ const active::utility::NameID& getID() const { return m_id; } + // MARK: - Functions (mutating) + + /*! + Handle a document merge operation + @param event The merge event + @return True if the event should be closed + */ + bool handle(const event::DocStoreMergeEvent& event) override; + protected: /*! Read the data stored in the document (should be lazy-loading, only at the point where data is actually requested) @@ -73,9 +83,8 @@ namespace speckle::database { /*! Merge existing stored data with incoming stored data (from an external source) @param toMerge The external stored data to merge - @return The merged data to be stored */ - virtual active::utility::Memory mergeStore(const active::utility::Memory& toMerge) = 0; + virtual void mergeStore(const active::utility::Memory& toMerge) = 0; /*! Reset the stored data (some external change has invalidated previous data, e.g. the document was closed) */ diff --git a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h index 663d8ab..9231487 100644 --- a/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h +++ b/SpeckleLib/Speckle/Database/Storage/DocumentStore/DocumentStoreEngine.h @@ -120,7 +120,7 @@ namespace speckle::database { @param toMerge The external stored data to merge @return The merged data to be stored */ - active::utility::Memory mergeStore(const active::utility::Memory& toMerge) override; + void mergeStore(const active::utility::Memory& toMerge) override; /*! Reset the stored data (some external change has invalidated previous data, e.g. the document was closed) */ @@ -266,7 +266,7 @@ namespace speckle::database { --------------------------------------------------------------------*/ template requires DocumentStorable - active::utility::Memory DocumentStoreEngine::mergeStore(const active::utility::Memory& toMerge) { + void DocumentStoreEngine::mergeStore(const active::utility::Memory& toMerge) { //Import the incoming records from the data to merge Cache incoming; Transport().receive(std::forward(incoming), active::serialise::Identity{}, toMerge); @@ -275,7 +275,6 @@ namespace speckle::database { if (!cache) cache = std::make_unique(); //We still want to export an empty cache object even if it contains no records cache->merge(incoming); - return buildStore(); } //DocumentStoreEngine::mergeStore } diff --git a/SpeckleLib/Speckle/Event/Subscriber/DocStoreSubscriber.cpp b/SpeckleLib/Speckle/Event/Subscriber/DocStoreSubscriber.cpp new file mode 100644 index 0000000..00704da --- /dev/null +++ b/SpeckleLib/Speckle/Event/Subscriber/DocStoreSubscriber.cpp @@ -0,0 +1,88 @@ +#include "Speckle/Event/Subscriber/DocStoreSubscriber.h" + +#include "Speckle/Environment/Addon.h" +#include "Speckle/Database/Identity/Link.h" +#include "Speckle/Event/Type/DocStoreMergeEvent.h" + +#ifdef ARCHICAD +#include +#endif + +using namespace active::environment; +using namespace active::event; +using namespace speckle::database; +using namespace speckle::environment; +using namespace speckle::event; + +namespace { + +#ifdef ARCHICAD + /*! + Callback for an Archicad document merge operation + @param sourceObjects The source document objects to merge + */ + GSErrCode __ACENV_CALL docMergeCallback(const GS::Array& sourceObjects) { + if (addon() != nullptr) + addon()->publishExternal(DocStoreMergeEvent{sourceObjects}); + return NoError; + } +#endif + +} + + //True if a doc merge subscriber has already started (only one is required - there are no variants) +bool speckle::event::DocStoreSubscriber::m_isStarted = false; + +/*-------------------------------------------------------------------- + Get the event subscription list + + return: The subscription list (an empty list will put the subscriber into a suspended state) + --------------------------------------------------------------------*/ +Subscriber::Subscription DocStoreSubscriber::subscription() const { + return { {DocStoreMergeEvent::ID} }; +} //DocStoreSubscriber::subscription + + +/*-------------------------------------------------------------------- + Receive a subscribed event + + event: The incoming event + + return: True if the event should be closed + --------------------------------------------------------------------*/ +bool DocStoreSubscriber::receive(const Event& event) { + //Pass a menu event to the specified handler function + if (auto selectEvent = dynamic_cast(&event); selectEvent != nullptr) + return handle(*selectEvent); + return false; +} //DocStoreSubscriber::receive + + +/*-------------------------------------------------------------------- + Attach participant components to the app (as required) + + return: True if the participant is able to function + --------------------------------------------------------------------*/ +bool DocStoreSubscriber::attach() { +#ifdef ARCHICAD + ACAPI_AddOnObject_RegisterAddOnObjectHandler(); +#endif + return true; +} //DocStoreSubscriber::attach + + +/*-------------------------------------------------------------------- + Start the participant operation + + return: True if the participant is able to continue + --------------------------------------------------------------------*/ +bool DocStoreSubscriber::start() { + if (m_isStarted) + return true; + m_isStarted = true; +#ifdef ARCHICAD + return (ACAPI_AddOnObject_InstallAddOnObjectMergeHandler(docMergeCallback) == NoError); +#else + return false; +#endif +} //DocStoreSubscriber::start diff --git a/SpeckleLib/Speckle/Event/Subscriber/DocStoreSubscriber.h b/SpeckleLib/Speckle/Event/Subscriber/DocStoreSubscriber.h new file mode 100644 index 0000000..d14e84c --- /dev/null +++ b/SpeckleLib/Speckle/Event/Subscriber/DocStoreSubscriber.h @@ -0,0 +1,74 @@ +#ifndef SPECKLE_EVENT_DOC_STORE_SUBSCRIBER +#define SPECKLE_EVENT_DOC_STORE_SUBSCRIBER + +#include "Active/Event/Subscriber.h" + +namespace speckle::event { + + class DocStoreMergeEvent; + + /*! + Base class for subscribers responding to document merge operations (notably those managing custom data in the document) + */ + class DocStoreSubscriber : public active::event::Subscriber { + public: + + // MARK: - Constructors + + /*! + Default constructor + */ + DocStoreSubscriber() = default; + /*! + Copy constructor + @param source The object to copy + */ + DocStoreSubscriber(const DocStoreSubscriber& source) = default; + /*! + Destructor + */ + virtual ~DocStoreSubscriber() {} + + // MARK: - Functions (const) + + /*! + Get the event subscription list + @return The subscription list (an empty list will put the subscriber into a suspended state) + */ + Subscription subscription() const override; + + // MARK: - Functions (mutating) + + /*! + Receive a subscribed event + @param event The incoming event + @return True if the event should be closed + */ + bool receive(const active::event::Event& event) override; + + protected: + /*! + Attach participant components to the app (as required) + @return True if the participant is able to function + */ + bool attach() override; + /*! + Start the participant operation + @return True if the participant is able to continue + */ + bool start() override; + /*! + Handle a document merge operation + @param event The merge event + @return True if the event should be closed + */ + virtual bool handle(const DocStoreMergeEvent& event) = 0; + + private: + ///True if a doc merge subscriber has already started (only one is required - there are no variants) + static bool m_isStarted; + }; + +} + +#endif //SPECKLE_EVENT_DOC_STORE_SUBSCRIBER diff --git a/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.cpp b/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.cpp index 955c265..576b4df 100644 --- a/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.cpp +++ b/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.cpp @@ -4,6 +4,10 @@ #include "Speckle/Database/Identity/Link.h" #include "Speckle/Event/Type/SelectionEvent.h" +#ifdef ARCHICAD +#include +#endif + using namespace active::environment; using namespace active::event; using namespace speckle::database; @@ -28,6 +32,9 @@ namespace { } + //True if a selection change subscriber has already started (only one is required - there are no variants) +bool speckle::event::SelectionSubscriber::m_isStarted = false; + /*-------------------------------------------------------------------- Get the event subscription list @@ -59,6 +66,9 @@ bool SelectionSubscriber::receive(const Event& event) { return: True if the participant is able to continue --------------------------------------------------------------------*/ bool SelectionSubscriber::start() { + if (m_isStarted) + return true; + m_isStarted = true; #ifdef ARCHICAD return (ACAPI_Notification_CatchSelectionChange(selectionCallback) == NoError); #else diff --git a/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h b/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h index 199184f..56314dd 100644 --- a/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h +++ b/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h @@ -3,16 +3,12 @@ #include "Active/Event/Subscriber.h" -#ifdef ARCHICAD -#include -#endif - namespace speckle::event { class SelectionEvent; /*! - Base class for subscribers responding to selectionm changes + Base class for subscribers responding to selection changes */ class SelectionSubscriber : public active::event::Subscriber { public: @@ -65,7 +61,7 @@ namespace speckle::event { private: ///True if a selection change subscriber has already started (only one is required - there are no variants) - int32_t m_isStarted = false; + static bool m_isStarted; }; } diff --git a/SpeckleLib/Speckle/Event/Type/DocStoreMergeEvent.h b/SpeckleLib/Speckle/Event/Type/DocStoreMergeEvent.h new file mode 100644 index 0000000..e944c5a --- /dev/null +++ b/SpeckleLib/Speckle/Event/Type/DocStoreMergeEvent.h @@ -0,0 +1,43 @@ +#ifndef SPECKLE_EVENT_DOC_STORE_MERGE_EVENT +#define SPECKLE_EVENT_DOC_STORE_MERGE_EVENT + +#include "Active/Event/Event.h" + +#include "Active/Utility/Guid.h" +#include "Active/Utility/String.h" +#include "Speckle/Database/Identity/Link.h" + +namespace speckle::event { + + /*! + Class representing a selection change event + */ + class DocStoreMergeEvent : public active::event::Event { + public: + + static const inline active::utility::NameID ID{active::utility::String{"document store merge"}, + active::utility::Guid{active::utility::String{"c92e2c51-d47c-44e3-a54f-5068dccaa35a"}}}; + + // MARK: - Constructors + + /*! + Default constructor + */ + DocStoreMergeEvent() : Event{ID} {} +#ifdef ARCHICAD + /*! + Constructor + @param incoming Incoming document objects to merge + */ + DocStoreMergeEvent(const GS::Array& incoming) : Event{ID} { objects = &incoming; } +#endif + +#ifdef ARCHICAD + //Incoming document objects to merge + const GS::Array* objects = nullptr; +#endif + }; + +} + +#endif //SPECKLE_EVENT_DOC_STORE_MERGE_EVENT diff --git a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj index f6d024e..0846643 100644 --- a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj +++ b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ 21D0BDB32C8F8AB60077E104 /* DocumentStoreCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21D0BDAC2C8F8AB60077E104 /* DocumentStoreCore.cpp */; }; 21D0BDB42C8F8AB60077E104 /* DocumentStoreCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 21D0BDAD2C8F8AB60077E104 /* DocumentStoreCore.h */; }; 21D0BDB52C8F8AB60077E104 /* DocumentStoreEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 21D0BDAE2C8F8AB60077E104 /* DocumentStoreEngine.h */; }; + 21D0BDBD2C90F2830077E104 /* DocStoreSubscriber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21D0BDBB2C90F2830077E104 /* DocStoreSubscriber.cpp */; }; + 21D0BDBF2C90F36B0077E104 /* DocStoreMergeEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 21D0BDBE2C90F36B0077E104 /* DocStoreMergeEvent.h */; }; 21F69F3B2C6B880C008B6A06 /* JSBaseTransport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F382C6B880B008B6A06 /* JSBaseTransport.cpp */; }; 21F69F512C6CCC25008B6A06 /* BrowserBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F4A2C6CCC25008B6A06 /* BrowserBridge.cpp */; }; 21F69F612C6D0286008B6A06 /* GetBindingsMethodNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F602C6D0286008B6A06 /* GetBindingsMethodNames.cpp */; }; @@ -138,6 +140,9 @@ 21D0BDAC2C8F8AB60077E104 /* DocumentStoreCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocumentStoreCore.cpp; sourceTree = ""; }; 21D0BDAD2C8F8AB60077E104 /* DocumentStoreCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocumentStoreCore.h; sourceTree = ""; }; 21D0BDAE2C8F8AB60077E104 /* DocumentStoreEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocumentStoreEngine.h; sourceTree = ""; }; + 21D0BDBA2C90F2830077E104 /* DocStoreSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocStoreSubscriber.h; sourceTree = ""; }; + 21D0BDBB2C90F2830077E104 /* DocStoreSubscriber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocStoreSubscriber.cpp; sourceTree = ""; }; + 21D0BDBE2C90F36B0077E104 /* DocStoreMergeEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocStoreMergeEvent.h; sourceTree = ""; }; 21F69F012C66C229008B6A06 /* Doxyfile */ = {isa = PBXFileReference; lastKnownFileType = text; name = Doxyfile; path = Documentation/Doxyfile; sourceTree = ""; }; 21F69F192C6A0FE2008B6A06 /* JSBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBinding.h; sourceTree = ""; }; 21F69F352C6AA9B3008B6A06 /* JSFunction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSFunction.h; sourceTree = ""; }; @@ -254,6 +259,8 @@ 2193517A2C624FC100E5A69C /* Subscriber */ = { isa = PBXGroup; children = ( + 21D0BDBB2C90F2830077E104 /* DocStoreSubscriber.cpp */, + 21D0BDBA2C90F2830077E104 /* DocStoreSubscriber.h */, 219351782C624FC100E5A69C /* MenuSubscriber.cpp */, 219351792C624FC100E5A69C /* MenuSubscriber.h */, 219351992C6278D900E5A69C /* SelectionSubscriber.cpp */, @@ -265,6 +272,7 @@ 2193518A2C62655700E5A69C /* Type */ = { isa = PBXGroup; children = ( + 21D0BDBE2C90F36B0077E104 /* DocStoreMergeEvent.h */, 219351892C62655700E5A69C /* MenuEvent.h */, 2193519C2C627E3100E5A69C /* SelectionEvent.h */, ); @@ -462,6 +470,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 21D0BDBF2C90F36B0077E104 /* DocStoreMergeEvent.h in Headers */, 21D0BD212C86F0280077E104 /* AccountDatabase.h in Headers */, 210CC86F2C7E879700610F58 /* ArgumentBase.h in Headers */, 210CC8A02C81E34400610F58 /* Platform.h in Headers */, @@ -608,6 +617,7 @@ 21F69F812C6FF3B0008B6A06 /* BridgeArgumentWrap.cpp in Sources */, 2193517B2C624FC100E5A69C /* MenuSubscriber.cpp in Sources */, 21F69F612C6D0286008B6A06 /* GetBindingsMethodNames.cpp in Sources */, + 21D0BDBD2C90F2830077E104 /* DocStoreSubscriber.cpp in Sources */, 21D0BDB32C8F8AB60077E104 /* DocumentStoreCore.cpp in Sources */, 21F93AEC2B2F406E009A2C5B /* Addon.cpp in Sources */, 21D0BD4E2C8901A00077E104 /* ServerInfo.cpp in Sources */,