Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

576 lines
15 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 1999
//
// File: menuitem.cpp
//
//--------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
// MenuItem.cpp : CMenuItem class implementation.
#include "stdafx.h"
#include "MenuItem.h"
#include "..\inc\stddbg.h" // ASSERT_OBJECTPTR
#include "util.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
DEBUG_DECLARE_INSTANCE_COUNTER(CMenuItem);
//############################################################################
//############################################################################
//
// Traces
//
//############################################################################
//############################################################################
#ifdef DBG
CTraceTag tagMenuItem(TEXT("Menu Items"), TEXT("Menu Item Path"));
#endif
//############################################################################
//############################################################################
//
// Implementation of class CMMCMenuItem
//
//############################################################################
//############################################################################
/*+-------------------------------------------------------------------------*
* class CMMCMenuItem
*
*
* PURPOSE: Encapsulates a single CMenuItem, and exposes the MenuItem interface.
*
*+-------------------------------------------------------------------------*/
class CMMCMenuItem:
public CMMCIDispatchImpl<MenuItem>,
public CTiedComObject<CMenuItem>
{
typedef CMMCMenuItem ThisClass;
typedef CMenuItem CMyTiedObject;
public:
BEGIN_MMC_COM_MAP(ThisClass)
END_MMC_COM_MAP()
// give a public access to IsTied();
bool IsTied() { return CTiedComObject<CMenuItem>::IsTied(); }
// MenuItem interface methods
public:
MMC_METHOD0(Execute);
MMC_METHOD1(get_DisplayName, PBSTR /*pbstrName*/);
MMC_METHOD1(get_LanguageIndependentName, PBSTR /*LanguageIndependentName*/);
MMC_METHOD1(get_Path, PBSTR /*pbstrPath*/);
MMC_METHOD1(get_LanguageIndependentPath, PBSTR /*LanguageIndependentPath*/);
MMC_METHOD1(get_Enabled, PBOOL /*pBool*/);
};
//############################################################################
//############################################################################
//
// Implementation of class CMenuItem
//
//############################################################################
//############################################################################
CMenuItem::CMenuItem( LPCTSTR lpszName,
LPCTSTR lpszStatusBarText,
LPCTSTR lpszLanguageIndependentName,
LPCTSTR lpszPath,
LPCTSTR lpszLanguageIndependentPath,
long nCommandID,
long nMenuItemID,
long nFlags,
MENU_OWNER_ID ownerID,
IExtendContextMenu * pExtendContextMenu,
IDataObject * pDataObject,
DWORD fSpecialFlags,
bool bPassCommandBackToSnapin /*= false*/) :
m_strName(lpszName),
m_strStatusBarText(lpszStatusBarText),
m_strPath(lpszPath),
m_strLanguageIndependentName(lpszLanguageIndependentName),
m_strLanguageIndependentPath(lpszLanguageIndependentPath),
m_nCommandID(nCommandID),
m_nMenuItemID(nMenuItemID),
m_nFlags(nFlags),
m_OwnerID(ownerID),
m_fSpecialFlags(fSpecialFlags),
m_PopupMenuHandle(NULL),
m_SubMenu(), // default c-tor
m_spExtendContextMenu(pExtendContextMenu),
m_pDataObject(pDataObject), //AddRef'ed in the c-tor body
m_bEnabled(true),
m_spMenuItem(), // default c-tor
m_bPassCommandBackToSnapin(bPassCommandBackToSnapin)
{
// Caller must check range on ID and State
// NULL is a special dataobject
if (! IS_SPECIAL_DATAOBJECT(m_pDataObject))
m_pDataObject->AddRef();
DEBUG_INCREMENT_INSTANCE_COUNTER(CMenuItem);
}
CMenuItem::~CMenuItem()
{
POSITION pos = m_SubMenu.GetHeadPosition();
while(pos)
{
CMenuItem* pItem = m_SubMenu.GetNext(pos);
ASSERT( pItem != NULL );
delete pItem;
}
m_SubMenu.RemoveAll();
m_spExtendContextMenu = NULL;
if (! IS_SPECIAL_DATAOBJECT(m_pDataObject))
m_pDataObject->Release();
m_spMenuItem = NULL;
DEBUG_DECREMENT_INSTANCE_COUNTER(CMenuItem);
}
void CMenuItem::AssertValid()
{
ASSERT(this != NULL);
if (m_nCommandID == -1 ||
m_nMenuItemID == OWNERID_INVALID ||
m_nFlags == -1
)
{
ASSERT( FALSE );
}
}
/*+-------------------------------------------------------------------------*
*
* CMenuItem::ScGetMenuItem
*
* PURPOSE: Creates an returns a tied MenuItem COM object.
*
* PARAMETERS:
* PPMENUITEM ppMenuItem :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CMenuItem::ScGetMenuItem(PPMENUITEM ppMenuItem)
{
DECLARE_SC(sc, TEXT("CMenuItem::ScGetMenuItem"));
sc = ScCheckPointers(ppMenuItem);
if(sc)
return sc;
// initialize out parameter
*ppMenuItem = NULL;
// create a CMMCMenuItem if needed.
sc = CTiedComObjectCreator<CMMCMenuItem>::ScCreateAndConnect(*this, m_spMenuItem);
if(sc)
return sc;
if(m_spMenuItem == NULL)
{
sc = E_UNEXPECTED;
return sc;
}
// addref the pointer for the client.
m_spMenuItem->AddRef();
*ppMenuItem = m_spMenuItem;
return sc;
}
//+-------------------------------------------------------------------
//
// class: CManageActCtx
//
// Purpose: To deactivate UI theme context (if set) and restore
// the context automatically.
//
// Usage: 1. Call Activate to set the activation context to V5
// common controls. This is needed before calling into snapins
// so that snapin created windows are not themed accidentally.
//
// The snapin can theme its windows by calling appropriate
// fusion apis while calling create-window.
//
// 2. Call Deactivate to restore the activation context.
// This is needed after calling into snapins, so that
// if we called from themed context then it is restored.
//
// Explanation:
// When MMC calls into the snapin if the last winproc which
// received a window message is themed and will result in a
// call to snapin then we will call the snapin in themed
// context. If snapin creates & displays any UI then it will
// be themed. This function is to de-activate the theming
// before calling the snapin.
//
//
//--------------------------------------------------------------------
class CManageActCtx
{
public:
CManageActCtx() : m_ulpCookie(0) { }
~CManageActCtx()
{
if (m_ulpCookie != 0)
MmcDownlevelDeactivateActCtx(0, m_ulpCookie);
}
BOOL Activate(HANDLE hActCtx)
{
if (m_ulpCookie != 0)
{
ULONG_PTR ulpTemp = m_ulpCookie;
m_ulpCookie = 0;
MmcDownlevelDeactivateActCtx(0, ulpTemp);
}
return MmcDownlevelActivateActCtx(hActCtx, &m_ulpCookie);
}
VOID Deactivate()
{
ULONG_PTR ulpTemp = m_ulpCookie;
if (ulpTemp != 0)
{
m_ulpCookie = 0;
MmcDownlevelDeactivateActCtx(0, ulpTemp);
}
}
private:
ULONG_PTR m_ulpCookie;
};
/*+-------------------------------------------------------------------------*
*
* CMenuItem::ScExecute
*
* PURPOSE: Executes the menu item.
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CMenuItem::ScExecute()
{
DECLARE_SC(sc, TEXT("CMenuItem::ScExecute"));
Trace(tagMenuItem, TEXT("\"%s\""), m_strPath);
// check whether the item is disabled.
BOOL bEnabled = FALSE;
sc = Scget_Enabled(&bEnabled);
if (sc)
return sc;
if (!bEnabled)
return sc = E_FAIL;
// if the command is to be passed to snapin - nothing can be done here
if ( m_bPassCommandBackToSnapin )
return sc;
sc = ScCheckPointers(m_spExtendContextMenu.GetInterfacePtr(), E_UNEXPECTED);
if(sc)
return sc;
MenuItemPtr spMenuItem;
sc = ScGetMenuItem( &spMenuItem );
if (sc)
return sc;
// Deactivate if theming (fusion or V6 common-control) context before calling snapins.
CManageActCtx mac;
if (! mac.Activate(NULL) )
return (sc = E_FAIL);
try
{
sc = m_spExtendContextMenu->Command(GetCommandID(), m_pDataObject);
}
catch(...)
{
// Do nothing.
}
mac.Deactivate();
if (sc)
return sc;
// get the pointer for com event emitting
CConsoleEventDispatcher *pConsoleEventDispatcher = NULL;
sc = CConsoleEventDispatcherProvider::ScGetConsoleEventDispatcher( pConsoleEventDispatcher );
if(sc)
{
sc.TraceAndClear(); // event does not affect item execution itself
return sc;
}
// fire event about successful execution (do not rely on 'this' to be valid)
if (pConsoleEventDispatcher != NULL)
{
// check if com object still points to a valid object
CMMCMenuItem *pMMCMenuItem = dynamic_cast<CMMCMenuItem *>( spMenuItem.GetInterfacePtr() );
// check the pointer
sc = ScCheckPointers( pMMCMenuItem, E_UNEXPECTED );
if (sc)
{
spMenuItem = NULL; // invalid anyway - do not pass to the script
sc.TraceAndClear(); // does not affect the result
}
else if ( !pMMCMenuItem->IsTied() ) // validate menu item
{
spMenuItem = NULL; // gone away - change to NULL instead of passing invalid object
}
// fire it!
sc = pConsoleEventDispatcher->ScOnContextMenuExecuted( spMenuItem );
if (sc)
sc.TraceAndClear(); // does not affect the result
}
else
{
// needs to be set prior to using
(sc = E_UNEXPECTED).TraceAndClear();
}
return sc;
}
/*+-------------------------------------------------------------------------*
*
* CMenuItem::Scget_DisplayName
*
* PURPOSE: Returns the display name of the menu item, which includes acclerators.
* Eg '&Properties ALT+ENTER'
*
* PARAMETERS:
* PBSTR pbstrName :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CMenuItem::Scget_DisplayName(PBSTR pbstrName)
{
DECLARE_SC(sc, TEXT("CMenuItem::Scget_DisplayName"));
sc = ScCheckPointers(pbstrName);
if(sc)
return sc;
CComBSTR bstrName = GetMenuItemName();
// give the
*pbstrName = bstrName.Detach();
return sc;
}
/*+-------------------------------------------------------------------------*
*
* CMenuItem::Scget_LanguageIndependentName
*
* PURPOSE: Returns the language-independent name of the menu item. If there is no
* language independent name, returns the display name without accelerators.
*
* PARAMETERS:
* PBSTR LanguageIndependentName :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CMenuItem::Scget_LanguageIndependentName(PBSTR LanguageIndependentName)
{
DECLARE_SC(sc, TEXT("CMenuItem::Scget_LanguageIndependentName"));
sc = ScCheckPointers(LanguageIndependentName);
if(sc)
return sc;
// initialize the out parameter
*LanguageIndependentName = NULL;
CComBSTR bstrLanguageIndependentName = GetLanguageIndependentName();
// set the output param
*LanguageIndependentName = bstrLanguageIndependentName.Detach();
return sc;
}
/*+-------------------------------------------------------------------------*
*
* CMenuItem::Scget_Path
*
* PURPOSE: Returns the path of the menu item starting from the root. Does not include
* accelerators. Eg View->Large
*
* PARAMETERS:
* PBSTR pbstrPath :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CMenuItem::Scget_Path(PBSTR pbstrPath)
{
DECLARE_SC(sc, TEXT("CMenuItem::Scget_Path"));
sc = ScCheckPointers(pbstrPath);
if(sc)
return sc.ToHr();
CComBSTR bstrPath = (LPCTSTR)m_strPath;
// give the
*pbstrPath = bstrPath.Detach();
return sc.ToHr();
}
/*+-------------------------------------------------------------------------*
*
* CMenuItem::Scget_LanguageIndependentPath
*
* PURPOSE: Returns the language independent path of the menu item starting from the root.
* Eg _VIEW->_LARGE
*
* PARAMETERS:
* PBSTR LanguageIndependentPath :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CMenuItem::Scget_LanguageIndependentPath(PBSTR LanguageIndependentPath)
{
DECLARE_SC(sc, TEXT("CMenuItem::Scget_LanguageIndependentPath"));
sc = ScCheckPointers(LanguageIndependentPath);
if(sc)
return sc;
// initialize the out parameter
*LanguageIndependentPath = NULL;
CComBSTR bstrLanguageIndependentPath = (LPCTSTR)GetLanguageIndependentPath();
// set the output param
*LanguageIndependentPath = bstrLanguageIndependentPath.Detach();
return sc;
}
/*+-------------------------------------------------------------------------*
*
* CMenuItem::Scget_Enabled
*
* PURPOSE: Returns whether the menu item is enabled.
*
* PARAMETERS:
* PBOOL pBool :
*
* RETURNS:
* HRESULT
*
*+-------------------------------------------------------------------------*/
SC
CMenuItem::Scget_Enabled(PBOOL pBool)
{
DECLARE_SC(sc, TEXT("CMenuItem::Scget_Enabled"));
sc = ScCheckPointers(pBool);
if(sc)
return sc.ToHr();
// the item is enabled only if it was never disabled via the Disable object model
// method and it is not grayed out or disabled via the MF_ flags.
*pBool = m_bEnabled &&
((m_nFlags & MF_DISABLED) == 0) &&
((m_nFlags & MF_GRAYED) == 0);
return sc.ToHr();
}
/***************************************************************************\
*
* METHOD: CMenuItem::ScFindMenuItemByPath
*
* PURPOSE: finds the menu item by matching the path
*
* PARAMETERS:
* LPCTSTR lpstrPath [in] manu item path
*
* RETURNS:
* CMenuItem* - found item (NULL == not found)
*
\***************************************************************************/
CMenuItem*
CMenuItem::FindItemByPath( LPCTSTR lpstrPath )
{
// first check if this item does not meet requirements
if ( 0 == m_strLanguageIndependentPath.Compare(lpstrPath)
|| 0 == m_strPath.Compare(lpstrPath) )
return this;
// recurse into subitems
POSITION pos = GetMenuItemSubmenu().GetHeadPosition();
while(pos)
{
CMenuItem* pItem = GetMenuItemSubmenu().GetNext(pos);
if (!pItem)
{
ASSERT(FALSE);
return NULL;
}
CMenuItem* pItemFound = pItem->FindItemByPath( lpstrPath );
if (pItemFound)
return pItemFound;
}
// not found
return NULL;
}