windows-nt/Source/XPSP1/NT/admin/activec/nodemgr/cmenu.cpp

1977 lines
60 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 1999
//
// File: cmenu.cpp
//
//--------------------------------------------------------------------------
// cmenu.cpp : Implementation of IContextMenuProvider and DLL registration.
#include "stdafx.h"
#include "oncmenu.h"
#include "menuitem.h"
#include "constatbar.h"
#include "regutil.h"
#include "moreutil.h"
#include "multisel.h"
#include "cmenuinfo.h"
#include "conview.h"
#include "scopndcb.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/*+-------------------------------------------------------------------------*
* class CNativeExtendContextMenu
*
*
* PURPOSE: implements IExtendContextMenu by forwarding calls to CContextMenu
* but does not affect lifetime of CContextMenu
*
*+-------------------------------------------------------------------------*/
class CNativeExtendContextMenu :
public CTiedComObject<CContextMenu>,
public CComObjectRoot,
public IExtendContextMenu // this is used so that menu items can be executed uniformly.
{
protected:
typedef CNativeExtendContextMenu ThisClass;
typedef CContextMenu CMyTiedObject;
public:
// com entry points
BEGIN_COM_MAP(ThisClass)
COM_INTERFACE_ENTRY(IExtendContextMenu)
END_COM_MAP()
DECLARE_NOT_AGGREGATABLE(ThisClass)
// IExtendContexMenu methods
MMC_METHOD3( AddMenuItems, LPDATAOBJECT, LPCONTEXTMENUCALLBACK, long * );
MMC_METHOD2( Command, long, LPDATAOBJECT );
};
//############################################################################
//############################################################################
//
// Implementation of methods on CNodeInitObject that
// forward to CContextMenu
//
//############################################################################
//############################################################################
CContextMenu *
CNodeInitObject::GetContextMenu()
{
DECLARE_SC(sc, TEXT("CNodeInitObject::GetContextMenu"));
if(m_spContextMenu == NULL)
{
// check internal pointers
sc = ScCheckPointers(m_spScopeTree, E_UNEXPECTED);
if (sc)
return NULL;
// get scopetree and call back pointers
CScopeTree* const pScopeTree =
dynamic_cast<CScopeTree*>(m_spScopeTree.GetInterfacePtr());
// if the menu is created by component data, it does not have the node.
// in that case menu is created by passing NULL pointers to some parameters.
// Menu should never need those pointers in the mentioned case
CNodeCallback* pNodeCallback = NULL;
if ( m_pNode != NULL )
{
// check other required pointers
sc = ScCheckPointers(m_pNode->GetViewData(), E_UNEXPECTED);
if (sc)
return NULL;
pNodeCallback =
dynamic_cast<CNodeCallback *>(m_pNode->GetViewData()->GetNodeCallback());
}
// create context menu
CContextMenu *pContextMenu = NULL;
sc = CContextMenu::ScCreateContextMenuForScopeNode(m_pNode, pNodeCallback, pScopeTree,
&m_spContextMenu, pContextMenu);
if (sc)
return NULL;
sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
if (sc)
return NULL;
return pContextMenu;
}
return dynamic_cast<CContextMenu *>(m_spContextMenu.GetInterfacePtr());
}
STDMETHODIMP
CNodeInitObject::AddItem(CONTEXTMENUITEM * pItem)
{
DECLARE_SC(sc, TEXT("CNodeInitObject::AddItem"));
CContextMenu *pContextMenu = GetContextMenu();
sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
if(sc)
return sc.ToHr();
sc = pContextMenu->ScAddItem(pItem, true/*bPassCommandBackToSnapin*/);
return sc.ToHr();
}
STDMETHODIMP
CNodeInitObject::EmptyMenuList ()
{
DECLARE_SC(sc, TEXT("CNodeInitObject::EmptyMenuList"));
if (m_spContextMenu == NULL)
return S_OK;
CContextMenu *pContextMenu = GetContextMenu();
sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
if(sc)
return sc.ToHr();
sc = pContextMenu->EmptyMenuList();
return sc.ToHr();
}
STDMETHODIMP
CNodeInitObject::AddThirdPartyExtensionItems(IDataObject* piDataObject )
{
DECLARE_SC(sc, TEXT("CNodeInitObject::AddThirdPartyExtensionItems"));
CContextMenu *pContextMenu = GetContextMenu();
sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
if(sc)
return sc.ToHr();
sc = pContextMenu->AddThirdPartyExtensionItems(piDataObject);
return sc.ToHr();
}
STDMETHODIMP
CNodeInitObject::AddPrimaryExtensionItems(IUnknown* piCallback, IDataObject* piDataObject )
{
DECLARE_SC(sc, TEXT("CNodeInitObject::AddPrimaryExtensionItems"));
CContextMenu *pContextMenu = GetContextMenu();
sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
if(sc)
return sc.ToHr();
sc = pContextMenu->AddPrimaryExtensionItems(piCallback, piDataObject);
return sc.ToHr();
}
STDMETHODIMP
CNodeInitObject::ShowContextMenu(HWND hwndParent, LONG xPos, LONG yPos, LONG* plSelected)
{
DECLARE_SC(sc, TEXT("CNodeInitObject::ShowContextMenu"));
CContextMenu *pContextMenu = GetContextMenu();
sc = ScCheckPointers(pContextMenu, E_UNEXPECTED);
if(sc)
return sc.ToHr();
pContextMenu->SetStatusBar(GetStatusBar()); // wire up the status bar.
sc = pContextMenu->ShowContextMenu(hwndParent, xPos, yPos, plSelected);
return sc.ToHr();
}
//############################################################################
//############################################################################
//
// Implementation of CCommandSink
//
//############################################################################
//############################################################################
/*+-------------------------------------------------------------------------*
* class CCommandSink
*
*
* PURPOSE:
*
*+-------------------------------------------------------------------------*/
class CCommandSink : public CWindowImpl<CCommandSink>
{
// Construction
public:
CCommandSink( CContextMenu& nodemgr, WTL::CMenu& menu, CConsoleStatusBar * pStatusbar);
virtual ~CCommandSink();
BOOL Init();
LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
BEGIN_MSG_MAP(CCommandSink)
MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
END_MSG_MAP()
private:
CContextMenu& m_nodemgr;
const WTL::CMenu& m_menu;
CConsoleStatusBar * m_pStatusBar;
};
CCommandSink::CCommandSink( CContextMenu& nodemgr, WTL::CMenu& menu, CConsoleStatusBar * pStatusbar)
: m_nodemgr( nodemgr ),
m_menu( menu ),
m_pStatusBar(pStatusbar)
{
}
CCommandSink::~CCommandSink()
{
/*
* clear the status bar text, if there's any there.
*/
if (m_pStatusBar != NULL)
m_pStatusBar->ScSetStatusText (NULL);
}
BOOL CCommandSink::Init()
{
RECT rcPos = {0,0,0,0};
Create(NULL, rcPos, _T("ACFx:CxtMenuSink"), WS_POPUP);
return TRUE;
}
LRESULT CCommandSink::OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
UINT nItemID = (UINT) LOWORD(wParam); // menu item or submenu index
UINT nFlags = (UINT) HIWORD(wParam); // menu flags
HMENU hSysMenu = (HMENU) lParam; // handle of menu clicked
TRACE(_T("CCommandSink::OnMenuSelect: nItemID=%d, nFlags=0x%X, hSysMenu=0x%X\n"), nItemID, nFlags, hSysMenu);
if ( 0xFFFF == nFlags && NULL == hSysMenu )
return 0; // as per Win32 ProgRef
if ( 0 == nItemID && !(nFlags & MF_POPUP) )
return 0; // no item selected
CMenuItem* pmenuitem = NULL;
if (nFlags & MF_POPUP)
{
if ( hSysMenu == m_menu.m_hMenu )
{
// We assume menu's cannot be longer than 256 chars
TCHAR szMenu[256];
MENUITEMINFO menuItemInfo;
menuItemInfo.cbSize = sizeof(MENUITEMINFO);
menuItemInfo.fMask = MIIM_TYPE;
menuItemInfo.fType = MFT_STRING;
menuItemInfo.cch = 256;
menuItemInfo.dwTypeData = szMenu;
::GetMenuItemInfo(hSysMenu, nItemID, TRUE, &menuItemInfo);
ASSERT(256 >= (menuItemInfo.cch+1));
pmenuitem = m_nodemgr.FindNthItemInSubmenu( NULL, nItemID, szMenu );
}
else
pmenuitem = m_nodemgr.FindNthItemInSubmenu( hSysMenu, nItemID, NULL );
}
else
pmenuitem = m_nodemgr.FindMenuItem( nItemID );
if ( NULL == pmenuitem )
{
ASSERT( FALSE );
return 0;
}
if(m_pStatusBar)
m_pStatusBar->ScSetStatusText( pmenuitem->GetMenuItemStatusBarText() );
return 0;
}
//############################################################################
//############################################################################
//
// CContextMenu methods - continued from oncmenu.cpp
// These methods were originally in this file and I dont want to move
// them and break history - vivekj
//
//############################################################################
//############################################################################
//+-------------------------------------------------------------------
//
// Member: CContextMenu::EmptyMenuList
//
// Synopsis: Clear the context menu.
//
// Arguments:
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CContextMenu::EmptyMenuList ()
{
DECLARE_SC(sc, _T("IContextMenuProvider::EmptyMenuList"));
START_CRITSEC_BOTH
delete m_pmenuitemRoot;
m_pmenuitemRoot = NULL;
m_nNextMenuItemID = MENUITEM_BASE_ID;
ReleaseSnapinList();
m_fAddedThirdPartyExtensions = FALSE;
m_MaxPrimaryOwnerID = OWNERID_PRIMARY_MIN;
m_MaxThirdPartyOwnerID = OWNERID_THIRD_PARTY_MIN;
m_CurrentExtensionOwnerID = OWNERID_NATIVE;
m_fPrimaryInsertionFlags = 0;
m_fThirdPartyInsertionFlags = 0;
END_CRITSEC_BOTH
return sc.ToHr();
}
/*+-------------------------------------------------------------------------*
*
* CContextMenu::RemoveAccelerators
*
* PURPOSE: Removes the accelerators from a context menu item name
*
* PARAMETERS:
* CStr & str :
*
* RETURNS:
* void
*
*+-------------------------------------------------------------------------*/
void
RemoveAccelerators(tstring &str)
{
// in some locales , the accelerators appear at the end eg: Start (&s). Therefore, remove anything after (&
int i = str.find(TEXT( "(&" ));
if (i != tstring::npos)
str.erase (i); // remove the waste left over after and including the string "(&"
tstring::iterator itToTrim = std::remove (str.begin(), str.end(), _T('&'));
// remove the waste left over after removing accelerator markers
str.erase (itToTrim, str.end());
}
//+-------------------------------------------------------------------
//
// Member: CContextMenu::AddItem
//
// Synopsis: Add a menu item to context menu.
//
// Arguments: CONTEXTMENUITEM*
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CContextMenu::AddItem( CONTEXTMENUITEM* pItem )
{
DECLARE_SC(sc, _T("IContextMenuCallback::AddItem"));
return ( sc = ScAddItem( pItem ) ).ToHr();
}
//+-------------------------------------------------------------------
//
// Member: CContextMenu::ScAddItem
//
// Synopsis: Add a menu item to context menu.
//
// Arguments: CONTEXTMENUITEM*
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CContextMenu::ScAddItem( CONTEXTMENUITEM* pItem, bool bPassCommandBackToSnapin /*= false*/ )
{
DECLARE_SC(sc, _T("IContextMenuCallback::ScAddItem"));
if (NULL == pItem)
{
sc = E_INVALIDARG;
TraceSnapinError(_T("NULL CONTEXTMENUITEM ptr"), sc);
return sc;
}
// added a non-langugage independent context menu item. Cook up a language independent ID.
// get the menu text and strip out accelerator markers
tstring strLanguageIndependentName;
if(pItem->strName)
{
USES_CONVERSION;
strLanguageIndependentName = OLE2CT(pItem->strName);
RemoveAccelerators(strLanguageIndependentName);
}
#ifdef DBG
TRACE(_T("CContextMenu::AddItem name \"%ls\" statusbartext \"%ls\" commandID %ld submenuID %ld flags %ld special %ld\n"),
SAFEDBGBSTR(pItem->strName),
SAFEDBGBSTR(pItem->strStatusBarText),
pItem->lCommandID,
pItem->lInsertionPointID,
pItem->fFlags,
pItem->fSpecialFlags);
#endif
// leaves critsec claim for DoAddMenuItem
USES_CONVERSION;
sc = DoAddMenuItem( OLE2CT(pItem->strName),
OLE2CT(pItem->strStatusBarText),
strLanguageIndependentName.data(),
pItem->lCommandID,
pItem->lInsertionPointID,
pItem->fFlags,
pItem->fSpecialFlags,
m_CurrentExtensionOwnerID,
NULL,
bPassCommandBackToSnapin );
return sc;
}
//+-------------------------------------------------------------------
//
// Member: CContextMenu::AddItem
//
// Synopsis: Add a menu item to context menu.
//
// Arguments: CONTEXTMENUITEM2* - includes a language independent name
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CContextMenu::AddItem( CONTEXTMENUITEM2* pItem )
{
DECLARE_SC(sc, _T("IContextMenuCallback::AddItem"));
if (NULL == pItem)
{
sc = E_INVALIDARG;
TraceSnapinError(_T("NULL CONTEXTMENUITEM ptr"), sc);
return sc.ToHr();
}
// No language-independent-id ?
if ( (pItem->strLanguageIndependentName == NULL) ||
(wcscmp(pItem->strLanguageIndependentName, L"") == 0) )
{
// and it is neither a separator nor insertion point.
if ( !(MF_SEPARATOR & pItem->fFlags) &&
!(CCM_SPECIAL_INSERTION_POINT & pItem->fSpecialFlags) )
{
sc = E_INVALIDARG;
TraceSnapinError(_T("NULL language-indexpendent-id passed"), sc);
return sc.ToHr();
}
}
#ifdef DBG
TRACE(_T("CContextMenu::AddItem name \"%ls\" statusbartext \"%ls\" languageIndependentName \"%ls\" commandID %ld submenuID %ld flags %ld special %ld\n"),
SAFEDBGBSTR(pItem->strName),
SAFEDBGBSTR(pItem->strStatusBarText),
SAFEDBGBSTR(pItem->strLanguageIndependentName),
pItem->lCommandID,
pItem->lInsertionPointID,
pItem->fFlags,
pItem->fSpecialFlags
);
#endif
// leaves critsec claim for DoAddMenuItem
USES_CONVERSION;
sc = DoAddMenuItem( OLE2CT(pItem->strName),
OLE2CT(pItem->strStatusBarText),
OLE2CT(pItem->strLanguageIndependentName),
pItem->lCommandID,
pItem->lInsertionPointID,
pItem->fFlags,
pItem->fSpecialFlags,
m_CurrentExtensionOwnerID );
return sc.ToHr();
}
//+-------------------------------------------------------------------
//
// Member: CContextMenu::AddPrimaryExtensionItems
//
// Synopsis: Ask primary snapin to add menu items.
//
// Arguments: [piExtension]
// [piDataobject]
//
// Note: claims critsec
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CContextMenu::AddPrimaryExtensionItems (
IUnknown* piExtension,
IDataObject* piDataObject )
{
DECLARE_SC(sc, _T("IContextMenuProvider::AddPrimaryExtensionItems"));
if (NULL == piExtension)
{
sc = E_INVALIDARG;
TraceSnapinError(_T("NULL IUnknown ptr"), sc);
return sc.ToHr();
}
if (NULL == piDataObject)
{
sc = E_INVALIDARG;
TraceSnapinError(_T("NULL IDataObject ptr"), sc);
return sc.ToHr();
}
// control reentrant access to this
if (!m_fAddingPrimaryExtensionItems)
{
m_fAddingPrimaryExtensionItems = true;
//HRESULT hr = ExtractObjectTypeCStr( piDataObject, &m_strObjectGUID );
//ASSERT( SUCCEEDED(hr) );
START_CRITSEC_SNAPIN;
sc = ScAddSnapinToList_IUnknown( piExtension, piDataObject, m_MaxPrimaryOwnerID++ );
END_CRITSEC_SNAPIN;
m_fAddingPrimaryExtensionItems = false;
// Clear view menu allowed flag
// A second call may be made to AddPrimaryExtensionItems to handle the other item
// types only, so the view items must be disabled after the first call.
m_fPrimaryInsertionFlags &= ~CCM_INSERTIONALLOWED_VIEW;
if (sc)
return sc.ToHr();
}
return sc.ToHr();
}
//+-------------------------------------------------------------------
//
// Member: CContextMenu::AddThirdPartyExtensionItems
//
// Synopsis: Ask extensions to add comtext menu items.
//
// Arguments: IDataObject*
//
// Note: claims critsec, potentially for a considerable period of time
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CContextMenu::AddThirdPartyExtensionItems (
IDataObject* piDataObject )
{
DECLARE_SC(sc, _T("IContextMenuProvider::AddThirdPartyExtensionItems"));
if (NULL == piDataObject)
{
sc = E_INVALIDARG;
TraceSnapinError(_T("NULL piDataObject"), sc);
return sc.ToHr();
}
START_CRITSEC_SNAPIN;
// Extensions may only be added once, otherwise return S_FALSE
if (m_fAddedThirdPartyExtensions == TRUE)
{
sc = S_FALSE;
TraceNodeMgrLegacy(_T("CContextMenu::AddThirdPartyExtensionItems>> Menu already extended"), sc);
return sc.ToHr();
}
m_fAddedThirdPartyExtensions = TRUE;
do // not a loop
{
CExtensionsIterator it;
sc = it.ScInitialize(piDataObject, g_szContextMenu);
if (sc)
{
sc = S_FALSE;
break;
}
BOOL fProblem = FALSE;
for (; it.IsEnd() == FALSE; it.Advance())
{
sc = ScAddSnapinToList_GUID(it.GetCLSID(), piDataObject,
m_MaxThirdPartyOwnerID++);
if (sc)
fProblem = TRUE; // Continue even on error.
}
if (fProblem == TRUE)
sc = S_FALSE;
} while (0);
END_CRITSEC_SNAPIN;
return sc.ToHr();
}
// claims critsec, potentially for a considerable period of time
STDMETHODIMP CContextMenu::AddMultiSelectExtensionItems (
LONG_PTR lMultiSelection)
{
MMC_TRY
if (lMultiSelection == 0)
return E_INVALIDARG;
CMultiSelection* pMS = reinterpret_cast<CMultiSelection*>(lMultiSelection);
ASSERT(pMS != NULL);
TRACE_METHOD(CContextMenu,AddThirdPartyExtensionItems);
TRACE(_T("CContextMenu::AddThirdPartyExtensionItems"));
START_CRITSEC_SNAPIN;
// Extensions may only be added once, otherwise return S_FALSE
if (m_fAddedThirdPartyExtensions == TRUE)
{
TRACE(_T("CContextMenu::AddThirdPartyExtensionItems>> Menu already extended"));
return S_FALSE;
}
m_fAddedThirdPartyExtensions = TRUE;
do // not a loop
{
CList<CLSID, CLSID&> snapinClsidList;
HRESULT hr = pMS->GetExtensionSnapins(g_szContextMenu, snapinClsidList);
BREAK_ON_FAIL(hr);
POSITION pos = snapinClsidList.GetHeadPosition();
if (pos == NULL)
break;
CLSID clsid;
IDataObjectPtr spDataObject;
hr = pMS->GetMultiSelDataObject(&spDataObject);
ASSERT(SUCCEEDED(hr));
BREAK_ON_FAIL(hr);
BOOL fProblem = FALSE;
while (pos)
{
clsid = snapinClsidList.GetNext(pos);
hr = ScAddSnapinToList_GUID(clsid, spDataObject,
m_MaxThirdPartyOwnerID++).ToHr();
CHECK_HRESULT(hr);
if (FAILED(hr))
fProblem = TRUE; // Continue even on error.
}
if (fProblem == TRUE)
hr = S_FALSE;
} while (0);
END_CRITSEC_SNAPIN;
return S_OK;
MMC_CATCH
}
// Worker function, called recursively by FindMenuItem
// critsec should already be claimed
// If fFindSubmenu, then nMenuItemID is actually an HMENU
CMenuItem* FindWorker( MenuItemList& list, LONG_PTR nMenuItemID, BOOL fFindSubmenu )
{
POSITION pos = list.GetHeadPosition();
while(pos)
{
CMenuItem* pItem = list.GetNext(pos);
if ( !fFindSubmenu && pItem->GetMenuItemID() == nMenuItemID )
{
// Found a match
return pItem;
} else
if ( pItem->HasChildList() )
{
if ( fFindSubmenu &&
pItem->GetPopupMenuHandle() == (HMENU)nMenuItemID &&
!pItem->IsSpecialInsertionPoint() ) // "insertion point" is not real menu
return pItem;
pItem = FindWorker( pItem->GetMenuItemSubmenu(), nMenuItemID, fFindSubmenu );
if (NULL != pItem)
return pItem;
}
}
return NULL;
}
MenuItemList* CContextMenu::GetMenuItemList()
{
if (NULL == m_pmenuitemRoot)
m_pmenuitemRoot = new CRootMenuItem;
if (m_pmenuitemRoot == NULL)
{
return NULL;
}
return &m_pmenuitemRoot->GetMenuItemSubmenu();
}
// critsec should already be claimed
CMenuItem* CContextMenu::FindMenuItem( LONG_PTR nMenuItemID, BOOL fFindSubmenu )
{
DECLARE_SC(sc, TEXT("CContextMenu::FindMenuItem"));
if (0 == nMenuItemID || CCM_INSERTIONPOINTID_ROOT_MENU == nMenuItemID)
return m_pmenuitemRoot;
else
{
MenuItemList* plist = GetMenuItemList();
sc = ScCheckPointers( plist );
if (sc)
return NULL;
return FindWorker( *plist, nMenuItemID, fFindSubmenu );
}
}
/*+-------------------------------------------------------------------------*
*
* ReverseFindWorker
*
* PURPOSE: Worker function, called recursively by ReverseFindMenuItem
* critsec should already be claimed
*
* PARAMETERS:
* MenuItemList& list :
* long nCommandID :
* MENU_OWNER_ID ownerID :
* CStr & strPath :
*
* RETURNS:
* CMenuItem*
*
*+-------------------------------------------------------------------------*/
CMenuItem*
ReverseFindWorker( MenuItemList& list, long nCommandID, MENU_OWNER_ID ownerID, CStr &strPath, CStr &strLanguageIndependentPath )
{
POSITION pos = list.GetHeadPosition();
while(pos)
{
CMenuItem* pItem = list.GetNext(pos);
if ( pItem->GetCommandID() == nCommandID
&& ( (pItem->GetMenuItemOwner() == ownerID)
|| IsSharedInsertionPointID(nCommandID)
)
)
{
// Found a match - add it to the path and return
strPath = pItem->GetPath();
strLanguageIndependentPath = pItem->GetLanguageIndependentPath();
return pItem;
}
else if ( pItem->HasChildList() )
{
pItem = ReverseFindWorker( pItem->GetMenuItemSubmenu(), nCommandID, ownerID, strPath, strLanguageIndependentPath );
if (NULL != pItem)
{
return pItem;
}
}
}
return NULL;
}
/*+-------------------------------------------------------------------------*
*
* CContextMenu::ReverseFindMenuItem
*
* PURPOSE: Searches for the specified menu item. Also builds up the
* path to the menu item in strPath.
*
* NOTE: critsec should already be claimed
*
* PARAMETERS:
* long nCommandID :
* MENU_OWNER_ID ownerID :
* CStr & strPath :
*
* RETURNS:
* CMenuItem*
*
*+-------------------------------------------------------------------------*/
CMenuItem*
CContextMenu::ReverseFindMenuItem( long nCommandID, MENU_OWNER_ID ownerID, CStr &strPath, CStr &strLanguageIndependentPath)
{
DECLARE_SC(sc, TEXT("CContextMenu::ReverseFindMenuItem"));
strPath = TEXT(""); // initialize
if (CCM_INSERTIONPOINTID_ROOT_MENU == nCommandID)
return m_pmenuitemRoot;
else
{
MenuItemList* plist = GetMenuItemList();
sc = ScCheckPointers( plist );
if (sc)
return NULL;
return ReverseFindWorker( *plist, nCommandID, ownerID, strPath, strLanguageIndependentPath);
}
}
//
// Find Nth item in specified menu/submenu
//
CMenuItem* CContextMenu::FindNthItemInSubmenu( HMENU hmenuParent, UINT iPosition, LPTSTR lpszMenuName )
{
// locate menu/submenu
MenuItemList* plist = GetMenuItemList();
if ( NULL != hmenuParent )
{
CMenuItem* pParent = FindMenuItem( (LONG_PTR)hmenuParent, TRUE );
if ( NULL == pParent )
{
ASSERT( FALSE );
return NULL;
}
plist = &pParent->GetMenuItemSubmenu();
}
if ( NULL == plist )
{
ASSERT( FALSE );
return NULL;
}
// find the Nth item
POSITION pos = plist->GetHeadPosition();
if (NULL != lpszMenuName)
{
while(pos)
{
CMenuItem* pItem = plist->GetNext(pos);
if (! _tcscmp(lpszMenuName, pItem->GetMenuItemName() ))
{
// Found the match
return pItem;
}
}
}
else
{
while(pos)
{
CMenuItem* pItem = plist->GetNext(pos);
if ( 0 == iPosition-- )
{
// Found a match
return pItem;
}
}
}
ASSERT( FALSE );
return NULL;
}
// claims critsec
STDMETHODIMP CContextMenu::DoAddMenuItem(LPCTSTR lpszName,
LPCTSTR lpszStatusBarText,
LPCTSTR lpszLanguageIndependentName,
LONG lCommandID,
LONG lInsertionPointID,
LONG fFlags,
LONG fSpecialFlags,
MENU_OWNER_ID ownerID,
CMenuItem** ppMenuItem /* = NULL */,
bool bPassCommandBackToSnapin /*= false*/ )
{
DECLARE_SC(sc, TEXT("CContextMenu::DoAddMenuItem"));
MMC_TRY
// init out param
if (ppMenuItem)
*ppMenuItem = NULL;
// Save test flag now because special flags are modified below
BOOL bTestOnly = fSpecialFlags & CCM_SPECIAL_TESTONLY;
if ( OWNERID_INVALID == ownerID )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): invalid ownerid"));
ASSERT(FALSE);
return E_INVALIDARG;
}
if ( (CCM_SPECIAL_SEPARATOR & fSpecialFlags)?0:1
+ ((CCM_SPECIAL_SUBMENU|CCM_SPECIAL_DEFAULT_ITEM) & fSpecialFlags)?0:1
+ (CCM_SPECIAL_INSERTION_POINT & fSpecialFlags)?0:1
> 1 )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): invalid combination of special flags"));
ASSERT(FALSE);
return E_INVALIDARG;
}
if (CCM_SPECIAL_SEPARATOR & fSpecialFlags)
{
lpszName = NULL;
lpszStatusBarText = NULL;
lCommandID = 0;
fFlags = MF_SEPARATOR | MF_GRAYED | MF_DISABLED;
}
if ( CCM_SPECIAL_INSERTION_POINT & fSpecialFlags )
{
fFlags = NULL; // be sure to clear MF_POPUP
fSpecialFlags = CCM_SPECIAL_INSERTION_POINT;
}
if ( (CCM_SPECIAL_SUBMENU & fSpecialFlags) && !(MF_POPUP & fFlags) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): CCM_SPECIAL_SUBMENU requires MF_POPUP"));
ASSERT(FALSE);
return E_INVALIDARG;
}
if ( (MF_OWNERDRAW|MF_BITMAP) & fFlags )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): MF_OWNERDRAW and MF_BITMAP are invalid"));
ASSERT(FALSE);
return E_INVALIDARG;
}
else if ( !(MF_SEPARATOR & fFlags) &&
!(CCM_SPECIAL_INSERTION_POINT & fSpecialFlags) &&
NULL == lpszName )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): invalid menuitem text\n"));
ASSERT(FALSE);
return E_INVALIDARG;
}
// note that NULL==lpszStatusBarText is permitted
START_CRITSEC_MENU;
//
// An insertion point of 0 is interpreted the same as CCM_INSERTIONPOINTID_ROOT_MENU
//
if (0 == lInsertionPointID)
lInsertionPointID = CCM_INSERTIONPOINTID_ROOT_MENU;
//
// Check that the insertion point ID specified is legal for this customer
//
do // false loop
{
if ( !IsSpecialInsertionPointID(lInsertionPointID) )
break;
if ( IsReservedInsertionPointID(lInsertionPointID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): using reserved insertion point ID\n"));
return E_INVALIDARG;
}
if ( !IsSharedInsertionPointID(lInsertionPointID) )
break;
if ( !IsAddPrimaryInsertionPointID(lInsertionPointID) )
{
if ( IsPrimaryOwnerID(ownerID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): not addprimary insertion point ID\n"));
return E_INVALIDARG;
}
}
if ( !IsAdd3rdPartyInsertionPointID(lInsertionPointID) )
{
if ( IsThirdPartyOwnerID(ownerID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): not add3rdpartyinsertion point ID\n"));
return E_INVALIDARG;
}
}
} while (FALSE); // false loop
//
// Check that the command ID specified is legal for this customer
//
if ( (MF_POPUP & fFlags) || (CCM_SPECIAL_INSERTION_POINT & fSpecialFlags) )
{
do // false loop
{
if ( !IsSpecialInsertionPointID(lCommandID) )
break;
if ( IsReservedInsertionPointID(lCommandID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): adding reserved insertion point ID\n"));
ASSERT(FALSE);
return E_INVALIDARG;
}
if ( !IsSharedInsertionPointID(lCommandID) )
break;
if ( IsThirdPartyOwnerID(ownerID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): 3rdparty cannot add shared insertion point"));
ASSERT(FALSE);
return E_INVALIDARG;
}
else if ( IsPrimaryOwnerID(ownerID) )
{
if ( !IsCreatePrimaryInsertionPointID(lCommandID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): only system for new !PRIMARYCREATE submenu"));
ASSERT(FALSE);
return E_INVALIDARG;
}
}
else if ( IsSystemOwnerID(ownerID) )
{
if ( IsCreatePrimaryInsertionPointID(lCommandID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): only primary extension for new PRIMARYCREATE submenu"));
ASSERT(FALSE);
return E_INVALIDARG;
}
}
} while (FALSE); // false loop
}
else if ( !(CCM_SPECIAL_SEPARATOR & fSpecialFlags) )
{
if ( IsReservedCommandID(lCommandID) )
{
TRACE(_T("CContextMenu::DoAddMenuItem(): no new RESERVED menu items"));
ASSERT(FALSE);
return E_INVALIDARG;
}
}
if (NULL == m_pmenuitemRoot)
m_pmenuitemRoot = new CRootMenuItem;
CStr strPath, strLanguageIndependentPath; // this builds up the path of the menu item.
CMenuItem* pParent = ReverseFindMenuItem( lInsertionPointID, ownerID, strPath, strLanguageIndependentPath);
if (NULL == pParent)
{
TRACE(_T("CContextMenu::DoAddMenuItem(): submenu with command ID %ld owner %ld does not exist"), lInsertionPointID, ownerID );
ASSERT(FALSE);
return E_INVALIDARG;
}
MenuItemList& rMenuList = pParent->GetMenuItemSubmenu();
// If this is only a test add, return with success now
if (bTestOnly)
return S_OK;
// get the data object and IExtendContextMenu pointer to set in the item.
IExtendContextMenuPtr spExtendContextMenu;
IDataObject* pDataObject = NULL; // This is used JUST to hold on to the object until Command completes.
// locate the IExtendContextMenu of the snapin.
{
// The selected item was added by an extension
SnapinStruct* psnapin = FindSnapin( ownerID );
if(psnapin != NULL)
{
pDataObject = psnapin->m_pIDataObject;
spExtendContextMenu = psnapin->pIExtendContextMenu;
}
else
{
CTiedComObjectCreator<CNativeExtendContextMenu>::
ScCreateAndConnect(*this, spExtendContextMenu);
// built in items are handled by CContextMenu itself.
}
}
// compute the language independent and language dependent paths for the context menu item.
CStr strLanguageIndependentName = lpszLanguageIndependentName;
tstring tstrName = lpszName ? lpszName : TEXT("");
RemoveAccelerators(tstrName);
CStr strName;
strName = tstrName.data(); // got to standardise on either tstring or CStr
// add a "->" separator to the path if needed
if(!strPath.IsEmpty() && !strName.IsEmpty())
strPath += _T("->");
strPath += strName;
// add a "->" separator to the language independent path if needed
if(!strLanguageIndependentPath.IsEmpty() && !strLanguageIndependentName.IsEmpty())
strLanguageIndependentPath += _T("->");
strLanguageIndependentPath += strLanguageIndependentName;
CMenuItem* pItem = new CMenuItem(
lpszName,
lpszStatusBarText,
lpszLanguageIndependentName,
(LPCTSTR)strPath,
(LPCTSTR)strLanguageIndependentPath,
lCommandID,
m_nNextMenuItemID++,
fFlags,
ownerID,
spExtendContextMenu,
pDataObject,
fSpecialFlags,
bPassCommandBackToSnapin);
ASSERT( pItem );
if (pItem == NULL)
return E_OUTOFMEMORY;
rMenuList.AddTail(pItem);
// If this is a system defined insertion point, update the insertion flags
if (IsSharedInsertionPointID(lCommandID) && !IsCreatePrimaryInsertionPointID(lCommandID))
{
long fFlag = ( 1L << (lCommandID & CCM_INSERTIONPOINTID_MASK_FLAGINDEX));
if (IsAddPrimaryInsertionPointID(lCommandID))
m_fPrimaryInsertionFlags |= fFlag;
if (IsAdd3rdPartyInsertionPointID(lCommandID))
m_fThirdPartyInsertionFlags |= fFlag;
}
// return the item if required
if (ppMenuItem)
*ppMenuItem = pItem;
END_CRITSEC_MENU;
return S_OK;
MMC_CATCH
}
// APP HACK. Workarounding dependency on older FP where they were QI'ing for IConsole from
// IContextMenuCallback, which was working in MMC 1.2, but cannot work in mmc 2.0
// See bug 200621 (Windows bugs (ntbug9) 11/15/2000)
#define WORKAROUND_FOR_FP_REQUIRED
#if defined (WORKAROUND_FOR_FP_REQUIRED)
/***************************************************************************\
*
* CLASS: CWorkaroundWrapperForFrontPageMenu
*
* PURPOSE: Used from subclassed MMC's IExtendContextMenu interface for FrontPage.
* Contains (in com sense) IContextMenuCallback2 and IContextMenuCallback by forwarding
* them to original interface, but in addition supports QI for IConsole.
* This is a requirement for older FrontPage to work
*
\***************************************************************************/
class CWorkaroundWrapperForFrontPageMenu :
public IContextMenuCallback,
public IContextMenuCallback2,
public IConsole2, // workaround for bug 200621. This is a dummy implementation of IConsole2
public CComObjectRoot
{
friend class CWorkaroundMMCWrapperForFrontPageMenu;
// pointer to context menu object
IContextMenuCallbackPtr m_spIContextMenuCallback;
IContextMenuCallback2Ptr m_spIContextMenuCallback2;
public:
typedef CWorkaroundWrapperForFrontPageMenu ThisClass;
// com entry points
BEGIN_COM_MAP(ThisClass)
COM_INTERFACE_ENTRY(IContextMenuCallback) // the IContextMenuProvider and IContextMenu
COM_INTERFACE_ENTRY(IContextMenuCallback2)
COM_INTERFACE_ENTRY(IConsole)
COM_INTERFACE_ENTRY(IConsole2)
END_COM_MAP()
// just forward...
STDMETHOD(AddItem) ( CONTEXTMENUITEM* pItem )
{
if ( m_spIContextMenuCallback == NULL )
return E_UNEXPECTED;
return m_spIContextMenuCallback->AddItem( pItem );
}
// just forward...
STDMETHOD(AddItem) ( CONTEXTMENUITEM2* pItem )
{
if ( m_spIContextMenuCallback2 == NULL )
return E_UNEXPECTED;
return m_spIContextMenuCallback2->AddItem( pItem );
}
// IConsole2 methods - DUMMY - workaround for bug 200621
STDMETHOD(SetHeader)( LPHEADERCTRL pHeader) {return E_NOTIMPL;}
STDMETHOD(SetToolbar)( LPTOOLBAR pToolbar) {return E_NOTIMPL;}
STDMETHOD(QueryResultView)( LPUNKNOWN* pUnknown) {return E_NOTIMPL;}
STDMETHOD(QueryScopeImageList)( LPIMAGELIST* ppImageList) {return E_NOTIMPL;}
STDMETHOD(QueryResultImageList)( LPIMAGELIST* ppImageList) {return E_NOTIMPL;}
STDMETHOD(UpdateAllViews)( LPDATAOBJECT lpDataObject,LPARAM data,LONG_PTR hint) {return E_NOTIMPL;}
STDMETHOD(MessageBox)( LPCWSTR lpszText, LPCWSTR lpszTitle,UINT fuStyle, int* piRetval) {return E_NOTIMPL;}
STDMETHOD(QueryConsoleVerb)( LPCONSOLEVERB * ppConsoleVerb) {return E_NOTIMPL;}
STDMETHOD(SelectScopeItem)( HSCOPEITEM hScopeItem) {return E_NOTIMPL;}
STDMETHOD(GetMainWindow)( HWND* phwnd)
{
if (!phwnd)
return E_INVALIDARG;
*phwnd = (CScopeTree::GetScopeTree() ? CScopeTree::GetScopeTree()->GetMainWindow() : NULL);
return S_OK;
}
STDMETHOD(NewWindow)( HSCOPEITEM hScopeItem, unsigned long lOptions) {return E_NOTIMPL;}
STDMETHOD(Expand)( HSCOPEITEM hItem, BOOL bExpand) {return E_NOTIMPL;}
STDMETHOD(IsTaskpadViewPreferred)() {return E_NOTIMPL;}
STDMETHOD(SetStatusText )( LPOLESTR pszStatusText) {return E_NOTIMPL;}
};
/***************************************************************************\
*
* CLASS: CWorkaroundMMCWrapperForFrontPageMenu
*
* PURPOSE: Subclasses MMC's IExtendContextMenu interface for FrontPage.
* Contains ( in com sense) IExtendContextMenu; Forwards calls to default MMC implementation,
* but for AddMenuItems gives itself as a callback interface.
* [ main purpose to have this object is to avoid changing main MMC functions ]
* [ to implement this workaround ]
*
\***************************************************************************/
class CWorkaroundMMCWrapperForFrontPageMenu :
public IExtendContextMenu,
public CComObjectRoot
{
// pointer to context menu object
IExtendContextMenuPtr m_spExtendContextMenu;
CNode *m_pNode;
public:
typedef CWorkaroundMMCWrapperForFrontPageMenu ThisClass;
// this method is null for all snapins, but FrontPage
// for FrontPage it wraps and replaces spIUnknown paramter
static SC ScSubclassFP(const CLSID& clsid,IUnknownPtr &spIUnknown)
{
DECLARE_SC(sc, TEXT("CWorkaroundMMCWrapperForFrontPageMenu::ScSubclassFP"));
static const CLSID CLSID_Fpsrvmmc = { 0xFF5903A8, 0x78D6, 0x11D1,
{ 0x92, 0xF6, 0x00, 0x60, 0x97, 0xB0, 0x10, 0x56 } };
// only required intercept one clsid
if ( clsid != CLSID_Fpsrvmmc )
return sc;
// create self
typedef CComObject<CWorkaroundMMCWrapperForFrontPageMenu> ThisComObj_t;
ThisComObj_t *pObj = NULL;
sc = ThisComObj_t::CreateInstance(&pObj);
if (sc)
return sc;
// cast to avoid member access problems (workarounding compiler)
ThisClass *pThis = pObj;
sc = ScCheckPointers( pThis, E_UNEXPECTED );
if (sc)
return sc;
// maintain the lifetime in case of accident
IUnknownPtr spThis = pThis->GetUnknown();
// grab on snapin's interface
pThis->m_spExtendContextMenu = spIUnknown;
sc = ScCheckPointers( pThis->m_spExtendContextMenu, E_UNEXPECTED );
if (sc)
return sc;
// substitute the snapin (in-out parameter)
spIUnknown = spThis;
return sc;
}
// com entry points
BEGIN_COM_MAP(ThisClass)
COM_INTERFACE_ENTRY(IExtendContextMenu)
END_COM_MAP()
// AddMenuItems is the method this object exists for.
// If we got here, mmc is about to ask FrontPage to add its items to context menu.
// We'll wrap the callback interface given by MMC with the object implementing
// phony IConsole - this is required for older FP to work
STDMETHOD(AddMenuItems)( LPDATAOBJECT piDataObject, LPCONTEXTMENUCALLBACK piCallback, long * pInsertionAllowed )
{
DECLARE_SC(sc, TEXT("CWorkaroundMMCWrapperForFrontPageMenu::AddMenuItems"));
IContextMenuCallbackPtr spIContextMenuCallback = piCallback;
IContextMenuCallback2Ptr spIContextMenuCallback2 = piCallback;
if ( m_spExtendContextMenu == NULL || spIContextMenuCallback == NULL || spIContextMenuCallback2 == NULL )
return E_UNEXPECTED;
// create a wrapper for FP
typedef CComObject<CWorkaroundWrapperForFrontPageMenu> WrapperComObj_t;
WrapperComObj_t *pObj = NULL;
sc = WrapperComObj_t::CreateInstance(&pObj);
if (sc)
return sc.ToHr();
// cast to avoid member access problems (workarounding compiler)
CWorkaroundWrapperForFrontPageMenu *pWrapper = pObj;
sc = ScCheckPointers( pWrapper, E_UNEXPECTED );
if (sc)
return sc.ToHr();
// maintain the lifetime in case of accident
IUnknownPtr spWrapper = pWrapper->GetUnknown();
// grab on snapin's interface
pWrapper->m_spIContextMenuCallback = spIContextMenuCallback;
pWrapper->m_spIContextMenuCallback2 = spIContextMenuCallback2;
// call snapin on behave on mmc, but pass itself as callback
sc = m_spExtendContextMenu->AddMenuItems( piDataObject, pWrapper, pInsertionAllowed );
// fall thru even on error - need to release interfaces
// reset callback interfaces - not valid after the call anyway...
// this will let context menu go, and prevent FP from suicide (AV);
// Following this all calls to IContextMenuCallback would fail,
// but that's ok, since it is not legal to call them after AddMenuItems.
pWrapper->m_spIContextMenuCallback = NULL;
pWrapper->m_spIContextMenuCallback2 = NULL;
return sc.ToHr();
}
// simply forward....
STDMETHOD(Command)(long lCommandID, LPDATAOBJECT piDataObject)
{
ASSERT( m_spExtendContextMenu != NULL );
if ( m_spExtendContextMenu == NULL )
return E_UNEXPECTED;
return m_spExtendContextMenu->Command(lCommandID, piDataObject);
}
};
#endif // defined (WORKAROUND_FOR_FP_REQUIRED)
// critsec should already be claimed
SC CContextMenu::ScAddSnapinToList_GUID(
const CLSID& clsid,
IDataObject* piDataObject,
MENU_OWNER_ID ownerID )
{
DECLARE_SC(sc, TEXT("CContextMenu::ScAddSnapinToList_GUID"));
// cocreate extension
IUnknownPtr spIUnknown;
sc = ::CoCreateInstance(clsid, NULL, MMC_CLSCTX_INPROC,
IID_IUnknown, (LPVOID*)&spIUnknown);
if (sc)
return sc;
#if defined (WORKAROUND_FOR_FP_REQUIRED)
sc = CWorkaroundMMCWrapperForFrontPageMenu::ScSubclassFP(clsid, spIUnknown);
#endif // defined (WORKAROUND_FOR_FP_REQUIRED)
// get IExtendContextMenu interface
IExtendContextMenuPtr spIExtendContextMenu = spIUnknown;
sc = ScCheckPointers(spIExtendContextMenu, E_NOINTERFACE);
if (sc)
return sc;
// add menu items
sc = ScAddSnapinToList_IExtendContextMenu(spIExtendContextMenu,
piDataObject, ownerID );
if (sc)
return sc;
return sc;
}
// does not AddRef() or Release() interface pointer
// critsec should already be claimed
SC CContextMenu::ScAddSnapinToList_IUnknown(
IUnknown* piExtension,
IDataObject* piDataObject,
MENU_OWNER_ID ownerID )
{
DECLARE_SC(sc, TEXT("CContextMenu::AddSnapinToList_IUnknown"));
// parameter check
sc = ScCheckPointers(piExtension);
if (sc)
return sc;
IExtendContextMenuPtr spIExtendContextMenu = piExtension;
if (spIExtendContextMenu == NULL)
return sc; // snapin does not extend context menus
// add menu items
sc = ScAddSnapinToList_IExtendContextMenu( spIExtendContextMenu, piDataObject, ownerID );
if (sc)
return sc;
return sc;
}
// Interface pointer is Release()d when menu list is emptied
// critsec should already be claimed
SC CContextMenu::ScAddSnapinToList_IExtendContextMenu(
IExtendContextMenu* pIExtendContextMenu,
IDataObject* piDataObject,
MENU_OWNER_ID ownerID )
{
DECLARE_SC(sc, TEXT("CContextMenu::ScAddSnapinToList_IExtendContextMenu"));
// parameter check
sc = ScCheckPointers(pIExtendContextMenu);
if (sc)
return sc;
SnapinStruct* psnapstruct = new SnapinStruct( pIExtendContextMenu, piDataObject, ownerID );
sc = ScCheckPointers(psnapstruct, E_OUTOFMEMORY);
if (sc)
return sc;
m_SnapinList->AddTail(psnapstruct);
m_CurrentExtensionOwnerID = ownerID;
long fInsertionFlags = IsPrimaryOwnerID(ownerID) ? m_fPrimaryInsertionFlags : m_fThirdPartyInsertionFlags;
// if view items are requested, then allow only view items
// view item requests go to the IComponent. If other item types are allowed there
// will be a second pass through this code directed to the IComponentData.
long lTempFlags = fInsertionFlags;
if ( fInsertionFlags & CCM_INSERTIONALLOWED_VIEW )
lTempFlags = CCM_INSERTIONALLOWED_VIEW;
try
{
sc = pIExtendContextMenu->AddMenuItems( piDataObject, this, &lTempFlags );
#ifdef DBG
if (sc)
TraceSnapinError(_T("IExtendContextMenu::AddMenuItems failed"), sc);
#endif
}
catch (...)
{
if (DOBJ_CUSTOMOCX == piDataObject)
{
ASSERT( FALSE && "IExtendContextMenu::AddMenuItem of IComponent is called with DOBJ_CUSTOMOCX and snapin derefed this custom data object. Please handle special dataobjects in your snapin.");
sc = E_UNEXPECTED;
}
else if (DOBJ_CUSTOMWEB == piDataObject)
{
ASSERT( FALSE && "IExtendContextMenu::AddMenuItem of IComponent is called with DOBJ_CUSTOMWEB and snapin derefed this custom data object. Please handle special dataobjects in your snapin.");
sc = E_UNEXPECTED;
}
else
{
ASSERT( FALSE && "IExtendContextMenu::AddMenuItem implemented by snapin has thrown an exception.");
sc = E_UNEXPECTED;
}
}
m_CurrentExtensionOwnerID = OWNERID_NATIVE;
if (sc)
return sc;
// Primary snapin is allowed to clear extension snapin insertion flags
if ( IsPrimaryOwnerID(ownerID) )
m_fThirdPartyInsertionFlags &= fInsertionFlags;
return sc;
}
// All snapin interface pointers are Release()d
// critsec should already be claimed
void CContextMenu::ReleaseSnapinList()
{
ASSERT(m_SnapinList != NULL);
if (m_SnapinList != NULL && m_SnapinList->GetCount() != 0)
{
POSITION pos = m_SnapinList->GetHeadPosition();
while(pos)
{
SnapinStruct* pItem = (SnapinStruct*)m_SnapinList->GetNext(pos);
ASSERT_OBJECTPTR( pItem );
delete pItem;
}
m_SnapinList->RemoveAll();
}
}
// critsec should already be claimed
SnapinStruct* CContextMenu::FindSnapin( MENU_OWNER_ID ownerID )
{
ASSERT(m_SnapinList != NULL);
if (m_SnapinList != NULL && m_SnapinList->GetCount() != 0)
{
POSITION pos = m_SnapinList->GetHeadPosition();
while(pos)
{
SnapinStruct* pItem = (SnapinStruct*)m_SnapinList->GetNext(pos);
ASSERT( NULL != pItem );
if ( ownerID == pItem->m_OwnerID )
return pItem;
}
}
return NULL;
}
// Worker function, called recursively by ShowContextMenu
// critsec should already be claimed
HRESULT CollapseInsertionPoints( CMenuItem* pmenuitemParent )
{
ASSERT( NULL != pmenuitemParent && !pmenuitemParent->IsSpecialInsertionPoint() );
MenuItemList& rMenuList = pmenuitemParent->GetMenuItemSubmenu();
POSITION pos = rMenuList.GetHeadPosition();
while(pos)
{
POSITION posThisItem = pos;
CMenuItem* pItem = (rMenuList.GetNext(pos));
ASSERT( pItem != NULL );
if ( pItem->IsPopupMenu() )
{
ASSERT( !pItem->IsSpecialInsertionPoint() );
HRESULT hr = CollapseInsertionPoints( pItem );
if ( FAILED(hr) )
{
ASSERT( FALSE );
return hr;
}
continue;
}
if ( !pItem->IsSpecialInsertionPoint() )
continue;
// we found an insertion point, move its items into this list
MenuItemList& rInsertedList = pItem->GetMenuItemSubmenu();
POSITION posInsertAfterThis = posThisItem;
while ( !rInsertedList.IsEmpty() )
{
CMenuItem* pInsertedItem = rInsertedList.RemoveHead();
posInsertAfterThis = rMenuList.InsertAfter( posInsertAfterThis, pInsertedItem );
}
// delete the insertion point item
rMenuList.RemoveAt(posThisItem);
delete pItem;
// restart at head of list, in case of recursive insertion points
pos = rMenuList.GetHeadPosition();
}
return S_OK;
}
// Worker function, called recursively by ShowContextMenu
// critsec should already be claimed and CollapseInsertionPoints should have been called
HRESULT CollapseSpecialSeparators( CMenuItem* pmenuitemParent )
{
ASSERT( NULL != pmenuitemParent && !pmenuitemParent->IsSpecialInsertionPoint() );
MenuItemList& rMenuList = pmenuitemParent->GetMenuItemSubmenu();
CMenuItem* pItem = NULL;
BOOL fLastItemWasReal = FALSE;
POSITION pos = rMenuList.GetHeadPosition();
POSITION posThisItem = pos;
while(pos)
{
posThisItem = pos;
pItem = (rMenuList.GetNext(pos));
ASSERT( pItem != NULL );
ASSERT( !pItem->IsSpecialInsertionPoint() );
if ( pItem->IsPopupMenu() )
{
ASSERT( !pItem->IsSpecialSeparator() );
HRESULT hr = CollapseSpecialSeparators( pItem );
if ( FAILED(hr) )
{
ASSERT( FALSE );
return hr;
}
fLastItemWasReal = TRUE;
continue;
}
if ( !pItem->IsSpecialSeparator() )
{
fLastItemWasReal = TRUE;
continue;
}
if ( fLastItemWasReal )
{
fLastItemWasReal = FALSE;
continue;
}
// Found two consecutive special separators, or special seperator as first item
// delete the insertion point item
rMenuList.RemoveAt(posThisItem);
delete pItem;
}
if ( !fLastItemWasReal && !rMenuList.IsEmpty() )
{
// Found special separator as last item
delete rMenuList.RemoveTail();
}
return S_OK;
}
// Worker function, called recursively by ShowContextMenu
// critsec should already be claimed
HRESULT BuildContextMenu( WTL::CMenu& menu,
CMenuItem* pmenuitemParent )
{
MenuItemList& rMenuList = pmenuitemParent->GetMenuItemSubmenu();
int nCount = 0;
bool fInsertedItemSinceLastSeparator = false;
POSITION pos = rMenuList.GetHeadPosition();
while(pos)
{
CMenuItem* pItem = (rMenuList.GetNext(pos));
ASSERT( pItem != NULL );
ASSERT( !pItem->IsSpecialInsertionPoint() );
UINT_PTR nCommandID = pItem->GetMenuItemID();
long nFlags = pItem->GetMenuItemFlags();
/*
* special processing for submenus
*/
if ( pItem->IsPopupMenu() )
{
// add items to a submenu
WTL::CMenu submenu;
VERIFY( submenu.CreatePopupMenu() );
HRESULT hr = BuildContextMenu( submenu, pItem );
if ( FAILED(hr) )
return hr;
HMENU hSubmenu = submenu.Detach();
ASSERT( NULL != hSubmenu );
nCommandID = (UINT_PTR)hSubmenu;
pItem->SetPopupMenuHandle( hSubmenu );
if ( pItem->IsSpecialSubmenu() )
{
MenuItemList& rChildMenuList = pItem->GetMenuItemSubmenu();
if ( rChildMenuList.IsEmpty() )
{
// Bug 151435: remove instead of disabling unused submenus
// pItem->SetMenuItemFlags(nFlags | (MF_GRAYED|MF_DISABLED));
::DestroyMenu(hSubmenu);
continue;
}
}
fInsertedItemSinceLastSeparator = true;
}
/*
* special processing for separators
*/
else if (nFlags & MF_SEPARATOR)
{
/*
* if we haven't inserted an item since the last separator,
* we don't want to insert this one or we'll have consecutive
* separators, or an unnecessary separator at the top of the menu
*/
if (!fInsertedItemSinceLastSeparator)
continue;
/*
* if there aren't any more items after this separator,
* we don't want to insert this one or we'll have an
* unnecessary separator at the bottom of the menu
*/
if (pos == NULL)
continue;
fInsertedItemSinceLastSeparator = false;
}
/*
* just a normal menu item
*/
else
{
fInsertedItemSinceLastSeparator = true;
}
if (!menu.AppendMenu(nFlags, nCommandID, pItem->GetMenuItemName()))
{
#ifdef DBG
TRACE(_T("BuildContextMenu: AppendMenu(%ld, %ld, \"%s\") reports error\n"),
nFlags,
nCommandID,
SAFEDBGTCHAR(pItem->GetMenuItemName()) );
#endif
ASSERT( FALSE );
return E_UNEXPECTED;
}
if (pItem->IsSpecialItemDefault())
{
VERIFY( ::SetMenuDefaultItem(menu, nCount, TRUE) );
}
++nCount;
}
return S_OK;
}
/*+-------------------------------------------------------------------------*
* CContextMenu::BuildContextMenu
*
* PURPOSE:
*
* PARAMETERS:
* WTL::CMenu & menu:
*
* RETURNS:
* HRESULT
/*+-------------------------------------------------------------------------*/
HRESULT
CContextMenu::BuildContextMenu(WTL::CMenu &menu)
{
HRESULT hr = S_OK;
hr = ::CollapseInsertionPoints( m_pmenuitemRoot );
if ( FAILED(hr) )
return hr;
hr = ::CollapseSpecialSeparators( m_pmenuitemRoot );
if ( FAILED(hr) )
return hr;
hr = ::BuildContextMenu( menu, m_pmenuitemRoot );
if ( FAILED(hr) )
return hr;
UINT iItems = menu.GetMenuItemCount();
if ((UINT)-1 == iItems)
{
TRACE(_T("CContextMenu::BuildContextMenu(): itemcount error"));
ASSERT( FALSE );
return E_UNEXPECTED;
}
else if (0 >= iItems)
{
TRACE(_T("CContextMenu::BuildContextMenu(): no items added"));
return S_OK;
}
return hr;
}
/*+-------------------------------------------------------------------------*
* CContextMenu::ShowContextMenu
*
* PURPOSE:
*
* PARAMETERS:
* WND hwndParent:
* LONG xPos:
* LONG yPos:
* LONG* plSelected:
*
* RETURNS:
* HRESULT
/*+-------------------------------------------------------------------------*/
STDMETHODIMP
CContextMenu::ShowContextMenu( HWND hwndParent, LONG xPos,
LONG yPos, LONG* plSelected)
{
return (ShowContextMenuEx (hwndParent, xPos, yPos, NULL/*prcExclude*/,
true/*bAllowDefaultMenuItem*/, plSelected));
}
STDMETHODIMP
CContextMenu::ShowContextMenuEx(HWND hwndParent, LONG xPos,
LONG yPos, LPCRECT prcExclude,
bool bAllowDefaultMenuItem, LONG* plSelected)
{
DECLARE_SC(sc, _T("IContextMenuProvider::ShowContextMenuEx"));
if (NULL == plSelected)
{
sc = E_INVALIDARG;
TraceSnapinError(_T("NULL selected ptr"), sc);
return sc.ToHr();
}
*plSelected = 0;
WTL::CMenu menu;
VERIFY( menu.CreatePopupMenu() );
START_CRITSEC_BOTH;
if (NULL == m_pmenuitemRoot)
return sc.ToHr();
sc = BuildContextMenu(menu); // build the context menu
if (sc)
return sc.ToHr();
CMenuItem* pItem = NULL;
LONG lSelected = 0;
CConsoleStatusBar *pStatusBar = GetStatusBar();
// At this point, pStatusBar should be non-NULL, either because
// 1) This function was called by CNodeInitObject, which calls SetStatusBar() first,
// or 2) by the object model, where m_pNode is always non-NULL.
ASSERT(pStatusBar);
// set up the menu command sink and hook up the status bar.
CCommandSink comsink( *this, menu, pStatusBar);
if ( !comsink.Init() )
{
sc = E_UNEXPECTED;
TraceNodeMgrLegacy(_T("CContextMenu::ShowContextMenuEx(): comsink error\n"), sc);
return sc.ToHr();
}
/*
* if we got an exclusion rectangle, set up a TPMPARAMS to specify it
*/
TPMPARAMS* ptpm = NULL;
TPMPARAMS tpm;
if (prcExclude != NULL)
{
tpm.cbSize = sizeof(tpm);
tpm.rcExclude = *prcExclude;
ptpm = &tpm;
}
/*
* Bug 139708: menu bar popups shouldn't have default menu items. If
* we can't have one on this popup, remove any default item now.
*/
if (!bAllowDefaultMenuItem)
SetMenuDefaultItem (menu, -1, false);
lSelected = menu.TrackPopupMenuEx(
TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_VERTICAL,
xPos,
yPos,
comsink.m_hWnd, // CODEWORK can we eliminate this?
ptpm );
comsink.DestroyWindow();
pItem = (0 == lSelected) ? NULL : FindMenuItem( lSelected );
if ( pItem != NULL )
{
// execute the menu item
sc = ExecuteMenuItem(pItem);
if(sc)
return sc.ToHr();
// in some cases we'll need to pass command to the sanpin
if ( pItem->NeedsToPassCommandBackToSnapin() )
*plSelected = pItem->GetCommandID();
}
else
ASSERT( 0 == lSelected ); // no items selected.
END_CRITSEC_BOTH;
return sc.ToHr();
}
HRESULT
CContextMenu::ExecuteMenuItem(CMenuItem *pItem)
{
DECLARE_SC(sc, TEXT("CContextMenu::ExecuteMenuItem"));
sc = ScCheckPointers(pItem);
if(sc)
return sc.ToHr();
// execute it;
sc = pItem->ScExecute();
if(sc)
return sc.ToHr();
return sc.ToHr();
}