1630 lines
62 KiB
C++
1630 lines
62 KiB
C++
#include "shellprv.h"
|
|
#include "caggunk.h"
|
|
#include "datautil.h"
|
|
#include "ids.h"
|
|
#include "defview.h"
|
|
#include "_security.h"
|
|
#include "shitemid.h"
|
|
#include "idlcomm.h"
|
|
#include "cowsite.h"
|
|
|
|
extern "C"
|
|
{
|
|
#include "bookmk.h"
|
|
#include "fstreex.h"
|
|
}
|
|
|
|
#define TF_DRAGDROP 0x04000000
|
|
|
|
STDAPI_(BOOL) ExtractImageURLFromCFHTML(LPSTR pszHTML, SIZE_T cbSizeHTML, LPSTR pszImg, DWORD dwSize);
|
|
STDAPI_(BOOL) IsFromSneakernetBriefcase(LPCITEMIDLIST pidlSource, LPCTSTR pszTarget);
|
|
STDAPI_(BOOL) IsBriefcaseRoot(IDataObject* pDataObj);
|
|
STDAPI_(BOOL) DroppingAnyFolders(HDROP hDrop);
|
|
|
|
typedef struct
|
|
{
|
|
HWND hwnd;
|
|
DWORD dwFlags;
|
|
POINTL pt;
|
|
CHAR szUrl[INTERNET_MAX_URL_LENGTH];
|
|
} ADDTODESKTOP;
|
|
|
|
|
|
DWORD CALLBACK AddToActiveDesktopThreadProc(void* pv)
|
|
{
|
|
ADDTODESKTOP* pToAD = (ADDTODESKTOP*)pv;
|
|
CHAR szFilePath[MAX_PATH];
|
|
DWORD cchFilePath = SIZECHARS(szFilePath);
|
|
BOOL fAddComp = TRUE;
|
|
|
|
if (SUCCEEDED(PathCreateFromUrlA(pToAD->szUrl, szFilePath, &cchFilePath, 0))) {
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
SHAnsiToTChar(szFilePath, szPath, ARRAYSIZE(szPath));
|
|
|
|
// If the Url is in the Temp directory
|
|
if (PathIsTemporary(szPath)) {
|
|
if (IDYES == ShellMessageBox(g_hinst, pToAD->hwnd, MAKEINTRESOURCE(IDS_REASONS_URLINTEMPDIR),
|
|
MAKEINTRESOURCE(IDS_AD_NAME), MB_YESNO | MB_ICONQUESTION)) {
|
|
TCHAR szFilter[64], szTitle[64];
|
|
TCHAR szFilename[MAX_PATH];
|
|
LPTSTR psz;
|
|
OPENFILENAME ofn = {0};
|
|
|
|
LoadString(g_hinst, IDS_ALLFILESFILTER, szFilter, ARRAYSIZE(szFilter));
|
|
LoadString(g_hinst, IDS_SAVEAS, szTitle, ARRAYSIZE(szTitle));
|
|
|
|
psz = szFilter;
|
|
|
|
//Strip out the # and make them Nulls for SaveAs Dialog
|
|
while (*psz) {
|
|
if (*psz == (WCHAR)('#'))
|
|
*psz = (WCHAR)('\0');
|
|
psz++;
|
|
}
|
|
|
|
lstrcpy(szFilename, PathFindFileName(szPath));
|
|
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = pToAD->hwnd;
|
|
ofn.hInstance = g_hinst;
|
|
ofn.lpstrFilter = szFilter;
|
|
ofn.lpstrFile = szFilename;
|
|
ofn.nMaxFile = ARRAYSIZE(szFilename);
|
|
ofn.lpstrTitle = szTitle;
|
|
ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
|
|
|
|
if (GetSaveFileName(&ofn)) {
|
|
SHFILEOPSTRUCT sfo = {0};
|
|
|
|
szPath[lstrlen(szPath) + 1] = 0;
|
|
ofn.lpstrFile[lstrlen(ofn.lpstrFile) + 1] = 0;
|
|
|
|
sfo.hwnd = pToAD->hwnd;
|
|
sfo.wFunc = FO_COPY;
|
|
sfo.pFrom = szPath;
|
|
sfo.pTo = ofn.lpstrFile;
|
|
|
|
cchFilePath = SIZECHARS(szPath);
|
|
if (SHFileOperation(&sfo) == 0 && SUCCEEDED(UrlCreateFromPath(szPath, szPath, &cchFilePath, 0))) {
|
|
SHTCharToAnsi(szPath, pToAD->szUrl, ARRAYSIZE(pToAD->szUrl));
|
|
} else
|
|
fAddComp = FALSE;
|
|
} else
|
|
fAddComp = FALSE;
|
|
} else
|
|
fAddComp = FALSE;
|
|
}
|
|
}
|
|
if (fAddComp)
|
|
CreateDesktopComponents(pToAD->szUrl, NULL, pToAD->hwnd, pToAD->dwFlags, pToAD->pt.x, pToAD->pt.y);
|
|
|
|
LocalFree((HLOCAL)pToAD);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// {F20DA720-C02F-11CE-927B-0800095AE340}
|
|
const GUID CLSID_CPackage = {0xF20DA720L, 0xC02F, 0x11CE, 0x92, 0x7B, 0x08, 0x00, 0x09, 0x5A, 0xE3, 0x40};
|
|
// old packager guid...
|
|
// {0003000C-0000-0000-C000-000000000046}
|
|
const GUID CLSID_OldPackage = {0x0003000CL, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46};
|
|
|
|
|
|
typedef struct {
|
|
DWORD dwDefEffect;
|
|
IDataObject* pdtobj;
|
|
POINTL pt;
|
|
DWORD* pdwEffect;
|
|
HKEY hkeyProgID;
|
|
HKEY hkeyBase;
|
|
HMENU hmenu;
|
|
UINT idCmd;
|
|
DWORD grfKeyState;
|
|
} FSDRAGDROPMENUPARAM;
|
|
|
|
typedef struct
|
|
{
|
|
HMENU hMenu;
|
|
UINT uCopyPos;
|
|
UINT uMovePos;
|
|
UINT uLinkPos;
|
|
} FSMENUINFO;
|
|
|
|
class CFSDropTarget : public CAggregatedUnknown, CObjectWithSite, public IDropTarget
|
|
{
|
|
public:
|
|
// *** IUnknown ***
|
|
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj)
|
|
{
|
|
return CAggregatedUnknown::QueryInterface(riid, ppvObj);
|
|
};
|
|
STDMETHODIMP_(ULONG) AddRef(void)
|
|
{
|
|
return CAggregatedUnknown::AddRef();
|
|
};
|
|
STDMETHODIMP_(ULONG) Release(void)
|
|
{
|
|
return CAggregatedUnknown::Release();
|
|
};
|
|
|
|
// *** IDropTarget ***
|
|
STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
|
|
STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
|
|
STDMETHODIMP DragLeave();
|
|
STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
|
|
|
|
protected:
|
|
CFSDropTarget(IUnknown* punkOuter);
|
|
~CFSDropTarget();
|
|
|
|
HRESULT v_InternalQueryInterface(REFIID riid, void** ppvObj);
|
|
HRESULT _Init(CFSFolder* pFSFolder, HWND hwnd);
|
|
BOOL _IsDesktopFolder() { return _pFolder->_pidl && ILIsEmpty(_pFolder->_pidl); };
|
|
|
|
HRESULT _GetDDInfoFileContents(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoOleObj(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _GetDDInfoOleLink(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
|
|
DWORD _PickDefFSOperation(DWORD dwCurEffectAvail);
|
|
HRESULT _GetPath(LPTSTR pszPath);
|
|
HRESULT _ZoneCheckDataObject(DWORD dwEffect);
|
|
DWORD _LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed);
|
|
DWORD _GetStdDefEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect);
|
|
DWORD _DetermineEffects(DWORD grfKeyState, DWORD* pdwEffectInOut, HMENU hmenu);
|
|
static VOID _AddVerbs(DWORD* pdwEffects,
|
|
DWORD dwEffectAvail,
|
|
DWORD dwDefEffect,
|
|
UINT idCopy,
|
|
UINT idMove,
|
|
UINT idLink,
|
|
DWORD dwForceEffect,
|
|
FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _DragDropMenu(FSDRAGDROPMENUPARAM* pddm);
|
|
HRESULT _CreatePackage();
|
|
HRESULT _CreateURLDeskComp(int x, int y);
|
|
HRESULT _CreateDeskCompImage(POINTL pt);
|
|
void _GetStateFromSite();
|
|
|
|
CFSFolder* _pFolder;
|
|
HWND _hwndOwner; // EVIL: used as a site and UI host
|
|
DWORD _grfKeyStateLast; // for previous DragOver/Enter
|
|
IDataObject* _pdtobj;
|
|
DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover
|
|
DWORD _dwData; // DTID_*
|
|
DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT
|
|
BOOL _fSameHwnd; // the drag source and target are the same folder
|
|
BOOL _fDragDrop; //
|
|
BOOL _fBkDropTarget;
|
|
POINT _ptDrop;
|
|
|
|
private:
|
|
friend HRESULT CFSDropTarget_CreateInstance(CFSFolder* pFSFolder, HWND hwnd, IDropTarget** ppdt);
|
|
};
|
|
|
|
STDAPI CFSDropTarget_CreateInstance(CFSFolder* pFSFolder, HWND hwnd, IDropTarget** ppdt)
|
|
{
|
|
ASSERT(pFSFolder && ppdt);
|
|
HRESULT hr;
|
|
|
|
*ppdt = NULL;
|
|
|
|
CFSDropTarget* pFSDT = new CFSDropTarget(NULL);
|
|
if (pFSDT) {
|
|
hr = pFSDT->_Init(pFSFolder, hwnd);
|
|
if (SUCCEEDED(hr))
|
|
pFSDT->QueryInterface(IID_PPV_ARG(IDropTarget, ppdt));
|
|
pFSDT->Release();
|
|
} else {
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
CFSDropTarget::CFSDropTarget(IUnknown* punkOuter) :
|
|
CAggregatedUnknown(punkOuter)
|
|
{
|
|
ASSERT(NULL == _hwndOwner);
|
|
ASSERT(0 == _grfKeyStateLast);
|
|
ASSERT(NULL == _pdtobj);
|
|
ASSERT(0 == _dwEffectLastReturned);
|
|
ASSERT(0 == _dwData);
|
|
ASSERT(0 == _dwEffectPreferred);
|
|
}
|
|
|
|
CFSDropTarget::~CFSDropTarget()
|
|
{
|
|
if (_pFolder)
|
|
CFSFolder_Release((IShellFolder2*)&(_pFolder->sf));
|
|
|
|
// if we hit this a lot maybe we should just release it
|
|
AssertMsg(_pdtobj == NULL, TEXT("didn't get matching DragLeave. this=%#08lx"), this);
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_Init(CFSFolder* pFSFolder, HWND hwnd)
|
|
{
|
|
_pFolder = pFSFolder;
|
|
CFSFolder_AddRef((IShellFolder2*)&(_pFolder->sf));
|
|
_hwndOwner = hwnd;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::v_InternalQueryInterface(REFIID riid, void** ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CFSDropTarget, IDropTarget),
|
|
QITABENT(CFSDropTarget, IObjectWithSite),
|
|
QITABENTMULTI2(CFSDropTarget, IID_IDropTargetWithDADSupport, IDropTarget),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
ASSERT(NULL == _pdtobj); // req DragDrop protocol, someone forgot to call DragLeave
|
|
|
|
// init our registerd data formats
|
|
IDLData_InitializeClipboardFormats();
|
|
|
|
_grfKeyStateLast = grfKeyState;
|
|
_dwData = 0;
|
|
IUnknown_Set((IUnknown**)&_pdtobj, pDataObj);
|
|
|
|
if (pDataObj) {
|
|
IEnumFORMATETC* penum;
|
|
HRESULT hres = pDataObj->EnumFormatEtc(DATADIR_GET, &penum);
|
|
if (SUCCEEDED(hres)) {
|
|
FORMATETC fmte;
|
|
ULONG celt;
|
|
while (penum->Next(1, &fmte, &celt) == S_OK) {
|
|
if (fmte.cfFormat == CF_HDROP && (fmte.tymed & TYMED_HGLOBAL))
|
|
_dwData |= DTID_HDROP;
|
|
|
|
if (fmte.cfFormat == g_cfHIDA && (fmte.tymed & TYMED_HGLOBAL))
|
|
_dwData |= DTID_HIDA;
|
|
|
|
if (fmte.cfFormat == g_cfEmbeddedObject && (fmte.tymed & TYMED_ISTORAGE))
|
|
_dwData |= DTID_EMBEDDEDOBJECT;
|
|
|
|
if (fmte.cfFormat == g_cfFileContents && (fmte.tymed & (TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)))
|
|
_dwData |= DTID_CONTENTS;
|
|
|
|
if (fmte.cfFormat == g_cfFileGroupDescriptorA && (fmte.tymed & TYMED_HGLOBAL))
|
|
_dwData |= DTID_FDESCA;
|
|
|
|
if (fmte.cfFormat == g_cfFileGroupDescriptorW && (fmte.tymed & TYMED_HGLOBAL))
|
|
_dwData |= DTID_FDESCW;
|
|
|
|
if ((fmte.cfFormat == g_cfPreferredDropEffect) &&
|
|
(fmte.tymed & TYMED_HGLOBAL) &&
|
|
(DROPEFFECT_NONE != (_dwEffectPreferred = DataObj_GetDWORD(pDataObj, g_cfPreferredDropEffect, DROPEFFECT_NONE)))) {
|
|
_dwData |= DTID_PREFERREDEFFECT;
|
|
}
|
|
#ifdef DEBUG
|
|
TCHAR szFormat[MAX_PATH];
|
|
if (GetClipboardFormatName(fmte.cfFormat, szFormat, ARRAYSIZE(szFormat))) {
|
|
TraceMsg(TF_DRAGDROP, "CFSDropTarget - cf %s, tymed %d", szFormat, fmte.tymed);
|
|
} else {
|
|
TraceMsg(TF_DRAGDROP, "CFSDropTarget - cf %d, tymed %d", fmte.cfFormat, fmte.tymed);
|
|
}
|
|
#endif // DEBUG
|
|
}
|
|
penum->Release();
|
|
}
|
|
|
|
|
|
// HACK:
|
|
// Win95 always did the GetData below which can be quite expensive if
|
|
// the data is a directory structure on an ftp server etc.
|
|
// dont check for FD_LINKUI if the data object has a preferred effect
|
|
|
|
if ((_dwData & (DTID_PREFERREDEFFECT | DTID_CONTENTS)) == DTID_CONTENTS) {
|
|
if (_dwData & DTID_FDESCA) {
|
|
FORMATETC fmteRead = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (pDataObj->GetData(&fmteRead, &medium) == S_OK) {
|
|
FILEGROUPDESCRIPTORA* pfgd = (FILEGROUPDESCRIPTORA*)GlobalLock(medium.hGlobal);
|
|
if (pfgd) {
|
|
if (pfgd->cItems >= 1) {
|
|
if (pfgd->fgd[0].dwFlags & FD_LINKUI)
|
|
_dwData |= DTID_FD_LINKUI;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
} else if (_dwData & DTID_FDESCW) {
|
|
FORMATETC fmteRead = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (pDataObj->GetData(&fmteRead, &medium) == S_OK) {
|
|
FILEGROUPDESCRIPTORW* pfgd = (FILEGROUPDESCRIPTORW*)GlobalLock(medium.hGlobal);
|
|
if (pfgd) {
|
|
if (pfgd->cItems >= 1) {
|
|
if (pfgd->fgd[0].dwFlags & FD_LINKUI)
|
|
_dwData |= DTID_FD_LINKUI;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OleQueryCreateFromData(pDataObj) == S_OK)
|
|
_dwData |= DTID_OLEOBJ;
|
|
|
|
if (OleQueryLinkFromData(pDataObj) == S_OK)
|
|
_dwData |= DTID_OLELINK;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - CIDL::DragEnter this->dwData = %x"), _dwData);
|
|
}
|
|
|
|
// stash this away
|
|
if (pdwEffect)
|
|
_dwEffectLastReturned = *pdwEffect;
|
|
|
|
DWORD dwDefault = _DetermineEffects(grfKeyState, pdwEffect, NULL);
|
|
|
|
// The cursor always indicates the default action.
|
|
ASSERT(pdwEffect);
|
|
*pdwEffect = dwDefault;
|
|
|
|
_dwEffectLastReturned = *pdwEffect;
|
|
TraceMsg(TF_DRAGDROP, "CFSDropTarget::DragEnter() _grfKeyStateLast=%#08lx, dwDefault=%#08lx", _grfKeyStateLast, dwDefault);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
ASSERT(pdwEffect);
|
|
if (_grfKeyStateLast != grfKeyState) {
|
|
_grfKeyStateLast = grfKeyState;
|
|
DWORD dwDefault = _DetermineEffects(grfKeyState, pdwEffect, NULL);
|
|
|
|
// The cursor always indicates the default action.
|
|
*pdwEffect = dwDefault;
|
|
_dwEffectLastReturned = *pdwEffect;
|
|
} else {
|
|
*pdwEffect = _dwEffectLastReturned;
|
|
}
|
|
|
|
TraceMsg(TF_DRAGDROP, "CFSDropTarget::DragOver() this=%#08lx, _pdtobj=%#08lx, _grfKeyStateLast=%#08lx, *pdwEffect=%#08lx", this, _pdtobj, _grfKeyStateLast, *pdwEffect);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::DragLeave()
|
|
{
|
|
TraceMsg(TF_DRAGDROP, "CFSDropTarget::DragLeave() this=%#08lx, _grfKeyStateLast=%#08lx, _pdtobj=%#08lx", this, _grfKeyStateLast, _pdtobj);
|
|
ATOMICRELEASE(_pdtobj);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// init data from our site that we will need in processing the drop
|
|
|
|
void CFSDropTarget::_GetStateFromSite()
|
|
{
|
|
IShellFolderView* psfv;
|
|
if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_DefView, IID_PPV_ARG(IShellFolderView, &psfv)))) {
|
|
_fSameHwnd = S_OK == psfv->IsDropOnSource((IDropTarget*)this);
|
|
_fDragDrop = S_OK == psfv->GetDropPoint(&_ptDrop);
|
|
_fBkDropTarget = S_OK == psfv->IsBkDropTarget(NULL);
|
|
psfv->Release();
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLinkOnly;
|
|
|
|
// OLE may give us a different data object (fully marshalled)
|
|
// from the one we've got on DragEnter (does not seem to happen on Win2k?)
|
|
|
|
IUnknown_Set((IUnknown**)&_pdtobj, pDataObj);
|
|
|
|
_GetStateFromSite();
|
|
|
|
// note, that on the drop the mouse buttons are not down so the grfKeyState
|
|
// is not what we saw on the DragOver/DragEnter, thus we need to cache
|
|
// the grfKeyState to detect left vs right drag
|
|
|
|
// ASSERT(this->grfKeyStateLast == grfKeyState);
|
|
|
|
HMENU hmenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_TEMPLATEDD);
|
|
DWORD dwDefEffect = _DetermineEffects(grfKeyState, pdwEffect, hmenu);
|
|
|
|
if (dwDefEffect == DROPEFFECT_NONE) {
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
DAD_SetDragImage(NULL, NULL);
|
|
hr = S_OK;
|
|
goto DragLeaveAndReturn;
|
|
}
|
|
|
|
// Get the hkeyProgID and hkeyBaseProgID
|
|
HKEY hkeyBaseProgID, hkeyProgID;
|
|
SHGetClassKey(_pFolder->_pidl, &hkeyProgID, &hkeyBaseProgID);
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
_GetPath(szPath);
|
|
|
|
|
|
// Set fLinkOnly if the only option is link and the source hasn't
|
|
// explicitly told us that it wants only a link created.
|
|
|
|
fLinkOnly = ((*pdwEffect == DROPEFFECT_LINK) &&
|
|
(!(_dwData & DTID_PREFERREDEFFECT) ||
|
|
(_dwEffectPreferred != DROPEFFECT_LINK)));
|
|
|
|
|
|
// this doesn't actually do the menu if (grfKeyState MK_LBUTTON)
|
|
|
|
FSDRAGDROPMENUPARAM ddm;
|
|
ddm.dwDefEffect = dwDefEffect;
|
|
ddm.pdtobj = pDataObj;
|
|
ddm.pt = pt;
|
|
ddm.pdwEffect = pdwEffect;
|
|
ddm.hkeyProgID = hkeyProgID;
|
|
ddm.hkeyBase = hkeyBaseProgID;
|
|
ddm.hmenu = hmenu;
|
|
ddm.grfKeyState = grfKeyState;
|
|
|
|
hr = _DragDropMenu(&ddm);
|
|
|
|
SHCloseClassKey(hkeyProgID);
|
|
SHCloseClassKey(hkeyBaseProgID);
|
|
|
|
DestroyMenu(hmenu);
|
|
|
|
if (hr == S_FALSE) {
|
|
// let callers know where this is about to go
|
|
// SHScrap cares because it needs to close the file so we can copy/move it
|
|
DataObj_SetDropTarget(pDataObj, &CLSID_ShellFSFolder);
|
|
|
|
switch (ddm.idCmd) {
|
|
case DDIDM_CONTENTS_DESKCOMP:
|
|
hr = CreateDesktopComponents(NULL, pDataObj, _hwndOwner, 0, ddm.pt.x, ddm.pt.y);
|
|
break;
|
|
|
|
case DDIDM_CONTENTS_DESKURL:
|
|
hr = _CreateURLDeskComp(ddm.pt.x, ddm.pt.y);
|
|
break;
|
|
|
|
case DDIDM_CONTENTS_DESKIMG:
|
|
hr = _CreateDeskCompImage(ddm.pt);
|
|
break;
|
|
|
|
case DDIDM_CONTENTS_COPY:
|
|
case DDIDM_CONTENTS_MOVE:
|
|
case DDIDM_CONTENTS_LINK:
|
|
hr = FS_AsyncCreateFileFromClip(_hwndOwner, szPath, pDataObj, pt, pdwEffect, _fBkDropTarget);
|
|
break;
|
|
|
|
case DDIDM_SCRAP_COPY:
|
|
case DDIDM_SCRAP_MOVE:
|
|
case DDIDM_DOCLINK:
|
|
hr = FS_CreateBookMark(_hwndOwner, szPath, pDataObj, pt, pdwEffect);
|
|
break;
|
|
|
|
case DDIDM_OBJECT_COPY:
|
|
case DDIDM_OBJECT_MOVE:
|
|
{
|
|
hr = _CreatePackage();
|
|
if (E_UNEXPECTED == hr) {
|
|
// _CreatePackage() can only expand certain types of packages
|
|
// back into files. For example, it doesn't handle CMDLINK files.
|
|
|
|
// If _CreatePackage() didn't recognize the stream format, we fall
|
|
// back to FS_CreateBookMark(), which should create a scrap:
|
|
hr = FS_CreateBookMark(_hwndOwner, szPath, pDataObj, pt, pdwEffect);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DDIDM_COPY:
|
|
case DDIDM_SYNCCOPY:
|
|
case DDIDM_SYNCCOPYTYPE:
|
|
case DDIDM_MOVE:
|
|
case DDIDM_LINK:
|
|
default:
|
|
hr = _ZoneCheckDataObject(*pdwEffect);
|
|
|
|
if (S_OK == hr) {
|
|
FSTHREADPARAM* pfsthp = (FSTHREADPARAM*)LocalAlloc(LPTR, SIZEOF(FSTHREADPARAM));
|
|
if (pfsthp) {
|
|
CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObj, &pfsthp->pstmDataObj);
|
|
|
|
ASSERT(pfsthp->pDataObj == NULL);
|
|
|
|
pfsthp->dwEffect = *pdwEffect;
|
|
pfsthp->fLinkOnly = fLinkOnly;
|
|
|
|
pfsthp->fSameHwnd = _fSameHwnd;
|
|
pfsthp->fDragDrop = _fDragDrop;
|
|
pfsthp->ptDrop = _ptDrop;
|
|
pfsthp->fBkDropTarget = _fBkDropTarget;
|
|
|
|
pfsthp->bSyncCopy = (DDIDM_SYNCCOPY == ddm.idCmd) || (DDIDM_SYNCCOPYTYPE == ddm.idCmd);
|
|
pfsthp->idCmd = ddm.idCmd;
|
|
pfsthp->pidl = ILClone(_pFolder->_pidl);
|
|
pfsthp->grfKeyState = _grfKeyStateLast;
|
|
pfsthp->hwndOwner = _hwndOwner;
|
|
_GetPath(pfsthp->szPath);
|
|
|
|
if (DataObj_CanGoAsync(pDataObj) || DataObj_GoAsyncForCompat(pDataObj)) {
|
|
// create another thread to avoid blocking the source thread.
|
|
if (SHCreateThread(FileDropTargetThreadProc, pfsthp, CTF_COINIT, NULL)) {
|
|
hr = S_OK;
|
|
} else {
|
|
FreeFSThreadParam(pfsthp); // cleanup
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
} else {
|
|
FileDropTargetThreadProc(pfsthp); // synchronously
|
|
}
|
|
} else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
// in these CF_HDROP cases "Move" is always an optimized move, we delete the
|
|
// source. make sure we don't return DROPEFFECT_MOVE so the source does not
|
|
// try to do this too...
|
|
// even if we have not done anything yet since we may have
|
|
// kicked of a thread to do this
|
|
|
|
DataObj_SetDWORD(pDataObj, g_cfLogicalPerformedDropEffect, *pdwEffect);
|
|
if (DROPEFFECT_MOVE == *pdwEffect)
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DragLeaveAndReturn:
|
|
|
|
DragLeave();
|
|
|
|
TraceMsg(TF_DRAGDROP, "CFSDropTarget::Drop(pDataObj=%#08lx) this=%#08lx, _grfKeyStateLast=%#08lx, *pdwEffect=%#08lx, hr=%#08lx", _pdtobj, this, _grfKeyStateLast, *pdwEffect, hr);
|
|
|
|
if (FAILED(hr))
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
|
|
ASSERT(*pdwEffect == DROPEFFECT_COPY ||
|
|
*pdwEffect == DROPEFFECT_LINK ||
|
|
*pdwEffect == DROPEFFECT_MOVE ||
|
|
*pdwEffect == DROPEFFECT_NONE);
|
|
return hr;
|
|
}
|
|
|
|
VOID CFSDropTarget::_AddVerbs(DWORD* pdwEffects,
|
|
DWORD dwEffectAvail,
|
|
DWORD dwDefEffect,
|
|
UINT idCopy,
|
|
UINT idMove,
|
|
UINT idLink,
|
|
DWORD dwForceEffect,
|
|
FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
MENUITEMINFO mii;
|
|
TCHAR szCmd[MAX_PATH];
|
|
if (NULL != pfsMenuInfo) {
|
|
mii.cbSize = sizeof(mii);
|
|
mii.dwTypeData = szCmd;
|
|
mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
|
|
mii.fType = MFT_STRING;
|
|
}
|
|
if ((DROPEFFECT_COPY == (DROPEFFECT_COPY & dwEffectAvail)) &&
|
|
((0 == (*pdwEffects & DROPEFFECT_COPY)) || (dwForceEffect & DROPEFFECT_COPY))) {
|
|
ASSERT(0 != idCopy);
|
|
if (NULL != pfsMenuInfo) {
|
|
LoadString(HINST_THISDLL, idCopy + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd));
|
|
mii.fState = MFS_ENABLED | ((DROPEFFECT_COPY == dwDefEffect) ? MFS_DEFAULT : 0);
|
|
mii.wID = idCopy;
|
|
InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uCopyPos, TRUE, &mii);
|
|
pfsMenuInfo->uCopyPos++;
|
|
pfsMenuInfo->uMovePos++;
|
|
pfsMenuInfo->uLinkPos++;
|
|
}
|
|
}
|
|
if ((DROPEFFECT_MOVE == (DROPEFFECT_MOVE & dwEffectAvail)) &&
|
|
((0 == (*pdwEffects & DROPEFFECT_MOVE)) || (dwForceEffect & DROPEFFECT_MOVE))) {
|
|
ASSERT(0 != idMove);
|
|
if (NULL != pfsMenuInfo) {
|
|
LoadString(HINST_THISDLL, idMove + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd));
|
|
mii.fState = MFS_ENABLED | ((DROPEFFECT_MOVE == dwDefEffect) ? MFS_DEFAULT : 0);
|
|
mii.wID = idMove;
|
|
InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uMovePos, TRUE, &mii);
|
|
pfsMenuInfo->uMovePos++;
|
|
pfsMenuInfo->uLinkPos++;
|
|
}
|
|
}
|
|
if ((DROPEFFECT_LINK == (DROPEFFECT_LINK & dwEffectAvail)) &&
|
|
((0 == (*pdwEffects & DROPEFFECT_LINK)) || (dwForceEffect & DROPEFFECT_LINK))) {
|
|
ASSERT(0 != idLink);
|
|
if (NULL != pfsMenuInfo) {
|
|
LoadString(HINST_THISDLL, idLink + IDS_DD_FIRST, szCmd, ARRAYSIZE(szCmd));
|
|
mii.fState = MFS_ENABLED | ((DROPEFFECT_LINK == dwDefEffect) ? MFS_DEFAULT : 0);
|
|
mii.wID = idLink;
|
|
InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uLinkPos, TRUE, &mii);
|
|
pfsMenuInfo->uLinkPos++;
|
|
}
|
|
}
|
|
*pdwEffects |= dwEffectAvail;
|
|
}
|
|
|
|
DWORD CFSDropTarget::_GetStdDefEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect)
|
|
{
|
|
DWORD dwDefEffect = 0;
|
|
|
|
// Alter the default effect depending on modifier keys.
|
|
|
|
switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT)) {
|
|
case MK_CONTROL: dwDefEffect = DROPEFFECT_COPY; break;
|
|
case MK_SHIFT: dwDefEffect = DROPEFFECT_MOVE; break;
|
|
case MK_SHIFT | MK_CONTROL: dwDefEffect = DROPEFFECT_LINK; break;
|
|
case MK_ALT: dwDefEffect = DROPEFFECT_LINK; break;
|
|
default:
|
|
|
|
// no modifier keys:
|
|
// if the data object contains a preferred drop effect, try to use it
|
|
|
|
DWORD dwPreferred = DataObj_GetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE) & dwAllEffectAvail;
|
|
|
|
if (dwPreferred) {
|
|
if (dwPreferred & DROPEFFECT_MOVE) {
|
|
dwDefEffect = DROPEFFECT_MOVE;
|
|
} else if (dwPreferred & DROPEFFECT_COPY) {
|
|
dwDefEffect = DROPEFFECT_COPY;
|
|
} else if (dwPreferred & DROPEFFECT_LINK) {
|
|
dwDefEffect = DROPEFFECT_LINK;
|
|
}
|
|
} else {
|
|
dwDefEffect = dwOrigDefEffect;
|
|
}
|
|
break;
|
|
}
|
|
return dwDefEffect & dwCurEffectAvail;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(_dwData & DTID_HDROP);
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (!SHRestricted(REST_NOACTIVEDESKTOP) &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder()) {
|
|
hr = IsDeskCompHDrop(_pdtobj);
|
|
if (S_OK == hr) {
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = dwEffectsAvail & DROPEFFECT_LINK;
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects,
|
|
dwEffectAdd,
|
|
dwDefEffect,
|
|
0,
|
|
0,
|
|
DDIDM_CONTENTS_DESKCOMP,
|
|
DROPEFFECT_LINK, // force add the DDIDM_CONTENTS_DESKCOMP verb
|
|
pfsMenuInfo);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(_dwData & DTID_HDROP);
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
// Is this the sneakernet case?
|
|
ASSERT(_pdtobj);
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(_pdtobj, &medium);
|
|
if (pida) {
|
|
LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1);
|
|
if (pidlParent) {
|
|
TCHAR szTargetFolder[MAX_PATH];
|
|
|
|
_GetPath(szTargetFolder);
|
|
|
|
if (IsFromSneakernetBriefcase(pidlParent, szTargetFolder)) {
|
|
// Yes; show the non-default briefcase cm
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM mediumT;
|
|
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &mediumT))) {
|
|
BOOL fSyncCopyType = DroppingAnyFolders((HDROP)mediumT.hGlobal);
|
|
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = DROPEFFECT_COPY & dwEffectsAvail;
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_SYNCCOPY, 0, 0, 0, pfsMenuInfo);
|
|
|
|
// Call _AddVerbs() again to force "Sync Copy of Type" as a 2nd DROPEFFECT_COPY verb:
|
|
if (fSyncCopyType && (DROPEFFECT_COPY & dwEffectsAvail)) {
|
|
_AddVerbs(pdwEffects,
|
|
DROPEFFECT_COPY,
|
|
0,
|
|
DDIDM_SYNCCOPYTYPE,
|
|
0,
|
|
0,
|
|
DROPEFFECT_COPY,
|
|
pfsMenuInfo);
|
|
}
|
|
|
|
ReleaseStgMedium(&mediumT);
|
|
}
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(_dwData & DTID_HDROP);
|
|
ASSERT(pdwEffects);
|
|
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE);
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, _PickDefFSOperation(dwEffectAdd));
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_COPY, DDIDM_MOVE, 0, 0, pfsMenuInfo);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoFileContents(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
|
|
if ((_dwData & (DTID_CONTENTS | DTID_FDESCA)) == (DTID_CONTENTS | DTID_FDESCA) ||
|
|
(_dwData & (DTID_CONTENTS | DTID_FDESCW)) == (DTID_CONTENTS | DTID_FDESCW)) {
|
|
DWORD dwEffectAdd, dwSuggestedEffect;
|
|
|
|
// HACK: if there is a preferred drop effect and no HIDA
|
|
// then just take the preferred effect as the available effects
|
|
// this is because we didn't actually check the FD_LINKUI bit
|
|
// back when we assembled dwData! (performance)
|
|
|
|
if ((_dwData & (DTID_PREFERREDEFFECT | DTID_HIDA)) == DTID_PREFERREDEFFECT) {
|
|
dwEffectAdd = _dwEffectPreferred;
|
|
dwSuggestedEffect = _dwEffectPreferred;
|
|
} else if (_dwData & DTID_FD_LINKUI) {
|
|
dwEffectAdd = DROPEFFECT_LINK;
|
|
dwSuggestedEffect = DROPEFFECT_LINK;
|
|
} else {
|
|
dwEffectAdd = DROPEFFECT_COPY | DROPEFFECT_MOVE;
|
|
dwSuggestedEffect = DROPEFFECT_COPY;
|
|
}
|
|
dwEffectAdd &= dwEffectsAvail;
|
|
|
|
DWORD dwDefEffect = 0;
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, dwSuggestedEffect);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects,
|
|
dwEffectAdd,
|
|
dwDefEffect,
|
|
DDIDM_CONTENTS_COPY,
|
|
DDIDM_CONTENTS_MOVE,
|
|
DDIDM_CONTENTS_LINK,
|
|
0,
|
|
pfsMenuInfo);
|
|
return S_OK;
|
|
} else {
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(_dwData & DTID_HIDA);
|
|
ASSERT(pdwEffects);
|
|
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail;
|
|
// NOTE: we only add a HIDA default effect if HDROP isn't going to add a default
|
|
// effect. This preserves shell behavior with file system data objects without
|
|
// requiring us to change the enumerator order in CIDLData. When we do change
|
|
// the enumerator order, we can remove this special case:
|
|
if ((NULL != pdwDefaultEffect) &&
|
|
((0 == (_dwData & DTID_HDROP)) ||
|
|
(0 == _GetStdDefEffect(grfKeyFlags,
|
|
dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE),
|
|
dwEffectsAvail,
|
|
_PickDefFSOperation(dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE)))))) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_LINK, 0, pfsMenuInfo);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(_dwData & DTID_EMBEDDEDOBJECT);
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (NULL != pdwDefaultEffect) {
|
|
*pdwDefaultEffect = 0;
|
|
}
|
|
|
|
FORMATETC fmte = {g_cfObjectDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
ASSERT(NULL != _pdtobj);
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) {
|
|
// we've got an object descriptor
|
|
OBJECTDESCRIPTOR* pOD = (OBJECTDESCRIPTOR*)GlobalLock(medium.hGlobal);
|
|
if (pOD) {
|
|
if (IsEqualCLSID(CLSID_OldPackage, pOD->clsid) ||
|
|
IsEqualCLSID(CLSID_CPackage, pOD->clsid)) {
|
|
// This is a package - proceed
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = (DROPEFFECT_COPY | DROPEFFECT_MOVE) & dwEffectsAvail;
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags,
|
|
dwEffectAdd,
|
|
dwEffectsAvail,
|
|
DROPEFFECT_COPY);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects,
|
|
dwEffectAdd,
|
|
dwDefEffect,
|
|
DDIDM_OBJECT_COPY,
|
|
DDIDM_OBJECT_MOVE,
|
|
0,
|
|
0,
|
|
pfsMenuInfo);
|
|
|
|
hr = S_OK;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (NULL != pdwDefaultEffect) {
|
|
*pdwDefaultEffect = 0;
|
|
}
|
|
|
|
if (!SHRestricted(REST_NOACTIVEDESKTOP) &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder()) {
|
|
FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) {
|
|
//DANGER WILL ROBINSON:
|
|
//HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
|
|
// it as is it were ANSI. Find a way to escape the sequences...
|
|
CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal);
|
|
if (pszData) {
|
|
CHAR szUrl[MAX_URL_STRING];
|
|
if (ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, ARRAYSIZE(szUrl))) {
|
|
// The HTML contains an image tag - carry on...
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = DROPEFFECT_LINK; // NOTE: ignoring dwEffectsAvail!
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags,
|
|
dwEffectAdd,
|
|
dwEffectsAvail | DROPEFFECT_LINK,
|
|
DROPEFFECT_LINK);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects,
|
|
dwEffectAdd,
|
|
dwDefEffect,
|
|
0,
|
|
0,
|
|
DDIDM_CONTENTS_DESKIMG,
|
|
0,
|
|
pfsMenuInfo);
|
|
|
|
hr = S_OK;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (NULL != pdwDefaultEffect) {
|
|
*pdwDefaultEffect = 0;
|
|
}
|
|
|
|
if (!SHRestricted(REST_NOACTIVEDESKTOP) &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder()) {
|
|
FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) {
|
|
// DANGER WILL ROBINSON:
|
|
// HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
|
|
// it as is it were ANSI. Find a way to escape the sequences...
|
|
CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal);
|
|
if (pszData) {
|
|
int nScheme = GetUrlSchemeA(pszData);
|
|
if ((nScheme != URL_SCHEME_INVALID) && (nScheme != URL_SCHEME_FTP)) {
|
|
// This is an internet scheme - carry on...
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail;
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects,
|
|
dwEffectAdd,
|
|
dwDefEffect,
|
|
0,
|
|
0,
|
|
DDIDM_CONTENTS_DESKURL,
|
|
DROPEFFECT_LINK, // force add this verb
|
|
pfsMenuInfo);
|
|
|
|
hr = S_OK;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoOleObj(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (_dwData & DTID_OLEOBJ) {
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = (DROPEFFECT_COPY | DROPEFFECT_MOVE) & dwEffectsAvail;
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_COPY);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects,
|
|
dwEffectAdd,
|
|
dwDefEffect,
|
|
DDIDM_SCRAP_COPY,
|
|
DDIDM_SCRAP_MOVE,
|
|
0,
|
|
0,
|
|
pfsMenuInfo);
|
|
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetDDInfoOleLink(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (_dwData & DTID_OLELINK) {
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = DROPEFFECT_LINK & dwEffectsAvail;
|
|
if (NULL != pdwDefaultEffect) {
|
|
dwDefEffect = _GetStdDefEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects,
|
|
dwEffectAdd,
|
|
dwDefEffect,
|
|
0,
|
|
0,
|
|
DDIDM_DOCLINK,
|
|
0,
|
|
pfsMenuInfo);
|
|
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_CreateURLDeskComp(int x, int y)
|
|
{
|
|
// This code should only be entered if DDIDM_CONTENTS_DESKURL was added to the menu,
|
|
// and it has these checks:
|
|
ASSERT(!SHRestricted(REST_NOACTIVEDESKTOP) &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder());
|
|
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
HRESULT hr = _pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hr)) {
|
|
//DANGER WILL ROBINSON:
|
|
//HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
|
|
// it as is it were ANSI. Find a way to escape the sequences...
|
|
CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal);
|
|
if (pszData) {
|
|
int nScheme = GetUrlSchemeA(pszData);
|
|
if ((nScheme != URL_SCHEME_INVALID) && (nScheme != URL_SCHEME_FTP)) {
|
|
// This is an internet scheme - URL
|
|
|
|
hr = CreateDesktopComponents(pszData, NULL, _hwndOwner, DESKCOMP_URL, x, y);
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
} else {
|
|
hr = E_FAIL;
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_CreateDeskCompImage(POINTL pt)
|
|
{
|
|
ASSERT(!SHRestricted(REST_NOACTIVEDESKTOP) &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder());
|
|
|
|
FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
HRESULT hres = _pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hres)) {
|
|
//DANGER WILL ROBINSON:
|
|
//HTML is UTF-8, a mostly ANSI cross of ANSI and Unicode. Play with
|
|
// it as is it were ANSI. Find a way to escape the sequences...
|
|
CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal);
|
|
if (pszData) {
|
|
CHAR szUrl[MAX_URL_STRING];
|
|
if (ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, ARRAYSIZE(szUrl))) {
|
|
// The HTML contains an image tag - carry on...
|
|
ADDTODESKTOP* pToAD = (ADDTODESKTOP*)LocalAlloc(LPTR, sizeof(*pToAD));
|
|
if (pToAD) {
|
|
pToAD->hwnd = _hwndOwner;
|
|
lstrcpyA(pToAD->szUrl, szUrl);
|
|
pToAD->dwFlags = DESKCOMP_IMAGE;
|
|
pToAD->pt = pt;
|
|
|
|
if (SHCreateThread(AddToActiveDesktopThreadProc, pToAD, CTF_COINIT, NULL)) {
|
|
hres = NOERROR;
|
|
} else {
|
|
LocalFree(pToAD);
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
} else {
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
} else {
|
|
hres = E_FAIL;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
} else {
|
|
hres = E_FAIL;
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
// read byte by byte until we hit the null terminating char
|
|
// return: the number of bytes read
|
|
|
|
STDAPI StringReadFromStream(IStream* pstm, LPSTR pszBuf, UINT cchBuf)
|
|
{
|
|
UINT cch = 0;
|
|
|
|
do {
|
|
pstm->Read(pszBuf, sizeof(CHAR), NULL);
|
|
cch++;
|
|
} while (*pszBuf++ && cch <= cchBuf);
|
|
return cch;
|
|
}
|
|
|
|
STDAPI CopyStreamToFile(IStream* pstmSrc, LPCTSTR pszFile)
|
|
{
|
|
IStream* pstmFile;
|
|
HRESULT hr = SHCreateStreamOnFile(pszFile, OF_CREATE | OF_WRITE | OF_SHARE_DENY_WRITE, &pstmFile);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = CopyStreamUI(pstmSrc, pstmFile, NULL);
|
|
pstmFile->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_CreatePackage()
|
|
{
|
|
ILockBytes* pLockBytes;
|
|
HRESULT hr = CreateILockBytesOnHGlobal(NULL, TRUE, &pLockBytes);
|
|
if (SUCCEEDED(hr)) {
|
|
STGMEDIUM medium;
|
|
medium.tymed = TYMED_ISTORAGE;
|
|
hr = StgCreateDocfileOnILockBytes(pLockBytes,
|
|
STGM_DIRECT | STGM_READWRITE | STGM_CREATE |
|
|
STGM_SHARE_EXCLUSIVE, 0, &medium.pstg);
|
|
if (SUCCEEDED(hr)) {
|
|
FORMATETC fmte = {g_cfEmbeddedObject, NULL, DVASPECT_CONTENT, -1, TYMED_ISTORAGE};
|
|
ASSERT(NULL != _pdtobj);
|
|
hr = _pdtobj->GetDataHere(&fmte, &medium);
|
|
if (SUCCEEDED(hr)) {
|
|
IStream* pstm;
|
|
#ifdef DEBUG
|
|
STATSTG stat;
|
|
if (SUCCEEDED(medium.pstg->Stat(&stat, STATFLAG_NONAME))) {
|
|
ASSERT(IsEqualCLSID(CLSID_OldPackage, stat.clsid) ||
|
|
IsEqualCLSID(CLSID_CPackage, stat.clsid));
|
|
}
|
|
#endif // DEBUG
|
|
#define PACKAGER_ICON 2
|
|
#define PACKAGER_CONTENTS L"\001Ole10Native"
|
|
#define PACKAGER_EMBED_TYPE 3
|
|
hr = medium.pstg->OpenStream(PACKAGER_CONTENTS, 0,
|
|
STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
|
|
0, &pstm);
|
|
if (SUCCEEDED(hr)) {
|
|
DWORD dw;
|
|
WORD w;
|
|
CHAR szName[MAX_PATH];
|
|
CHAR szTemp[MAX_PATH];
|
|
if (SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL)) && // pkg size
|
|
SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // pkg appearance
|
|
(PACKAGER_ICON == w) &&
|
|
SUCCEEDED(StringReadFromStream(pstm, szName, ARRAYSIZE(szName))) &&
|
|
SUCCEEDED(StringReadFromStream(pstm, szTemp, ARRAYSIZE(szTemp))) && // icon path
|
|
SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // icon index
|
|
SUCCEEDED(pstm->Read(&w, sizeof(w), NULL)) && // panetype
|
|
(PACKAGER_EMBED_TYPE == w) &&
|
|
SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL)) && // filename size
|
|
SUCCEEDED(pstm->Read(szTemp, dw, NULL)) && // filename
|
|
SUCCEEDED(pstm->Read(&dw, sizeof(dw), NULL))) // get file size
|
|
{
|
|
// The rest of the stream is the file contents
|
|
TCHAR szPath[MAX_PATH], szBase[MAX_PATH], szDest[MAX_PATH];
|
|
_GetPath(szPath);
|
|
|
|
SHAnsiToTChar(szName, szBase, ARRAYSIZE(szBase));
|
|
PathAppend(szPath, szBase);
|
|
PathYetAnotherMakeUniqueName(szDest, szPath, NULL, szBase);
|
|
TraceMsg(TF_GENERAL, "CFSIDLDropTarget pkg: %s", szDest);
|
|
|
|
hr = CopyStreamToFile(pstm, szDest);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szDest, NULL);
|
|
if (_fBkDropTarget && _hwndOwner) {
|
|
FS_PositionFileFromDrop(_hwndOwner, szDest, NULL);
|
|
}
|
|
}
|
|
} else {
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
pstm->Release();
|
|
}
|
|
}
|
|
medium.pstg->Release();
|
|
}
|
|
pLockBytes->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetPath(LPTSTR pszPath)
|
|
{
|
|
return CFSFolder_GetPath(_pFolder, pszPath);
|
|
}
|
|
|
|
STDAPI_(BOOL) AllRegisteredPrograms(HDROP hDrop);
|
|
|
|
|
|
// Returns:
|
|
// If the data object does NOT contain HDROP -> "none"
|
|
// else if the source is root or registered progam -> "link"
|
|
// else if this is within a volume -> "move"
|
|
// else if this is a briefcase -> "move"
|
|
// else -> "copy"
|
|
|
|
DWORD CFSDropTarget::_PickDefFSOperation(DWORD dwCurEffectAvail)
|
|
{
|
|
ASSERT(_pdtobj);
|
|
DWORD dwDefEffect = 0; // assume no HDROP
|
|
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) {
|
|
TCHAR szPath[MAX_PATH], szFolder[MAX_PATH];
|
|
|
|
_GetPath(szFolder);
|
|
DragQueryFile((HDROP)medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)); // focused item
|
|
|
|
// Determine the default operation depending on the item.
|
|
if (PathIsRoot(szPath) || AllRegisteredPrograms((HDROP)medium.hGlobal)) {
|
|
dwDefEffect = DROPEFFECT_LINK;
|
|
} else if (PathIsSameRoot(szPath, szFolder)) {
|
|
dwDefEffect = DROPEFFECT_MOVE;
|
|
} else if (IsBriefcaseRoot(_pdtobj)) {
|
|
// briefcase default to move even accross volumes
|
|
dwDefEffect = DROPEFFECT_MOVE;
|
|
} else {
|
|
dwDefEffect = DROPEFFECT_COPY;
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
} else {
|
|
// GetData failed. Let's see if QueryGetData failed or not.
|
|
if (SUCCEEDED(_pdtobj->QueryGetData(&fmte))) {
|
|
// this means this data object has HDROP but can't
|
|
// provide it until it is dropped. Let's assume we are copying.
|
|
dwDefEffect = DROPEFFECT_COPY;
|
|
}
|
|
}
|
|
// Switch default verb if the dwCurEffectAvail hint suggests that we picked an
|
|
// unavailable effect (this code applies to MOVE and COPY only):
|
|
dwCurEffectAvail &= (DROPEFFECT_MOVE | DROPEFFECT_COPY);
|
|
if ((DROPEFFECT_MOVE == dwDefEffect) && (DROPEFFECT_COPY == dwCurEffectAvail)) {
|
|
// If we were going to return MOVE, and only COPY is available, return COPY:
|
|
dwDefEffect = DROPEFFECT_COPY;
|
|
} else if ((DROPEFFECT_COPY == dwDefEffect) && (DROPEFFECT_MOVE == dwCurEffectAvail)) {
|
|
// If we were going to return COPY, and only MOVE is available, return MOVE:
|
|
dwDefEffect = DROPEFFECT_MOVE;
|
|
}
|
|
return dwDefEffect;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_ZoneCheckDataObject(DWORD dwEffect)
|
|
{
|
|
ASSERT(NULL != _pdtobj);
|
|
|
|
CHAR szUrl[INTERNET_MAX_URL_LENGTH];
|
|
szUrl[0] = 0;
|
|
|
|
// Grab a URL and use it for the zone check if possible:
|
|
if (!SHRestricted(REST_NOACTIVEDESKTOP) &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder()) {
|
|
FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) {
|
|
CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal);
|
|
if (pszData) {
|
|
ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, sizeof(szUrl));
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
} else {
|
|
fmte.cfFormat = g_cfShellURL;
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium))) {
|
|
CHAR* pszData = (CHAR*)GlobalLock(medium.hGlobal);
|
|
if (pszData) {
|
|
lstrcpynA(szUrl, (LPCSTR)pszData, ARRAYSIZE(szUrl));
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (szUrl[0]) {
|
|
return ZoneCheckUrlA(szUrl,
|
|
URLACTION_SHELL_MOVE_OR_COPY,
|
|
PUAF_FORCEUI_FOREGROUND | PUAF_WARN_IF_DENIED | PUAF_CHECK_TIFS,
|
|
NULL);
|
|
} else {
|
|
return ZoneCheckHDrop(_pdtobj,
|
|
dwEffect,
|
|
URLACTION_SHELL_MOVE_OR_COPY,
|
|
PUAF_FORCEUI_FOREGROUND | PUAF_WARN_IF_DENIED | PUAF_CHECK_TIFS,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
|
|
// make sure that the default effect is among the allowed effects
|
|
|
|
DWORD CFSDropTarget::_LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed)
|
|
{
|
|
if (dwDefEffect & dwEffectsAllowed)
|
|
return dwDefEffect;
|
|
|
|
if (dwEffectsAllowed & DROPEFFECT_COPY)
|
|
return DROPEFFECT_COPY;
|
|
|
|
if (dwEffectsAllowed & DROPEFFECT_MOVE)
|
|
return DROPEFFECT_MOVE;
|
|
|
|
if (dwEffectsAllowed & DROPEFFECT_LINK)
|
|
return DROPEFFECT_LINK;
|
|
|
|
return DROPEFFECT_NONE;
|
|
}
|
|
|
|
typedef struct {
|
|
HRESULT(CFSDropTarget::* pfnGetDragDropInfo)(
|
|
IN FORMATETC* pfmte,
|
|
IN DWORD grfKeyFlags,
|
|
IN DWORD dwEffectsAvail,
|
|
IN OUT DWORD* pdwEffectsUsed,
|
|
OUT DWORD* pdwDefaultEffect,
|
|
IN OUT FSMENUINFO* pfsMenuInfo);
|
|
FORMATETC fmte;
|
|
} FS_DATA_HANDLER;
|
|
|
|
#define CFFMTE(cf, tymed) {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, tymed}
|
|
|
|
// This function returns the default effect.
|
|
// This function also modifies *pdwEffectInOut to indicate "available" operations.
|
|
|
|
DWORD CFSDropTarget::_DetermineEffects(DWORD grfKeyState,
|
|
DWORD* pdwEffectInOut,
|
|
HMENU hmenu)
|
|
{
|
|
DWORD dwDefEffect = DROPEFFECT_NONE;
|
|
DWORD dwEffectsUsed = DROPEFFECT_NONE;
|
|
|
|
// NOTE: the order is important (particularly for multiple entries with the same FORMATETC)
|
|
FS_DATA_HANDLER rg_data_handlers[] = {
|
|
_GetDDInfoFileContents, CFFMTE(g_cfFileGroupDescriptorW, TYMED_HGLOBAL),
|
|
_GetDDInfoFileContents, CFFMTE(g_cfFileGroupDescriptorA, TYMED_HGLOBAL),
|
|
_GetDDInfoFileContents, CFFMTE(g_cfFileContents, TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE),
|
|
_GetDDInfoBriefcase, CFFMTE(CF_HDROP, TYMED_HGLOBAL),
|
|
_GetDDInfoHDROP, CFFMTE(CF_HDROP, TYMED_HGLOBAL),
|
|
_GetDDInfoDeskCompHDROP, CFFMTE(CF_HDROP, TYMED_HGLOBAL),
|
|
_GetDDInfoHIDA, CFFMTE(g_cfHIDA, TYMED_HGLOBAL),
|
|
_GetDDInfoOlePackage, CFFMTE(g_cfEmbeddedObject, TYMED_ISTORAGE),
|
|
_GetDDInfoDeskImage, CFFMTE(g_cfHTML, TYMED_HGLOBAL),
|
|
_GetDDInfoDeskComp, CFFMTE(g_cfShellURL, TYMED_HGLOBAL),
|
|
_GetDDInfoOleObj, CFFMTE(0, TYMED_HGLOBAL),
|
|
_GetDDInfoOleLink, CFFMTE(0, TYMED_HGLOBAL),
|
|
};
|
|
// Loop through formats, factoring in both the order of the enumerator and
|
|
// the order of our rg_data_handlers to determine the default effect
|
|
// (and possibly, to create the drop context menu)
|
|
FSMENUINFO fsmi = {hmenu, 0, 0, 0};
|
|
IEnumFORMATETC* penum;
|
|
AssertMsg((NULL != _pdtobj), TEXT("CFSDropTarget::_DetermineEffects() _pdtobj is NULL but we need it. this=%#08lx"), this);
|
|
if (_pdtobj && SUCCEEDED(_pdtobj->EnumFormatEtc(DATADIR_GET, &penum))) {
|
|
FORMATETC fmte;
|
|
ULONG celt;
|
|
while (penum->Next(1, &fmte, &celt) == S_OK) {
|
|
for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++) {
|
|
if (rg_data_handlers[i].fmte.cfFormat == fmte.cfFormat &&
|
|
rg_data_handlers[i].fmte.dwAspect == fmte.dwAspect &&
|
|
(0 != (rg_data_handlers[i].fmte.tymed & fmte.tymed))) {
|
|
(this->*(rg_data_handlers[i].pfnGetDragDropInfo))(
|
|
&fmte,
|
|
grfKeyState,
|
|
*pdwEffectInOut,
|
|
&dwEffectsUsed,
|
|
(DROPEFFECT_NONE == dwDefEffect) ?
|
|
&dwDefEffect : NULL,
|
|
(NULL != hmenu) ? &fsmi : NULL);
|
|
}
|
|
}
|
|
}
|
|
penum->Release();
|
|
}
|
|
// Loop through the rg_data_handlers that don't have an associated clipboard format last
|
|
for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++) {
|
|
if (0 == rg_data_handlers[i].fmte.cfFormat) {
|
|
(this->*(rg_data_handlers[i].pfnGetDragDropInfo))(
|
|
NULL,
|
|
grfKeyState,
|
|
*pdwEffectInOut,
|
|
&dwEffectsUsed,
|
|
(DROPEFFECT_NONE == dwDefEffect) ?
|
|
&dwDefEffect : NULL,
|
|
(NULL != hmenu) ? &fsmi : NULL);
|
|
}
|
|
}
|
|
|
|
*pdwEffectInOut &= dwEffectsUsed;
|
|
|
|
dwDefEffect = _LimitDefaultEffect(dwDefEffect, *pdwEffectInOut);
|
|
|
|
DebugMsg(TF_FSTREE, TEXT("CFSDT::GetDefaultEffect dwDef=%x, dwEffUsed=%x, *pdw=%x"),
|
|
dwDefEffect, dwEffectsUsed, *pdwEffectInOut);
|
|
|
|
return dwDefEffect;
|
|
}
|
|
|
|
struct FS_EFFECT {
|
|
UINT uID;
|
|
DWORD dwEffect;
|
|
};
|
|
|
|
// This is used to map command id's back to dropeffect's:
|
|
|
|
const FS_EFFECT c_IDFSEffects[] = {
|
|
DDIDM_COPY, DROPEFFECT_COPY,
|
|
DDIDM_MOVE, DROPEFFECT_MOVE,
|
|
DDIDM_CONTENTS_DESKCOMP, DROPEFFECT_LINK,
|
|
DDIDM_LINK, DROPEFFECT_LINK,
|
|
DDIDM_SCRAP_COPY, DROPEFFECT_COPY,
|
|
DDIDM_SCRAP_MOVE, DROPEFFECT_MOVE,
|
|
DDIDM_DOCLINK, DROPEFFECT_LINK,
|
|
DDIDM_CONTENTS_COPY, DROPEFFECT_COPY,
|
|
DDIDM_CONTENTS_MOVE, DROPEFFECT_MOVE,
|
|
DDIDM_CONTENTS_LINK, DROPEFFECT_LINK,
|
|
DDIDM_CONTENTS_DESKIMG, DROPEFFECT_LINK,
|
|
DDIDM_SYNCCOPYTYPE, DROPEFFECT_COPY, // (order is important)
|
|
DDIDM_SYNCCOPY, DROPEFFECT_COPY,
|
|
DDIDM_OBJECT_COPY, DROPEFFECT_COPY,
|
|
DDIDM_OBJECT_MOVE, DROPEFFECT_MOVE,
|
|
DDIDM_CONTENTS_DESKURL, DROPEFFECT_LINK,
|
|
};
|
|
|
|
HRESULT CFSDropTarget::_DragDropMenu(FSDRAGDROPMENUPARAM* pddm)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY; // assume error
|
|
DWORD dwEffectOut = 0; // assume no-ope.
|
|
if (pddm->hmenu) {
|
|
int nItem;
|
|
UINT idCmd;
|
|
UINT idCmdFirst = DDIDM_EXTFIRST;
|
|
HDXA hdxa = HDXA_Create();
|
|
HDCA hdca = DCA_Create();
|
|
if (hdxa && hdca) {
|
|
// BUGBUG (toddb): Even though pddm->hkeyBase does not have the same value as
|
|
// pddm->hkeyProgID they can both be the same registry key (HKCR\FOLDER, for example).
|
|
// As a result we sometimes enumerate this key twice looking for the same data. As
|
|
// this is sometimes a slow operation we should avoid this. The comparision
|
|
// done below was never valid on NT and might not be valid on win9x.
|
|
|
|
// Add extended menu for "Base" class.
|
|
if (pddm->hkeyBase && pddm->hkeyBase != pddm->hkeyProgID)
|
|
DCA_AddItemsFromKey(hdca, pddm->hkeyBase, STRREG_SHEX_DDHANDLER);
|
|
|
|
// Enumerate the DD handlers and let them append menu items.
|
|
if (pddm->hkeyProgID)
|
|
DCA_AddItemsFromKey(hdca, pddm->hkeyProgID, STRREG_SHEX_DDHANDLER);
|
|
|
|
idCmdFirst = HDXA_AppendMenuItems(hdxa, pddm->pdtobj, 1,
|
|
&pddm->hkeyProgID, _pFolder->_pidl, pddm->hmenu, 0,
|
|
DDIDM_EXTFIRST, DDIDM_EXTLAST, 0, hdca);
|
|
}
|
|
|
|
// If this dragging is caused by the left button, simply choose
|
|
// the default one, otherwise, pop up the context menu. If there
|
|
// is no key state info and the original effect is the same as the
|
|
// current effect, choose the default one, otherwise pop up the
|
|
// context menu.
|
|
if ((_grfKeyStateLast & MK_LBUTTON) ||
|
|
(!_grfKeyStateLast && (*(pddm->pdwEffect) == pddm->dwDefEffect))) {
|
|
idCmd = GetMenuDefaultItem(pddm->hmenu, MF_BYCOMMAND, 0);
|
|
|
|
// This one MUST be called here. Please read its comment block.
|
|
DAD_DragLeave();
|
|
|
|
if (_hwndOwner)
|
|
SetForegroundWindow(_hwndOwner);
|
|
} else {
|
|
// Note that SHTrackPopupMenu calls DAD_DragLeave().
|
|
idCmd = SHTrackPopupMenu(pddm->hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
|
|
pddm->pt.x, pddm->pt.y, 0, _hwndOwner, NULL);
|
|
|
|
}
|
|
|
|
// We also need to call this here to release the dragged image.
|
|
DAD_SetDragImage(NULL, NULL);
|
|
|
|
// Check if the user selected one of add-in menu items.
|
|
if (idCmd == 0) {
|
|
hres = S_OK; // Canceled by the user, return S_OK
|
|
} else if (InRange(idCmd, DDIDM_EXTFIRST, DDIDM_EXTLAST)) {
|
|
// Yes. Let the context menu handler process it.
|
|
|
|
CMINVOKECOMMANDINFOEX ici = {
|
|
SIZEOF(CMINVOKECOMMANDINFOEX),
|
|
0L,
|
|
_hwndOwner,
|
|
(LPSTR)MAKEINTRESOURCE(idCmd - DDIDM_EXTFIRST),
|
|
NULL, NULL,
|
|
SW_NORMAL,
|
|
};
|
|
|
|
// record if the shift/control keys were down at the time of the drop
|
|
if (_grfKeyStateLast & MK_SHIFT) {
|
|
ici.fMask |= CMIC_MASK_SHIFT_DOWN;
|
|
}
|
|
|
|
if (_grfKeyStateLast & MK_CONTROL) {
|
|
ici.fMask |= CMIC_MASK_CONTROL_DOWN;
|
|
}
|
|
|
|
// We may not want to ignore the error code. (Can happen when you use the context menu
|
|
// to create new folders, but I don't know if that can happen here.).
|
|
HDXA_LetHandlerProcessCommandEx(hdxa, &ici, NULL);
|
|
hres = S_OK;
|
|
} else {
|
|
for (nItem = 0; nItem < ARRAYSIZE(c_IDFSEffects); ++nItem) {
|
|
if (idCmd == c_IDFSEffects[nItem].uID) {
|
|
dwEffectOut = c_IDFSEffects[nItem].dwEffect;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if hmenuReplace had menu commands other than DDIDM_COPY,
|
|
// DDIDM_MOVE, DDIDM_LINK, and that item was selected,
|
|
// this assert will catch it. (dwEffectOut is 0 in this case)
|
|
ASSERT(nItem < ARRAYSIZE(c_IDFSEffects));
|
|
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
if (hdca)
|
|
DCA_Destroy(hdca);
|
|
|
|
if (hdxa)
|
|
HDXA_Destroy(hdxa);
|
|
|
|
pddm->idCmd = idCmd;
|
|
}
|
|
|
|
*(pddm->pdwEffect) = dwEffectOut;
|
|
|
|
return hres;
|
|
}
|