Changing the context menu when the user right-clicks on your tool palette is relatively simple. AcadToolImpl derives from IAcadToolContextMenu. However, its method definitions simply return E_NOTIMPL to avoid linker errors. Therefore, you must override the interface's two methods: IAcadToolContextMenu::Customize() and IAcadToolContextMenu::InvokeMenuCommand().
Because you can change the context menu only at runtime, you must define your menu commands dynamically. The framework calls your implementation of IAcadToolContextMenu::Customize() when the user right-clicks any palette in the Tool Palettes window. It passes you the menu handle, ID of the current palette, upper and lower bounds for menu command IDs, and a context flag. Your implementation should do the following:
On each right-click event, the framework calls the Customize() function on your stock tool COM object. If your application allows a user to define your stock tool multiple times, you may inadvertently re-populate the context menu for each of those definitions. To guard against this scenario, verify that your stock tool's GUID is not in the stock tool catalog before creating a new instance.
If the user chooses a command from the menu, the InvokeMenuCommand() method is called on every tool object that implements IAcadToolContextMenu. When InvokeMenuCommand() is called, the framework passes you the selected menu item's command ID, the tool palette ID, and the window handle. If the command ID matches one of yours, and if the tool palette ID and window handle are acceptable, you execute appropriate actions.
The following procedures use MFC menu objects to append three menu items to the context menu.
#define MAX_MENU_ENTRIES 3
static UINT m_nMenuIds[MAX_MENU_ENTRIES] = {0,0,0};
// IAcadToolContextMenu members STDMETHOD(Customize)(/* [in] */ int nContextFlag, /* [in] */ DWORD hMenu, /* [in] */ UINT idCmdFirst, /* [in] */ UINT idCmdLast, /* [in] */ GUID *pPaletteId, /* [retval][out] */ DWORD *pFlag); STDMETHOD(InvokeMenuCommand)(/* [in] */ UINT idCmd, /* [in] */ GUID *pPaletteId, /* [in] */ DWORD hWnd, /* [retval][out] */ DWORD *pFlag);
You now are ready to implement your context menu functionality.
To implement the Customize() method
int maxMenu = (idCmdLast - idCmdFirst) < MAX_MENU_ENTRIES ? (idCmdLast - idCmdFirst) : MAX_MENU_ENTRIES;
AcTcPalette* pPalette = (AcTcPalette*)(AcTcGetManager()->FindItem(*pPaletteId)); ASSERT(pPalette != NULL); TCHAR pszString[1024]; // arbitrary buffer size; not // limited by the framework pPalette->GetName(pszString, 1024);
if (_tcscmp(pszString, "SimplePalette")!= 0) { for (i=0; i < maxMenu ; i++) { m_nMenuIds[i] = 0; } return E_NOTIMPL; }
int i; for (i=0; i < maxMenu ; i++) { m_nMenuIds[i] = idCmdFirst + i; }
CMenu* menu = new CMenu;
menu->Attach(HMENU(hMenu);
menu->InsertMenu(-1, MF_BYPOSITION, m_nMenuIds[0], "Menu&1"); menu->InsertMenu(-1, MF_BYPOSITION, m_nMenuIds[1], "Menu&2"); menu->InsertMenu(-1, MF_BYPOSITION, m_nMenuIds[2], "Menu&3");
menu->Detach(); delete menu;
Here is the finished Customize() method override:
STDMETHODIMP CSimpleTool::Customize(/* [in] */ int nContextFlag, /* [in] */ DWORD hMenu, /* [in] */ UINT idCmdFirst, /* [in] */ UINT idCmdLast, /* [in] */ GUID *pPaletteId, /* [retval][out] */ DWORD *pFlag) { AcTcPalette* pPalette = (AcTcPalette*)(AcTcGetManager()->FindItem(*pPaletteId)); ASSERT(pPalette != NULL); TCHAR pszString[128]; pPalette->GetName(pszString, 128); int i=0; int maxMenu = (idCmdLast - idCmdFirst) < MAX_MENU_ENTRIES ? (idCmdLast - idCmdFirst) : MAX_MENU_ENTRIES; if (_tcscmp(pszString, "SimplePalette")!= 0) { // Zero-out any existing IDs for (i=0; i < maxMenu ; i++) { m_nMenuIds[i] = 0; } return E_NOTIMPL; } for (i=0; i < maxMenu ; i++) m_nMenuIds[i] = idCmdFirst + i; CMenu* menu = new CMenu; if (menu->Attach(HMENU(hMenu))) { menu->InsertMenu(-1, MF_BYPOSITION, m_nMenuIds[0], "Menu&1"); menu->InsertMenu(-1, MF_BYPOSITION, m_nMenuIds[1], "Menu&2"); menu->InsertMenu(-1, MF_BYPOSITION, m_nMenuIds[2], "Menu&3"); menu->Detach(); } delete menu; return S_OK; }
The last Customize() function parameter, pFlag, is an [out,retval] parameter, which can be used to suppress the display of the context menu. See the ObjectARX Reference for more information on using this parameter.
The final step in implementing context menu functionality is to override the InvokeMenuCommand() method. For this simple case, you merely compare the idCmd argument's value to the menu IDs that you saved in the Customize() method. If you find one of your IDs, take appropriate action. The following code implements InvokeMenuCommand().
STDMETHODIMP CSimpleTool::InvokeMenuCommand(/* [in] */ UINT idCmd, /* [in] */ GUID *pPaletteId, /* [in] */ DWORD hWnd, /* [retval][out] */ DWORD *pFlag) { if (idCmd == m_nMenuIds[0]) ::AfxMessageBox("Menu1 chosen"); else if (idCmd == m_nMenuIds[1]) ::AfxMessageBox("Menu2 chosen"); else if (idCmd == m_nMenuIds[2]) ::AfxMessageBox("Menu3 chosen"); return S_OK; }