template <class T>
class CMenu
{
public:
	void InPlaceMenuCreate(IOleInPlaceFrame* pPassedFrame = NULL)
	{
		IOleInPlaceFrame*	pFrame;
		HRESULT				hr;
		HMENU				hOurMenu;
		OLEMENUGROUPWIDTHS	mgw;
		T* pT = static_cast<T*>(this);

		if (pPassedFrame == NULL)
		{
			// We weren't given an IOleInPlaceFrame pointer, so we'll have to get it
			// ourselves.
			OLEINPLACEFRAMEINFO frameInfo;
			RECT rcPos, rcClip;
			CComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
			frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);
			pT->m_spInPlaceSite->GetWindowContext(&pFrame, 
				&spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
		}
		else
			pFrame = pPassedFrame;

		ZeroMemory(&mgw, sizeof(OLEMENUGROUPWIDTHS));

		// Create a blank menu and ask the container to add it's menus into the
		// OLEMENUGROUPWIDTHS structure
		m_hMenuShared = ::CreateMenu();
		hr = pFrame->InsertMenus(m_hMenuShared, &mgw);
		if (FAILED(hr))
		{
			::DestroyMenu(m_hMenuShared);
			m_hMenuShared = NULL;
			return;
		}
		// Insert our menus
		hOurMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINMENU));
		MergeMenus(m_hMenuShared, hOurMenu, &mgw.width[0], 1);
		DestroyMenu(hOurMenu);
		// Send the menu to the client
		m_hOleMenu = (HMENU)OleCreateMenuDescriptor(m_hMenuShared, &mgw);
		pFrame->SetMenu(m_hMenuShared, m_hOleMenu, pT->m_hWnd);

		// If we got the IOleInPlaceFrame pointer ourselves we have to release it
		if (pPassedFrame == NULL)
			pFrame->Release();
	}

	void InPlaceMenuDestroy()
	{
		T* pT = static_cast<T*>(this);
		CComPtr<IOleInPlaceFrame> spInPlaceFrame;
		CComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
		OLEINPLACEFRAMEINFO frameInfo;
		RECT rcPos, rcClip;

		// Get the clients IOleInPlaceFrame so we can ask it to remove it's menu
		pT->m_spInPlaceSite->GetWindowContext(&spInPlaceFrame, 
			&spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
		if (spInPlaceFrame)
			spInPlaceFrame->SetMenu(NULL, NULL, NULL);

		OleDestroyMenuDescriptor(m_hOleMenu);
		m_hOleMenu = NULL;

		HMENU hOurMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINMENU));
		UnmergeMenus(m_hMenuShared, hOurMenu);
		DestroyMenu(hOurMenu);
	}

	void MergeMenus(HMENU hMenuShared, HMENU hMenuSource, LONG* lpMenuWidths, int nWidthIndex)
	{
		// Copy the popups from the pMenuSource
		int nMenuItems = GetMenuItemCount(hMenuSource);
		int nGroupWidth = 0;
		int nPosition = 0;

		// Insert at appropriate spot depending on nWidthIndex
		_ASSERTE(nWidthIndex == 0 || nWidthIndex == 1);
		if (nWidthIndex == 1)
			nPosition = (int)lpMenuWidths[0];

		for (int i = 0; i < nMenuItems; i++)
		{
			// Get the HMENU of the popup
			HMENU hMenuPopup = ::GetSubMenu(hMenuSource, i);

			// Separators move us to next group
			UINT state = GetMenuState(hMenuSource, i, MF_BYPOSITION);
			if (hMenuPopup == NULL && (state & MF_SEPARATOR) != 0)
			{
				_ASSERTE(nWidthIndex <= 5);   // Servers should not touch past 5
				lpMenuWidths[nWidthIndex] = nGroupWidth;
				nGroupWidth = 0;
				if (nWidthIndex < 5)
					nPosition += (int)lpMenuWidths[nWidthIndex+1];
				nWidthIndex += 2;
			}
			else
			{
				// Get the menu item text
				TCHAR szItemText[256];
				int nLen = GetMenuString(hMenuSource, i, szItemText,
					sizeof szItemText, MF_BYPOSITION);

				// Popups are handled differently than normal menu items
				if (hMenuPopup != NULL)
				{
					if (::GetMenuItemCount(hMenuPopup) != 0)
					{
						// Strip the HIBYTE because it contains a count of items
						state = LOBYTE(state) | MF_POPUP;   // Must be popup

						// Non-empty popup -- add it to the shared menu bar
						InsertMenu(hMenuShared, nPosition, state | MF_BYPOSITION,
							(UINT)hMenuPopup, szItemText);
						++nPosition;
						++nGroupWidth;
					}
				}
				else if (nLen > 0)
				{
					// only non-empty items are added
					_ASSERTE(szItemText[0] != 0);

					// here the state does not contain a count in the HIBYTE
					InsertMenu(hMenuShared, nPosition, state | MF_BYPOSITION,
						GetMenuItemID(hMenuSource, i), szItemText);
					++nPosition;
					++nGroupWidth;
				}
			}
		}
	}

	void UnmergeMenus(HMENU hMenuShared, HMENU hMenuSource)
	{
		int cOurItems = GetMenuItemCount(hMenuSource);
		int cMenuItems = GetMenuItemCount(hMenuShared);

		for (int i = cMenuItems-1; i >= 0; i--)
		{
			// Check the popup menus
			HMENU hMenuPopup = ::GetSubMenu(hMenuShared, i);
			if (hMenuPopup != NULL)
			{
				// If it is one of ours, remove it from the hMenuShared
				for (int j = 0; j < cOurItems; j++)
				{
					if (::GetSubMenu(hMenuSource, j) == hMenuPopup)
					{
						// Remove the menu from hMenuShared
						RemoveMenu(hMenuShared, i, MF_BYPOSITION);
						break;
					}
				}
			}
		}
	}

protected:
	HMENU	m_hOleMenu;
	HMENU	m_hMenuShared;
};
