1977 lines
60 KiB
C++
1977 lines
60 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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();
|
|
}
|
|
|