diff --git a/SpeckleConnector/Connector.vcxproj b/SpeckleConnector/Connector.vcxproj index 9ac1fc7..acf33d4 100644 --- a/SpeckleConnector/Connector.vcxproj +++ b/SpeckleConnector/Connector.vcxproj @@ -111,6 +111,9 @@ + + + @@ -163,6 +166,9 @@ + + + diff --git a/SpeckleConnector/Connector.vcxproj.filters b/SpeckleConnector/Connector.vcxproj.filters index e879d95..374c759 100644 --- a/SpeckleConnector/Connector.vcxproj.filters +++ b/SpeckleConnector/Connector.vcxproj.filters @@ -65,6 +65,12 @@ {6693f9a9-5ece-4853-b008-4064d1c551ab} + + {806f4af5-fa02-49b8-ac01-297991fe90ea} + + + {8bb3df60-affe-4b66-8d78-f1b98e6ba8df} + @@ -228,6 +234,15 @@ Connector\Record\Collection + + Connector\Interface\Browser\Bridge\Selection + + + Connector\Interface\Browser\Bridge\Selection + + + Connector\Interface\Browser\Bridge\Selection\Arg + @@ -387,5 +402,14 @@ Connector\Record\Collection + + Connector\Interface\Browser\Bridge\Selection + + + Connector\Interface\Browser\Bridge\Selection + + + Connector\Interface\Browser\Bridge\Selection\Arg + \ No newline at end of file diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.cpp new file mode 100644 index 0000000..94d5453 --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.cpp @@ -0,0 +1,92 @@ +#include "Active/Serialise/Item/Wrapper/ValueWrap.h" +#include "Active/Serialise/Package/Wrapper/ContainerWrap.h" +#include "Connector/Connector.h" +#include "Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h" +#include "Speckle/Database/BIMElementDatabase.h" +#include "Speckle/Environment/Project.h" +#include "Speckle/Record/Element/Element.h" + +#include + +using namespace active::serialise; +using namespace connector::interfac::browser::bridge; +using namespace speckle::record::element; + +namespace { + + ///Serialisation fields + enum FieldIndex { + selectedObjectIdsID, + summaryID, + }; + + ///Serialisation field IDs + static std::array fieldID = { + Identity{"selectedObjectIds"}, + Identity{"summary"}, + }; + +} + +SelectionInfo::SelectionInfo() { + initialize(); +} + +void SelectionInfo::initialize() { + auto project = connector()->getActiveProject().lock(); + if (!project) { + // TODO: is thi OK? + return; + } + + auto elementDatabase = project->getElementDatabase(); + auto selected = elementDatabase->getSelection(); + + active::utility::String summary(selected.size()); + summary += " objects selected."; + m_summary = summary; + + for (const auto& link : selected) { + m_selectedElementIds.push_back(link); + } +} + +/*-------------------------------------------------------------------- + Fill an inventory with the package items + + inventory: The inventory to receive the package items + + return: True if the package has added items to the inventory + --------------------------------------------------------------------*/ +bool SelectionInfo::fillInventory(Inventory& inventory) const { + using enum Entry::Type; + inventory.merge(Inventory{ + { + { fieldID[selectedObjectIdsID], selectedObjectIdsID, element }, + { fieldID[summaryID], summaryID, element }, + }, + }.withType(&typeid(SelectionInfo))); + return true; +} //SelectionInfo::fillInventory + + +/*-------------------------------------------------------------------- + Get the specified cargo + + item: The inventory item to retrieve + + return: The requested cargo (nullptr on failure) + --------------------------------------------------------------------*/ +Cargo::Unique SelectionInfo::getCargo(const Inventory::Item& item) const { + if (item.ownerType != &typeid(SelectionInfo)) + return nullptr; + using namespace active::serialise; + switch (item.index) { + case selectedObjectIdsID: + return std::make_unique>>(m_selectedElementIds); + case summaryID: + return std::make_unique>(m_summary); + default: + return nullptr; //Requested an unknown index + } +} //SelectionInfo::getCargo diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h new file mode 100644 index 0000000..e10bd6f --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h @@ -0,0 +1,49 @@ +#ifndef CONNECTOR_INTERFACE_BRIDGE_SELECTION_INFO +#define CONNECTOR_INTERFACE_BRIDGE_SELECTION_INFO + +#include "Active/Serialise/Package/Package.h" + +namespace connector::interfac::browser::bridge { + + /*! + Configuration settings class + */ + class SelectionInfo : public active::serialise::Package { + public: + + // MARK: - Types + + using base = active::serialise::Package; + + // MARK: - Constructors + + /*! + Default constructor + */ + SelectionInfo(); + + active::utility::String m_summary = "No objects selected"; + std::vector m_selectedElementIds; + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + + private: + + void initialize(); + }; +} + +#endif //CONNECTOR_INTERFACE_BRIDGE_SELECTION_INFO diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/GetSelection.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/GetSelection.cpp new file mode 100644 index 0000000..37f918a --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/GetSelection.cpp @@ -0,0 +1,23 @@ +#include "Active/Serialise/CargoHold.h" +#include "Connector/Interface/Browser/Bridge/Selection/GetSelection.h" +#include "Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h" + +using namespace active::serialise; +using namespace connector::interfac::browser::bridge; + +/*-------------------------------------------------------------------- + Default constructor + --------------------------------------------------------------------*/ +GetSelection::GetSelection() : BridgeMethod{"GetSelection", [&]() { + return run(); +}} {} + + +/*-------------------------------------------------------------------- + Get the current selection info + based on the ArchiCAD mdoel selection + --------------------------------------------------------------------*/ +std::unique_ptr GetSelection::run() const { + auto selectionInfo = std::make_unique(); + return std::make_unique>(std::move(selectionInfo)); +} //GetSelection::run diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/GetSelection.h b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/GetSelection.h new file mode 100644 index 0000000..66e827f --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/GetSelection.h @@ -0,0 +1,30 @@ +#ifndef CONNECTOR_INTERFACE_BRIDGE_GETSELECTION +#define CONNECTOR_INTERFACE_BRIDGE_GETSELECTION + +#include "Speckle/Interface/Browser/Bridge/BridgeMethod.h" + +namespace connector::interfac::browser::bridge { + + class GetSelection : public speckle::interfac::browser::bridge::BridgeMethod { + public: + + // MARK: - Constructors + + /*! + Constructor + @param bridge The parent bridge object (provides access to bridge methods) + */ + GetSelection(); + + // MARK: - Functions (const) + + /*! + Get the current selection info + based on the ArchiCAD mdoel selection + */ + std::unique_ptr run() const; + }; + +} + +#endif //CONNECTOR_INTERFACE_BRIDGE_GETSELECTION diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/SelectionBridge.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/SelectionBridge.cpp new file mode 100644 index 0000000..33ce06b --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/SelectionBridge.cpp @@ -0,0 +1,27 @@ +#include "Active/Serialise/CargoHold.h" +#include "Connector/Interface/Browser/Bridge/Selection/SelectionBridge.h" +#include "Connector/Interface/Browser/Bridge/Selection/GetSelection.h" +#include "Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h" + +using namespace active::serialise; +using namespace connector::interfac::browser::bridge; + +/*-------------------------------------------------------------------- + Default constructor + --------------------------------------------------------------------*/ +SelectionBridge::SelectionBridge() : BrowserBridge{"selectionBinding"} { + //Add bridge methods + addMethod(); +} //SelectionBridge::SelectionBridge + +/*! +Handle the menu selection +@param event The selection event +@return True if the event should be closed +*/ +bool SelectionBridge::handle(const speckle::event::SelectionEvent& event) { + auto selectionInfo = std::make_unique(); + auto wrapped = std::make_unique>(std::move(selectionInfo)); + sendEvent("setSelection", std::move(wrapped)); + return true; +} //SelectionBridge::handle diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/SelectionBridge.h b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/SelectionBridge.h new file mode 100644 index 0000000..7da2690 --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Selection/SelectionBridge.h @@ -0,0 +1,39 @@ +#ifndef CONNECTOR_INTERFACE_BRIDGE_SELECTION_BRIDGE +#define CONNECTOR_INTERFACE_BRIDGE_SELECTION_BRIDGE + +#include "Speckle/Interface/Browser/Bridge/BrowserBridge.h" +#include "Speckle/Event/Subscriber/SelectionSubscriber.h" +#include "Speckle/Event/Type/SelectionEvent.h" + +namespace connector::interfac::browser::bridge { + + /*! + A browser bridge to support sending model data to a Speckle server + */ + class SelectionBridge : public speckle::interfac::browser::bridge::BrowserBridge, public speckle::event::SelectionSubscriber { + public: + + // MARK: - Types + + using base = speckle::interfac::browser::bridge::BrowserBridge; + + // MARK: - Constructors + + using base::base; + /*! + Default constructor + */ + SelectionBridge(); + + protected: + /*! + Handle the menu selection + @param event The selection event + @return True if the event should be closed + */ + bool handle(const speckle::event::SelectionEvent& event) override; + }; + +} + +#endif //CONNECTOR_INTERFACE_BRIDGE_SELECTION_BRIDGE diff --git a/SpeckleConnector/Connector/Interface/ConnectorPalette.cpp b/SpeckleConnector/Connector/Interface/ConnectorPalette.cpp index 6810467..da4a920 100644 --- a/SpeckleConnector/Connector/Interface/ConnectorPalette.cpp +++ b/SpeckleConnector/Connector/Interface/ConnectorPalette.cpp @@ -4,12 +4,14 @@ #include "Active/Utility/String.h" #include "Active/Serialise/JSON/JSONTransport.h" #include "Active/Utility/BufferOut.h" +#include "Connector/Connector.h" #include "Connector/ConnectorResource.h" #include "Connector/Event/ConnectorEventID.h" #include "Connector/Interface/Browser/Bridge/Account/AccountBridge.h" #include "Connector/Interface/Browser/Bridge/Base/BaseBridge.h" #include "Connector/Interface/Browser/Bridge/Config/ConfigBridge.h" #include "Connector/Interface/Browser/Bridge/Send/SendBridge.h" +#include "Connector/Interface/Browser/Bridge/Selection/SelectionBridge.h" #include "Connector/Interface/Browser/Bridge/Test/TestBridge.h" #include "Speckle/Environment/Addon.h" #include "Speckle/Event/Type/MenuEvent.h" @@ -76,9 +78,6 @@ namespace { virtual void PanelResized(const DG::PanelResizeEvent& ev) override; virtual void PanelCloseRequested(const DG::PanelCloseRequestEvent& ev, bool* accepted) override; - static GS::Array GetSelectedElements(); - static void ModifySelection(const GS::UniString& elemGuidStr, SelectionModification modification); - static GSErrCode __ACENV_CALL PaletteControlCallBack(Int32 paletteId, API_PaletteMessageID messageID, GS::IntPtr param); static GS::Ref instance; @@ -170,6 +169,12 @@ BrowserPalette::BrowserPalette() : install(); install(); install(); + if (auto ref = install(); ref) { + if (auto selectionBridgeRef = std::dynamic_pointer_cast(ref); selectionBridgeRef) { + connector::connector()->addWeak(selectionBridgeRef); + selectionBridgeRef->start(); + } + } install(); InitBrowserControl(); } @@ -243,31 +248,6 @@ void BrowserPalette::PanelCloseRequested(const DG::PanelCloseRequestEvent&, bool *accepted = true; } -GS::Array BrowserPalette::GetSelectedElements() { - API_SelectionInfo selectionInfo; - GS::Array selNeigs; - ACAPI_Selection_Get(&selectionInfo, &selNeigs, false, false); - BMKillHandle((GSHandle*)&selectionInfo.marquee.coords); - - GS::Array selectedElements; - for(const API_Neig& neig : selNeigs) { - API_Elem_Head elemHead = {}; - elemHead.guid = neig.guid; - ACAPI_Element_GetHeader(&elemHead); - - ElementInfo elemInfo; - elemInfo.guidStr = APIGuidToString(elemHead.guid); - ACAPI_Element_GetElemTypeName(elemHead.type, elemInfo.typeName); - ACAPI_Element_GetElementInfoString(&elemHead.guid, &elemInfo.elemID); - selectedElements.Push(elemInfo); - } - return selectedElements; -} - -void BrowserPalette::ModifySelection(const GS::UniString& elemGuidStr, BrowserPalette::SelectionModification modification) { - ACAPI_Selection_Select({ API_Neig(APIGuidFromString(elemGuidStr.ToCStr().Get())) }, modification == AddToSelection); -} - GSErrCode __ACENV_CALL BrowserPalette::PaletteControlCallBack(Int32, API_PaletteMessageID messageID, GS::IntPtr param) { switch(messageID) { case APIPalMsg_OpenPalette: diff --git a/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h b/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h index b4a7e64..c9e1da3 100644 --- a/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h +++ b/SpeckleLib/Speckle/Event/Subscriber/SelectionSubscriber.h @@ -45,17 +45,18 @@ namespace speckle::event { @return True if the event should be closed */ bool receive(const active::event::Event& event) override; - - protected: /*! Start the participant operation @return True if the participant is able to continue */ - bool start() override; + virtual bool start() override; /*! Stop participation (release resources etc) */ void stop() override; + + protected: + /*! Handle a selection change @param event The selection event diff --git a/SpeckleLib/Speckle/Event/Type/SelectionEvent.h b/SpeckleLib/Speckle/Event/Type/SelectionEvent.h index 396f698..365f70f 100644 --- a/SpeckleLib/Speckle/Event/Type/SelectionEvent.h +++ b/SpeckleLib/Speckle/Event/Type/SelectionEvent.h @@ -24,7 +24,7 @@ namespace speckle::event { Constructor @param selected A link to a selected element (nullopt if the selection is empty) */ - SelectionEvent(speckle::database::BIMLink::Option selected) : m_selectedLink{selected} {} + SelectionEvent(speckle::database::BIMLink::Option selected) : Event{ ID }, m_selectedLink{selected} {} /*! Copy constructor @param source The object to copy diff --git a/SpeckleLib/Speckle/Interface/Browser/JSObject.h b/SpeckleLib/Speckle/Interface/Browser/JSObject.h index 05732e6..46843e0 100644 --- a/SpeckleLib/Speckle/Interface/Browser/JSObject.h +++ b/SpeckleLib/Speckle/Interface/Browser/JSObject.h @@ -38,7 +38,7 @@ namespace speckle::interfac::browser { */ template explicit JSObject(const speckle::utility::String& name, const std::initializer_list& items) : base{items}, m_name{name} {} - + virtual ~JSObject() {} // MARK: - Functions (const) /*! diff --git a/SpeckleLib/Speckle/Interface/Browser/JSPortal.h b/SpeckleLib/Speckle/Interface/Browser/JSPortal.h index 14bcc26..159eeb7 100644 --- a/SpeckleLib/Speckle/Interface/Browser/JSPortal.h +++ b/SpeckleLib/Speckle/Interface/Browser/JSPortal.h @@ -48,14 +48,14 @@ namespace speckle::interfac::browser { @param object The object to install @return True if the object was successfully installed */ - bool install(std::shared_ptr> object); + std::shared_ptr> install(std::shared_ptr> object); /*! Install a JS function object @return True if the object was successfully installed @tparam T The type of object to install */ template requires std::is_base_of_v, T> - bool install() { return install(std::make_shared()); } + std::shared_ptr> install() { return install(std::make_shared()); } protected: #ifdef ARCHICAD @@ -101,12 +101,12 @@ namespace speckle::interfac::browser { return: True if the object was successfully installed --------------------------------------------------------------------*/ template - bool JSPortal::install(std::shared_ptr> object) { + std::shared_ptr> JSPortal::install(std::shared_ptr> object) { try { #ifdef ARCHICAD auto engine = getJSEngine(); if (!engine) - return false; + return nullptr; //Define the JS object JS::Object* acObject = new JS::Object(object->getName()); //Add all the functions supported by this object @@ -125,13 +125,13 @@ namespace speckle::interfac::browser { if (engine->RegisterAsynchJSObject(acObject)) { base::push_back(object); object->setPortal(*this); - return true; + return object; } #endif } catch(...) { ///TODO: Need to discuss the best course of action to notify of a failure } - return false; + return nullptr; } //JSPortal::install }