|
|
//+-------------------------------------------------------------------------
//
// 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(); }
|