220 lines
6.2 KiB
Plaintext
220 lines
6.2 KiB
Plaintext
// ShellContextMenu.cpp: implementation of the CShellContextMenu class.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2000-2001 by Paolo Messina
|
|
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
|
|
//
|
|
// The contents of this file are subject to the Artistic License (the "License").
|
|
// You may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at:
|
|
// http://www.opensource.org/licenses/artistic-license.html
|
|
//
|
|
// If you find this code useful, credits would be nice!
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "ShellContextMenu.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CShellContextMenu::InvokeCommand(UINT nCmdID)
|
|
{
|
|
if (!IsValid() || !IsCommandInRange(nCmdID))
|
|
return FALSE;
|
|
|
|
CMINVOKECOMMANDINFO cmi;
|
|
ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
|
|
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
|
|
cmi.hwnd = m_pOwner->GetSafeHwnd();
|
|
cmi.lpVerb = MAKEINTRESOURCE(nCmdID - m_nCmdFirstID);
|
|
cmi.nShow = SW_SHOWNORMAL;
|
|
|
|
return (NOERROR == m_pCtxMenu->InvokeCommand(&cmi));
|
|
}
|
|
|
|
BOOL CShellContextMenu::FillMenu(CMenu *pMenu, UINT nStartIndex, UINT nCmdFirstID, UINT nCmdLastID, UINT nFlags)
|
|
{
|
|
if (!IsValid())
|
|
return FALSE;
|
|
|
|
m_nCmdFirstID = nCmdFirstID;
|
|
m_nCmdLastID = nCmdLastID;
|
|
|
|
UINT nDefID = pMenu->GetDefaultItem(GMDI_USEDISABLED, TRUE);
|
|
|
|
HRESULT hr = m_pCtxMenu->QueryContextMenu(pMenu->GetSafeHmenu(),
|
|
nStartIndex, m_nCmdFirstID, m_nCmdLastID, nFlags);
|
|
|
|
if (nFlags & CMF_NODEFAULT)
|
|
pMenu->SetDefaultItem(nDefID, TRUE);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
m_nCmdLastID = m_nCmdFirstID = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
m_nCmdLastID = m_nCmdFirstID + HRESULT_CODE(hr) - 1;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CShellContextMenu::HandleMenuMessage(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pLResult)
|
|
{
|
|
if (!IsValid())
|
|
return FALSE;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_MENUSELECT:
|
|
{
|
|
// look for a parent frame, to update its status bar
|
|
if (m_pOwner == NULL)
|
|
return FALSE;
|
|
CFrameWnd* pFrame = m_pOwner->GetTopLevelFrame();
|
|
if (pFrame == NULL)
|
|
return FALSE;
|
|
|
|
// provide status bar text
|
|
*pLResult = 0;
|
|
CString sStatusText;
|
|
|
|
UINT uItem = (UINT) LOWORD(wParam);
|
|
UINT fuFlags = (UINT) HIWORD(wParam);
|
|
|
|
if (fuFlags == 0xFFFF) // menu was closed
|
|
return FALSE;
|
|
|
|
// get the first item of submenu, if popup item
|
|
if (fuFlags & MF_POPUP)
|
|
uItem = ::GetMenuItemID(::GetSubMenu((HMENU)lParam, uItem), 0);
|
|
|
|
// check if it's a valid item
|
|
if (!(fuFlags & MF_SEPARATOR)
|
|
&& (uItem == 0 || !IsCommandInRange(uItem)))
|
|
return FALSE;
|
|
|
|
// get description
|
|
if (!(fuFlags & MF_SEPARATOR))
|
|
GetCommandDescription(uItem, sStatusText);
|
|
|
|
// update status text, even if empty
|
|
pFrame->SetMessageText(sStatusText);
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_INITMENUPOPUP: // initialize sub-menus
|
|
case WM_MEASUREITEM: // handle owner-draw items
|
|
case WM_DRAWITEM:
|
|
{
|
|
// prepare result in case we handle the message
|
|
// (if we don't, it will be discarded)
|
|
*pLResult = TRUE;
|
|
if (message == WM_INITMENUPOPUP)
|
|
*pLResult = 0;
|
|
|
|
// check if it's a menu message
|
|
if (message == WM_MEASUREITEM
|
|
&& ((LPMEASUREITEMSTRUCT)lParam)->CtlType != ODT_MENU)
|
|
return FALSE;
|
|
if (message == WM_DRAWITEM
|
|
&& ((LPDRAWITEMSTRUCT)lParam)->CtlType != ODT_MENU)
|
|
return FALSE;
|
|
|
|
// these messages need interface version 2
|
|
SContextMenu2Ptr pCtxMenu2(m_pCtxMenu);
|
|
if (!pCtxMenu2.IsValid())
|
|
return FALSE;
|
|
|
|
return (NOERROR == pCtxMenu2->HandleMenuMsg(message, wParam, lParam));
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CShellContextMenu::GetCommandDescription(UINT nCmdID, CString& rText)
|
|
{
|
|
if (!IsValid() || !IsCommandInRange(nCmdID))
|
|
return FALSE;
|
|
|
|
// make a buffer large enough to hold either a Unicode or Ansi
|
|
// string and initialize it to zeroes, because some shell
|
|
// extensions return success even when they don't copy anything
|
|
// to the buffer. Conversion is achieved by a CString object.
|
|
WCHAR szHelpText[MAX_PATH];
|
|
ZeroMemory(szHelpText, sizeof(WCHAR)*MAX_PATH);
|
|
|
|
// try with Unicode first (it seems Explorer does so)
|
|
HRESULT hr = m_pCtxMenu->GetCommandString(nCmdID - m_nCmdFirstID,
|
|
GCS_HELPTEXTW, NULL, (LPSTR)szHelpText, MAX_PATH);
|
|
if (*(LPWSTR)szHelpText != 0) // buffer was used
|
|
rText = (LPWSTR)szHelpText;
|
|
else
|
|
{
|
|
// otherwise try with Ansi
|
|
hr = m_pCtxMenu->GetCommandString(nCmdID - m_nCmdFirstID,
|
|
GCS_HELPTEXTA, NULL, (LPSTR)szHelpText, MAX_PATH);
|
|
if (*(LPSTR)szHelpText != 0) // buffer was used
|
|
rText = (LPSTR)szHelpText;
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
BOOL CShellContextMenu::Create(IShellFolder *pParentFolder, LPCITEMIDLIST pidl)
|
|
{
|
|
m_pCtxMenu = SContextMenuPtr(pParentFolder, pidl);
|
|
return m_pCtxMenu.IsValid();
|
|
}
|
|
|
|
// a NULL pointer to unsubclass
|
|
void CShellContextMenu::SetOwner(CWnd *pOwner)
|
|
{
|
|
if (m_pOwner != NULL || pOwner == NULL)
|
|
UnsubclassOwner();
|
|
|
|
m_pOwner = pOwner;
|
|
if (m_pOwner != NULL)
|
|
SubclassOwner();
|
|
}
|
|
|
|
const LPCTSTR SH_CTXMENU_OBJ = _T("ShellContextMenu");
|
|
|
|
void CShellContextMenu::SubclassOwner()
|
|
{
|
|
ASSERT(::IsWindow(m_pOwner->GetSafeHwnd()));
|
|
|
|
m_oldWndProc = (WNDPROC)::GetWindowLong(*m_pOwner, GWL_WNDPROC);
|
|
::SetProp(*m_pOwner, SH_CTXMENU_OBJ, (HANDLE)this);
|
|
::SetWindowLong(*m_pOwner, GWL_WNDPROC, (LONG)WindowProc);
|
|
}
|
|
|
|
LRESULT CALLBACK CShellContextMenu::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CShellContextMenu *pThis =
|
|
(CShellContextMenu*)::GetProp(hwnd, SH_CTXMENU_OBJ);
|
|
|
|
LRESULT lResult;
|
|
if (pThis->HandleMenuMessage(message, wParam, lParam, &lResult))
|
|
return lResult; // success, no default processing
|
|
|
|
if (message == WM_DESTROY)
|
|
pThis->UnsubclassOwner();
|
|
|
|
return ::CallWindowProc(pThis->m_oldWndProc, hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
void CShellContextMenu::UnsubclassOwner()
|
|
{
|
|
ASSERT(::IsWindow(m_pOwner->GetSafeHwnd()));
|
|
|
|
::RemoveProp(*m_pOwner, SH_CTXMENU_OBJ);
|
|
::SetWindowLong(*m_pOwner, GWL_WNDPROC, (LONG)m_oldWndProc);
|
|
m_pOwner = NULL;
|
|
}
|