#include "Connector/Interface/ConnectorPalette.h" #include "Active/Event/Event.h" #include "Active/Setting/ValueSetting.h" #include "Active/Serialise/JSON/JSONTransport.h" #include "Active/Utility/BufferOut.h" #include "Active/Utility/String.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/Receive/ReceiveBridge.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" #include "Speckle/Event/Type/ProjectEvent.h" #include "Speckle/Interface/Browser/JSPortal.h" #include #include #include using namespace active::environment; using namespace active::event; using namespace active::setting; using namespace connector; using namespace speckle::interfac::browser; using namespace connector::interfac::browser::bridge; using namespace speckle::event; //NB: Following is placeholder from GS example code - will be refactored to better suit our purposes #define BrowserPaletteResId 32500 #define BrowserPaletteMenuResId 32500 #define BrowserPaletteMenuItemIndex 1 namespace { static const GS::Guid paletteGuid("{FEE27B6B-3873-4834-98B5-F0081AA4CD45}"); // --- Class declaration: BrowserPalette ------------------------------------------ class BrowserPalette final : public DG::Palette, public DG::PanelObserver, public JSPortal<> { public: enum SelectionModification { RemoveFromSelection, AddToSelection }; struct ElementInfo { GS::UniString guidStr; GS::UniString typeName; GS::UniString elemID; }; BrowserPalette(); virtual ~BrowserPalette(); static bool HasInstance(); static void CreateInstance(); static BrowserPalette& GetInstance(); static void DestroyInstance(); void Show(); void Hide(); static GSErrCode RegisterPaletteControlCallBack(); protected: enum { BrowserId = 1 }; std::shared_ptr browser; void InitBrowserControl(); void publshVisibilityChange(bool); virtual void PanelResized(const DG::PanelResizeEvent& ev) override; virtual void PanelCloseRequested(const DG::PanelCloseRequestEvent& ev, bool* accepted) override; static GSErrCode __ACENV_CALL PaletteControlCallBack(Int32 paletteId, API_PaletteMessageID messageID, GS::IntPtr param); static GS::Ref instance; #ifdef ARCHICAD /*! Get the portal Javascript engine @return The portal Javascript engine */ std::shared_ptr getJSEngine() const override { return browser; } #endif }; GS::Ref BrowserPalette::instance; } /*-------------------------------------------------------------------- Default constructor --------------------------------------------------------------------*/ ConnectorPalette::ConnectorPalette() { } //ConnectorPalette::ConnectorPalette /*-------------------------------------------------------------------- Get the event subscription list return: The subscription list (an empty list will put the subscriber into a suspended state) --------------------------------------------------------------------*/ ConnectorPalette::Subscription ConnectorPalette::subscription() const { auto result = ProjectSubscriber::subscription(); result.insert(toggleConnectorPaletteID); return result; } //ConnectorPalette::subscription /*-------------------------------------------------------------------- Start the participant operation return: True if the participant is able to continue --------------------------------------------------------------------*/ bool ConnectorPalette::start() { return (BrowserPalette::RegisterPaletteControlCallBack() == NoError); } //ConnectorPalette::start /*-------------------------------------------------------------------- Receive a subscribed event event: The incoming event return: True if the event should be closed --------------------------------------------------------------------*/ bool ConnectorPalette::receive(const active::event::Event& event) { if (event == toggleConnectorPaletteID) { if (BrowserPalette::HasInstance() && BrowserPalette::GetInstance().IsVisible()) { BrowserPalette::GetInstance().Hide(); } else { if (!BrowserPalette::HasInstance()) BrowserPalette::CreateInstance(); BrowserPalette::GetInstance().Show(); } return true; } return ProjectSubscriber::receive(event); } //ConnectorPalette::receive /*-------------------------------------------------------------------- Handle a project event event: The project event return: True if the event should be closed --------------------------------------------------------------------*/ bool ConnectorPalette::handle(const speckle::event::ProjectEvent& event) { using enum speckle::event::ProjectEvent::Type; switch (event.getType()) { case open: { if (BrowserPalette::HasInstance() && !BrowserPalette::GetInstance().IsVisible()) { BrowserPalette::GetInstance().Show(); BrowserPalette::GetInstance().EnableItems(); } } break; case close: { if (BrowserPalette::HasInstance() && BrowserPalette::GetInstance().IsVisible()) { BrowserPalette::GetInstance().Hide(); } } break; default: break; } return false; } //ConnectorPalette::handle //NB: Following is placeholder from GS example code - will be refactored to better suit our purposes // ----------------------------------------------------------------------------- // Project event handler function // ----------------------------------------------------------------------------- static GSErrCode __ACENV_CALL NotificationHandler(API_NotifyEventID notifID, Int32 /*param*/) { switch(notifID) { case APINotify_Quit: BrowserPalette::DestroyInstance(); break; } return NoError; } // NotificationHandler BrowserPalette::BrowserPalette() : DG::Palette(ACAPI_GetOwnResModule(), BrowserPaletteResId, ACAPI_GetOwnResModule(), paletteGuid) { browser = std::make_shared(GetReference(), BrowserId); #ifdef ServerMainVers_2700 ACAPI_ProjectOperation_CatchProjectEvent(APINotify_Quit, NotificationHandler); #else ACAPI_Notify_CatchProjectEvent(APINotify_Quit, NotificationHandler); #endif Attach(*this); BeginEventProcessing(); //Install required connector bridges install(); if (auto ref = install(); ref) { if (auto baseBridgeRef = std::dynamic_pointer_cast(ref); baseBridgeRef) { connector::connector()->addWeak(baseBridgeRef); } } install(); install(); if (auto ref = install(); ref) { if (auto sendBridgeRef = std::dynamic_pointer_cast(ref); sendBridgeRef) { connector::connector()->addWeak(sendBridgeRef); sendBridgeRef->start(); } } if (auto ref = install(); ref) { if (auto selectionBridgeRef = std::dynamic_pointer_cast(ref); selectionBridgeRef) { connector::connector()->addWeak(selectionBridgeRef); selectionBridgeRef->start(); } } install(); InitBrowserControl(); } BrowserPalette::~BrowserPalette() { EndEventProcessing(); } bool BrowserPalette::HasInstance() { return instance != nullptr; } void BrowserPalette::CreateInstance() { DBASSERT(!HasInstance()); instance = new BrowserPalette(); ACAPI_KeepInMemory(true); } BrowserPalette& BrowserPalette::GetInstance() { DBASSERT(HasInstance()); return *instance; } void BrowserPalette::DestroyInstance() { instance = nullptr; } void BrowserPalette::Show() { DG::Palette::Show(); publshVisibilityChange(true); } void BrowserPalette::Hide() { DG::Palette::Hide(); publshVisibilityChange(false); } void BrowserPalette::InitBrowserControl() { #ifdef TESTING_MODE //browser->LoadURL("https://boisterous-douhua-e3cefb.netlify.app/test"); browser->LoadURL("https://boisterous-douhua-e3cefb.netlify.app"); #else browser->LoadURL("https://boisterous-douhua-e3cefb.netlify.app/"); #endif } void BrowserPalette::publshVisibilityChange(bool isChecked) { //Signal that the palette visibility has changed app()->publish(Event{reflectPaletteVisibilityID, { ValueSetting{isChecked, paletteVisibilityStateID} }}); } void BrowserPalette::PanelResized(const DG::PanelResizeEvent& ev) { BeginMoveResizeItems(); browser->Resize(ev.GetHorizontalChange(), ev.GetVerticalChange()); EndMoveResizeItems(); } void BrowserPalette::PanelCloseRequested(const DG::PanelCloseRequestEvent&, bool* accepted) { Hide(); *accepted = true; } GSErrCode __ACENV_CALL BrowserPalette::PaletteControlCallBack(Int32, API_PaletteMessageID messageID, GS::IntPtr param) { switch(messageID) { case APIPalMsg_OpenPalette: if(!HasInstance()) CreateInstance(); GetInstance().Show(); break; case APIPalMsg_ClosePalette: if(!HasInstance()) break; GetInstance().Hide(); break; case APIPalMsg_HidePalette_Begin: if(HasInstance() && GetInstance().IsVisible()) GetInstance().Hide(); break; case APIPalMsg_HidePalette_End: if(HasInstance() && !GetInstance().IsVisible()) { GetInstance().Show(); } break; case APIPalMsg_IsPaletteVisible: *(reinterpret_cast(param)) = HasInstance() && GetInstance().IsVisible(); break; default: break; } return NoError; } GSErrCode BrowserPalette::RegisterPaletteControlCallBack() { return ACAPI_RegisterModelessWindow( GS::CalculateHashValue(paletteGuid), PaletteControlCallBack, API_PalEnabled_FloorPlan + API_PalEnabled_Section + API_PalEnabled_Elevation + API_PalEnabled_InteriorElevation + API_PalEnabled_3D + API_PalEnabled_Detail + API_PalEnabled_Worksheet + API_PalEnabled_Layout + API_PalEnabled_DocumentFrom3D, GSGuid2APIGuid(paletteGuid)); }