Archicad C++ API
About Archicad add-on development using the C++ API.

[SOLVED] Dockable palette - is it possible?

Anonymous
Not applicable
Is it possible to create a dockable custom palette like Info Box - in the other words, is it possible to create a window, which can be made floating or docked to some side of the working area?
Thanks.
24 REPLIES 24
Anonymous
Not applicable
The only question: is it possible to make the palette name to appear in the Window > Palettes list, so the user can toggle the palette visibility by clicking the corresponding menu item?
Solve this by using MenuCode_Palettes indentifier in ACAPI_Register_Menu(). Oh, silly me :oops:
Anonymous
Not applicable
Can't make palette dockable. Tried using pure API calls and DG::Palette class.

static CntlDlgData cntlDlgData;
static GS::Guid dialogGUID = GS::Guid( "sDock1" );
static short dialogID = 0;

GSErrCode InitPalette()
{
	GSErrCode err = NoError;
	
	// Inintialize the reference in menu command handler
	// and show the palette
	if ( dialogID == 0 ) {
		dialogGUID.ConvertFromString( "sDock1" );
		GSResModule actResModule = ACAPI_UseOwnResModule();
		dialogID = DGCreateDockablePalette( 32400, CntlDlgCallBack, (long)(void*)&cntlDlgData, dialogGUID );
		ACAPI_ResetResModule( actResModule );
	}

	DGBeginProcessEvents( dialogID );
	DGShowModelessDialog( dialogID );
	return err;
}
After calling the InitPalette() function the panel appears, but it is not dockable. What's the trick?
Anonymous
Not applicable
Did you try to add ACAPI_RegisterModelessWindow()?
Anonymous
Not applicable
No, I didn't.
Anonymous
Not applicable
Just added a call to ACAPI_RegisterModelessWindow() and implemented the APIPaletteControlCallBackProc callback function. The docking is still not working.
Anonymous
Not applicable
Here is what I have now:

GSErrCode InitPalette()
{
	GSErrCode err = NoError;
	
	// Initialize the reference in menu command handler
	// and show the palette
	if ( dialogID == 0 ) {
		dialogGUID.ConvertFromString( "sPanel1" );
		GSResModule actResModule = ACAPI_UseOwnResModule();
		dialogID = DGCreateDockablePalette( 32400, CntlDlgCallBack, (long)(void*)&cntlDlgData, dialogGUID );
		ACAPI_ResetResModule( actResModule );
	}

	DGBeginProcessEvents( dialogID );
	DGShowModelessDialog( dialogID );
	return err;
}

// -----------------------------------------------------------------------------
// Callback function for the palette
// -----------------------------------------------------------------------------
short DGCALLBACK CntlDlgCallBack( short message, short dialID, short item, long userData, long /*msgData*/ )
{
	CntlDlgData *dlgData = (CntlDlgData*)userData;

	switch( message ) {
		case DG_MSG_INIT:
			if ( ACAPI_RegisterModelessWindow( dialogID, PaletteAPIControlCallBack, API_PalEnabled_FloorPlan + API_PalEnabled_Section + API_PalEnabled_Elevation + API_PalEnabled_InteriorElevation + API_PalEnabled_Detail + API_PalEnabled_Worksheet + API_PalEnabled_3D + API_PalEnabled_Layout) != NoError )
				WriteReport( "Test::ACAPI_RegisterModelessWindow failed" );
			break;

		case DG_MSG_ACTIVATE:
			break;

		case DG_MSG_UPDATE:
			break;

		case DG_MSG_CLOSE:
			DGDestroyPalette( dialogID );
			dialogID = 0;
			break;
	}
	// Save preferences :
	ACAPI_SetPreferences( 1, sizeof(CntlDlgData), &cntlDlgData );
	return (0);
}

GSErrCode __ACENV_CALL PaletteAPIControlCallBack( long referenceID, API_PaletteMessageID messageID )
{
	if (referenceID == dialogID) {
		switch (messageID) {
			case APIPalMsg_ClosePalette:
				DGModelessClose( dialogID );
				break;
		}
	}

	return NoError;
}
The docking is not working.
Anonymous
Not applicable
Sorry, EzPresso, have no time to test your code.
I am using DG::Palette and DG::PanelObserver. Here is an extracts. Hope it helps..

////////////////////////////////////////////////////////////////////////
// Addon.cpp


// --- Addon ------------------------------------------------------------

...

#include "Palette.h"

...

GSErrCode __ACENV_CALL	Initialize (void)
{
	GSErrCode err = NoError;

	// Prevent the add-on auto-unloading 
        ACAPI_KeepInMemory(1);

	// Install menu handler
	err = ACAPI_Install_MenuHandler (32500, APIMenuCommandProc_32500);

	...


	return err;
}		/* Initialize */


GSErrCode __ACENV_CALL	FreeData (void)
{
	...	

	Palette::DeleteInstance();

	return NoError;
}		/* FreeData */



GSErrCode __ACENV_CALL APIMenuCommandProc_32500 (const API_MenuParams *menuParams)
{
   DBPrintf ("Test::APIMenuCommandProc_32500() %d/%d\n", menuParams->menuItemRef.menuResID, menuParams->menuItemRef.itemIndex);
   ACAPI_KeepInMemory(1);
   Palette::ToggleVisibility();
   return NoError;
}



////////////////////////////////////////////////////////////////////////
// Palette.h

#pragma once;

// --- Palette ------------------------------------------------------------

class Palette: public DG::Palette
{
  friend class PaletteObserver;

private:
	static Palette* m_pPalette;
  static PaletteObserver* m_pPaletteObserver;

  enum {
     CloseButtonId      = 1
  };

DG::Button closeButton;  

private:
  Palette(short resId);

public:
  ~Palette();
  void SelectionChanged(const API_Neig *selElemNeig);


static Palette& GetInstance(); 
static bool HasInstance(); 
static void DeleteInstance(); 
static void ToggleVisibility();

};

// --- PaletteObserver ------------------------------------------------------------

class PaletteObserver: private DG::PanelObserver,
  public  DG::ButtonItemObserver,
  public  DG::CompoundItemObserver
{
private:
  Palette*   m_palette;
public:
  PaletteObserver (Palette* palette);
  ~PaletteObserver();
  virtual void   SourceDestroyed (GS::EventSource* evSource);
  virtual void   PanelOpened (const DG::PanelOpenEvent& ev);
  virtual void   PanelCloseRequested (const DG::PanelCloseRequestEvent& ev, bool* accepted);
  virtual void   PanelClosed (const DG::PanelCloseEvent& ev);
  virtual void   PanelResized (const DG::PanelResizeEvent& ev);
  virtual void   ButtonClicked (const DG::ButtonClickEvent& ev);
};


////////////////////////////////////////////////////////////////////////
// Palette.cpp

#if !defined (ACExtension)
#define	ACExtension
#endif

#if defined (_MSC_VER)
	#if !defined (WINDOWS)
		#define WINDOWS
	#endif
#endif

#if defined (WINDOWS)
	#include "Win32Interface.hpp"
	#pragma warning (disable: 4068)
#endif

// ------------------------------ Includes -------------------------------------

#include	<stdio.h>
#include	<math.h>
#include	<string.h>

#if defined (macintosh)
#include	<MacTypes.h>
#endif

#include	"DG.h"
#include	"DGModule.hpp"

#include	"ACAPinc.h"
#include	"APICommon.h"	// also includes GSRoot.hpp


#include "Palette.h"

static GS::Guid   PaletteGuid  ("{9597C98D-95BD-48d9-9999-C2297834806B}");

static GSErrCode __ACENV_CALL   ModelessWindowCallBack (long referenceID, API_PaletteMessageID messageID)
{
	if (Palette::HasInstance() && referenceID == Palette::GetInstance().GetId()) {
		switch (messageID) {
      case APIPalMsg_ClosePalette:   
       	Palette::GetInstance().SendCloseRequest();  
	      break;
      case APIPalMsg_HidePalette_Begin:   
	      break;
      case APIPalMsg_HidePalette_End:   
	      break;
      case APIPalMsg_DisableItems_Begin:   
	      break;
      case APIPalMsg_DisableItems_End:   
      	      break;
	  }
	}
	return NoError;
}

// --- Palette ------------------------------------------------------------

/*static*/ Palette* Palette::m_pPalette = NULL;
/*static*/ PaletteObserver* Palette::m_pPaletteObserver = NULL;

///////////////////////////////////////////

/*static*/ Palette& Palette::GetInstance() 
{
	if (!m_pPalette) {
     GSResModule actResModule = ACAPI_UseOwnResModule ();
	   m_pPalette = new Palette(32420);  
     ACAPI_ResetResModule (actResModule);
		 m_pPaletteObserver = new PaletteObserver(m_pPalette);
	}
	return *m_pPalette;
}

/*static*/ bool Palette::HasInstance() 
{
	return m_pPalette != NULL;
}

/*static*/ void Palette::DeleteInstance() 
{
	DBASSERT(m_pPalette != NULL);
	if (m_pPalette != NULL) {
		delete m_pPalette;
		m_pPalette = NULL;
	}
	if (m_pPaletteObserver != NULL) {
		delete m_pPaletteObserver;
		m_pPaletteObserver = NULL;
	}
}

/*static*/ void Palette::ToggleVisibility() {
	if (m_pPalette != NULL && m_pPalette->IsVisible()) {
	   m_pPalette->EndEventProcessing();
	   m_pPalette->Hide();
	} else {
          Palette::GetInstance().BeginEventProcessing();
          m_pPalette->Show(); 
	}
}

//////////////////////////////////////////////////////////

Palette::Palette(short resId) : DG::Palette(resId, PaletteGuid),
 closeButton (GetReference (), CloseButtonId)
{
   ACAPI_RegisterModelessWindow (GetId(), ModelessWindowCallBack,
          API_PalEnabled_FloorPlan + 
					API_PalEnabled_Section + 
					API_PalEnabled_Detail + 
					API_PalEnabled_Layout + 
					API_PalEnabled_3D
	 );
}


Palette::~Palette ()
{
	ACAPI_UnregisterModelessWindow (GetId());
}

void Palette::SelectionChanged(const API_Neig *selElemNeig)
{
  if (selElemNeig->neigID != APINeig_None) 
	{
      DBPrintf("Last selected element: NeigID %d; guid: %s, inIndex: %d\n",
               selElemNeig->neigID, (const char *) APIGuid2GSGuid (selElemNeig->guid).ToUniString ().ToCStr (),
               selElemNeig->inIndex);
			closeButton.Enable();
	} else {
      DBPrintf("All elements deselected\n");
			closeButton.Disable();
	}
}





// --- PaletteObserver ------------------------------------------------------------

PaletteObserver::PaletteObserver (Palette* palette)
: m_palette (palette)
{
   if (DBVERIFY (m_palette != NULL)) {
      palette->Attach (*this);
      AttachToAllItems (*m_palette);
   }
}


PaletteObserver::~PaletteObserver ()
{
   if (m_palette != NULL) {
      m_palette->Detach (*this);
      DetachFromAllItems (*m_palette);
   }
}


// ... Public methods ..........................................................

void PaletteObserver::SourceDestroyed (GS::EventSource* evSource)
{
   if (evSource == m_palette) {
      //MarkAsFinished ();
      //notesObserver->FindPaletteDestroyed ();
      //palette = NULL;
   }
}


// ... Dialog notifications ....................................................

void PaletteObserver::PanelOpened (const DG::PanelOpenEvent& /*ev*/)
{
   SetMenuItemMark(32500, 1, true);

   /*palette->findEdit.SetText (palette->findData->findText);
   palette->replaceEdit.SetText (palette->findData->replaceText);
   FindValidate ();

   if (palette->findData->findWholeWord)
      palette->wholeWordCheck.Check ();
   if (palette->findData->findMatchCase)
      palette->matchCaseCheck.Check ();
   if (palette->findData->findWrap)
      palette->wrapCheck.Check ();

   if (palette->findData->findUp)
      palette->upRadio.Select ();
   else
      palette->downRadio.Select ();

   palette->findEdit.SetFocus ();*/
}


void PaletteObserver::PanelCloseRequested (const DG::PanelCloseRequestEvent& /*ev*/, bool* /*accepted*/)
{
   m_palette->EndEventProcessing ();
}

void PaletteObserver::PanelClosed (const DG::PanelCloseEvent& /*ev*/)
{
   SetMenuItemMark(32500, 1, false);
}

void PaletteObserver::PanelResized (const DG::PanelResizeEvent& ev)
{
   short dh = ev.GetHorizontalChange ();
   short dv = ev.GetVerticalChange ();

   m_palette->BeginMoveResizeItems();

   //dialog->separator.Move (dh, 0);

   m_palette->closeButton.Move (dh, dv);

   m_palette->EndMoveResizeItems();
}

// ... Dialog item notifications ...............................................

void   PaletteObserver::ButtonClicked (const DG::ButtonClickEvent& ev)
{
   if (ev.GetSource () == &m_palette->closeButton) {
      m_palette->SendCloseRequest ();
   }
}
Anonymous
Not applicable
Oh, Vladimir, Thank you a lot!
It works now! The Guid was the trick, I just changed
static GS::Guid dialogGUID = GS::Guid( "sDock1" );
to yours
static GS::Guid dialogGUID( "{9597C98D-95BD-48d9-9999-C2297834806B}" );
Anonymous
Not applicable
Great, EzPresso, glad for you! But, please, dont use my guid, otherwise our addons cannot live together (just joking)
Anonymous
Not applicable
Yes , I have generated another one for myself. Thank you!