/*****************************************************************************/
/* SOURCE FILE */
/*****************************************************************************/
/*
$Archive: $
$Revision: $
Last Checkin:
$Date: $
By:
$Author: $
Last Modification:
$ModTime: $
Description: Implementation of a mixin context menu handling system
TOOL And XML FORMS License
==========================
Except where otherwise noted, all of the documentation
and software included in the TOOL package is
copyrighted by Michael Swartzendruber.
Copyright (C) 2005 Michael John Swartzendruber.
All rights reserved.
Access to this code, whether intentional or accidental,
does NOT IMPLY any transfer of rights.
This software is provided "as-is," without any express
or implied warranty. In no event shall the author be held
liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for
any purpose, including commercial applications, and to
alter and redistribute it, provided that the following
conditions are met:
1. All redistributions of source code files must retain
all copyright notices that are currently in place,
and this list of conditions without modification.
2. The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
3. If you use this software in another product, an acknowledgment
in the product documentation would be appreciated but is
not required.
4. Modified versions in source or binary form must be plainly
marked as such, and must not be misrepresented as being
the original software.
*/
static char OBJECT_ID[] = "$Revision: $ : $JustDate: $";
/*****************************************************************************/
#ifndef WINVER // 2b|!2b==?
#define WINVER 0x0501 // 2b|!2b==?
#endif // 2b|!2b==?
#include "XMLFormMixinContextMenu.h"
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::CXMLFormMixinContextMenu
DESCRIPTION: ctor
INPUT: void
OUTPUT: none
RETURNS: none
*/
CXMLFormMixinContextMenu::CXMLFormMixinContextMenu( void )
{
}
/* End of function "CXMLFormMixinContextMenu::CXMLFormMixinContextMenu"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::~CXMLFormMixinContextMenu
DESCRIPTION: dtor
INPUT: void
OUTPUT: none
RETURNS: none
*/
CXMLFormMixinContextMenu::~CXMLFormMixinContextMenu( void )
{
// iterate over each menu defined in the entire menu set
//
XML_CONTEXT_SELECTION_ITER oMenuIter;
for ( oMenuIter = m_oMenuSet.begin();
oMenuIter != m_oMenuSet.end();
oMenuIter++ )
{
// the menu set contains a collection of context
// wrappers created from the heap
//
XML_CONTEXT_WRAPPERS* poMenu = (*oMenuIter).second;
XML_CONTEXT_WRAPPERS_ITER oCmdsIter;
// iterate over the collection of command containers
// contained in a single context collection
//
for ( oCmdsIter = poMenu->begin();
oCmdsIter != poMenu->end();
oCmdsIter++ )
{
P_XML_MIXIN_CONTAINER pxCommand = (*oCmdsIter).second;
// if this was assigned ownership over the handler
// object, then delete it here
//
if ( pxCommand->m_bOwnPointer )
{
delete pxCommand->m_poHandler;
}
delete pxCommand;
}
poMenu->clear();
delete poMenu;
}
m_oMenuSet.clear();
}
/* End of function "CXMLFormMixinContextMenu::~CXMLFormMixinContextMenu"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::SetCommandUpdater
DESCRIPTION: Allows application to hook in their own update ui handling
mechanism
INPUT: poUpdater - pointer to object that will be called to do
update ui handling just prior to menu display
OUTPUT: none
RETURNS: void
*/
void CXMLFormMixinContextMenu::SetCommandUpdater( CXMLFormMixinUpdateCommandState* poUpdater )
{
m_poUpdater = poUpdater;
}
/* End of function "CXMLFormMixinContextMenu::SetCommandUpdater"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::DoPopup
DESCRIPTION: called to create/display/process the selected menu.
INPUT: poWindow - pointer to window to use as the 'owner' of
the menu
dwSelector - menu selector
oPoint - point where the menu should be placed
wParam - parameter passed to handler. not used here
lParam - parameter passed to handler. not used here.
OUTPUT:
RETURNS: results of call to handler . or false if failed
*/
bool CXMLFormMixinContextMenu::DoPopup( CWnd* poWindow,
DWORD dwSelector,
CPoint oPoint,
WPARAM wParam,
LPARAM lParam )
{
// first select the menu container
//
XML_CONTEXT_SELECTION_ITER oMenuIter;
oMenuIter = m_oMenuSet.find( dwSelector );
// if menu meta-data not found, then no more
// work can be done here
//
if ( m_oMenuSet.end() == oMenuIter )
{
return( false );
}
// get the menu meta data
//
XML_CONTEXT_WRAPPERS* poCommands = (*oMenuIter).second;
// now build the menu from the meta data
//
CMenu oMenu;
VERIFY( oMenu.CreatePopupMenu() );
XML_CONTEXT_WRAPPERS_ITER oIter;
for ( oIter = poCommands->begin();
oIter != poCommands->end();
oIter++ )
{
P_XML_MIXIN_CONTAINER pxCommand = (*oIter).second;
if ( !pxCommand->m_bHidden )
{
oMenu.AppendMenu( MF_STRING,
pxCommand->m_dwCommandID,
pxCommand->m_oCommandText.c_str() );
}
}
// now that the menu is built, let the application
// have a chance to enable/disable each menu command
//
//
InvokeUpdateUIHandler( &oMenu );
// run the menu now
//
poWindow->ClientToScreen( &oPoint );
DWORD dwResult;
dwResult = oMenu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,
oPoint.x,
oPoint.y,
poWindow );
oMenu.DestroyMenu();
// use the result of the menu as a key to
// look up the handler for the returned
// command, then run the handler
//
oIter = poCommands->find( dwResult );
if ( oIter != poCommands->end() )
{
CXMLFormMixinCommand* poHandler;
poHandler = (*oIter).second->m_poHandler;
if ( NULL == poHandler )
{
return( false );
}
else
{
return( poHandler->HandleCommand( poWindow,
dwResult,
wParam,
lParam ) );
}
}
else
{
return( false );
}
}
/* End of function "CXMLFormMixinContextMenu::DoPopup"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::AddCommand
DESCRIPTION: Adds a menu command object to the internal collection
INPUT: dwSelector - selects the menu to add the command on to
dwCommandID - the id for this command
pchCommandText - the text for this command
poHandler - pointer to a command handler to invoke when
this command is selected
bOwnPointer - true if this collection should 'take ownership
and responsibility' for the pointer. In other
words, does this delete the handler during dtor
OUTPUT:
RETURNS: void
*/
void CXMLFormMixinContextMenu::AddCommand( DWORD dwSelector,
DWORD dwCommandID,
const char* pchCommandText,
CXMLFormMixinCommand* poHandler,
bool bOwnPointer )
{
// first select the menu container
//
XML_CONTEXT_SELECTION_ITER oMenuIter;
oMenuIter = m_oMenuSet.find( dwSelector );
XML_CONTEXT_WRAPPERS* poCommands = NULL;
// if menu meta-data for this selector is not found,
// then create one and continue
//
//
if ( m_oMenuSet.end() == oMenuIter )
{
poCommands = new XML_CONTEXT_WRAPPERS;
m_oMenuSet.insert( XML_CONTEXT_SELECTION::value_type( dwSelector, poCommands ) );
}
else
{
poCommands = (*oMenuIter).second;
}
// determine if there is a command definition already stored
// under the given command ID
//
XML_CONTEXT_WRAPPERS_ITER oIter = poCommands->find( dwCommandID );
if ( oIter != poCommands->end() )
{
// if there is already a command stored under this key,
// then remove the old one from the collection
//
// WARNING: This is a dangerous approach and if the user
// is messing up, then we'll just help them
// crash-n-burn here ;P
//
P_XML_MIXIN_CONTAINER pxCommand = (*oIter).second;
if ( pxCommand->m_bOwnPointer )
{
delete pxCommand->m_poHandler;
}
delete pxCommand;
poCommands->erase( dwCommandID );
}
// create and store the command container
//
P_XML_MIXIN_CONTAINER pxCommand = new XML_MIXIN_CONTAINER;
pxCommand->m_bOwnPointer = bOwnPointer;
pxCommand->m_poHandler = poHandler;
pxCommand->m_dwCommandID = dwCommandID;
pxCommand->m_oCommandText = std::string( pchCommandText );
pxCommand->m_bHidden = false;
poCommands->insert( XML_CONTEXT_WRAPPERS::value_type( dwCommandID, pxCommand ) );
}
/* End of function "CXMLFormMixinContextMenu::AddCommand"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::RemoveMenu
DESCRIPTION: completely removes a menu from collection. does all
appropriate heap clean up as well
INPUT: dwSelector - the menu to remove
OUTPUT: none
RETURNS: true if worked, false if not
*/
bool CXMLFormMixinContextMenu::RemoveMenu( DWORD dwSelector )
{
// first select the menu container
//
XML_CONTEXT_SELECTION_ITER oMenuIter;
oMenuIter = m_oMenuSet.find( dwSelector );
// if menu meta-data not found, then bail
//
if ( m_oMenuSet.end() == oMenuIter )
{
return( false );
}
// get the menu meta data
//
XML_CONTEXT_WRAPPERS* poCommands = (*oMenuIter).second;
XML_CONTEXT_WRAPPERS_ITER oIter;
for ( oIter = poCommands->begin();
oIter != poCommands->end();
oIter++ )
{
P_XML_MIXIN_CONTAINER pxCommand = (*oIter).second;
if ( pxCommand->m_bOwnPointer )
{
delete pxCommand->m_poHandler;
}
delete pxCommand;
}
poCommands->erase( oIter );
delete poCommands;
return( true );
}
/* End of function "CXMLFormMixinContextMenu::RemoveMenu"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::HideCommand
DESCRIPTION: sets/resets the 'hidden bit' on the given menu command.
Hidden menu commands do not appear when the popup is
created. This provides a way to 'refine the appearance'
of menu objects
INPUT: dwSelector - selects which menu collection for the given
command
dwCommandID - the command to hide/show
bHideShow - true hides the menu item, false shows it
OUTPUT:
RETURNS: true if worked, false if not
*/
bool CXMLFormMixinContextMenu::HideCommand( DWORD dwSelector, DWORD dwCommandID, bool bHideShow )
{
// first select the menu container
//
XML_CONTEXT_SELECTION_ITER oMenuIter;
oMenuIter = m_oMenuSet.find( dwSelector );
// if menu meta-data not found, then bail
//
if ( m_oMenuSet.end() == oMenuIter )
{
return( false );
}
// get the menu meta data
//
XML_CONTEXT_WRAPPERS* poCommands = (*oMenuIter).second;
XML_CONTEXT_WRAPPERS_ITER oIter;
oIter = poCommands->find( dwCommandID );
if ( oIter == poCommands->end() )
{
return( false );
}
else
{
P_XML_MIXIN_CONTAINER pxCommand = (*oIter).second;
pxCommand->m_bHidden = bHideShow;
return( true );
}
}
/* End of function "CXMLFormMixinContextMenu::HideCommand"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::RemoveCommand
DESCRIPTION: removes a specific command from the collection. The selector
is used to define which menu collection to operate on
INPUT: dwSelector - selects which menu collection to operate on
dwCommandID - defines which command to removed from the
menu
OUTPUT:
RETURNS: true if worked, false if not
*/
bool CXMLFormMixinContextMenu::RemoveCommand( DWORD dwSelector, DWORD dwCommandID )
{
// first select the menu container
//
XML_CONTEXT_SELECTION_ITER oMenuIter;
oMenuIter = m_oMenuSet.find( dwSelector );
// if menu meta-data not found, then bail
//
if ( m_oMenuSet.end() == oMenuIter )
{
return( false );
}
// get the menu meta data
//
XML_CONTEXT_WRAPPERS* poCommands = (*oMenuIter).second;
XML_CONTEXT_WRAPPERS_ITER oIter;
oIter = poCommands->find( dwCommandID );
if ( oIter == poCommands->end() )
{
return( false );
}
else
{
P_XML_MIXIN_CONTAINER pxCommand = (*oIter).second;
if ( pxCommand->m_bOwnPointer )
{
delete pxCommand->m_poHandler;
}
delete pxCommand;
poCommands->erase( oIter );
return( true );
}
}
/* End of function "CXMLFormMixinContextMenu::RemoveCommand"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::FindPopupMenuIndexFromID
DESCRIPTION: finds the 'menu offset index' for the given command in the
given menu
INPUT: poMenuToSearch - pointer to menu to search
iMenuCmdToFind - id of the menu command to find
OUTPUT:
RETURNS: the index for the given command or -1 if not found
*/
UINT CXMLFormMixinContextMenu::FindPopupMenuIndexFromID( CMenu* poMenuToSearch,
UINT iMenuCmdToFind )
{
ASSERT_VALID( poMenuToSearch );
UINT iTop = poMenuToSearch->GetMenuItemCount();
for ( UINT iLoop = 0; iLoop < iTop; iLoop++ )
{
// walk through all items, looking for ID match
//
if ( poMenuToSearch->GetMenuItemID( iLoop ) == iMenuCmdToFind )
{
return iLoop;
}
}
// not found
//
return -1;
}
/* End of function "CXMLFormMixinContextMenu::FindPopupMenuIndexFromID"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::InvokeUpdateUIHandler
DESCRIPTION: Given a menu pointer and an ID for a command to update,
does the work required to invoke the update UI command
handling functionality of MFC
INPUT: poMenu - pointer to menu resource to operate against
iMenuCmdID - value of menu command to run the update for
OUTPUT:
RETURNS: void
*/
void CXMLFormMixinContextMenu::InvokeUpdateUIHandler( CMenu* poMenu, UINT iMenuCmdID )
{
CCmdUI oState;
oState.m_nIndex = FindPopupMenuIndexFromID( poMenu, iMenuCmdID );
if ( -1 != oState.m_nIndex )
{
oState.m_pMenu = poMenu;
oState.m_nIndexMax = poMenu->GetMenuItemCount();
oState.m_nID = iMenuCmdID;
oState.m_pSubMenu = NULL;
// if mixin updater is provided, use it. If not, use MFC default processing
//
if ( NULL != m_poUpdater )
{
m_poUpdater->UpdateCommandState( &oState );
}
else
{
oState.DoUpdate( AfxGetMainWnd(), FALSE );
}
}
}
/* End of function "CXMLFormMixinContextMenu::InvokeUpdateUIHandler"
/*****************************************************************************/
/*****************************************************************************/
/*
FUNCTION NAME: CXMLFormMixinContextMenu::InvokeUpdateUIHandler
DESCRIPTION: Accepts a menu object as an input and iterates over each
command and sub-menu on the menu object. Each menu item
is passed to a subordinate for further processing. This
method will recurse for all sub-menus found in the menu
object
INPUT: poMenu - pointer to menu object
OUTPUT: none
RETURNS: void
*/
void CXMLFormMixinContextMenu::InvokeUpdateUIHandler( CMenu* poMenu )
{
UINT iTop = poMenu->GetMenuItemCount();
for ( UINT iLoop = 0; iLoop < iTop; iLoop++ )
{
UINT iCmdID = poMenu->GetMenuItemID( iLoop );
if ( iCmdID == 0 )
{
continue;
}
else
if ( iCmdID == (UINT) -1 )
{
// possibly a popup menu, route to child menu if so
//
CMenu* poSubMenu = poMenu->GetSubMenu( iLoop );
if ( NULL != poSubMenu )
{
// recurse...
//
InvokeUpdateUIHandler( poSubMenu );
}
}
else
{
InvokeUpdateUIHandler( poMenu, iCmdID );
}
}
}
/* End of function "CXMLFormMixinContextMenu::InvokeUpdateUIHandler"
/*****************************************************************************/
/*****************************************************************************/
/* Check-in history
$WorkFile: $
$Archive: $
*$Log: $
*/
/*****************************************************************************/