Click here to Skip to main content
15,891,951 members
Articles / Containers / Virtual Machine

TOOL

Rate me:
Please Sign up or sign in to vote.
4.98/5 (52 votes)
23 Oct 200676 min read 230.9K   5.4K   147  
TOOL (Tiny Object Oriented Language) is an easily-embedded, object-oriented, C++-like-language interpreter. The purpose of this article is to introduce the TOOL interpreter and language from the perspective of a person who has a desire to include a scripting solution as part of his project.
/*****************************************************************************/
/*                              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:  $
*/
/*****************************************************************************/


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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions