489 lines
14 KiB
C++
489 lines
14 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1997.
|
||
|
//
|
||
|
// File: I C O N T E X T M . C P P
|
||
|
//
|
||
|
// Contents: IContextMenu implementation for CUPnPDeviceFolderExtractIcon
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// Author: jeffspr 24 Oct 1997
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include "pch.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "upsres.h"
|
||
|
#include "oncommand.h"
|
||
|
#include "cmdtable.h"
|
||
|
|
||
|
struct ContextMenuEntry
|
||
|
{
|
||
|
INT iMenu;
|
||
|
INT iVerbMenu;
|
||
|
INT iDefaultCmd;
|
||
|
};
|
||
|
|
||
|
static const ContextMenuEntry c_CMEArray[] =
|
||
|
{
|
||
|
// standard (no menu deviances yet)
|
||
|
{ MENU_STANDARD, MENU_STANDARD_V, CMIDM_INVOKE },
|
||
|
};
|
||
|
|
||
|
const DWORD g_dwContextMenuEntryCount = celems(c_CMEArray);
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CUPnPDeviceFolderContextMenu::CreateInstance
|
||
|
//
|
||
|
// Purpose: Create an instance of the CUPnPDeviceFolderContextMenu object
|
||
|
//
|
||
|
// Arguments:
|
||
|
// riid [in] Interface requested
|
||
|
// ppv [out] Pointer to receive the requested interface
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Author: jeffspr 7 Aug 1998
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
HRESULT CUPnPDeviceFolderContextMenu::CreateInstance(
|
||
|
REFIID riid,
|
||
|
void** ppv,
|
||
|
CMENU_TYPE cmt,
|
||
|
HWND hwndOwner,
|
||
|
UINT cidl,
|
||
|
LPCITEMIDLIST * apidl,
|
||
|
LPSHELLFOLDER psf)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
|
||
|
CUPnPDeviceFolderContextMenu * pObj = NULL;
|
||
|
|
||
|
TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::CreateInstance");
|
||
|
|
||
|
pObj = new CComObject <CUPnPDeviceFolderContextMenu>;
|
||
|
if (pObj)
|
||
|
{
|
||
|
// Do the standard CComCreator::CreateInstance stuff.
|
||
|
//
|
||
|
pObj->SetVoid (NULL);
|
||
|
pObj->InternalFinalConstructAddRef ();
|
||
|
hr = pObj->FinalConstruct ();
|
||
|
pObj->InternalFinalConstructRelease ();
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pObj->HrInitialize(cmt, hwndOwner, cidl, apidl, psf);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pObj->QueryInterface (riid, ppv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete pObj;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolderContextMenu::CreateInstance");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CUPnPDeviceFolderContextMenu::CUPnPDeviceFolderContextMenu
|
||
|
//
|
||
|
// Purpose: Constructor for CUPnPDeviceFolderContextMenu. Initialize
|
||
|
// data members
|
||
|
//
|
||
|
// Arguments:
|
||
|
// (none)
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Author: jeffspr 7 Aug 1998
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
CUPnPDeviceFolderContextMenu::CUPnPDeviceFolderContextMenu()
|
||
|
{
|
||
|
m_psf = NULL;
|
||
|
m_cidl = 0;
|
||
|
m_apidl = NULL;
|
||
|
m_hwndOwner = NULL;
|
||
|
m_cmt = CMT_OBJECT; // arbitrary. Just make sure it's something
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CUPnPDeviceFolderContextMenu::HrInitialize
|
||
|
//
|
||
|
// Purpose: Initialization for the context menu object. Make copies of
|
||
|
// the pidl array, etc.
|
||
|
//
|
||
|
// Arguments:
|
||
|
// hwndOwner [in] Our parent hwnd
|
||
|
// cidl [in] Count of objects
|
||
|
// apidl [in] Pidl array of selected items
|
||
|
// psf [in] Our shell folder pointer
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Author: jeffspr 7 Aug 1998
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
HRESULT CUPnPDeviceFolderContextMenu::HrInitialize(
|
||
|
CMENU_TYPE cmt,
|
||
|
HWND hwndOwner,
|
||
|
UINT cidl,
|
||
|
LPCITEMIDLIST * apidl,
|
||
|
LPSHELLFOLDER psf)
|
||
|
{
|
||
|
HRESULT hr = NOERROR;
|
||
|
|
||
|
// Grab and addref the folder object
|
||
|
//
|
||
|
Assert(psf);
|
||
|
psf->AddRef();
|
||
|
m_psf = static_cast<CUPnPDeviceFolder *>(psf);
|
||
|
|
||
|
// Copy the context menu type (object -vs- background)
|
||
|
//
|
||
|
m_cmt = cmt;
|
||
|
|
||
|
// Note: This will be NULL if the context menu is invoked from the desktop
|
||
|
//
|
||
|
m_hwndOwner = hwndOwner;
|
||
|
|
||
|
if (cidl)
|
||
|
{
|
||
|
Assert(CMT_OBJECT == cmt);
|
||
|
|
||
|
// Clone the pidl array
|
||
|
//
|
||
|
hr = HrCloneRgIDL(apidl, cidl, &m_apidl, &m_cidl);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
TraceHr(ttidError, FAL, hr, FALSE, "HrCloneRgIDL failed on apidl in "
|
||
|
"CUPnPDeviceFolderContextMenu::HrInitialize");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert(CMT_BACKGROUND == cmt);
|
||
|
}
|
||
|
|
||
|
TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolderContextMenu::HrInitialize");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CUPnPDeviceFolderContextMenu::~CUPnPDeviceFolderContextMenu
|
||
|
//
|
||
|
// Purpose: Destructor. Free the pidl array and release the shell folder
|
||
|
// object
|
||
|
//
|
||
|
// Arguments:
|
||
|
// (none)
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Author: jeffspr 7 Aug 1998
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
CUPnPDeviceFolderContextMenu::~CUPnPDeviceFolderContextMenu()
|
||
|
{
|
||
|
if (m_apidl)
|
||
|
{
|
||
|
FreeRgIDL(m_cidl, m_apidl);
|
||
|
m_apidl = NULL;
|
||
|
m_cidl = 0;
|
||
|
}
|
||
|
|
||
|
if (m_psf)
|
||
|
{
|
||
|
ReleaseObj(reinterpret_cast<LPSHELLFOLDER>(m_psf));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CUPnPDeviceFolderContextMenu::QueryContextMenu
|
||
|
//
|
||
|
// Purpose: Adds menu items to the specified menu. The menu items should
|
||
|
// be inserted in the menu at the position specified by
|
||
|
// indexMenu, and their menu item identifiers must be between
|
||
|
// the idCmdFirst and idCmdLast parameter values.
|
||
|
//
|
||
|
// Arguments:
|
||
|
// hmenu [in] Handle to the menu. The handler should specify this
|
||
|
// handle when adding menu items
|
||
|
// indexMenu [in] Zero-based position at which to insert the first
|
||
|
// menu item.
|
||
|
// idCmdFirst [in] Min value the handler can specify for a menu item
|
||
|
// idCmdLast [in] Max value the handler can specify for a menu item
|
||
|
// uFlags [in] Optional flags specifying how the context menu
|
||
|
// can be changed. See MSDN for the full list.
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Author: jeffspr 7 Aug 1998
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
HRESULT CUPnPDeviceFolderContextMenu::QueryContextMenu(
|
||
|
HMENU hmenu,
|
||
|
UINT indexMenu,
|
||
|
UINT idCmdFirst,
|
||
|
UINT idCmdLast,
|
||
|
UINT uFlags)
|
||
|
{
|
||
|
HRESULT hr = NOERROR;
|
||
|
QCMINFO qcm = {hmenu, indexMenu, idCmdFirst, idCmdLast};
|
||
|
INT iDefaultCmd = 0;
|
||
|
BOOL fValidMenu = FALSE;
|
||
|
BOOL fVerbsOnly = !!(uFlags & CMF_VERBSONLY);
|
||
|
INT iMenuResourceId = 0;
|
||
|
INT iPopupResourceId = 0;
|
||
|
|
||
|
TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::QueryContextMenu");
|
||
|
|
||
|
if (!(uFlags & CMF_DVFILE))
|
||
|
{
|
||
|
if (CMT_OBJECT == m_cmt)
|
||
|
{
|
||
|
PUPNPDEVICEFOLDPIDL pudfp = NULL;
|
||
|
|
||
|
if (FIsUPnPDeviceFoldPidl(m_apidl[0]))
|
||
|
{
|
||
|
pudfp = ConvertToUPnPDevicePIDL(m_apidl[0]);
|
||
|
}
|
||
|
|
||
|
if(pudfp)
|
||
|
{
|
||
|
DWORD dwLoop = 0;
|
||
|
|
||
|
for (dwLoop = 0; (dwLoop < g_dwContextMenuEntryCount) && !fValidMenu; dwLoop++)
|
||
|
{
|
||
|
|
||
|
// Leave dwLoop alone, since this is how we pick the menu below
|
||
|
fValidMenu = TRUE;
|
||
|
|
||
|
if (fValidMenu)
|
||
|
{
|
||
|
iPopupResourceId = 0;
|
||
|
if (fVerbsOnly)
|
||
|
{
|
||
|
iMenuResourceId = c_CMEArray[dwLoop].iVerbMenu;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iMenuResourceId = c_CMEArray[dwLoop].iMenu;
|
||
|
}
|
||
|
|
||
|
iDefaultCmd = c_CMEArray[dwLoop].iDefaultCmd;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (CMT_BACKGROUND == m_cmt)
|
||
|
{
|
||
|
AssertSz(m_hwndOwner, "Background context menu requires a valid HWND");
|
||
|
if (m_cidl > 0)
|
||
|
{
|
||
|
AssertSz(FALSE, "We shouldn't be getting this interface if connections are selected");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fValidMenu = TRUE;
|
||
|
iMenuResourceId = MENU_MERGE_FOLDER_BACKGROUND;
|
||
|
iPopupResourceId = POPUP_MERGE_FOLDER_BACKGROUND;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AssertSz(FALSE, "Don't support context menus for this CMENU_TYPE. Not background or object?");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If we have a valid menu to give to the user, then do the merge,
|
||
|
// enable or disable as appropriate, and set the appropriate HR to
|
||
|
// specify the number of items added.
|
||
|
//
|
||
|
if (fValidMenu)
|
||
|
{
|
||
|
MergeMenu(_Module.GetResourceInstance(),
|
||
|
iMenuResourceId,
|
||
|
iPopupResourceId,
|
||
|
(LPQCMINFO)&qcm);
|
||
|
|
||
|
// Enable/Disable the menu items as appropriate. Ignore the return from this
|
||
|
// as we're getting it for debugging purposes only.
|
||
|
//
|
||
|
hr = HrEnableOrDisableMenuItems(
|
||
|
m_hwndOwner,
|
||
|
(LPCITEMIDLIST *) m_apidl,
|
||
|
m_cidl,
|
||
|
hmenu,
|
||
|
idCmdFirst);
|
||
|
|
||
|
if (CMT_OBJECT == m_cmt)
|
||
|
{
|
||
|
// $$REVIEW: Find out why I'm only doing this for CMT_OBJECT instead of for background.
|
||
|
// Pre-icomtextm|mb combine, mb had this commented out.
|
||
|
//
|
||
|
SetMenuDefaultItem(hmenu, idCmdFirst + iDefaultCmd, FALSE);
|
||
|
}
|
||
|
|
||
|
hr = ResultFromShort(qcm.idCmdFirst - idCmdFirst);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ignore this trace if it's a short, basically.
|
||
|
//
|
||
|
TraceHr(ttidError, FAL, hr, SUCCEEDED(hr), "CUPnPDeviceFolderContextMenu::QueryContextMenu");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CUPnPDeviceFolderContextMenu::InvokeCommand
|
||
|
//
|
||
|
// Purpose: Carries out the command associated with a context menu item.
|
||
|
//
|
||
|
// Arguments:
|
||
|
// lpici [in] Address of a CMINVOKECOMMANDINFO structure containing
|
||
|
// information about the command.
|
||
|
//
|
||
|
// Returns: Returns NOERROR if successful, or an OLE-defined
|
||
|
// error code otherwise.
|
||
|
//
|
||
|
// Author: jeffspr 27 Apr 1999
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
HRESULT CUPnPDeviceFolderContextMenu::InvokeCommand(
|
||
|
LPCMINVOKECOMMANDINFO lpici)
|
||
|
{
|
||
|
HRESULT hr = NOERROR;
|
||
|
UINT uiCmd = 0;
|
||
|
|
||
|
TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::InvokeCommand");
|
||
|
|
||
|
Assert(lpici);
|
||
|
Assert(lpici->lpVerb);
|
||
|
|
||
|
if (HIWORD(lpici->lpVerb))
|
||
|
{
|
||
|
// Deal with string commands
|
||
|
PSTR pszCmd = (PSTR)lpici->lpVerb;
|
||
|
|
||
|
// Only folder objects currently support string-based invoke commands.
|
||
|
// (The background does not)
|
||
|
//
|
||
|
if (CMT_OBJECT == m_cmt)
|
||
|
{
|
||
|
if (0 == lstrcmpA(pszCmd, "delete"))
|
||
|
{
|
||
|
uiCmd = CMIDM_DELETE;
|
||
|
}
|
||
|
else if (0 == lstrcmpA(pszCmd, "properties"))
|
||
|
{
|
||
|
uiCmd = CMIDM_PROPERTIES;
|
||
|
}
|
||
|
else if (0 == lstrcmpA(pszCmd, "rename"))
|
||
|
{
|
||
|
uiCmd = CMIDM_RENAME;
|
||
|
}
|
||
|
else if (0 == lstrcmpA(pszCmd, "link"))
|
||
|
{
|
||
|
uiCmd = CMIDM_CREATE_SHORTCUT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (0 == uiCmd)
|
||
|
{
|
||
|
TraceTag(ttidError, "Unprocessed InvokeCommand<%s>\n", pszCmd);
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uiCmd = (UINT)LOWORD((DWORD_PTR)lpici->lpVerb);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// Handle the actual command
|
||
|
//
|
||
|
hr = HrFolderCommandHandler(uiCmd, m_apidl, m_cidl, m_hwndOwner, lpici, m_psf);
|
||
|
}
|
||
|
|
||
|
TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolderContextMenu::InvokeCommand");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CUPnPDeviceFolderContextMenu::GetCommandString(
|
||
|
UINT_PTR idCmd,
|
||
|
UINT uType,
|
||
|
UINT * pwReserved,
|
||
|
PSTR pszName,
|
||
|
UINT cchMax)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL; // Not handled
|
||
|
|
||
|
TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::GetCommandString");
|
||
|
|
||
|
// note(cmr): GCS_HELPTEXT is defined as GCS_HELPTEXTW on UNICODE builds
|
||
|
// and GCS_HELPTEXTA otherwise.
|
||
|
// When we get GCS_HELPTEXTW, we really need to return a PWSTR
|
||
|
// in pszName (yes, even though it's defined as a PSTR).
|
||
|
// When we get GCS_HELPTEXTA, we just return a PSTR in pszName.
|
||
|
// GCS_VERB has the same semantics (with GCS_VERBW and GCS_VERBA)
|
||
|
// Basically, we just treat pszName as a PTSTR, and everything
|
||
|
// works fine.
|
||
|
|
||
|
*((PTSTR)pszName) = TEXT('\0');
|
||
|
|
||
|
if (uType == GCS_HELPTEXT)
|
||
|
{
|
||
|
int iLength = LoadString( _Module.GetResourceInstance(),
|
||
|
idCmd + IDS_CMIDM_START,
|
||
|
(LPTSTR)pszName,
|
||
|
cchMax);
|
||
|
if (iLength > 0)
|
||
|
{
|
||
|
hr = NOERROR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AssertSz(FALSE, "Resource string not found for one of the connections folder commands");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (uType == GCS_VERB && idCmd == CMIDM_RENAME)
|
||
|
{
|
||
|
// "rename" is language independent
|
||
|
_tcsncpy((LPTSTR)pszName, TEXT("rename"), cchMax);
|
||
|
pszName[cchMax - 1] = TEXT('\0');
|
||
|
|
||
|
hr = NOERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr(ttidError, FAL, hr, (hr == E_FAIL), "CUPnPDeviceFolderContextMenu::GetCommandString");
|
||
|
return hr;
|
||
|
}
|