OnInitMenuPopup

Hi Experts,

I am trying to use OnInitMenuPopup to dynamically modify my mainframe menus (File, Edit, etc) based on user settings in my application.  However, I'm having a problem with DeleteMenu.  It seems like once I remove a menu item using this command it is permanently gone - it doesn't show up the next time OnInitMenuPopup is called for that menu.  I'd like to have my default menu as the starting point each time OnInitMenuPopup is called and then adjust the settings based on the current settings.  Is there a good way to do this?

Thanks,
Kevin
kjc1111Asked:
Who is Participating?
 
kjc1111Connect With a Mentor Author Commented:
Hi Andy and Zoppo,

I was kicking around the idea of setting up some template menus as Andy suggested, and both your responses led me to believe I'd have to implement some custom stuff myself, which I did.  The code is below for anyone who's interested.  It works for both mainframe menus and context menus.  I just finished coding it so there might still be some bugs, but it seems to work so far.

Regards,
Kevin


CMainFrame.h:

	// Protected functions.

	void BuildMenu( CMenu* pOriginal, CMenu* pNew );
	void CustomizeMenu( CMenu* pMenu );
	void DeleteSubMenus();
	void PopulateMRUList( CMenu* pMenu );

	// Message handlers.

	afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu);

	// This variable contains the default menu settings.

	CMenu* m_pDefaultMenu;

	// This list contains our working pop-up menus.

	CList< CMenu*, CMenu* > m_listSubMenus;


CMainFrame.cpp

	// Message map.

	ON_WM_INITMENUPOPUP()



int
CMainFrame::OnCreate
(
	LPCREATESTRUCT lpCreateStruct
)
{

	...

	// Create the default menu.

	m_pDefaultMenu = new CMenu();
	m_pDefaultMenu->LoadMenu( IDM_MY_MENU );

	DeleteSubMenus();

	...

}


CMainFrame::~CMainFrame
(
	void
)
{

	...

	// Delete menus.

	if ( m_pDefaultMenu != NULL )
	{
		delete m_pDefaultMenu;
	}

	DeleteSubMenus();

	...

}


void 
CMainFrame::DeleteSubMenus
( 
	void 
) 
{ 

	POSITION pos = m_listSubMenus.GetHeadPosition();

	while ( pos != NULL )
	{
		CMenu* pMenu = m_listSubMenus.GetNext( pos );
		delete pMenu;
	}

	m_listSubMenus.RemoveAll();

} 


void 
CMainFrame::OnInitMenuPopup
(
	CMenu* pPopupMenu, 
	UINT nIndex, 
	BOOL bSysMenu 
)
{

	// Make sure we have menu items to work with.

	if ( pPopupMenu->GetMenuItemCount() == 0 )
	{
		SECWorkbook::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
		return;
	}

	// If this is a sub-menu then it will have been customized when
	// the top-level menu was handled earlier.

	if ( nIndex > 0 )
	{
		SECWorkbook::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
		return;
	}

	// Get the appropriate sub-menu index for our default menu.
	// This code assumes that the first item in each top-level 
	// menu will never be changed.

	CString strKey;
	pPopupMenu->GetMenuString( 0, strKey, MF_BYPOSITION );

	CMenu* pTemplate = NULL;
	int nCount = m_pDefaultMenu->GetMenuItemCount();

	for ( int nSubItem = 0; nSubItem < nCount; nSubItem++ )
	{
		CMenu* pTest = m_pDefaultMenu->GetSubMenu( nSubItem );

		CString strTest;
		pTest->GetMenuString( 0, strTest, MF_BYPOSITION );

		if ( strTest == strKey )
		{
			pTemplate = pTest;
			break;
		}
	}

	// If we couldn't find the sub-menu then this is a context menu.
	// These are dynamically created from the master context menu.
	// We can skip right to the customization since any modifications 
	// we make won't affect future calls.  Otherwise we need to 
	// create a copy of the template so it doesn't get permanently
	// changed.

	if ( pTemplate != NULL )
	{
		// Clear the current set of menu items.

		while ( pPopupMenu->GetMenuItemCount() > 0 )
		{
			pPopupMenu->DeleteMenu( 0, MF_BYPOSITION );
		}

		// Clear any working sub-menus from the previous call.

		DeleteSubMenus();

		// Recursively re-build the menu list using our master list.

		BuildMenu( pTemplate, pPopupMenu );
	}

	// Recursively customize the menu.

	CustomizeMenu( pPopupMenu );

}


void 
CMainFrame::BuildMenu
( 
	CMenu* pOriginal,
	CMenu* pNew
) 
{ 

	CString strText;

	// Loop through all menu items in the original.

	for ( int nIndex = 0; nIndex < pOriginal->GetMenuItemCount(); nIndex++ )
	{
		pOriginal->GetMenuString( nIndex, strText, MF_BYPOSITION );
		UINT nId = pOriginal->GetMenuItemID( nIndex );

		// Handle separators.

		if ( nId == 0 )
		{
			pNew->AppendMenu( MF_SEPARATOR );
		}

		// MRU files are a special case.

		else if ( nId == ID_FILE_MRU_FILE1 )
		{
			PopulateMRUList( pNew );
		}

		// Handle sub-menus.

		else if ( nId == ( UINT )-1 )
		{
			CMenu* pOriginalSubMenu = pOriginal->GetSubMenu( nIndex );

			// Create the sub-menu.

			CMenu* pNewSubMenu = new CMenu();
			pNewSubMenu->CreatePopupMenu();
			m_listSubMenus.AddTail( pNewSubMenu );

			// Recursively populate the sub-menu.

			BuildMenu( pOriginalSubMenu, pNewSubMenu );

			// Add the pop-up text.

			HMENU hMenu = pNewSubMenu->m_hMenu;
			pNew->AppendMenu( MF_POPUP, ( UINT )hMenu, strText );
		}

		// This is a normal menu item.

		else
		{
			pNew->AppendMenu( MF_STRING, nId, strText );
		}
	}

}  


void 
CMainFrame::CustomizeMenu
( 
	CMenu* pMenu
) 
{ 

	// Loop through and recursively customize sub-menus.

	for ( int nIndex = 0; nIndex < pMenu->GetMenuItemCount(); nIndex++ )
	{
		UINT nId = pMenu->GetMenuItemID( nIndex );

		if ( nId == ( UINT )-1 )
		{
			CMenu* pSubMenu = pMenu->GetSubMenu( nIndex );
			CustomizeMenu( pSubMenu );
		}
	}

	// Customize the menu.

	m_pMyCustomizeClass->Customize( pMenu );

} 
 

void 
CMainFrame::PopulateMRUList
( 
	CMenu* pMenu
) 
{ 

	// Get the MRU list.

	CRecentFileList* pRecent = GetApp()->GetRecentFileList();

	// Check if we have any valid files.

	if ( pRecent->m_arrNames[ 0 ].IsEmpty() )
	{
		CString strText = _T( "No Recent Files" );
		pMenu->AppendMenu( ( MF_STRING | MF_GRAYED ), 0, strText );
		return;
	}

	// Add all valid files.

	TCHAR szCurDir[ _MAX_PATH ];
	GetCurrentDirectory( _MAX_PATH, szCurDir );
	int nCurDir = lstrlen( szCurDir );

	for ( int nFile = 0; nFile < pRecent->GetSize(); nFile++ )
	{
		int nMRU = ID_FILE_MRU_FILE1 + nFile;
		CString strText = pRecent->m_arrNames[ nFile ];
		
		if ( strText.IsEmpty() )
		{
			return;
		}

		pRecent->GetDisplayName( strText, nFile, szCurDir, nCurDir );
		pMenu->AppendMenu( MF_STRING, nMRU, strText );
	}

} 
 

bool
MyCustomizeClass::Customize
( 
	CMenu* pMenu
) 
{ 
	
	// Do whatever you like to the menu in here.

}

Open in new window

0
 
AndyAinscowFreelance programmer / ConsultantCommented:
>> It seems like once I remove a menu item using this command it is permanently gone

That is what the DeleteMenu does - it deletes the menu item.  But why should it be a problem, either the user can use that item or they can't so why should you need to keep deleting it.


If the user can somehow change during the running of the app then you could try just disabling the menu item instead.
0
 
kjc1111Author Commented:
Hi Andy,

It is an MDI editing program and there are a lot of menu items available in the app that won't necessariliy be required for each type of file they are editing.  I want to provide a way for the user to customize the functionality for the current file and remove items they don't currently need.  This can even vary from file to file.  I know I can just disable unneeded items but my preference would be to remove them, if possible.  Kind of like toolbars - some apps allow you to dynamically show them or hide them, and if you decide to hide them you can still change your settings to get them back.

Thanks,
Kevin
0
Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
ZoppoCommented:
Hi kjc1111,

IMO there's no generic way to do what you want, at least not in 'old' MFC - is it an option for you to use 'new' MFC (either with VS 2010 or from MFC FeaturePack for VS 2008) using a CMFCMenuBar in your mainframe?

If so I could post you some code how this could work ...

ZOPPO
0
 
kjc1111Author Commented:
Hi Zoppo,

I'm stuck with VC6 since I'm using Stingray Studio as well, and it costs too much to upgrade Stingray so it will support later versions of VS.

Thanks,
Kevin

0
 
AndyAinscowFreelance programmer / ConsultantCommented:
The only thing I can think of is when your app start you build up some collections of what the current menu's are  (maybe as CMenu objects themselves?).  Then in the OnInitMenuPopup code you have you remove as at present or add if required.

0
 
kjc1111Author Commented:
I noticed that when OnInitMenuPopup is called the nIndex value is always 0 for my mainframe menus.  Is that normal or am I missing a setting somewhere?  If it's normal, is there a way to determine which menu is active, or do I have to do something like check the labels of the menu items for a match to a known value?

Thanks,
Kevin
0
All Courses

From novice to tech pro — start learning today.