Click here to Skip to main content
15,885,366 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 254.8K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
// ==========================================================================
//						   Class Specification : 
//				COXMenuBar & COXMenuDockBar & COXMenuBarFrame
// ==========================================================================

// Version: 9.3

// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
                          
// //////////////////////////////////////////////////////////////////////////

/*

Many products (Misrosoft Office, Visual Studio IDE to name just a few) 
use special replacement for standard menu bar in their frame window. 
It looks like common dockable flat toolbar while implements all the
functionality found in standard menu bar (accelerators, mouse and 
keyboard navigation). 

Due to similarity to standard toolbar control we thought it would be natural 
to try to use this control in order to implement the menubar.

But we have to note that only using toolbar control that is available
in comctl32.dll v4.71 and higher would allow us to implement the 
functionality of menubar to full extent (primarily, because new button
style TBSTYLE_AUTOSIZE, which allows to use buttons of different width 
in the toolbar, was introduced only in v4.71). As long as comctl32.dll
was freely redistributable we felt like it wasn't a big deal. 

Then, we already had the COXCoolToolBar class in our library that could be
good base class for menubar implementation. 

So we developed new class that is called COXMenuBar and is based on
COXCoolToolBar. If you try to use it with old version of comctl32.dll
we will throw unsupported exception.


The problem with replacing standard window menu with dockable menubar
had to be resolved in tight cooperation between frame window and 
menubar control. The solution should have worked with any CFrameWnd
derived window (CFrameWnd had to be used because it is support
docking functionality). The problem turned out to be more complicated
due to the fact that we had to provide our own CDockBar derived class
(COXMenuDockBar) in order to implement menu bar (it has to do with docking 
functionality). CDockBar is undocumented MFC class that is used 
internaly to provide docking functionality for any CControlBar derived
class within CFrameWnd derived framework window. 

Moreover, implementation for SDI and MDI main frame window was a little
bit different in both cases. And anyway it was highly possible to assume 
that programmers could use their own CFrameWnd derivates in their 
applications (e.g. they could have used our COXMDIFrameWndSizeDock class
in order to provide docking windows functionality).

So, we had major problem: implementation that will work with any 
CFrameWnd derived class. 

And we came up with the following solution.
We designed template class COXMenuBarFrame that defined as follows:


	template<class PARENTFRAME, class PARENTDOCKBAR>
	class COXMenuBarFrame : public PARENTFRAME
	{
		. . . . . . . . . . . . . . 
	};


where	PARENTFRAME is the name of the parent frame class (e.g. CFrameWnd)
		PARENTDOCKBAR is the name of parent dock bar class (e.g. CDockBar)

Using this approach you can define your mainframe window as:

	class CMainFrame : public COXMenuBarFrame<CMDIFrameWnd,CDockBar>

in order to implement standard MDI application with automatic menu bar. 
That's all what you have to do in order to include in your application 
support for menubar. We would like to note that in standard macros
like IMPLEMENT_DYNAMIC or BEGIN_MESSAGE_MAP you have to eplicitly use 
the name of the parent frame class:

	BEGIN_MESSAGE_MAP(CMainFrame,CMDIFrameWnd)


Although, COXMenuBarFrame, COXMenuDockBar and COXMenuBar classes define
a number of public functions you wouldn't normally use them because
the functionality that we provide is self sufficient and doesn't provide
any additional customization functionality.


*/

#ifndef _MENUBAR_H
#define _MENUBAR_H

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#include "OXDllExt.h"
#include "OXMainRes.h"

#include <afxole.h>
#include <afxtempl.h>
#include <afxpriv.h>

#include "UTB64Bit.h"

#ifndef __OX_OLEIMPL2_H__
#ifdef MAP_LOGHIM_TO_PIX
#undef MAP_LOGHIM_TO_PIX
#endif
#ifdef MAP_PIX_TO_LOGHIM
#undef MAP_PIX_TO_LOGHIM
#endif

#if _MFC_VER < 0x0700
	#include "..\src\oleimpl2.h"
#else
	#include "..\src\mfc\oleimpl2.h"
#endif

#define __OX_OLEIMPL2_H__
#endif


#if _MFC_VER>=0x0420

#include "OXCoolToolBar.h"

// default ID for menubar
#define AFX_IDW_MENUBAR			0xE810


// base id for menubar buttons that correspond to the index
#define ID_CMDBASE				1
// the gap between close, restore and minimize buttons and menu items
#define ID_BUTTONSGAP			6

typedef struct _tagOXCUSTOMIZEPOPUPMENUINFO
{
	HMENU hMenu;
	UINT nFlags;
	POINT ptStart;
} OXCUSTOMIZEPOPUPMENUINFO;


// hit test values
typedef enum _tagMENUBARHITTEST
{
	OX_MNU_ICON=-5,
	OX_MNU_CLOSEBTN=-4,
	OX_MNU_RESTOREBTN=-3,
	OX_MNU_MINIMIZEBTN=-2,
	OX_MNU_NONE=-1
} MENUBARHITTEST;
//////////////////////

// test point origin
typedef enum _tagHITTESTORIGIN
{
	OX_MNU_CLIENT,
	OX_MNU_SCREEN,
	OX_MNU_ZEROBASED

} HITTESTORIGIN;
/////////////////////////
	

// ID of the expansion item
#ifndef ID_OX_SHOWALLITEMS
#define ID_OX_SHOWALLITEMS					0x0000ff56
#endif	//	ID_OX_SHOWALLITEMS

// Define the WM_QUERYSNAPPING user message used to query a frame window whether or not
// the snaping and tear-off behavior is enabled
#ifndef WM_QUERYSNAPPING
	#define WM_QUERYSNAPPING (WM_USER + 92)
#endif

/////////////////////////////////////////////////////////////////////////////
// COXMenuBar window

class COXMenuSkin;
class COXBitmapMenu;

class OX_CLASS_DECL COXMenuBar : public COXCoolToolBar
{
	friend class COXToolbarSkinClassic;
	friend class COXToolbarSkinXP;
	friend class COXToolbarSkin2003;

	DECLARE_DYNAMIC(COXMenuBar)
// Construction
public:
	// --- In  :
	// --- Out : 
	// --- Returns:
	// --- Effect : Constructs the object
	COXMenuBar();

public:
	// handle to the submenu that will contain the MRU MDIChild windows
	HMENU m_hMDIWindowMenu;

	// static function that will search for menubar 
	static COXMenuBar* FindMenuBar(CWnd* pStartWnd=NULL);

// Attributes
public:
	// handle to the menu that is currently displayed in the menu bar
	HMENU m_hMenu;

	// zero-based index of the menu item that is currently displayed
	// in the dropdown state (the corresponding popup menu is active)
	int m_nActiveMenuItem;
	// zero-based index of the menu item that will be activated (the 
	// corresponding popup menu will be active) after the currently 
	// active popup menu is deactivated
	int m_nActivateNextItem;
	// zero-based index of the menu item that cannot be set as 
	// m_nActivateNextItem
	int m_nForbiddenItem;

	// v9.3 update 01 modification manfred for DisplayPopupMenu code
	int m_nActivateNextItemShowAll;

	// flag that specifies that menu should take entire row
	// while displayed
	BOOL m_bForceEntireRow;

	// flag that specifies that menu bar should state 
	// in surfing mode after Esc key was pressed when
	// top level popup menu was active
	BOOL m_bStaySurfing;

protected:
	// flag that specifies that menu bar will take the entire row or column 
	// while docked. At the moment this flag is always TRUE
	BOOL m_bTakeEntireRow;

	// pointer to the parent frame window
	CWnd* m_pFrameWnd;
	// accelerator table for menu items 
	CMap<TCHAR,TCHAR,int,int> m_accelTable;

	// rectangles for close, restore and minimize buttons on the bar
	CRect m_rectCloseBtn;
	CRect m_rectRestoreBtn;
	CRect m_rectMinimizeBtn;
	////////////////////////////////////////////////////////////////

	// currently pressed button (close, restore or minimize)
	MENUBARHITTEST m_pressedBtn;

	// flag that specifies that menu bar is in surfing mode when user can 
	// use keyboard to navigate through menu items.
	BOOL m_bIsInSurfingMode;

	// font used to draw the menu bar
	CFont m_fontMenu;
	// flag that specifies that current menu display settings has been 
	// retrieved and they are up-to-date
	BOOL m_bIsMenuMetricInitialized;

	// map of all menus associated with all document templates defined
	// in application. Only this menus are customizable
	CMap<HMENU,HMENU,CDocTemplate*,CDocTemplate*> m_mapCustomizableMenus;
	// map of all menus that has been customized but hasn't been saved yet
	CMap<HMENU,HMENU,int,int> m_mapChangedMenus;

	// array of created popup menus that should be destroyed at the end
	CArray<HMENU,HMENU> m_arrCreatedPopupMenus;

	// timer for drag'n'drop event
	UINT_PTR m_nCheckForDragDropEventTimerID;

	// item that will be dragged
	int m_nWouldBeDraggedItem;

private:
	static LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
	static HHOOK m_hMouseHook;
	static HWND m_hwndPrevMouseMoveWnd;
	COXMenuSkin* GetMenuSkin();
	COXMenuSkin* m_pMenuSkin;
	void RecalculateItemWidths();

// Operations

public:
	
	// --- In  :	pParentWnd	-	Pointer to the window that is the 
	//								menubar's parent
	//				dwStyle		-	The menubar style. Refer to decription
	//								of CToolBar::Create() function for details
	//				nID			-	The toolbar's child-window ID.
	// --- Out : 
	// --- Returns:	TRUE if menubar was successfully created, 
	//				or FALSE otherwise
	// --- Effect : Creates the menubar
	BOOL Create(CWnd* pParentWnd, 
		DWORD dwStyle=WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_SIZE_DYNAMIC,
		UINT nID=AFX_IDW_MENUBAR);


	// --- In  :	
	// --- Out : 
	// --- Returns:	Handle to the menu that is used in the menu bar
	// --- Effect : Retrieves handle to the menubar menu
	// v9.3 update 02 - VS2008 - GetMenu returns CMenu*, so this override
	// invalid - added GetHMenu. 
	// TD - tried defaulting to base GetMenu adding operator HMENU with macro
	// in calling code but seemed problematic - this is clunky but clearer.
#if _MFC_VER < 0x0800
	HMENU GetMenu() const { 
		ASSERT(::IsWindow(GetSafeHwnd()));
		return m_hMenu; 
	}
#else
	HMENU GetHMenu() const { 
		ASSERT(::IsWindow(GetSafeHwnd()));
		return m_hMenu; 
	}
#endif

	// --- In  :	hMenu	-	handle to the valid menu that will be 
	//							displayed in the menubar
	// --- Out : 
	// --- Returns:	TRUE if the menu was successfully set,
	//				or FALSE otherwise
	// --- Effect : Sets menu to be displayed in the menubar
	BOOL SetMenu(HMENU hMenu);


	// --- In  :	bTakeEntireRow	-	flag that specifies whether the 
	//									menubar will take the entire 
	//									row/column
	//				bRedraw			-	flag that specifies whethern the
	//									menubar will be redrawn or not
	// --- Out : 
	// --- Returns:	
	// --- Effect : Sets the flag that specifies whether the menubar 
	//				will take the entire row/column
	inline void SetTakeEntireRow(BOOL bTakeEntireRow, BOOL bRedraw=TRUE) 
	{
		m_bTakeEntireRow=bTakeEntireRow;
		if(bRedraw)
		{
			RedrawToolBar();
		}
	}

	// --- In  :	
	// --- Out : 
	// --- Returns:	
	// --- Effect : Retrieves the flag that specifies whether the menubar 
	//				will take the entire row/column
	inline BOOL GetTakeEntireRow() const { return m_bTakeEntireRow; }


	// --- In  :	bHorz	-	TRUE if the size of row is retrieved, 
	//							FALSE if the size of column is retrieved
	// --- Out : 
	// --- Returns:	Size of row/column to display the menu bar in
	// --- Effect : Retrieves the size of row/column to display the menu bar in
	CSize GetEntireRow(BOOL bHorz) const;


	// --- In  :	bIsInSurfingMode	-	flag that specifies whether the 
	//										surfing mode is set/removed.
	//				nItem				-	if bIsInSurfingMode is TRUE then
	//										this variable specifies the index
	//										of the item that has to be 
	//										initially displayed as hot in the
	//										surfing mode.
	// --- Out :	
	// --- Returns:	TRUE if surfing mode was successfully set/removed
	// --- Effect : Sets/removes the surfing mode. If menu bar is in surfing 
	//				mode then user can use keyboard to navigate through menu 
	//				items
	BOOL SetSurfingMode(BOOL bIsInSurfingMode=TRUE, int nItem=0);

	// --- In  :	
	// --- Out :	
	// --- Returns:	TRUE if menubar is in surfing mode,	or FALSE otherwise
	// --- Effect : Retrives the flag that specifies whether the menubar is
	//				in the surfing mode or not. If menu bar is in surfing 
	//				mode then user can use keyboard to navigate through 
	//				menu items
	inline BOOL IsInSurfingMode() const 
	{ 
		ASSERT(::IsWindow(m_hWnd));
		return m_bIsInSurfingMode; 
	}


	// --- In  :	
	// --- Out :	
	// --- Returns:	TRUE if menubar is in displaying mode, or FALSE otherwise.
	// --- Effect : Retrives the flag that specifies whether the menubar is
	//				in the displaying mode or not. If menu bar is in 
	//				displaying mode then one of the popup menus is displayed
	inline BOOL IsInDisplayingMode() const 
	{ 
		ASSERT(::IsWindow(m_hWnd));
		return (m_nActiveMenuItem>=0 && 
			m_nActiveMenuItem<=GetToolBarCtrl().GetButtonCount()); 
	}


	// returns TRUE if menu content can be dynamically changed in Customize Manger
	virtual BOOL IsCustomizable(BOOL bAdvanced=TRUE) const;


	// Save to and load from registry state of application menus.
	// We use default registry key assigned to your application by MFC as
	// a storage. We distinguish 
	//
	////////////////////////
	virtual BOOL SaveBarState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName, 
		BOOL bProperties=TRUE);
	virtual BOOL LoadBarState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName, 
		BOOL bProperties=TRUE);

	// saves/restores the state of menu in/from registry
	BOOL SaveMenuState(HMENU hMenu, LPCTSTR lpszSubKey);
	BOOL LoadMenuState(HMENU hMenu, LPCTSTR lpszSubKey);


	virtual BOOL DisplayCustomizeButtonContextMenu(int nButtonIndex, CPoint point);

	// mark the menu as the changed one
	inline void MarkAsChanged()
	{
		// mark this menu as a changed one
		if(!IsMarkedAsChanged())
		{
			// v9.3 update 02 - using GetHMenu for VS2008
#if _MFC_VER >= 0x0800
			m_mapChangedMenus.SetAt(GetHMenu(),PtrToInt(m_mapChangedMenus.GetCount()));
#else
			m_mapChangedMenus.SetAt(GetMenu(),PtrToInt(m_mapChangedMenus.GetCount()));
#endif
		}
	}

	// check if menu is marked as the changed one
	inline BOOL IsMarkedAsChanged()
	{
		int nIndex=-1;
		// v9.3 update 02 - using GetHMenu for VS2008
#if _MFC_VER >= 0x0800
		HMENU hMenu= GetHMenu();
#else 
		HMENU hMenu= GetMenu();	
#endif
		return (m_mapChangedMenus.Lookup(hMenu,nIndex));
	}

	// --- In  :	
	// --- Out :	
	// --- Returns:
	// --- Effect : This member function fills the RECT structure whose address is 
	//				contained in lpRect with the coordinates of the button or separator
	//				specified by nIndex. Coordinates are in pixels relative to the
	//				upper-left corner of the toolbar.
	virtual void GetItemRect(int nIndex, LPRECT lpRect);

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMainFrame)
	//}}AFX_VIRTUAL

protected:
	// repiopulate the menu bar
	BOOL RecreateMenuBar(BOOL bRedraw=TRUE);
	// refresh the menubar icon if any is displayed
	void UpdateIcon(BOOL bRedraw=TRUE);
	// sets the specified menu item to pressed state and
	// displays the corresponding popup menu
	void DisplayPopupMenu(int nMenuItem);
	// restores the state of the previously ative menu item
	void RestoreMenuButton();
	// calculates the size of rectangles for close, restore 
	// and minimize buttons
	int CalcButtonRects();
	// draws close, restore and minimize buttons
	virtual void DrawButtons(CDC& dc);

	// function that is called when the size of the menu bar 
	// is requested by dock bar or dock context
	virtual CSize CalcLayout(DWORD nMode, int nLength=-1);

	// returns the index of the previous (only visible if specified)
	// item in the menu bar
	int GetPrevMenuItem(int nItem, BOOL bEnsureVisile=TRUE);
	// returns the index of the next (only visible if specified)
	// item in the menu bar
	int GetNextMenuItem(int nItem, BOOL bEnsureVisible=TRUE);

// Implementation
public:
	virtual CSize CalcSize(TBBUTTON* pData, int nCount);
	// --- In  :	
	// --- Out : 
	// --- Returns:
	// --- Effect : Destructs the object
	virtual ~COXMenuBar();

	
	// --- In  :	ptTest	-	point to test
	//				Origin	-	origin of the point. Can be one of the 
	//							following:
	//		
	//							OX_MNU_CLIENT		-	ptTest is in client coordinates
	//							OX_MNU_SCREEN		-	ptTest is in screen coordinates
	//							OX_MNU_ZEROBASED	-	ptTest is in coordinates where 
	//											TopLeft point of menubar window
	//											is the origin point.
	// --- Out :	
	// --- Returns:	the index of the corresponding menu item or one of the 
	//				following values:
	//
	//				OX_MNU_ICON		-	ptTest is over the icon rectangle
	//				OX_MNU_CLOSEBTN	-	ptTest is over the close button rectangle
	//				OX_MNU_RESTOREBTN	-	ptTest is over the restore button rectangle
	//				OX_MNU_MINIMIZEBTN	-	ptTest is over the minimize button rectangle
	//				OX_MNU_NONE		-	ptTest is in void space
	//
	// --- Effect : Retrieves the item that is under the specified point
	int HitTest(const CPoint& ptTest, HITTESTORIGIN Origin=OX_MNU_ZEROBASED) const;

	// --- In  :	
	// --- Out : 
	// --- Returns:
	// --- Effect : Redraws the menu bar. If a menu bar is changed after 
	//				Windows has created the window, call this function to 
	//				draw the changed menu bar.
	inline void DrawMenuBar() 
	{ 
		if(::IsWindow(GetSafeHwnd()))
		{
			RecreateMenuBar(TRUE);
		}
	}

	
	// --- In  :	nChar	-	Specifies the virtual-key code of the given key.
	// --- Out :	
	// --- Returns:	TRUE if key was handled, or FALSE otherwise
	// --- Effect : Handles key pressed by user (accelerator key or 
	//				navigate keys)
	BOOL HandleMenuKey(UINT nChar);

protected:
	// virtual function for designating some space. We book space for
	// close, restore and minimize buttons.
	virtual void BookSpace(CRect& rectBookedSpace, DWORD dwMode);
	// Draw in Booked space 
	virtual void DrawInBookedSpace(CDC& dc, CRect& rectToDrawIn);

	// function called all the time when menubar changes its state from
	// docking to floating and vise versa
	virtual void OnFloatingDocking(BOOL bFloating);

	void UpdateMenuMetrics(BOOL bRedraw=TRUE);

	// function called everytime when toolbar changes its docking side
	virtual void OnChangeDockSide(DWORD dwDockSide);

	// saves/restores the contents of a menu in/from registry
	BOOL SaveMenuContents(HMENU hMenu, CArchive& ar);
	BOOL LoadMenuContents(HMENU hMenu, CArchive& ar);

	// Saves the customized menu in case it has been modified
	BOOL SaveCustomizedMenu();

	// Generated message map functions
protected:
	CString GetItemText(int iIndex);
	// overwrite standard handlers to overcome some problems with MFC
	// standard painting routine
	//{{AFX_MSG(COXMenuBar)
	afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point);
#if _MSC_VER >= 1400
	afx_msg LRESULT OnNcHitTest(CPoint point);
#else
	afx_msg UINT OnNcHitTest(CPoint point);
#endif
	afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	afx_msg void OnNcLButtonDblClk(UINT nHitTest, CPoint point);
	afx_msg void OnKillFocus(CWnd* pNewWnd);
	afx_msg void OnSettingChange(UINT uFlags, LPCTSTR lpszSection);
	// v9.3 - update 03 - 64-bit - using OXTPARAM here - see UTB64Bit.h
	afx_msg void OnTimer(OXTPARAM nIDEvent);
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnDestroy();
	afx_msg void OnNcMouseMove(UINT nHitTest, CPoint point);
	afx_msg void OnSize(UINT nType, int cx, int cy);
	//}}AFX_MSG
    // v9.3 - update 03 - 64-bit - return value was declared as LONG - changed to LRESULT
	afx_msg LRESULT OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam);
	afx_msg void OnUpdateMenuItem(CCmdUI* pCmd);
	afx_msg BOOL OnMenuDropDown(NMHDR* pNotifyStruct, LRESULT* result);
	// v9.3 - update 03 - 64-bit - return value was declared as LONG - changed to LRESULT
	afx_msg LRESULT OnDisplayPopupMenu(WPARAM wParam, LPARAM lParam);
	
	// v9.3 update 01 modification (added) Manfred Drasch
	// v9.3 - update 03 - 64-bit - return value was declared as LONG - changed to LRESULT
	afx_msg LRESULT OnDisplayPopupMenuAllItems(WPARAM wParam, LPARAM lParam);

	afx_msg void OnCustTBDelete();

	// reflect the messages to let the MenuBar provide 
	// customization functionality
	afx_msg BOOL OnTBNBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);

	DECLARE_MESSAGE_MAP()

	// drag and drop support
	// v9.3 - update 03 - 64-bit - return values were declared as LONG - changed to LRESULT
	virtual LRESULT OnDragOver(WPARAM wParam, LPARAM lParam);
	virtual LRESULT OnDrop(WPARAM wParam, LPARAM lParam);

	// handler that is called when button is to be removed due to to 
	// drag'n'drop operation
	virtual void OnRemoveDraggedButton(int nButtonIndex);

	virtual void RetrieveDragDropMenuItem(BYTE*& lpData, HMENU hMenu, 
		int nButtonIndex);
};


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


template<class PARENTWND>
class COXMenuBarHost : public PARENTWND
{
public:
	// --- In  :	
	// --- Out : 
	// --- Returns:
	// --- Effect : Constructs the object
	COXMenuBarHost();


// Operations
protected:
	// --- In  :	hMenu		-	valid handle of the menu that will be set
	//								to menu bar
	//				pWnd		-	Pointer to the window that is the 
	//								menubar's parent.
	//				dwStyle		-	The menubar style. Refer to decription
	//								of CToolBar::Create() function for details
	//				nID			-	The toolbar's child-window ID.
	// --- Out : 
	// --- Returns:	TRUE if menubar was successfully created, 
	//				or FALSE otherwise
	// --- Effect : Creates the menubar
	virtual BOOL OnCreateMenuBar(HMENU hMenu, CWnd* pWnd, 
		DWORD dwStyle, UINT nID);

// Attributes
public:

	// global map of all menu bar frame windows
	static CMap<HWND,HWND,void*,void*> g_arrMenuBarFrames;

	// handle of the old mouse hook procedure
	static HHOOK g_pfnOldMouseHookProc;
	// handle of the old keyboard hook procedure
	static HHOOK g_pfnOldKeyboardHookProc;
	/////////////////

protected:
	// flag that specifies that the window is CFrameWnd derived 
	BOOL m_bIsFrame;

	// flag that specifies that the window is MDIFrame
	BOOL m_bIsMDI;

	// menubar object
	COXMenuBar m_menuBar;

	// default menu of MDIFrame window
	HMENU m_hDefaultMenu;

	// flag that specifies that currently selected item in popup menu is 
	// popup item
	BOOL m_bIsPopupMenuItem;
	// array of currently displayed popup menus
	CArray<HMENU,HMENU> m_arrPopupMenu;

public:
	// --- In  :
	// --- Out : 
	// --- Returns:
	// --- Effect : Destructs the object
	virtual ~COXMenuBarHost();

	
	// --- In  :	nCode	-	Specifies a code the hook procedure uses 
	//							to determine how to process the mouse message. 
	//							If nCode is less than zero, the rocedure must 
	//							pass the message to the CallNextHookEx function 
	//							without further processing and should return 
	//							the value returned by CallNextHookEx. 
	//				wParam	-	Specifies the identifier of the mouse message. 
	//				lParam	-	Pointer to a MOUSEHOOKSTRUCT structure. 
	// --- Out : 
	// --- Returns:	TRUE if the message has been handled, or FALSE otherwise
	// --- Effect : Handles all mouse messages in the current thread
	virtual BOOL HandleMouseMsg(int nCode, WPARAM wParam, LPARAM lParam);

	
	// --- In  :	nCode	-	Specifies a code the hook procedure uses 
	//							to determine how to process the mouse message. 
	//							If nCode is less than zero, the rocedure must 
	//							pass the message to the CallNextHookEx function 
	//							without further processing and should return 
	//							the value returned by CallNextHookEx. 
	//				wParam	-	Specifies the virtual-key code of the key that 
	//							generated the keystroke message
	//				lParam	-	Specifies the repeat count, scan code, 
	//							extended-key flag, context code, previous 
	//							key-state flag, and transition-state flag
	// --- Out : 
	// --- Returns:	TRUE if the message has been handled, or FALSE otherwise
	// --- Effect : Handles all keyboard messages in the current thread
	virtual BOOL HandleKeyboardMsg(int nCode, WPARAM wParam, LPARAM lParam);

	// --- In  :	
	// --- Out : 

	// --- Returns:	reference to COXMenuBar object used to represent 
	//				the menu bar
	// --- Effect :	Retrieves menu bar object
	virtual COXMenuBar& GetMenuBar();

	// --- In  :	
	// --- Out : 
	// --- Returns:	
	// --- Effect :	Redraws menu bar
	virtual void DrawMenuBar();

	// --- In  :	
	// --- Out : 
	// --- Returns:	pointer to the window menu
	// --- Effect :	Retrieves window's menu
	virtual CMenu* GetMenu();

	// --- In  :	pMenu	-	pointer to the new window menu
	// --- Out : 
	// --- Returns:	TRUE id succeeded or FALSE otherwise
	// --- Effect :	Sets window's menu
	virtual BOOL SetMenu(CMenu* pMenu);


	// --- In  :	nHiliteItemIndex	-	Specifies the menu item to be highlighted
	//				bHilite				-	Specifies whether the menu item is 
	//										highlighted or the highlight is removed. 
	// --- Out : 
	// --- Returns:	TRUE if succeed; FALSE otherwise
	// --- Effect :	Highlights or removes the highlight from a top-level 
	//				(menu-bar) menu item
	BOOL HiliteMenuItem(int nHiliteItemIndex, BOOL bHilite);


	// mark the menu as the changed one
	void MarkMenuAsChanged();


protected:
	// --- In  :	msg	-	message ID
	//				wp	-	WPARAM
	//				lp	-	LPARAM
	// --- Out : 
	// --- Returns:	result of message handling. Different for different messages.
	// --- Effect :	Handle all messages that go to the window
	virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);	

	// helper function to add the object to global list of existing 
	// COXMenuBarHost objects
	void AddToGlobalList();

	// helper function to remove the object from the global list of existing 
	// COXMenuBarHost objects
	void RemoveFromGlobalList();

	// hook procedure for mouse messages
	static LRESULT CALLBACK MenuMouseHookProc(int nCode, WPARAM wParam, 
		LPARAM lParam);
	// hook procedure for keyboard messages
	static LRESULT CALLBACK MenuKeyboardHookProc(int nCode, WPARAM wParam, 
		LPARAM lParam);
};



// global map of all menu bar frame windows
template<class PARENTWND> 
CMap<HWND,HWND,void*,void*> COXMenuBarHost<PARENTWND>::g_arrMenuBarFrames;

// handle of the old mouse hook procedure
template<class PARENTWND> 
HHOOK COXMenuBarHost<PARENTWND>::g_pfnOldMouseHookProc=NULL;
// handle of the old keyboard hook procedure
template<class PARENTWND> 
HHOOK COXMenuBarHost<PARENTWND>::g_pfnOldKeyboardHookProc=NULL;
///////////////////////////////////////////


template<class PARENTWND> 
COXMenuBarHost<PARENTWND>::COXMenuBarHost()
{
	// has to be derived from CWnd or its derivates
	if(!IsKindOf(RUNTIME_CLASS(CWnd)))
	{
		TRACE(_T("COXMenuBarHost has to be derived from CFrameWnd or its derivates\n"));
		AfxThrowNotSupportedException();
	}

	// is it CFrameWnd derived
	m_bIsFrame=IsKindOf(RUNTIME_CLASS(CFrameWnd));

	// is it MDI
	m_bIsMDI=IsKindOf(RUNTIME_CLASS(CMDIFrameWnd));

	m_hDefaultMenu=NULL;

	m_bIsPopupMenuItem=FALSE;
}

template<class PARENTWND>
COXMenuBarHost<PARENTWND>::~COXMenuBarHost()
{
	if(::IsWindow(GetSafeHwnd()))
	{
		DestroyWindow();
	}

	ASSERT(g_arrMenuBarFrames.GetCount()>0 || g_pfnOldMouseHookProc==NULL);
	ASSERT(g_arrMenuBarFrames.GetCount()>0 || g_pfnOldKeyboardHookProc==NULL);
}


template<class PARENTWND>
BOOL COXMenuBarHost<PARENTWND>::OnCreateMenuBar(HMENU hMenu, CWnd* pWnd, 
												DWORD dwStyle, UINT nID)
{
	ASSERT(pWnd!=NULL);
	ASSERT(::IsWindow(pWnd->m_hWnd));

	if(hMenu==NULL)
		dwStyle&=~WS_VISIBLE;

	if(!GetMenuBar().Create(pWnd,dwStyle,nID))
	{
		TRACE(_T("COXMenuBarHost<PARENTWND>::OnCreateMenuBar: failed to create menu bar\n"));
		return FALSE;
	}
	if(!GetMenuBar().SetMenu(hMenu))
	{
		TRACE(_T("COXMenuBarHost<PARENTWND>::OnCreateMenuBar: failed to set menu to menu bar\n"));
		return FALSE;
	}

	if(m_bIsFrame)
	{
		((CFrameWnd*)this)->m_hMenuDefault=hMenu;
	}


	return TRUE;
}

template<class PARENTWND>
LRESULT COXMenuBarHost<PARENTWND>::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
	AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif

	ASSERT(::IsWindow(m_hWnd));

	switch(msg) 
	{
	// handle the creation of the frame
	case WM_CREATE:
		{
			// call parent implementation
			LRESULT lResult=PARENTWND::WindowProc(msg,wp,lp);
			if(lResult!=-1)
			{
				// there shouldn't be any menu bar created at the moment
				ASSERT(!::IsWindow(GetMenuBar().GetSafeHwnd()));

				AddToGlobalList();

				if(::IsWindow(GetMenuBar().GetSafeHwnd()))
				{
					GetMenuBar().DestroyWindow();
				}

				// retrieve default menu
				HMENU hMenu=::GetMenu(m_hWnd);
				if(hMenu==NULL || !::IsMenu(hMenu))
				{
					if(m_bIsMDI)
					{
						TRACE(_T("COXMenuBarHost<PARENTWND>::WindowProc: There is no menu associated with frame window\n"));
					}
					m_hDefaultMenu=NULL;
				}
				else
				{
					// save default menu
					m_hDefaultMenu=hMenu;
					// remove menu from window
					SetMenu(NULL);
				}

				// for SDI frame window it's important to set application
				// main window right now
				if(AfxGetApp()->m_pMainWnd==NULL && !m_bIsMDI && m_bIsFrame && 
					(GetStyle() & WS_CHILD)!=WS_CHILD)
				{
					AfxGetApp()->m_pMainWnd=this;
				}

				// create menu bar and set the menu
				if(!OnCreateMenuBar(m_hDefaultMenu,this,
					WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_SIZE_DYNAMIC|CBRS_GRIPPER,AFX_IDW_MENUBAR))
				{
					TRACE(_T("COXMenuBarHost<PARENTWND>::WindowProc: failed to initialize COXMenuBar object\n"));
					return lResult;
				}
	
				// setup hooks for mouse and keyboard messages
				if(g_pfnOldMouseHookProc==NULL)
				{
					g_pfnOldMouseHookProc=::SetWindowsHookEx(
						WH_MOUSE,MenuMouseHookProc,NULL,::GetCurrentThreadId());
				}
				if(g_pfnOldKeyboardHookProc==NULL)
				{
					g_pfnOldKeyboardHookProc=::SetWindowsHookEx(
						WH_KEYBOARD,MenuKeyboardHookProc,NULL,::GetCurrentThreadId());
				}
				//////////////////////////////////////////////
			}

			return lResult;
		}
	
	case WM_DESTROY:
		{
			RemoveFromGlobalList();

			// reset the menu customized mode
			if(::IsWindow(GetMenuBar().GetSafeHwnd()))
			{
				GetMenuBar().SetAdvancedCustomizationMode(FALSE);
			}

			// destroy menu bar
			if(::IsWindow(GetMenuBar().GetSafeHwnd()))
			{
				GetMenuBar().DestroyWindow();
			}

			if(g_arrMenuBarFrames.GetCount()==0)
			{
				// unhook mouse messages
				if(g_pfnOldMouseHookProc!=NULL)
				{
					VERIFY(::UnhookWindowsHookEx(g_pfnOldMouseHookProc));
					g_pfnOldMouseHookProc=NULL;
				}

				// unhook keyboard messages
				if(g_pfnOldKeyboardHookProc!=NULL)
				{
					VERIFY(::UnhookWindowsHookEx(g_pfnOldKeyboardHookProc));
					g_pfnOldKeyboardHookProc=NULL;
				}
			}

			break;
		}
	case WM_INITMENUPOPUP:
		{
			m_bIsPopupMenuItem=FALSE;
			m_arrPopupMenu.Add((HMENU)wp);

			WORD hiWord=HIWORD(lp);
			WORD loWord=LOWORD(lp);
			if(hiWord==0)
			{
				if(::IsWindow(GetMenuBar().GetSafeHwnd()))
				{
					loWord=(WORD)GetMenuBar().m_nActiveMenuItem;
				}
			}
			lp=MAKELPARAM(loWord,hiWord);
			
#if _MFC_VER>0x0421
#ifndef _AFX_NO_OLE_SUPPORT
			if(m_bIsFrame)
			{
				CFrameWnd* pFrameWnd=DYNAMIC_DOWNCAST(CFrameWnd,this);
				ASSERT(pFrameWnd!=NULL);
				if(pFrameWnd->m_pNotifyHook!=NULL)
				{
					if(pFrameWnd->m_pNotifyHook->OnInitMenuPopup(
						CMenu::FromHandle((HMENU)wp),LOWORD(lp),HIWORD(lp)))
					{
						return 0;
					}

					if(pFrameWnd->m_pNotifyHook->m_pActiveItem!=NULL)
					{
						CWnd* pWnd=pFrameWnd->m_pNotifyHook->
							m_pActiveItem->GetInPlaceWindow();
						ASSERT(pWnd!=NULL);
						pWnd->SendMessage(msg,wp,lp);
						return 0;
					}
				}
			}
#endif	//	_AFX_NO_OLE_SUPPORT
#endif	//	_MFC_VER>0x0421
			break;
		}

	case WM_MENUSELECT:
		{
			UINT nFlags=HIWORD(wp);
			m_bIsPopupMenuItem=((nFlags&MF_POPUP)!=0 && 
				!(nFlags==0xFFFF && lp==NULL) ? TRUE : FALSE);

#if _MFC_VER>0x0421
#ifndef _AFX_NO_OLE_SUPPORT
			if(m_bIsFrame)
			{
				CFrameWnd* pFrameWnd=DYNAMIC_DOWNCAST(CFrameWnd,this);
				ASSERT(pFrameWnd!=NULL);
				if(pFrameWnd->m_pNotifyHook!=NULL)
				{
					if(pFrameWnd->m_pNotifyHook->m_pActiveItem!=NULL)
					{
						CWnd* pWnd=pFrameWnd->m_pNotifyHook->
							m_pActiveItem->GetInPlaceWindow();
						ASSERT(pWnd!=NULL);
						pWnd->SendMessage(msg,wp,lp);
					}
				}
			}
#endif	//	_AFX_NO_OLE_SUPPORT
#endif	//	_MFC_VER>0x0421

			if((nFlags&MF_HILITE)!=0)
			{
				while(m_arrPopupMenu.GetSize()>0 && 
					m_arrPopupMenu[m_arrPopupMenu.GetSize()-1]!=(HMENU)lp)
				{
					m_arrPopupMenu.RemoveAt(m_arrPopupMenu.GetSize()-1);
				}
			}

			break;
		}

	case WM_EXITMENULOOP:
		{
			if(wp==0)
			{
				m_arrPopupMenu.RemoveAll();
			}
			break;
		}

	case WM_COMMAND:
		{
			if(HIWORD(wp)==0)
			{
				int nCmdID=LOWORD(wp);
				if(nCmdID==ID_OX_SHOWALLITEMS)
				{
					// v9.3 update 01 modification (replaced) Manfred Drasch
					// GetMenuBar().m_nActivateNextItem=GetMenuBar().m_nActiveMenuItem;
					GetMenuBar().PostMessage(WM_DISPLAYPOPUPMENU_ALLITEMS);
				}
			}
			break;
		}

	case WM_OX_MENUCHANGED:
		{
			MarkMenuAsChanged();
			break;
		}
	}

	// I don't handle it: pass along
	return PARENTWND::WindowProc(msg,wp,lp);
}


template<class PARENTWND>
void COXMenuBarHost<PARENTWND>::MarkMenuAsChanged()
{
	// mark the current menu as a changed one
	GetMenuBar().MarkAsChanged();
}


template<class PARENTWND>
void COXMenuBarHost<PARENTWND>::AddToGlobalList()
{
	ASSERT(::IsWindow(GetSafeHwnd()));
	g_arrMenuBarFrames.SetAt(GetSafeHwnd(),(void*)this);
}


template<class PARENTWND>
void COXMenuBarHost<PARENTWND>::RemoveFromGlobalList()
{
	ASSERT(::IsWindow(GetSafeHwnd()));
	g_arrMenuBarFrames.RemoveKey(GetSafeHwnd());
}

// last mouse cursor position when hew menubar item was activated 
static CPoint g_ptLast(-1,-1);

template<class PARENTWND>
BOOL COXMenuBarHost<PARENTWND>::HandleMouseMsg(int nCode, 
											   WPARAM wParam, 
											   LPARAM lParam)
{
	if(!::IsWindow(m_hWnd) || GetMenuBar().IsInAdvancedCustomizationMode())
	{
		return FALSE;
	}

	if(nCode==HC_ACTION && ::IsWindow(GetMenuBar().GetSafeHwnd()))
	{
		MOUSEHOOKSTRUCT* pMHS=(MOUSEHOOKSTRUCT*)lParam;
		// there should be an active menu bar at the moment
		if(GetMenuBar().IsInDisplayingMode() && 
			pMHS->hwnd!=GetMenuBar().GetSafeHwnd())
		{
			CRect rect;
			GetMenuBar().GetWindowRect(rect);
			// handle WM_MOUSEMOVE and WM_LBUTTONDOWN messages
			if((wParam==WM_MOUSEMOVE ||	wParam==WM_LBUTTONDOWN) &&
				rect.PtInRect(pMHS->pt) &&  
				::WindowFromPoint(pMHS->pt)==GetMenuBar().GetSafeHwnd())
			{
				CPoint point=pMHS->pt;
				GetMenuBar().ScreenToClient(&point);
				// test the mouse cursor point
				int hitTest=GetMenuBar().HitTest(point,OX_MNU_CLIENT);

				if(wParam==WM_MOUSEMOVE && g_ptLast!=point &&
					hitTest!=GetMenuBar().m_nActiveMenuItem && 
					(hitTest>=0 || hitTest==OX_MNU_ICON) &&
					!(GetMenuBar().m_nActiveMenuItem==
					::GetMenuItemCount(GetMenuBar().m_hMenu) && 
					hitTest==OX_MNU_ICON))
				{
					// set the menu item that will be activated next
					GetMenuBar().m_nActivateNextItem=(hitTest!=OX_MNU_ICON) ? hitTest : 
						::GetMenuItemCount(GetMenuBar().m_hMenu);

					// emulate mouse pressing
					::PostMessage(pMHS->hwnd,WM_LBUTTONDOWN,MK_LBUTTON,
						MAKELPARAM(point.x,point.y));
					::PostMessage(pMHS->hwnd,WM_LBUTTONUP,MK_LBUTTON,
						MAKELPARAM(point.x,point.y));
	
					return TRUE;
				}
				else if(wParam==WM_LBUTTONDOWN && (hitTest>=0 || hitTest==OX_MNU_ICON))
				{
					// set the forbidden item
					GetMenuBar().m_nForbiddenItem=(hitTest!=OX_MNU_ICON) ? hitTest : 
						::GetMenuItemCount(GetMenuBar().m_hMenu);
				}

				g_ptLast=point;
			}
		}
	}

    return FALSE;
}


// flag that ALT was pressed down
static BOOL g_altDownNoOtherKey=FALSE;

template<class PARENTWND>
BOOL COXMenuBarHost<PARENTWND>::
HandleKeyboardMsg(int nCode, WPARAM wParam, LPARAM lParam)
{
	ASSERT(::IsWindow(m_hWnd));

	if(nCode==HC_ACTION && ::IsWindow(GetMenuBar().GetSafeHwnd()) && 
		IsWindowEnabled() && ::GetActiveWindow()==GetSafeHwnd())
	{
		// handle differently keyboard messages when menubar is in 
		// displaying mode
		if(!GetMenuBar().IsInDisplayingMode())
		{
			if(wParam==VK_MENU)
			{
				if(lParam>=0)
				{
					if(!(lParam&(1<<30)))
					{
						g_altDownNoOtherKey=TRUE;
						return TRUE;
					}
				}
				else if(g_altDownNoOtherKey)
				{
					// ALT key was pressed
					GetMenuBar().HandleMenuKey(0);
					g_altDownNoOtherKey=FALSE;
					return TRUE;
				}
			}

			if(wParam!=VK_MENU && (::GetAsyncKeyState(VK_MENU)<0 || 
				GetMenuBar().IsInSurfingMode()))
			{
				g_altDownNoOtherKey=FALSE;
				if((lParam&0x80000000)==0 && int(lParam)>=0 && 
					::GetAsyncKeyState(VK_F4)>=0)
				{
					// handle the key 
					if(GetMenuBar().HandleMenuKey(PtrToUint(wParam)))
						return TRUE;
				}
			}
		}
		else
		{
			// handle only VK_LEFT, VK_RIGHT and VK_ESCAPE in the specific way 
			// while in displaying mode
			if(int(lParam)>=0 && ((wParam==VK_LEFT && m_arrPopupMenu.GetSize()<=1) || 
				(wParam==VK_RIGHT && !m_bIsPopupMenuItem) || 
				(wParam==VK_ESCAPE && m_arrPopupMenu.GetSize()==1)))
			{
				// handle the key 
				if(GetMenuBar().HandleMenuKey(PtrToUint(wParam)))
					return TRUE;
			}
		}
	}

    return FALSE;
}


template<class PARENTWND>
COXMenuBar& COXMenuBarHost<PARENTWND>::GetMenuBar()
{
	return m_menuBar;
}


template<class PARENTWND>
void COXMenuBarHost<PARENTWND>::DrawMenuBar()
{
	if(::IsWindow(GetMenuBar().GetSafeHwnd()))
		GetMenuBar().DrawMenuBar();
	else
		PARENTWND::DrawMenuBar();
}


template<class PARENTWND>
CMenu* COXMenuBarHost<PARENTWND>::GetMenu()
{
	if(::IsWindow(GetMenuBar().GetSafeHwnd()))
// v9.3 update 02 - using GetHMenu for VS2008
#if _MFC_VER >= 0x0800
		return CMenu::FromHandle(GetMenuBar().GetHMenu());
#else
		return CMenu::FromHandle(GetMenuBar().GetMenu());
#endif
	else
		return PARENTWND::GetMenu();
}


template<class PARENTWND>
BOOL COXMenuBarHost<PARENTWND>::SetMenu(CMenu* pMenu)
{
	if(::IsWindow(GetMenuBar().GetSafeHwnd()))
		return GetMenuBar().SetMenu(pMenu==NULL ? NULL : pMenu->GetSafeHmenu());
	else
		return PARENTWND::SetMenu(pMenu);
}


template<class PARENTWND>
BOOL COXMenuBarHost<PARENTWND>::HiliteMenuItem(int nHiliteItemIndex, BOOL bHilite)
{
	if(GetMenuBar().IsInDisplayingMode())
	{
		return FALSE;
	}
	
	return GetMenuBar().SetSurfingMode(bHilite,nHiliteItemIndex);
}


template<class PARENTWND>
LRESULT CALLBACK COXMenuBarHost<PARENTWND>::
MenuMouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
	AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif

	ASSERT(g_pfnOldMouseHookProc!=NULL);

	if(nCode<0)
	{
		// have to call the default implementation
	    return ::CallNextHookEx(g_pfnOldMouseHookProc,nCode,wParam,lParam);
	}

	DWORD dwMessagePos=::GetMessagePos();
	POINTS points=MAKEPOINTS(dwMessagePos);
	CPoint ptLast(points.x,points.y);
	CWnd* pWnd=WindowFromPoint(ptLast);
	ASSERT(pWnd!=NULL);
	CWnd* pTopLevelWnd=pWnd->GetTopLevelParent();
	ASSERT(pTopLevelWnd!=NULL);

	ASSERT(g_arrMenuBarFrames.GetCount()>0);
	POSITION pos=g_arrMenuBarFrames.GetStartPosition();
	while(pos!=NULL)
	{
		HWND hWnd=NULL;
		void* pMenuBarFrame=NULL;
		g_arrMenuBarFrames.GetNextAssoc(pos,hWnd,pMenuBarFrame);
		ASSERT(pMenuBarFrame!=NULL);
		// call COXMenuBarHost implementation
		if(::IsWindow(((COXMenuBarHost<PARENTWND>*)pMenuBarFrame)->m_hWnd) && 
			(pMenuBarFrame==pTopLevelWnd))
		{
			if(((COXMenuBarHost<PARENTWND>*)pMenuBarFrame)->
				HandleMouseMsg(nCode,wParam,lParam))
			{
				return 0;
			}
		}
	}

    return ::CallNextHookEx(g_pfnOldMouseHookProc,nCode,wParam,lParam);
}


template<class PARENTWND>
LRESULT CALLBACK COXMenuBarHost<PARENTWND>::
MenuKeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
	AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif

	ASSERT(g_pfnOldKeyboardHookProc!=NULL);

	if(nCode<0)
	{
		// have to call the default implementation
	    return ::CallNextHookEx(g_pfnOldKeyboardHookProc,nCode,wParam,lParam);
	}

	CWnd* pTopLevelWnd=NULL;
	CWnd* pWnd=GetFocus();
	if(pWnd!=NULL)
	{
		pTopLevelWnd=pWnd->GetTopLevelParent();
	}
	else
	{
		pTopLevelWnd=CWnd::GetDesktopWindow();
	}
	ASSERT(pTopLevelWnd!=NULL);

	ASSERT(g_arrMenuBarFrames.GetCount()>0);
	POSITION pos=g_arrMenuBarFrames.GetStartPosition();
	while(pos!=NULL)
	{
		HWND hWnd=NULL;
		void* pMenuBarFrame=NULL;
		g_arrMenuBarFrames.GetNextAssoc(pos,hWnd,pMenuBarFrame);
		ASSERT(pMenuBarFrame!=NULL);
		// call COXMenuBarHost implementation
		if(::IsWindow(((COXMenuBarHost<PARENTWND>*)pMenuBarFrame)->m_hWnd) && 
			pMenuBarFrame==pTopLevelWnd)
		{
			if(((COXMenuBarHost<PARENTWND>*)pMenuBarFrame)->
				HandleKeyboardMsg(nCode,wParam,lParam))
			{
				return 1;
			}
		}
	}

    return ::CallNextHookEx(g_pfnOldKeyboardHookProc,nCode,wParam,lParam);
}


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

template<class PARENTDOCKBAR>
class COXMenuDockBar : public PARENTDOCKBAR
{
public:
	// --- In  :
	// --- Out : 
	// --- Returns:
	// --- Effect : Constructs the object
	COXMenuDockBar();


// Operations

	// --- In  :	bStretch	-	Indicates whether the bar should be 
	//								stretched to the size of the frame. 
	//				bHorz		-	Indicates that the bar is horizontally 
	//								or vertically oriented. The bHorz parameter 
	//								is nonzero if the bar is horizontally 
	//								oriented and is 0 if it is vertically 
	//								oriented.
	// --- Out : 
	// --- Returns:	The dock bar size, in pixels, of a CSize object
	// --- Effect : Calculates size of the dock bar
	virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz);

public:
	// --- In  :	
	// --- Out : 
	// --- Returns:
	// --- Effect : Destructs the object
	virtual ~COXMenuDockBar();
};


// constructor
template<class PARENTDOCKBAR>
COXMenuDockBar<PARENTDOCKBAR>::COXMenuDockBar()
{
	// has to be derived from CDockBar or its derivates
	if(!IsKindOf(RUNTIME_CLASS(CDockBar)))
	{
		TRACE(_T("COXMenuDockBar has to be derived from CDockBar or its derivations\n"));
		AfxThrowNotSupportedException();
	}
}

// destructor
template<class PARENTDOCKBAR>
COXMenuDockBar<PARENTDOCKBAR>::~COXMenuDockBar()
{
}

// mostly MFC code
template<class PARENTDOCKBAR>
CSize COXMenuDockBar<PARENTDOCKBAR>::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
{
	// scan all the control bars
	int nPos=0;
	for (nPos=0; nPos < m_arrBars.GetSize(); nPos++)
	{
		CControlBar* pBar=GetDockedControlBar(nPos);
		if(pBar!=NULL && ::IsWindow(pBar->m_hWnd) && pBar->IsVisible() && 
			pBar->IsKindOf(RUNTIME_CLASS(COXMenuBar)) &&
			((COXMenuBar*)pBar)->GetTakeEntireRow())
		{
			((COXMenuBar*)pBar)->m_bForceEntireRow=TRUE;
		}
	}

	// call the parent implementation
	CSize sizeFixed=PARENTDOCKBAR::CalcFixedLayout(bStretch,bHorz);
	CPoint pt(-afxData.cxBorder2, -afxData.cyBorder2);

	// get max size
	CSize sizeMax;
	if(!m_rectLayout.IsRectEmpty())
	{
		sizeMax=m_rectLayout.Size();
	}
	else
	{
		CRect rectFrame;
		CFrameWnd* pFrame=GetParentFrame();
		ASSERT(pFrame!=NULL);
		pFrame->GetClientRect(rectFrame);
		pFrame->ClientToScreen(rectFrame);

		for(int nID=AFX_IDW_CONTROLBAR_FIRST; nID<=AFX_IDW_CONTROLBAR_LAST; nID++)
		{
			CControlBar* pBar=m_pDockSite->GetControlBar(nID);

			if(pBar!=NULL && pBar->IsDockBar() && 
				pBar->IsVisible() && !pBar->IsFloating())
			{
				CRect rectBar;
				pBar->GetWindowRect(rectBar);
				if(pBar->GetStyle() & CBRS_TOP)
				{
					rectFrame.top=__max(rectFrame.top,rectBar.bottom);
				}
				else if(pBar->GetStyle() & CBRS_BOTTOM)
				{
					rectFrame.bottom=__min(rectFrame.bottom,rectBar.top);
				}
			}
		}

		sizeMax=rectFrame.Size();
	}

	// prepare for layout
    AFX_SIZEPARENTPARAMS layout;
    layout.hDWP=m_bLayoutQuery ? NULL : 
		::BeginDeferWindowPos(PtrToInt(m_arrBars.GetSize()));
	
	BOOL bAdjustBars=FALSE;
	CSize sizeOffset(0,0);
	// scan all the control bars
	for (nPos=0; nPos < m_arrBars.GetSize(); nPos++)
	{
		CControlBar* pBar=GetDockedControlBar(nPos);
		if(pBar!=NULL && ::IsWindow(pBar->m_hWnd) && pBar->IsVisible() && 
			pBar->IsKindOf(RUNTIME_CLASS(COXMenuBar)) &&
			((COXMenuBar*)pBar)->GetTakeEntireRow())
		{
			// get ideal rect for bar
			DWORD dwMode=0;
			if((pBar->m_dwStyle & CBRS_SIZE_DYNAMIC) &&
				(pBar->m_dwStyle & CBRS_FLOATING))
				dwMode |= LM_HORZ | LM_MRUWIDTH;
			else if(pBar->m_dwStyle & CBRS_ORIENT_HORZ)
				dwMode |= LM_HORZ | LM_HORZDOCK;
			else
				dwMode |=  LM_VERTDOCK;

			if((dwMode&LM_HORZDOCK) || (dwMode&LM_VERTDOCK))
			{
				// get current rect for bar
				CRect rectBar;
				pBar->GetWindowRect(&rectBar);
				ScreenToClient(&rectBar);

				CSize sizeBar(rectBar.Width(),rectBar.Height());
				CRect rect(pt,sizeBar);

				if(bHorz)
				{
					rect.right=sizeMax.cx+afxData.cxBorder2;
					rect.OffsetRect(0,rectBar.top-rect.top);

					// check if menubar was droped on the same row with other
					// control bar(s)
					if(rectBar.left>rect.left)
					{
						ASSERT(nPos>0);
						int nPrevPos=nPos-1;
						CControlBar* pPrevBar=NULL;
						while(TRUE)
						{
							ASSERT(nPrevPos>=0);
							pPrevBar=GetDockedControlBar(nPrevPos);
							if(pPrevBar==NULL || 
								!::IsWindow(pPrevBar->GetSafeHwnd()) || 
								pPrevBar->IsVisible())
							{
								break;
							}
							nPrevPos--;
						}

						if(pPrevBar!=NULL)
						{
							CRect rectPrevBar;
							pPrevBar->GetWindowRect(&rectPrevBar);
							rect.OffsetRect(0,rectPrevBar.Height()-
								afxData.cyBorder2);
							sizeOffset.cy+=__min(rectBar.Height(),
								rectPrevBar.Height())-afxData.cyBorder2;
							m_arrBars.InsertAt(nPos, (CObject*)NULL);
							nPos++;
						}
					}

					// mark the end of the row
					CControlBar* pNextBar=NULL;
					int nNextPos=nPos+1;
					while(nNextPos<m_arrBars.GetSize())
					{
						pNextBar=GetDockedControlBar(nNextPos);
						if(pNextBar==NULL || !::IsWindow(pNextBar->GetSafeHwnd()) || 
							pNextBar->IsVisible())
							break;
						nNextPos++;
					}

					if(pNextBar!=NULL)
					{
						m_arrBars.InsertAt(nPos+1, (CObject*)NULL);
						CRect rectNextBar;
						pNextBar->GetWindowRect(&rectNextBar);
						sizeOffset.cy+=rectNextBar.Height()-afxData.cyBorder2;
					}
				}
				else
				{
					rect.bottom=sizeMax.cy;
					rect.OffsetRect(rectBar.left-rect.left,0);

					// check if menubar was droped on the same row with other
					// control bar(s)
					if(rectBar.top>rect.top)
					{
						ASSERT(nPos>0);
						int nPrevPos=nPos-1;
						CControlBar* pPrevBar=NULL;
						while(TRUE)
						{
							ASSERT(nPrevPos>=0);
							pPrevBar=GetDockedControlBar(nPrevPos);
							if(pPrevBar==NULL || 
								!::IsWindow(pPrevBar->GetSafeHwnd()) || 
								pPrevBar->IsVisible())
								break;
							nPrevPos--;
						}

						if(pPrevBar!=NULL)
						{
							CRect rectPrevBar;
							pPrevBar->GetWindowRect(&rectPrevBar);
							rect.OffsetRect(rectPrevBar.Width()-afxData.cxBorder2,0);
							sizeOffset.cx+=__min(rectBar.Width(),
								rectPrevBar.Width())-afxData.cxBorder2;
							m_arrBars.InsertAt(nPrevPos+1, (CObject*)NULL);
							nPos++;
						}
					}

					// mark the end of row
					CControlBar* pNextBar=NULL;
					int nNextPos=nPos+1;
					while(nNextPos<m_arrBars.GetSize())
					{
						CControlBar* pNextBar=GetDockedControlBar(nNextPos);
						if(pNextBar==NULL || !::IsWindow(pNextBar->GetSafeHwnd()) || 
							pNextBar->IsVisible())
							break;
						nNextPos++;
					}

					if(pNextBar!=NULL)
					{
						m_arrBars.InsertAt(nPos+1, (CObject*)NULL);
					}
				}

				if(sizeOffset.cx!=0 || sizeOffset.cy!=0)
				{
					bAdjustBars=TRUE;
				}

				if((bHorz && rectBar.Width()!=rect.Width() && 
					!(rectBar.Width()>rect.Width())) || 
					(!bHorz && rectBar.Height()!=rect.Height() && 
					!(rectBar.Height()>rect.Height())))
				{
					AfxRepositionWindow(&layout, pBar->m_hWnd, &rect);
				}
			}
		}
		else if(pBar!=NULL && bAdjustBars)
		{
			// get current rect for bar
			CRect rectBar;
			pBar->GetWindowRect(&rectBar);
			ScreenToClient(&rectBar);
			rectBar+=sizeOffset;
			AfxRepositionWindow(&layout, pBar->m_hWnd, &rectBar);
		}
	}

	if(!m_bLayoutQuery)
	{
		if(layout.hDWP==NULL || !::EndDeferWindowPos(layout.hDWP))
		{
			TRACE(_T("Warning: DeferWindowPos failed - low system resources\n"));
		}
	}

	if(bAdjustBars)
	{
		sizeFixed+=sizeOffset;
	}


	// scan all the control bars
	for (nPos=0; nPos < m_arrBars.GetSize(); nPos++)
	{
		CControlBar* pBar=GetDockedControlBar(nPos);
		if(pBar!=NULL && ::IsWindow(pBar->m_hWnd) && pBar->IsVisible() && 
			pBar->IsKindOf(RUNTIME_CLASS(COXMenuBar)) &&
			((COXMenuBar*)pBar)->GetTakeEntireRow())
		{
			((COXMenuBar*)pBar)->m_bForceEntireRow=FALSE;
		}
	}

	return sizeFixed;
}


template<class PARENTFRAME, class PARENTDOCKBAR>
class COXMenuBarFrame : public COXMenuBarHost<PARENTFRAME>
{
public:
	// dock bar control used internally
	typedef COXMenuDockBar<PARENTDOCKBAR> CInternalDockBar;

	// --- In  :	dwDockStyle		-	The desired styles for the control bar. 
	//									menu bar style must be CBRS_ALIGN_XXX 
	//									or 0 only. Refer to the description of 
	//									CControlBar::SetBarStyle function 
	//									for details.
	// --- Out : 
	// --- Returns:
	// --- Effect : Constructs the object
	COXMenuBarFrame(DWORD dwDockStyle=CBRS_ALIGN_ANY, 
		CRuntimeClass* pFloatingFrameClass=RUNTIME_CLASS(CMiniDockFrameWnd));


// Operations

	// --- In  :	dwDockStyle		-	Specifies which sides of the frame 
	//									window can serve as docking sites for 
	//									control bars. Refer to the description
	//									of CFrameWnd::EnableDocking function 
	//									for details.
	// --- Out :	
	// --- Returns:
	// --- Effect : Call this function to enable dockable control bars in a 
	//				frame window. By default, control bars will be docked to a 
	//				side of the frame window in the following order: 
	//				top, bottom, left, right.
	virtual void EnableDocking(DWORD dwDockStyle);


	virtual void RecalcLayout(BOOL bNotify=TRUE);
	

	virtual BOOL LoadFrame(UINT nIDResource, 
		DWORD dwDefaultStyle=WS_OVERLAPPEDWINDOW|FWS_ADDTOTITLE,
		CWnd* pParentWnd=NULL, CCreateContext* pContext=NULL);

	virtual void OnUpdateFrameMenu(HMENU hMenuAlt);

	// --- In  :	bEnable		-	Specifies whether then snapping behaviour
	//								should be turned on or off
	// --- Out :	
	// --- Returns:
	// --- Effect : Call this function to turn on the snaping and tear-off behaviour
	//				of toolbars, menubars and docking windows
	void EnableSnapping(BOOL bEnable = TRUE);

protected:
	// --- In  :	lpcs		-	A pointer to a Windows CREATESTRUCT 
	//								structure.
	//				pContext	-	A pointer to a CCreateContext structure
	// --- Out :	
	// --- Returns:	Nonzero if successful; otherwise 0
	// --- Effect : 
	virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);

// Attributes
public:

protected:

	// Specifies whether the snaping and tear-off behaviour of toolbars,
	// menubars and docking windows is enabled
	BOOL m_bEnableSnapping;

	// Specifies which sides of the frame window can serve as docking 
	// sites for menu bar.
	DWORD m_dwDockStyle;

	// address of the window procedure, or a handle representing the address 
	// of the window procedure of MDIClient window
	WNDPROC m_pfnOldMDIClientProc;

	// flag that specifies that MDIClient window is subclassed
	BOOL m_bIsMDISubclassed;

public:
	// --- In  :
	// --- Out : 
	// --- Returns:
	// --- Effect : Destructs the object
	virtual ~COXMenuBarFrame();

	
	// --- In  :	hWnd	-	Handle to the window to receive the message. 
	//				msg		-	Specifies the message. 
	//				wp		-	Specifies additional message-specific 
	//							information. The contents of this parameter 
	//							depend on the value of the msg parameter. 
	//				lp		-	Specifies additional message-specific 
	//							information. The contents of this parameter 
	//							depend on the value of the Msg parameter. 
	// --- Out : 
	// --- Returns:	the result of handling of message that was sent to 
	//				MDIClient window
	// --- Effect : Handles all messages that go to MDIClient window
	virtual LRESULT HandleMDIClientMsg(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);

protected:
	// --- In  :	msg	-	message ID
	//				wp	-	WPARAM
	//				lp	-	LPARAM
	// --- Out : 
	// --- Returns:	result of message handling. Different for different messages.
	// --- Effect :	Handle all messages that go to the window
	virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);	

	// helper for resizing the menubar
	BOOL CheckMenuBarSize(BOOL bNotify=TRUE);

	// helper function to subclass MDIClient window
	void SubclassMDIClient();

	// handles context help
	afx_msg void OnContextHelp();
	// handles all messages in context help mode
	BOOL ProcessHelpMsg(MSG& msg, DWORD* pContext);

	// subclass window procedure for MDIClient window
	static LRESULT CALLBACK MDISubclassProc(HWND hWnd, UINT msg, 
		WPARAM wp, LPARAM lp);
};



// analogue of CFrameWnd::dwDockBarMap
static const DWORD g_dwDockBarMap[4][2] =
{
	{ AFX_IDW_DOCKBAR_TOP,      CBRS_TOP    },
	{ AFX_IDW_DOCKBAR_BOTTOM,   CBRS_BOTTOM },
	{ AFX_IDW_DOCKBAR_LEFT,     CBRS_LEFT   },
	{ AFX_IDW_DOCKBAR_RIGHT,    CBRS_RIGHT  },
};


template<class PARENTFRAME, class PARENTDOCKBAR>
COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::
COXMenuBarFrame(DWORD dwDockStyle/*=CBRS_ALIGN_ANY*/, 
				CRuntimeClass* pFloatingFrameClass/*=RUNTIME_CLASS(CMiniDockFrameWnd)*/)
{
	// has to be derived from CframeWnd or its derivates
	if(!IsKindOf(RUNTIME_CLASS(CFrameWnd)))
	{
		TRACE(_T("COXMenuBarFrame has to be derived from CFrameWnd or its derivates\n"));
		AfxThrowNotSupportedException();
	}

	// menu bar style must be CBRS_ALIGN_XXX or 0 only
	ASSERT(dwDockStyle==0 || (dwDockStyle & ~CBRS_ALIGN_ANY)==0);
	m_dwDockStyle=dwDockStyle;

	m_pfnOldMDIClientProc=NULL;

	m_bIsMDISubclassed=FALSE;

	m_pFloatingFrameClass=pFloatingFrameClass;

	m_bEnableSnapping = FALSE;
}

template<class PARENTFRAME, class PARENTDOCKBAR>
COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::~COXMenuBarFrame()
{
}


template<class PARENTFRAME, class PARENTDOCKBAR>
BOOL COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::
OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
	BOOL bResult=COXMenuBarHost<PARENTFRAME>::OnCreateClient(lpcs,pContext);
	
	AddToGlobalList();

	if(bResult && m_bIsMDI)
		SubclassMDIClient();

	return bResult;
}


template<class PARENTFRAME, class PARENTDOCKBAR>
void COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::RecalcLayout(BOOL bNotify/*=TRUE*/)
{
	COXMenuBarHost<PARENTFRAME>::RecalcLayout(bNotify);
	CheckMenuBarSize(bNotify);
}


template<class PARENTFRAME, class PARENTDOCKBAR>
BOOL COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::
LoadFrame(UINT nIDResource, 
		  DWORD dwDefaultStyle/*=WS_OVERLAPPEDWINDOW|FWS_ADDTOTITLE*/,
		  CWnd* pParentWnd/*=NULL*/, 
		  CCreateContext* pContext/*=NULL*/)
{
	if(COXMenuBarHost<PARENTFRAME>::LoadFrame(nIDResource,dwDefaultStyle,
		pParentWnd,pContext))
	{
		if(::IsWindow(GetMenuBar().GetSafeHwnd())) {
			// v9.3 update 02 - using GetHMenu for VS2008
#if _MFC_VER >= 0x0800
			m_hMenuDefault= GetMenuBar().GetHMenu();
#else
			m_hMenuDefault= GetMenuBar().GetMenu();
#endif
		}
		return TRUE;
	}

	return FALSE;
}


template<class PARENTFRAME, class PARENTDOCKBAR>
void COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::OnUpdateFrameMenu(HMENU hMenuAlt)
{
	if(!m_bIsMDI)
	{
		if(hMenuAlt==NULL)
		{
			// attempt to get default menu from document
			CDocument* pDoc=GetActiveDocument();
			if(pDoc!=NULL)
				hMenuAlt=pDoc->GetDefaultMenu();
			// use default menu stored in frame if none from document
			if(hMenuAlt==NULL)
				hMenuAlt=m_hMenuDefault;
		}
		// finally, set the menu
		SetMenu(CMenu::FromHandle(hMenuAlt));
	}
	else
	{
		PARENTFRAME::OnUpdateFrameMenu(hMenuAlt);
	}
}


template<class PARENTFRAME, class PARENTDOCKBAR>
void COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::EnableSnapping(BOOL bEnable/*=TRUE*/)
{
	m_bEnableSnapping = bEnable;
}

template<class PARENTFRAME, class PARENTDOCKBAR>
LRESULT COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::WindowProc(UINT msg, 
															   WPARAM wp, 
															   LPARAM lp)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
	AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif

	ASSERT(::IsWindow(((CFrameWnd*)this)->m_hWnd));

	switch(msg) 
	{
	// handle the creation of the frame
	case WM_CREATE:
		{
			// call parent implementation
			LRESULT lResult=COXMenuBarHost<PARENTFRAME>::WindowProc(msg,wp,lp);
			if(lResult!=-1)
			{
				// menu must be already created at the moment
				ASSERT(::IsWindow(GetMenuBar().GetSafeHwnd()));

				if(m_bIsMDI)
					// in order to subclass MDIClient if it's not done yet
					SendMessage(WM_IDLEUPDATECMDUI);

				// setup docking environment
				GetMenuBar().EnableDocking(m_dwDockStyle);
				if(m_dwDockStyle!=0)
				{
					EnableDocking(m_dwDockStyle);
					CRect rect(0,0,0,0);
					DockControlBar(&GetMenuBar(),(UINT)0,&rect);
				}
				//////////////////////////////
			}

			return lResult;
		}
	case WM_COMMAND:
		{
			WORD loWord=LOWORD(wp);
			if(loWord==ID_CONTEXT_HELP)
			{
				OnContextHelp();
				return 0;
			}

#if _MFC_VER>0x0421
#ifndef _AFX_NO_OLE_SUPPORT
			if(m_pNotifyHook!=NULL)
			{
				if(m_pNotifyHook->m_pActiveItem!=NULL)
				{
					CWnd* pWnd=m_pNotifyHook->m_pActiveItem->GetInPlaceWindow();
					ASSERT(pWnd!=NULL);
					if(pWnd->SendMessage(msg,wp,lp))
					{
						return TRUE;
					}
				}
			}
#endif	//	_AFX_NO_OLE_SUPPORT
#endif	//	_MFC_VER>0x0421

			break;
		}
	case WM_IDLEUPDATECMDUI:
		if(m_bIsMDI)
		{
			m_bIsMDISubclassed=FALSE;
			::SendMessage(((CMDIFrameWnd*)this)->m_hWndMDIClient,WM_NULL,0,0);
			if(!m_bIsMDISubclassed)
			{
				TRACE(_T("COXMenuBarFrame: menubar has been removed out of the list of objects that subclass MDIClient\n"));
				SubclassMDIClient();
				if(::GetMenu(m_hWnd)!=NULL)
				{
					SetMenu(NULL);
				}
			}
		}
		break;

	case WM_INITMENU:
		{
			if (GetMenuBar().m_hMenu != NULL)
				wp = (WPARAM) GetMenuBar().m_hMenu;
		}
		break;

	case WM_QUERYSNAPPING:
		{
			return m_bEnableSnapping;
		}
		break;
	}

	// I don't handle it: pass along
		return COXMenuBarHost<PARENTFRAME>::WindowProc(msg,wp,lp);
}


template<class PARENTFRAME, class PARENTDOCKBAR>
void COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::OnContextHelp()
{
	// don't enter twice, and don't enter if initialization fails
	if(m_bHelpMode==HELP_ACTIVE || !CanEnterHelpMode())
		return;

	// don't enter help mode with pending WM_EXITHELPMODE message
	MSG msg;
	if(PeekMessage(&msg, m_hWnd, WM_EXITHELPMODE, WM_EXITHELPMODE,
		PM_REMOVE|PM_NOYIELD))
	{
		return;
	}

	BOOL bHelpMode=m_bHelpMode;
	ASSERT(m_bHelpMode==HELP_INACTIVE || m_bHelpMode==HELP_ENTERING);
	m_bHelpMode=HELP_ACTIVE;

#ifndef _AFX_NO_OLE_SUPPORT
	// allow any in-place active servers to go into help mode
	if(bHelpMode!=HELP_ENTERING && m_pNotifyHook!=NULL &&
		!m_pNotifyHook->OnContextHelp(TRUE))
	{
		TRACE0("Error: an in-place server failed to enter context help mode.\n");
		m_pNotifyHook->OnContextHelp(FALSE);    // undo partial help mode
		m_bHelpMode=HELP_INACTIVE;
		return;
	}
#endif

	if(bHelpMode==HELP_INACTIVE)
	{
		// need to delay help startup until later
		PostMessage(WM_COMMAND, ID_CONTEXT_HELP);
		m_bHelpMode=HELP_ENTERING;
		return;
	}

	ASSERT(m_bHelpMode==HELP_ACTIVE);

	// display special help mode message on status bar
	UINT nMsgSave=(UINT)SendMessage(WM_SETMESSAGESTRING,
		(WPARAM)AFX_IDS_HELPMODEMESSAGE);
	if(nMsgSave==0)
	{
		nMsgSave=AFX_IDS_IDLEMESSAGE;
	}

	DWORD   dwContext=0;
	POINT   point;

	GetCursorPos(&point);
	SetHelpCapture(point, NULL);
	LONG lIdleCount=0;
	CWinApp* pApp=AfxGetApp();

	while(m_bHelpMode)
	{
		if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if(!ProcessHelpMsg(msg, &dwContext))
			{
				break;
			}
			ASSERT(dwContext==0);
		}
		else if(!pApp->OnIdle(lIdleCount++))
		{
			lIdleCount=0;
			WaitMessage();
		}
	}

	m_bHelpMode=HELP_INACTIVE;
	ReleaseCapture();

	// make sure the cursor is set appropriately
	SetCapture();
	ReleaseCapture();

	// restore original status bar text
	SendMessage(WM_SETMESSAGESTRING, (WPARAM)nMsgSave);

#ifndef _AFX_NO_OLE_SUPPORT
	// tell in-place servers to exit Shift+F1 help mode
	if(m_pNotifyHook!=NULL)
	{
		m_pNotifyHook->OnContextHelp(FALSE);
	}
#endif

	if(dwContext!=0)
	{
		if(dwContext==-1)
		{
			SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
		}
		else
		{
			pApp->WinHelp(dwContext);
		}
	}
	PostMessage(WM_KICKIDLE);    // trigger idle update
}


template<class PARENTFRAME, class PARENTDOCKBAR>
BOOL COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::ProcessHelpMsg(MSG& msg, 
																DWORD* pContext)
{
	ASSERT(pContext!=NULL);

	if(msg.message==WM_EXITHELPMODE ||
		(msg.message==WM_KEYDOWN && msg.wParam==VK_ESCAPE))
	{
		PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
		return FALSE;
	}

	CPoint point;
	if((msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) ||
		(msg.message >= WM_NCMOUSEFIRST && msg.message <= WM_NCMOUSELAST))
	{
		BOOL bDescendant;
		HWND hWndHit=SetHelpCapture(msg.pt, &bDescendant);
		if(hWndHit==NULL)
			return TRUE;

		BOOL bHitMenuBar=(hWndHit==GetMenuBar().GetSafeHwnd());

		if(bDescendant)
		{
			if(bHitMenuBar)
			{
				if(msg.message==WM_LBUTTONDOWN || GetMenuBar().IsInDisplayingMode())
				{
					ASSERT(::GetCapture()==m_hWnd);
					ReleaseCapture();
					GetMessage(&msg, NULL, msg.message, msg.message);
					DispatchMessage(&msg);
					GetCursorPos(&point);
					SetHelpCapture(point, NULL);
				}
				else
				{
					// Hit one of our owned windows -- eat the message.
					PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
					return TRUE;
				}
			}
			else
			{
				return COXMenuBarHost<PARENTFRAME>::ProcessHelpMsg(msg,pContext);
			}
		}
		else
		{
			// Hit one of our apps windows (or desktop) -- dispatch the message.
			PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);

			// Dispatch mouse messages that hit the desktop!
			DispatchMessage(&msg);
		}
	}
	else
	{
		return COXMenuBarHost<PARENTFRAME>::ProcessHelpMsg(msg,pContext);
	}

	return TRUE;
}


template<class PARENTFRAME, class PARENTDOCKBAR>
BOOL COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::
CheckMenuBarSize(BOOL bNotify/*=TRUE*/)
{
	BOOL bLayoutRecalculated=FALSE;

	COXMenuBar& menuBar=GetMenuBar();
	if(::IsWindow(menuBar.GetSafeHwnd()) && !menuBar.IsFloating())
	{
		CRect rectMenuBar;
		menuBar.GetWindowRect(rectMenuBar);
		BOOL bHorz=(menuBar.m_dwStyle & CBRS_ORIENT_HORZ);
		CControlBar* pDockBar=NULL;
		if(bHorz)
		{
			pDockBar=GetControlBar(AFX_IDW_DOCKBAR_TOP);
			if(pDockBar!=NULL)
				pDockBar=GetControlBar(AFX_IDW_DOCKBAR_BOTTOM);
		}
		else
		{
			pDockBar=GetControlBar(AFX_IDW_DOCKBAR_LEFT);
			if(pDockBar!=NULL)
				pDockBar=GetControlBar(AFX_IDW_DOCKBAR_RIGHT);
		}
		if(pDockBar!=NULL)
		{
			CRect rectDockBar;
			pDockBar->GetWindowRect(rectDockBar);
			if((bHorz && rectDockBar.Width()!=rectMenuBar.Width()) || 
				(!bHorz && rectDockBar.Height()!=rectMenuBar.Height()))
			{
				COXMenuBarHost<PARENTFRAME>::RecalcLayout(bNotify);
				bLayoutRecalculated=TRUE;
			}
		}
	}

	return bLayoutRecalculated;
}


template<class PARENTFRAME, class PARENTDOCKBAR>
void COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::SubclassMDIClient()
{
	ASSERT(::IsWindow(m_hWnd));

	if(m_bIsMDI)
	{
		// if MDI application then we have to handle MDIClient messages
		WNDPROC pfnOldMDIClientProc=
			(WNDPROC)::GetWindowLongPtr(((CMDIFrameWnd*)this)->
			m_hWndMDIClient,GWL_WNDPROC);
		if(m_pfnOldMDIClientProc!=pfnOldMDIClientProc)
		{
			m_pfnOldMDIClientProc=pfnOldMDIClientProc;
			::SetWindowLongPtr(((CMDIFrameWnd*)this)->m_hWndMDIClient,GWL_WNDPROC,
				(LONG_PTR)MDISubclassProc);
			m_bIsMDISubclassed=TRUE;
		} 
	}
}


// dock bars will be created in the order specified by g_dwDockBarMap
// this also controls which gets priority during layout
// this order can be changed by calling EnableDocking repetitively
// with the exact order of priority
template<class PARENTFRAME, class PARENTDOCKBAR>
void COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::
EnableDocking(DWORD dwDockStyle)
{
	// must be CBRS_ALIGN_XXX or CBRS_FLOAT_MULTI only
	ASSERT((dwDockStyle & ~(CBRS_ALIGN_ANY|CBRS_FLOAT_MULTI))==0);

	ASSERT(m_pFloatingFrameClass!=NULL);
	for (int i=0; i < 4; i++)
	{
		if(g_dwDockBarMap[i][1] & dwDockStyle & CBRS_ALIGN_ANY)
		{
			CInternalDockBar* pDock=
				(CInternalDockBar*)GetControlBar(g_dwDockBarMap[i][0]);
			if(pDock==NULL)
			{
				// use our internal implementation of the dock bar
				pDock=new CInternalDockBar;
				if(!pDock->Create(this,
					WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE|
					g_dwDockBarMap[i][1], g_dwDockBarMap[i][0]))
				{
					AfxThrowResourceException();
				}
			}
		}
	}
}


template<class PARENTFRAME, class PARENTDOCKBAR>
LRESULT COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::HandleMDIClientMsg(HWND hWnd, 
																	UINT msg, 
																	WPARAM wp, 
																	LPARAM lp)
{
	// only in MDIFrame
	ASSERT(m_bIsMDI);
	ASSERT(::IsWindow(m_hWnd));

    switch(msg)
    {
	case WM_MDISETMENU:
		ASSERT(::IsWindow(GetMenuBar().GetSafeHwnd()));
		// set new menu to the menubar
		if(NULL!=wp && ::IsWindow(GetMenuBar().GetSafeHwnd()) && 
			::IsWindow(GetMenuBar().GetToolBarCtrl().GetSafeHwnd()))
		{
			GetMenuBar().SetMenu((HMENU)wp);
		}
	    wp=NULL;
		if(GetMenuBar().m_hMDIWindowMenu!=NULL)
		{
			lp=(LPARAM)GetMenuBar().m_hMDIWindowMenu;
		}

		break;

	case WM_MDIACTIVATE:
	case WM_MDICREATE:
	case WM_MDIDESTROY:
		// the icon of the MDIChild window could have changed
		if(::IsWindow(GetMenuBar().GetSafeHwnd()))
			GetMenuBar().PostMessage(WM_IDLEUPDATECMDUI);
		break;
	case WM_NULL:
		m_bIsMDISubclassed=TRUE;
		break;
    }

	// handle other messages
    if(m_pfnOldMDIClientProc!=NULL)
        return ::CallWindowProc(m_pfnOldMDIClientProc,hWnd,msg,wp,lp);

    return 0;
}


template<class PARENTFRAME, class PARENTDOCKBAR>
LRESULT CALLBACK COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>::
MDISubclassProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
	AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif

	HWND hWndParent=::GetParent(hWnd);
	ASSERT(hWndParent!=NULL);

	void* pMenuBarFrame=NULL;
	if(!g_arrMenuBarFrames.Lookup(hWndParent,pMenuBarFrame))
		return 0;
	ASSERT(pMenuBarFrame!=NULL);
	// call COXMenuBarFrame implementation
	if(::IsWindow(((COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>*)
		pMenuBarFrame)->m_hWnd))
	{
		return ((COXMenuBarFrame<PARENTFRAME,PARENTDOCKBAR>*)pMenuBarFrame)->
			HandleMDIClientMsg(hWnd,msg,wp,lp);
	}

	return 0;
}



#endif	//	_MFC_VER>=0x0420


#endif // _MENUBAR_H

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions