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
}