3260 lines
110 KiB
C++
3260 lines
110 KiB
C++
#include "shellprv.h"
|
|
#include <cowsite.h>
|
|
#include "datautil.h"
|
|
#include "ids.h"
|
|
#include "defview.h"
|
|
#include "_security.h"
|
|
#include "shitemid.h"
|
|
#include "idlcomm.h"
|
|
#include "bitbuck.h"
|
|
#include "bookmk.h"
|
|
#include "filefldr.h"
|
|
#include "brfcase.h"
|
|
#include "copy.h"
|
|
#include "filetbl.h"
|
|
|
|
#define TF_DRAGDROP 0x04000000
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
typedef struct {
|
|
DWORD dwDefEffect;
|
|
IDataObject *pdtobj;
|
|
POINTL pt;
|
|
DWORD * pdwEffect;
|
|
HKEY rghk[MAX_ASSOC_KEYS];
|
|
DWORD ck;
|
|
HMENU hmenu;
|
|
UINT idCmd;
|
|
DWORD grfKeyState;
|
|
} FSDRAGDROPMENUPARAM;
|
|
|
|
typedef struct
|
|
{
|
|
HMENU hMenu;
|
|
UINT uCopyPos;
|
|
UINT uMovePos;
|
|
UINT uLinkPos;
|
|
} FSMENUINFO;
|
|
|
|
|
|
class CFSDropTarget : CObjectWithSite, public IDropTarget
|
|
{
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IDropTarget
|
|
STDMETHODIMP DragEnter(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
|
|
STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
|
|
STDMETHODIMP DragLeave();
|
|
STDMETHODIMP Drop(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
|
|
|
|
CFSDropTarget(CFSFolder *pFolder, HWND hwnd);
|
|
|
|
protected:
|
|
virtual ~CFSDropTarget();
|
|
BOOL _IsBriefcaseTarget() { return IsEqualCLSID(_pFolder->_clsidBind, CLSID_BriefcaseFolder); };
|
|
|
|
BOOL _IsDesktopFolder() { return _GetIDList() && ILIsEmpty(_GetIDList()); };
|
|
|
|
HRESULT _FilterFileContents(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterFileContentsOLEHack(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterSneakernetBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterOleObj(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
HRESULT _FilterOleLink(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo);
|
|
|
|
DWORD _FilesystemAdjustedDefaultEffect(DWORD dwCurEffectAvail);
|
|
HRESULT _GetPath(LPTSTR pszPath);
|
|
LPCITEMIDLIST _GetIDList();
|
|
DWORD _LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed);
|
|
DWORD _GetDefaultEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect);
|
|
DWORD _DetermineEffects(DWORD grfKeyState, DWORD *pdwEffectInOut, HMENU hmenu);
|
|
DWORD _EffectFromFolder();
|
|
|
|
typedef struct
|
|
{
|
|
CFSDropTarget *pThis;
|
|
IStream *pstmDataObj;
|
|
IStream *pstmFolderView;
|
|
} DROPTHREADPARAMS;
|
|
|
|
static void _FreeThreadParams(DROPTHREADPARAMS *pdtp);
|
|
|
|
static DWORD CALLBACK _DoDropThreadProc(void *pv);
|
|
void _DoDrop(IDataObject *pdtobj, IFolderView* pfv);
|
|
static void _AddVerbs(DWORD* pdwEffects, DWORD dwEffectAvail, DWORD dwDefEffect,
|
|
UINT idCopy, UINT idMove, UINT idLink,
|
|
DWORD dwForceEffect, FSMENUINFO* pfsMenuInfo);
|
|
void _FixUpDefaultItem(HMENU hmenu, DWORD dwDefEffect);
|
|
HRESULT _DragDropMenu(FSDRAGDROPMENUPARAM *pddm);
|
|
|
|
HRESULT _CreatePackage(IDataObject *pdtobj);
|
|
HRESULT _CreateURLDeskComp(IDataObject *pdtobj, POINTL pt);
|
|
HRESULT _CreateDeskCompImage(IDataObject *pdtobj, POINTL pt);
|
|
void _GetStateFromSite();
|
|
BOOL _IsFromSneakernetBriefcase();
|
|
BOOL _IsFromSameBriefcase();
|
|
void _MoveCopy(IDataObject *pdtobj, IFolderView* pfv, HDROP hDrop);
|
|
void _MoveSelectIcons(IDataObject *pdtobj, IFolderView* pfv, void *hNameMap, LPCTSTR pszFiles, BOOL fMove, HDROP hDrop);
|
|
|
|
LONG _cRef;
|
|
CFSFolder *_pFolder;
|
|
HWND _hwnd; // EVIL: used as a site and UI host
|
|
UINT _idCmd;
|
|
DWORD _grfKeyStateLast; // for previous DragOver/Enter
|
|
IDataObject *_pdtobj; // used durring Dragover() and DoDrop(), don't use on background thread
|
|
DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover
|
|
DWORD _dwEffect;
|
|
DWORD _dwData; // DTID_*
|
|
DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT
|
|
DWORD _dwEffectFolder; // folder desktop.ini preferred effect
|
|
BOOL _fSameHwnd; // the drag source and target are the same folder
|
|
BOOL _fDragDrop; //
|
|
BOOL _fUseExactDropPoint; // Don't transform the drop point. The target knows exactly where it wants things.
|
|
BOOL _fBkDropTarget;
|
|
POINT _ptDrop;
|
|
IFolderView* _pfv;
|
|
|
|
typedef struct {
|
|
FORMATETC fmte;
|
|
HRESULT (CFSDropTarget::*pfnGetDragDropInfo)(
|
|
IN FORMATETC* pfmte,
|
|
IN DWORD grfKeyFlags,
|
|
IN DWORD dwEffectsAvail,
|
|
IN OUT DWORD* pdwEffectsUsed,
|
|
OUT DWORD* pdwDefaultEffect,
|
|
IN OUT FSMENUINFO* pfsMenuInfo);
|
|
CLIPFORMAT *pcfInit;
|
|
} _DATA_HANDLER;
|
|
|
|
// HACKHACK: C++ doesn't let you initialize statics inside a class
|
|
// definition, and also doesn't let you specify an empty
|
|
// size (i.e., rg_data_handlers[]) inside a class definition
|
|
// either, so we have to have this bogus NUM_DATA_HANDLERS
|
|
// symbol that must manually be kept in sync.
|
|
|
|
enum { NUM_DATA_HANDLERS = 16 };
|
|
static _DATA_HANDLER rg_data_handlers[NUM_DATA_HANDLERS];
|
|
static void _Init_rg_data_handlers();
|
|
|
|
private:
|
|
friend HRESULT CFSDropTarget_CreateInstance(CFSFolder* pFolder, HWND hwnd, IDropTarget** ppdt);
|
|
};
|
|
|
|
CFSDropTarget::CFSDropTarget(CFSFolder *pFolder, HWND hwnd) : _cRef(1), _hwnd(hwnd), _pFolder(pFolder), _dwEffectFolder(-1)
|
|
{
|
|
ASSERT(0 == _grfKeyStateLast);
|
|
ASSERT(NULL == _pdtobj);
|
|
ASSERT(0 == _dwEffectLastReturned);
|
|
ASSERT(0 == _dwData);
|
|
ASSERT(0 == _dwEffectPreferred);
|
|
_pFolder->AddRef();
|
|
}
|
|
|
|
CFSDropTarget::~CFSDropTarget()
|
|
{
|
|
AssertMsg(_pdtobj == NULL, TEXT("didn't get matching DragLeave, fix that bug"));
|
|
|
|
ATOMICRELEASE(_pdtobj);
|
|
ATOMICRELEASE(_pfv);
|
|
|
|
_pFolder->Release();
|
|
}
|
|
|
|
STDAPI CFSDropTarget_CreateInstance(CFSFolder* pFolder, HWND hwnd, IDropTarget** ppdt)
|
|
{
|
|
*ppdt = new CFSDropTarget(pFolder, hwnd);
|
|
return *ppdt ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::QueryInterface(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_(ULONG) CFSDropTarget::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CFSDropTarget::Release()
|
|
{
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
void CFSDropTarget::_FreeThreadParams(DROPTHREADPARAMS *pdtp)
|
|
{
|
|
pdtp->pThis->Release();
|
|
ATOMICRELEASE(pdtp->pstmDataObj);
|
|
ATOMICRELEASE(pdtp->pstmFolderView);
|
|
LocalFree(pdtp);
|
|
}
|
|
|
|
|
|
// compute DTID_ bit flags from the data object to make format testing easier for
|
|
// DragOver() and Drop() code
|
|
|
|
STDAPI GetClipFormatFlags(IDataObject *pdtobj, DWORD *pdwData, DWORD *pdwEffectPreferred)
|
|
{
|
|
*pdwData = 0;
|
|
*pdwEffectPreferred = 0;
|
|
|
|
if (pdtobj)
|
|
{
|
|
IEnumFORMATETC *penum;
|
|
if (SUCCEEDED(pdtobj->EnumFormatEtc(DATADIR_GET, &penum)))
|
|
{
|
|
FORMATETC fmte;
|
|
ULONG celt;
|
|
while (S_OK == penum->Next(1, &fmte, &celt))
|
|
{
|
|
if (fmte.cfFormat == CF_HDROP && (fmte.tymed & TYMED_HGLOBAL))
|
|
*pdwData |= DTID_HDROP;
|
|
|
|
if (fmte.cfFormat == g_cfHIDA && (fmte.tymed & TYMED_HGLOBAL))
|
|
*pdwData |= DTID_HIDA;
|
|
|
|
if (fmte.cfFormat == g_cfNetResource && (fmte.tymed & TYMED_HGLOBAL))
|
|
*pdwData |= DTID_NETRES;
|
|
|
|
if (fmte.cfFormat == g_cfEmbeddedObject && (fmte.tymed & TYMED_ISTORAGE))
|
|
*pdwData |= DTID_EMBEDDEDOBJECT;
|
|
|
|
if (fmte.cfFormat == g_cfFileContents && (fmte.tymed & (TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)))
|
|
*pdwData |= DTID_CONTENTS;
|
|
|
|
if (fmte.cfFormat == g_cfFileGroupDescriptorA && (fmte.tymed & TYMED_HGLOBAL))
|
|
*pdwData |= DTID_FDESCA;
|
|
|
|
if (fmte.cfFormat == g_cfFileGroupDescriptorW && (fmte.tymed & TYMED_HGLOBAL))
|
|
*pdwData |= DTID_FDESCW;
|
|
|
|
if ((fmte.cfFormat == g_cfPreferredDropEffect) &&
|
|
(fmte.tymed & TYMED_HGLOBAL) &&
|
|
(DROPEFFECT_NONE != (*pdwEffectPreferred = DataObj_GetDWORD(pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE))))
|
|
{
|
|
*pdwData |= 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
|
|
SHFree(fmte.ptd);
|
|
}
|
|
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 ((*pdwData & (DTID_PREFERREDEFFECT | DTID_CONTENTS)) == DTID_CONTENTS)
|
|
{
|
|
if (*pdwData & DTID_FDESCA)
|
|
{
|
|
FORMATETC fmteRead = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
if (S_OK == pdtobj->GetData(&fmteRead, &medium))
|
|
{
|
|
FILEGROUPDESCRIPTORA * pfgd = (FILEGROUPDESCRIPTORA *)GlobalLock(medium.hGlobal);
|
|
if (pfgd)
|
|
{
|
|
if (pfgd->cItems >= 1)
|
|
{
|
|
if (pfgd->fgd[0].dwFlags & FD_LINKUI)
|
|
*pdwData |= DTID_FD_LINKUI;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
else if (*pdwData & DTID_FDESCW)
|
|
{
|
|
FORMATETC fmteRead = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
if (S_OK == pdtobj->GetData(&fmteRead, &medium))
|
|
{
|
|
FILEGROUPDESCRIPTORW * pfgd = (FILEGROUPDESCRIPTORW *)GlobalLock(medium.hGlobal);
|
|
if (pfgd)
|
|
{
|
|
if (pfgd->cItems >= 1)
|
|
{
|
|
if (pfgd->fgd[0].dwFlags & FD_LINKUI)
|
|
*pdwData |= DTID_FD_LINKUI;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (S_OK == OleQueryCreateFromData(pdtobj))
|
|
*pdwData |= DTID_OLEOBJ;
|
|
|
|
if (S_OK == OleQueryLinkFromData(pdtobj))
|
|
*pdwData |= DTID_OLELINK;
|
|
}
|
|
return S_OK; // for now always succeeds
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::DragEnter(IDataObject* pdtobj, 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;
|
|
IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
|
|
|
|
GetClipFormatFlags(_pdtobj, &_dwData, &_dwEffectPreferred);
|
|
|
|
*pdwEffect = _dwEffectLastReturned = _DetermineEffects(grfKeyState, pdwEffect, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
if (_grfKeyStateLast != grfKeyState)
|
|
{
|
|
_grfKeyStateLast = grfKeyState;
|
|
*pdwEffect = _dwEffectLastReturned = _DetermineEffects(grfKeyState, pdwEffect, NULL);
|
|
}
|
|
else
|
|
{
|
|
*pdwEffect = _dwEffectLastReturned;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::DragLeave()
|
|
{
|
|
IUnknown_Set((IUnknown **)&_pdtobj, NULL);
|
|
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_SFolderView, 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->QueryInterface(IID_PPV_ARG(IFolderView, &_pfv));
|
|
|
|
psfv->Release();
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CFSDropTarget::Drop(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
// OLE may give us a different data object (fully marshalled)
|
|
// from the one we've got on DragEnter (this is not the case on Win2k, this is a nop)
|
|
|
|
IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
|
|
|
|
_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 (DROPEFFECT_NONE == dwDefEffect)
|
|
{
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
DAD_SetDragImage(NULL, NULL);
|
|
IUnknown_Set((IUnknown **)&_pdtobj, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
_GetPath(szPath);
|
|
|
|
// this doesn't actually do the menu if (grfKeyState MK_LBUTTON)
|
|
|
|
FSDRAGDROPMENUPARAM ddm;
|
|
ddm.dwDefEffect = dwDefEffect;
|
|
ddm.pdtobj = pdtobj;
|
|
ddm.pt = pt;
|
|
ddm.pdwEffect = pdwEffect;
|
|
ddm.ck = SHGetAssocKeysForIDList(_GetIDList(), ddm.rghk, ARRAYSIZE(ddm.rghk));
|
|
ddm.hmenu = hmenu;
|
|
ddm.grfKeyState = grfKeyState;
|
|
|
|
HRESULT hr = _DragDropMenu(&ddm);
|
|
|
|
SHRegCloseKeys(ddm.rghk, ddm.ck);
|
|
|
|
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(pdtobj, &CLSID_ShellFSFolder);
|
|
|
|
switch (ddm.idCmd)
|
|
{
|
|
case DDIDM_CONTENTS_DESKCOMP:
|
|
hr = CreateDesktopComponents(NULL, pdtobj, _hwnd, 0, ddm.pt.x, ddm.pt.y);
|
|
break;
|
|
|
|
case DDIDM_CONTENTS_DESKURL:
|
|
hr = _CreateURLDeskComp(pdtobj, ddm.pt);
|
|
break;
|
|
|
|
case DDIDM_CONTENTS_DESKIMG:
|
|
hr = _CreateDeskCompImage(pdtobj, ddm.pt);
|
|
break;
|
|
|
|
case DDIDM_CONTENTS_COPY:
|
|
case DDIDM_CONTENTS_MOVE:
|
|
case DDIDM_CONTENTS_LINK:
|
|
hr = CFSFolder_AsyncCreateFileFromClip(_hwnd, szPath, pdtobj, pt, pdwEffect, _fBkDropTarget);
|
|
break;
|
|
|
|
case DDIDM_SCRAP_COPY:
|
|
case DDIDM_SCRAP_MOVE:
|
|
case DDIDM_DOCLINK:
|
|
hr = SHCreateBookMark(_hwnd, szPath, pdtobj, pt, pdwEffect);
|
|
break;
|
|
|
|
case DDIDM_OBJECT_COPY:
|
|
case DDIDM_OBJECT_MOVE:
|
|
hr = _CreatePackage(pdtobj);
|
|
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 SHCreateBookMark(), which should create a scrap:
|
|
hr = SHCreateBookMark(_hwnd, szPath, pdtobj, pt, pdwEffect);
|
|
}
|
|
break;
|
|
|
|
case DDIDM_COPY:
|
|
case DDIDM_SYNCCOPY:
|
|
case DDIDM_SYNCCOPYTYPE:
|
|
case DDIDM_MOVE:
|
|
case DDIDM_LINK:
|
|
|
|
_dwEffect = *pdwEffect;
|
|
_idCmd = ddm.idCmd;
|
|
|
|
if (DataObj_CanGoAsync(pdtobj) || DataObj_GoAsyncForCompat(pdtobj))
|
|
{
|
|
// create another thread to avoid blocking the source thread.
|
|
DROPTHREADPARAMS *pdtp;
|
|
hr = SHLocalAlloc(sizeof(*pdtp), &pdtp);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pdtp->pThis = this;
|
|
pdtp->pThis->AddRef();
|
|
|
|
CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdtobj, &pdtp->pstmDataObj);
|
|
CoMarshalInterThreadInterfaceInStream(IID_IFolderView, _pfv, &pdtp->pstmFolderView);
|
|
|
|
if (SHCreateThread(_DoDropThreadProc, pdtp, CTF_COINIT, NULL))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
_FreeThreadParams(pdtp);
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_DoDrop(pdtobj, _pfv); // synchronously
|
|
}
|
|
|
|
|
|
// 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(pdtobj, g_cfLogicalPerformedDropEffect, *pdwEffect);
|
|
if (DROPEFFECT_MOVE == *pdwEffect)
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
IUnknown_Set((IUnknown **)&_pdtobj, NULL); // don't use this any more
|
|
|
|
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 | MIIM_DATA;
|
|
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;
|
|
mii.dwItemData = DROPEFFECT_COPY;
|
|
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;
|
|
mii.dwItemData = DROPEFFECT_MOVE;
|
|
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;
|
|
mii.dwItemData = DROPEFFECT_LINK;
|
|
InsertMenuItem(pfsMenuInfo->hMenu, pfsMenuInfo->uLinkPos, TRUE, &mii);
|
|
pfsMenuInfo->uLinkPos++;
|
|
}
|
|
}
|
|
*pdwEffects |= dwEffectAvail;
|
|
}
|
|
|
|
// determine the default drop effect (move/copy/link) from the file type
|
|
//
|
|
// HKCR\.cda "DefaultDropEffect" = 4 // DROPEFFECT_LINK
|
|
|
|
DWORD EffectFromFileType(IDataObject *pdtobj)
|
|
{
|
|
DWORD dwDefEffect = DROPEFFECT_NONE; // 0
|
|
|
|
LPITEMIDLIST pidl;
|
|
if (SUCCEEDED(PidlFromDataObject(pdtobj, &pidl)))
|
|
{
|
|
IQueryAssociations *pqa;
|
|
if (SUCCEEDED(SHGetAssociations(pidl, (void **)&pqa)))
|
|
{
|
|
DWORD cb = sizeof(dwDefEffect);
|
|
pqa->GetData(0, ASSOCDATA_VALUE, L"DefaultDropEffect", &dwDefEffect, &cb);
|
|
pqa->Release();
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
|
|
return dwDefEffect;
|
|
}
|
|
|
|
// compute the default effect based on
|
|
// the allowed effects
|
|
// the keyboard state,
|
|
// the preferred effect that might be in the data object
|
|
// and previously computed default effect (if the above yields nothing)
|
|
|
|
DWORD CFSDropTarget::_GetDefaultEffect(DWORD grfKeyState, DWORD dwCurEffectAvail, DWORD dwAllEffectAvail, DWORD dwOrigDefEffect)
|
|
{
|
|
DWORD dwDefEffect = 0;
|
|
//
|
|
// keyboard, (explicit user input) gets first crack
|
|
//
|
|
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:
|
|
case MK_ALT:
|
|
dwDefEffect = DROPEFFECT_LINK;
|
|
break;
|
|
|
|
default: // no modifier keys case
|
|
// if the data object contains a preferred drop effect, try to use it
|
|
DWORD dwPreferred = DataObj_GetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE) & dwAllEffectAvail;
|
|
|
|
if (DROPEFFECT_NONE == dwPreferred)
|
|
{
|
|
dwPreferred = EffectFromFileType(_pdtobj) & 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::_FilterDeskCompHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (!PolicyNoActiveDesktop() &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder())
|
|
{
|
|
hr = IsDeskCompHDrop(_pdtobj);
|
|
if (S_OK == hr)
|
|
{
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = dwEffectsAvail & DROPEFFECT_LINK;
|
|
if (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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;
|
|
}
|
|
|
|
// see if a PIDL is scoped by a briefcaes
|
|
|
|
BOOL IsBriefcaseOrChild(LPCITEMIDLIST pidlIn)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPITEMIDLIST pidl = ILClone(pidlIn);
|
|
if (pidl)
|
|
{
|
|
do
|
|
{
|
|
CLSID clsid;
|
|
if (SUCCEEDED(GetCLSIDFromIDList(pidl, &clsid)) &&
|
|
IsEqualCLSID(clsid, CLSID_Briefcase))
|
|
{
|
|
bRet = TRUE; // it is a briefcase
|
|
break;
|
|
}
|
|
} while (ILRemoveLastID(pidl));
|
|
ILFree(pidl);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
// returns true if the data object represents items in a sneakernet briefcase
|
|
// (briefcase on removable media)
|
|
|
|
BOOL CFSDropTarget::_IsFromSneakernetBriefcase()
|
|
{
|
|
BOOL bRet = FALSE; // assume no
|
|
|
|
if (!_IsBriefcaseTarget())
|
|
{
|
|
STGMEDIUM medium = {0};
|
|
LPIDA pida = DataObj_GetHIDA(_pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
LPCITEMIDLIST pidlFolder = IDA_GetIDListPtr(pida, (UINT)-1);
|
|
TCHAR szSource[MAX_PATH];
|
|
if (SHGetPathFromIDList(pidlFolder, szSource))
|
|
{
|
|
// is source on removable device?
|
|
if (!PathIsUNC(szSource) && IsRemovableDrive(DRIVEID(szSource)))
|
|
{
|
|
TCHAR szTarget[MAX_PATH];
|
|
_GetPath(szTarget);
|
|
|
|
// is the target fixed media?
|
|
if (PathIsUNC(szTarget) || !IsRemovableDrive(DRIVEID(szTarget)))
|
|
{
|
|
bRet = IsBriefcaseOrChild(pidlFolder);
|
|
}
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
// TRUE if any folders are in hdrop
|
|
|
|
BOOL DroppingAnyFolders(HDROP hDrop)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
for (UINT i = 0; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); i++)
|
|
{
|
|
if (PathIsDirectory(szPath))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// sneakernet case:
|
|
// dragging a file/folder from a briefcase on removable media. we special case this
|
|
// and use this as a chance to connect up this target folder with the content of the briefcase
|
|
|
|
HRESULT CFSDropTarget::_FilterSneakernetBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
|
|
if (_IsFromSneakernetBriefcase())
|
|
{
|
|
// Yes; show the non-default briefcase cm
|
|
STGMEDIUM medium = {0};
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
|
|
{
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = DROPEFFECT_COPY & dwEffectsAvail;
|
|
if (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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 ((DROPEFFECT_COPY & dwEffectsAvail) &&
|
|
DroppingAnyFolders((HDROP)medium.hGlobal))
|
|
{
|
|
_AddVerbs(pdwEffects, DROPEFFECT_COPY, 0,
|
|
DDIDM_SYNCCOPYTYPE, 0, 0, DROPEFFECT_COPY, pfsMenuInfo);
|
|
}
|
|
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// returns true if the data object represents items from the same briefcase
|
|
// as this drop target
|
|
BOOL CFSDropTarget::_IsFromSameBriefcase()
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
STGMEDIUM medium;
|
|
FORMATETC fmteBrief = {g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
|
|
// Yes; are they from the same briefcase as the target?
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmteBrief, &medium)))
|
|
{
|
|
BriefObj *pbo = (BriefObj *)GlobalLock(medium.hGlobal);
|
|
|
|
TCHAR szBriefPath[MAX_PATH], szPath[MAX_PATH];
|
|
lstrcpy(szBriefPath, BOBriefcasePath(pbo));
|
|
lstrcpy(szPath, BOFileList(pbo)); // first file in list
|
|
|
|
TCHAR szPathTgt[MAX_PATH];
|
|
_GetPath(szPathTgt);
|
|
|
|
int cch = PathCommonPrefix(szPath, szPathTgt, NULL);
|
|
bRet = (0 < cch) && (lstrlen(szBriefPath) <= cch);
|
|
|
|
GlobalUnlock(medium.hGlobal);
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
// briefcase drop target specific handling gets computed here
|
|
|
|
HRESULT CFSDropTarget::_FilterBriefcase(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
if (_IsBriefcaseTarget() && !_IsFromSameBriefcase())
|
|
{
|
|
STGMEDIUM medium = {0};
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
|
|
{
|
|
DWORD dwDefEffect = DROPEFFECT_COPY;
|
|
DWORD dwEffectAdd = DROPEFFECT_COPY & dwEffectsAvail;
|
|
if (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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 ((DROPEFFECT_COPY & dwEffectsAvail) &&
|
|
DroppingAnyFolders((HDROP)medium.hGlobal))
|
|
{
|
|
_AddVerbs(pdwEffects, DROPEFFECT_COPY, 0,
|
|
DDIDM_SYNCCOPYTYPE, 0, 0, DROPEFFECT_COPY, pfsMenuInfo);
|
|
}
|
|
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CFSDropTarget::_FilterHDROP(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
|
|
DWORD dwDefEffect = 0;
|
|
DWORD dwEffectAdd = dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE);
|
|
if (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, _FilesystemAdjustedDefaultEffect(dwEffectAdd));
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, DDIDM_COPY, DDIDM_MOVE, 0, 0, pfsMenuInfo);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_FilterFileContents(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 (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, dwSuggestedEffect);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect,
|
|
DDIDM_CONTENTS_COPY, DDIDM_CONTENTS_MOVE, DDIDM_CONTENTS_LINK,
|
|
0, pfsMenuInfo);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Old versions of OLE have a bug where if two FORMATETCs use the same
|
|
// CLIPFORMAT, then only the first one makes it to the IEnumFORMATETC,
|
|
// even if the other parameters (such as DVASPECT) are different.
|
|
//
|
|
// This causes us problems because those other DVASPECTs might be useful.
|
|
// So if we see a FileContents with the wrong DVASPECT, sniff at the
|
|
// object to see if maybe it also contains a copy with the correct DVASPECT.
|
|
//
|
|
// This bug was fixed in 1996 on the NT side, but the Win9x side was
|
|
// not fixed. The Win9x OLE team was disbanded before the fix could
|
|
// be propagated. So we get to work around this OLE bug forever.
|
|
//
|
|
HRESULT CFSDropTarget::_FilterFileContentsOLEHack(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
FORMATETC fmte = *pfmte;
|
|
fmte.dwAspect = DVASPECT_CONTENT;
|
|
|
|
//
|
|
// Whoa, this test is so (intentionally) backwards it isn't funny.
|
|
//
|
|
// We want to see whether there is a DVASPECT_CONTENT available in
|
|
// the real object. So we first ask the object if it has a
|
|
// DVASPECT_CONTENT format already. If the answer is yes, then we
|
|
// **skip** this FORMATETC, because it will be found (or has already
|
|
// been found) by our big EnumFORMATETC loop.
|
|
//
|
|
// If the answer is NO, then maybe we're hitting an OLE bug.
|
|
// (They cache the list of available formats, but the bug is that
|
|
// their cache is broken.) Bypass the cache by actually getting the
|
|
// data. If it works, then run with it. Otherwise, I guess OLE wasn't
|
|
// kidding.
|
|
//
|
|
// Note that we do not GetData() unconditionally -- bad for perf.
|
|
// Only call GetData() after all the easy tests have failed.
|
|
//
|
|
|
|
HRESULT hr = _pdtobj->QueryGetData(&fmte);
|
|
if (hr == DV_E_FORMATETC)
|
|
{
|
|
// Maybe we are hitting the OLE bug. Try harder.
|
|
STGMEDIUM stgm = {0};
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &stgm)))
|
|
{
|
|
// Yup. OLE lied to us.
|
|
ReleaseStgMedium(&stgm);
|
|
|
|
hr = _FilterFileContents(&fmte, grfKeyFlags, dwEffectsAvail,
|
|
pdwEffects, pdwDefaultEffect, pfsMenuInfo);
|
|
}
|
|
else
|
|
{
|
|
// Whaddya know, OLE was telling the truth. Do nothing with this
|
|
// format.
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Either QueryGetData() failed in some bizarre way
|
|
// (in which case we ignore the problem) or the QueryGetData
|
|
// succeeded, in which case we ignore this FORMATETC since
|
|
// the big enumeration will find (or has already found) the
|
|
// DVASPECT_CONTENT.
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_FilterHIDA(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
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 CIDLDataObj. When we do change
|
|
// the enumerator order, we can remove this special case:
|
|
if (pdwDefaultEffect &&
|
|
((0 == (_dwData & DTID_HDROP)) ||
|
|
(0 == _GetDefaultEffect(grfKeyFlags,
|
|
dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE),
|
|
dwEffectsAvail,
|
|
_FilesystemAdjustedDefaultEffect(dwEffectsAvail & (DROPEFFECT_COPY | DROPEFFECT_MOVE))))))
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(grfKeyFlags, dwEffectAdd, dwEffectsAvail, DROPEFFECT_LINK);
|
|
*pdwDefaultEffect = dwDefEffect;
|
|
}
|
|
|
|
_AddVerbs(pdwEffects, dwEffectAdd, dwDefEffect, 0, 0, DDIDM_LINK, 0, pfsMenuInfo);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// {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};
|
|
|
|
HRESULT CFSDropTarget::_FilterOlePackage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (pdwDefaultEffect)
|
|
{
|
|
*pdwDefaultEffect = 0;
|
|
}
|
|
|
|
FORMATETC fmte = {g_cfObjectDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
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 (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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;
|
|
}
|
|
|
|
// REARCHITECT:
|
|
// This code has lots of problems. We need to fix this the text time we touch this code
|
|
// outside of ship mode. TO FIX:
|
|
// 1. Use SHAnsiToUnicode(CP_UTF8, ) to convert pszHTML to unicode. This will allow international
|
|
// paths to work.
|
|
// 2. Obey the selected range.
|
|
// 3. Use MSHTML to get the image. You can have trident parse the HTML via IHTMLTxtRange::pasteHTML.
|
|
// MS HTML has a special collection of images. Ask for the first image in that collection, or
|
|
// the first image in that collection within the selected range. (#1 isn't needed with this)
|
|
BOOL ExtractImageURLFromCFHTML(IN LPSTR pszHTML, IN SIZE_T cbHTMLSize, OUT LPSTR szImg, IN DWORD dwSize)
|
|
{
|
|
BOOL fSucceeded = FALSE;
|
|
|
|
// To avoid going nuts, only look at the first 64K of the HTML.
|
|
// (Important on Win64 because StrCpyNA doesn't support more than 4GB.)
|
|
if (cbHTMLSize > 0xFFFF)
|
|
cbHTMLSize = 0xFFFF;
|
|
|
|
// NT #391669: pszHTML isn't terminated, so terminate it now.
|
|
LPSTR pszCopiedHTML = (LPSTR) LocalAlloc(LPTR, cbHTMLSize + 1);
|
|
if (pszCopiedHTML)
|
|
{
|
|
LPSTR szTemp;
|
|
DWORD dwLen = dwSize;
|
|
BOOL bRet = TRUE;
|
|
|
|
StrCpyNA(pszCopiedHTML, pszHTML, (int)(cbHTMLSize + 1));
|
|
|
|
//DANGER WILL ROBINSON:
|
|
// HTML is comming in as UFT-8 encoded. Neither Unicode or Ansi,
|
|
// We've got to do something.... I'm going to party on it as if it were
|
|
// Ansi. This code will choke on escape sequences.....
|
|
|
|
//Find the base URL
|
|
//Locate <!--StartFragment-->
|
|
//Read the <IMG SRC="
|
|
//From there to the "> should be the Image URL
|
|
//Determine if it's an absolute or relative URL
|
|
//If relative, append to BASE url. You may need to lop off from the
|
|
// last delimiter to the end of the string.
|
|
|
|
//Pull out the SourceURL
|
|
|
|
LPSTR szBase = StrStrIA(pszCopiedHTML,"SourceURL:"); // Point to the char after :
|
|
if (szBase)
|
|
{
|
|
szBase += sizeof("SourceURL:")-1;
|
|
|
|
//Since each line can be terminated by a CR, CR/LF or LF check each case...
|
|
szTemp = StrChrA(szBase,'\n');
|
|
if (!szTemp)
|
|
szTemp = StrChrA(szBase,'\r');
|
|
|
|
if (szTemp)
|
|
*szTemp = '\0';
|
|
szTemp++;
|
|
}
|
|
else
|
|
szTemp = pszCopiedHTML;
|
|
|
|
|
|
//Pull out the Img Src
|
|
LPSTR pszImgSrc = StrStrIA(szTemp,"IMG");
|
|
if (pszImgSrc != NULL)
|
|
{
|
|
pszImgSrc = StrStrIA(pszImgSrc,"SRC");
|
|
if (pszImgSrc != NULL)
|
|
{
|
|
LPSTR pszImgSrcOrig = pszImgSrc;
|
|
pszImgSrc = StrChrA(pszImgSrc,'\"');
|
|
if (pszImgSrc)
|
|
{
|
|
pszImgSrc++; // Skip over the quote at the beginning of the src path.
|
|
szTemp = StrChrA(pszImgSrc,'\"'); // Find the end of the path.
|
|
}
|
|
else
|
|
{
|
|
LPSTR pszTemp1;
|
|
LPSTR pszTemp2;
|
|
|
|
pszImgSrc = StrChrA(pszImgSrcOrig,'=');
|
|
pszImgSrc++; // Skip past the equals to the first char in the path.
|
|
// Someday we may need to handle spaces between '=' and the path.
|
|
|
|
pszTemp1 = StrChrA(pszImgSrc,' '); // Since the path doesn't have quotes around it, assume a space will terminate it.
|
|
pszTemp2 = StrChrA(pszImgSrc,'>'); // Since the path doesn't have quotes around it, assume a space will terminate it.
|
|
|
|
szTemp = pszTemp1; // Assume quote terminates path.
|
|
if (!pszTemp1)
|
|
szTemp = pszTemp2; // Use '>' if quote not found.
|
|
|
|
if (pszTemp1 && pszTemp2 && (pszTemp2 < pszTemp1))
|
|
szTemp = pszTemp2; // Change to having '>' terminate path if both exist and it comes first.
|
|
}
|
|
|
|
*szTemp = '\0'; // Terminate path.
|
|
|
|
//At this point, I've reduced the 2 important strings. Now see if I need to
|
|
//Join them.
|
|
|
|
//If this fails, then we don't have a full URL, Only a relative.
|
|
if (!UrlIsA(pszImgSrc,URLIS_URL) && szBase)
|
|
{
|
|
if (SUCCEEDED(UrlCombineA(szBase, pszImgSrc, szImg, &dwLen,0)))
|
|
fSucceeded = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (lstrlenA(pszImgSrc) <= (int)dwSize)
|
|
lstrcpyA(szImg, pszImgSrc);
|
|
|
|
fSucceeded = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(pszCopiedHTML);
|
|
}
|
|
|
|
return fSucceeded;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_FilterDeskImage(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (pdwDefaultEffect)
|
|
{
|
|
*pdwDefaultEffect = 0;
|
|
}
|
|
|
|
if (!PolicyNoActiveDesktop() &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder())
|
|
{
|
|
FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
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 (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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::_FilterDeskComp(FORMATETC* pfmte, DWORD grfKeyFlags, DWORD dwEffectsAvail,
|
|
DWORD* pdwEffects, DWORD* pdwDefaultEffect, FSMENUINFO* pfsMenuInfo)
|
|
{
|
|
ASSERT(pdwEffects);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (pdwDefaultEffect)
|
|
{
|
|
*pdwDefaultEffect = 0;
|
|
}
|
|
|
|
if (!PolicyNoActiveDesktop() &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder())
|
|
{
|
|
FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
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 (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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::_FilterOleObj(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 (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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::_FilterOleLink(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 (pdwDefaultEffect)
|
|
{
|
|
dwDefEffect = _GetDefaultEffect(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(IDataObject *pdtobj, POINTL pt)
|
|
{
|
|
// This code should only be entered if DDIDM_CONTENTS_DESKURL was added to the menu,
|
|
// and it has these checks:
|
|
ASSERT(!PolicyNoActiveDesktop() &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder());
|
|
|
|
STGMEDIUM medium = {0};
|
|
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, _hwnd, DESKCOMP_URL, pt.x, pt.y);
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_CreateDeskCompImage(IDataObject *pdtobj, POINTL pt)
|
|
{
|
|
ASSERT(!PolicyNoActiveDesktop() &&
|
|
!SHRestricted(REST_NOADDDESKCOMP) &&
|
|
_IsDesktopFolder());
|
|
|
|
FORMATETC fmte = {g_cfHTML, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
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)
|
|
{
|
|
CHAR szUrl[MAX_URL_STRING];
|
|
if (ExtractImageURLFromCFHTML(pszData, GlobalSize(medium.hGlobal), szUrl, ARRAYSIZE(szUrl)))
|
|
{
|
|
// The HTML contains an image tag - carry on...
|
|
ADDTODESKTOP *pToAD;
|
|
hr = SHLocalAlloc(sizeof(*pToAD), &pToAD);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pToAD->hwnd = _hwnd;
|
|
lstrcpyA(pToAD->szUrl, szUrl);
|
|
pToAD->dwFlags = DESKCOMP_IMAGE;
|
|
pToAD->pt = pt;
|
|
|
|
if (SHCreateThread(AddToActiveDesktopThreadProc, pToAD, CTF_COINIT, NULL))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
LocalFree(pToAD);
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// read byte by byte until we hit the null terminating char
|
|
// return: the number of bytes read
|
|
//
|
|
HRESULT StringReadFromStream(IStream* pstm, LPSTR pszBuf, UINT cchBuf)
|
|
{
|
|
UINT cch = 0;
|
|
|
|
do {
|
|
pstm->Read(pszBuf, sizeof(CHAR), NULL);
|
|
cch++;
|
|
} while (*pszBuf++ && cch <= cchBuf);
|
|
return cch;
|
|
}
|
|
|
|
HRESULT CopyStreamToFile(IStream* pstmSrc, LPCTSTR pszFile, ULONGLONG ullFileSize)
|
|
{
|
|
IStream *pstmFile;
|
|
HRESULT hr = SHCreateStreamOnFile(pszFile, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, &pstmFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CopyStreamUI(pstmSrc, pstmFile, NULL, ullFileSize);
|
|
pstmFile->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_CreatePackage(IDataObject *pdtobj)
|
|
{
|
|
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};
|
|
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, dw);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szDest, NULL);
|
|
if (_fBkDropTarget && _hwnd)
|
|
{
|
|
PositionFileFromDrop(_hwnd, szDest, NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
pstm->Release();
|
|
}
|
|
}
|
|
medium.pstg->Release();
|
|
}
|
|
pLockBytes->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_GetPath(LPTSTR pszPath)
|
|
{
|
|
return _pFolder->_GetPath(pszPath);
|
|
}
|
|
|
|
LPCITEMIDLIST CFSDropTarget::_GetIDList()
|
|
{
|
|
return _pFolder->_GetIDList();
|
|
}
|
|
|
|
DWORD CFSDropTarget::_EffectFromFolder()
|
|
{
|
|
if (-1 == _dwEffectFolder)
|
|
{
|
|
_dwEffectFolder = DROPEFFECT_NONE; // re-set to nothing (0)
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
// add a simple pathisroot check here to prevent it from hitting the disk (mostly floppy)
|
|
// when we want the dropeffect probe to be fast (sendto, hovering over drives in view).
|
|
// its not likely that we'll want to modify the root's drop effect, and this still allows
|
|
// dropeffect modification on floppy subfolders.
|
|
if (SUCCEEDED(_GetPath(szPath)) && !PathIsRoot(szPath) && PathAppend(szPath, TEXT("desktop.ini")))
|
|
{
|
|
_dwEffectFolder = GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("DefaultDropEffect"), 0, szPath);
|
|
}
|
|
}
|
|
return _dwEffectFolder;
|
|
}
|
|
|
|
BOOL AllRegisteredPrograms(HDROP hDrop)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
for (UINT i = 0; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); i++)
|
|
{
|
|
if (!PathIsRegisteredProgram(szPath))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL IsBriefcaseRoot(IDataObject *pdtobj)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
// Is there a briefcase root in this pdtobj?
|
|
IShellFolder2 *psf;
|
|
if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, IDA_GetIDListPtr(pida, (UINT)-1), &psf))))
|
|
{
|
|
for (UINT i = 0; i < pida->cidl; i++)
|
|
{
|
|
CLSID clsid;
|
|
bRet = SUCCEEDED(GetItemCLSID(psf, IDA_GetIDListPtr(pida, i), &clsid)) &&
|
|
IsEqualCLSID(clsid, CLSID_Briefcase);
|
|
if (bRet)
|
|
break;
|
|
}
|
|
psf->Release();
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// the "default effect" defines what will be choosen out of the allowed effects
|
|
//
|
|
// If the data object does NOT contain HDROP -> "none"
|
|
// else if the source data object has a default drop effect folder list (maybe based on sub folderness)
|
|
// 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::_FilesystemAdjustedDefaultEffect(DWORD dwCurEffectAvail)
|
|
{
|
|
DWORD dwDefEffect = DROPEFFECT_NONE;
|
|
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, &medium)))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
DragQueryFile((HDROP) medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)); // focused item
|
|
|
|
// DROPEFFECTFOLDERLIST allows the source of the data
|
|
// to specify the desired drop effect for items under
|
|
// certain parts of the name space.
|
|
//
|
|
// cd-burning does this to avoid the default move/copy computation
|
|
// that would kick in for cross volume CD burning/staging area transfers
|
|
|
|
FORMATETC fmteDropFolders = {g_cfDropEffectFolderList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM mediumDropFolders = {0};
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmteDropFolders, &mediumDropFolders)))
|
|
{
|
|
DROPEFFECTFOLDERLIST *pdefl = (DROPEFFECTFOLDERLIST*)GlobalLock(mediumDropFolders.hGlobal);
|
|
if (pdefl)
|
|
{
|
|
// get the default effect from the list -- in the staging area case this is DROPEFFECT_COPY
|
|
// so its a copy even if the staging area and source are on the same volume.
|
|
dwDefEffect = pdefl->dwDefaultDropEffect;
|
|
for (INT i = 0; i < pdefl->cFolders; i++)
|
|
{
|
|
// some folders are excluded, for example if you move a file from one part of the staging
|
|
// area to another we override (to DROPEFFECT_MOVE in this case).
|
|
if (PathIsEqualOrSubFolder(pdefl->aFolders[i].wszPath, szPath))
|
|
{
|
|
dwDefEffect = pdefl->aFolders[i].dwDropEffect;
|
|
break;
|
|
}
|
|
}
|
|
GlobalUnlock(pdefl);
|
|
}
|
|
ReleaseStgMedium(&mediumDropFolders);
|
|
}
|
|
|
|
if (DROPEFFECT_NONE == dwDefEffect)
|
|
{
|
|
dwDefEffect = _EffectFromFolder();
|
|
}
|
|
|
|
// If we didn't get a drop effect (==0) then lets fall back to the old checks
|
|
if (DROPEFFECT_NONE == dwDefEffect)
|
|
{
|
|
TCHAR szFolder[MAX_PATH];
|
|
_GetPath(szFolder);
|
|
|
|
// drive/UNC roots and installed programs get link
|
|
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 if (SUCCEEDED(_pdtobj->QueryGetData(&fmte)))
|
|
{
|
|
// but QueryGetData() succeeds!
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
// Handy abbreviation
|
|
#define TYMED_ALLCONTENT (TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)
|
|
|
|
// Use FSDH for registered clipboard formats (anything of the form g_cf*)
|
|
// Use _FSDH for predefined clipboard formats (like CF_HDROP or 0)
|
|
// Generate the _DATA_HANDLER array
|
|
#define FSDH(pfn, cf, dva, tymed) { { 0, NULL, dva, -1, tymed }, pfn, &cf }
|
|
#define _FSDH(pfn, cf, dva, tymed) { { (CLIPFORMAT)cf, NULL, dva, -1, tymed }, pfn, NULL }
|
|
|
|
// NOTE: the order is important (particularly for multiple entries with the same FORMATETC)
|
|
|
|
CFSDropTarget::_DATA_HANDLER
|
|
CFSDropTarget::rg_data_handlers[NUM_DATA_HANDLERS] = {
|
|
FSDH(_FilterFileContents, g_cfFileGroupDescriptorW, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
FSDH(_FilterFileContentsOLEHack, g_cfFileGroupDescriptorW, DVASPECT_LINK, TYMED_HGLOBAL),
|
|
FSDH(_FilterFileContents, g_cfFileGroupDescriptorA, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
FSDH(_FilterFileContentsOLEHack, g_cfFileGroupDescriptorA, DVASPECT_LINK, TYMED_HGLOBAL),
|
|
FSDH(_FilterFileContents, g_cfFileContents, DVASPECT_CONTENT, TYMED_ALLCONTENT),
|
|
FSDH(_FilterFileContentsOLEHack, g_cfFileContents, DVASPECT_LINK, TYMED_ALLCONTENT),
|
|
_FSDH(_FilterBriefcase, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
_FSDH(_FilterSneakernetBriefcase, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
_FSDH(_FilterHDROP, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
_FSDH(_FilterDeskCompHDROP, CF_HDROP, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
FSDH(_FilterHIDA, g_cfHIDA, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
FSDH(_FilterOlePackage, g_cfEmbeddedObject, DVASPECT_CONTENT, TYMED_ISTORAGE),
|
|
FSDH(_FilterDeskImage, g_cfHTML, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
FSDH(_FilterDeskComp, g_cfShellURL, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
_FSDH(_FilterOleObj, 0, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
_FSDH(_FilterOleLink, 0, DVASPECT_CONTENT, TYMED_HGLOBAL),
|
|
};
|
|
|
|
// Note that it's safe to race with another thread in this code
|
|
// since the function is idemponent. (Call it as many times as you
|
|
// like -- only the first time through actually does anything.)
|
|
|
|
void CFSDropTarget::_Init_rg_data_handlers()
|
|
{
|
|
for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++)
|
|
{
|
|
// If this assertion fires, then you have to change the value of
|
|
// NUM_DATA_HANDLERS to match the number of entries in the array
|
|
// definition.
|
|
ASSERT(rg_data_handlers[i].fmte.tymed);
|
|
|
|
if (rg_data_handlers[i].pcfInit)
|
|
{
|
|
rg_data_handlers[i].fmte.cfFormat = *rg_data_handlers[i].pcfInit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// returns the default effect.
|
|
// also modifies *pdwEffectInOut to indicate "available" operations.
|
|
//
|
|
DWORD CFSDropTarget::_DetermineEffects(DWORD grfKeyState, DWORD *pdwEffectInOut, HMENU hmenu)
|
|
{
|
|
DWORD dwDefaultEffect = DROPEFFECT_NONE;
|
|
DWORD dwEffectsUsed = DROPEFFECT_NONE;
|
|
|
|
_Init_rg_data_handlers();
|
|
|
|
// 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 &&
|
|
(rg_data_handlers[i].fmte.tymed & fmte.tymed))
|
|
{
|
|
// keep passing dwDefaultEffect until someone computes one, this
|
|
// lets the first guy that figures out the default be the default
|
|
(this->*(rg_data_handlers[i].pfnGetDragDropInfo))(
|
|
&fmte, grfKeyState, *pdwEffectInOut, &dwEffectsUsed,
|
|
(DROPEFFECT_NONE == dwDefaultEffect) ? &dwDefaultEffect : NULL,
|
|
hmenu ? &fsmi : NULL);
|
|
}
|
|
}
|
|
SHFree(fmte.ptd);
|
|
}
|
|
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)
|
|
{
|
|
// if default effect is still not computed continue to pass that
|
|
(this->*(rg_data_handlers[i].pfnGetDragDropInfo))(
|
|
NULL, grfKeyState, *pdwEffectInOut, &dwEffectsUsed,
|
|
(DROPEFFECT_NONE == dwDefaultEffect) ? &dwDefaultEffect : NULL,
|
|
hmenu ? &fsmi : NULL);
|
|
}
|
|
}
|
|
|
|
*pdwEffectInOut &= dwEffectsUsed;
|
|
|
|
dwDefaultEffect = _LimitDefaultEffect(dwDefaultEffect, *pdwEffectInOut);
|
|
|
|
DebugMsg(TF_FSTREE, TEXT("CFSDT::GetDefaultEffect dwDef=%x, dwEffUsed=%x, *pdw=%x"),
|
|
dwDefaultEffect, dwEffectsUsed, *pdwEffectInOut);
|
|
|
|
return dwDefaultEffect; // this is what we want to do
|
|
}
|
|
|
|
// This is used to map command id's back to dropeffect's:
|
|
|
|
const struct {
|
|
UINT uID;
|
|
DWORD dwEffect;
|
|
} 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,
|
|
};
|
|
|
|
void CFSDropTarget::_FixUpDefaultItem(HMENU hmenu, DWORD dwDefEffect)
|
|
{
|
|
// only do stuff if there is no default item already and we have a default effect
|
|
if ((GetMenuDefaultItem(hmenu, MF_BYPOSITION, 0) == -1) && dwDefEffect)
|
|
{
|
|
for (int i = 0; i < GetMenuItemCount(hmenu); i++)
|
|
{
|
|
// for menu item matching default effect, make it the default.
|
|
MENUITEMINFO mii = { 0 };
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_DATA | MIIM_STATE;
|
|
if (GetMenuItemInfo(hmenu, i, MF_BYPOSITION, &mii) && (mii.dwItemData == dwDefEffect))
|
|
{
|
|
mii.fState |= MFS_DEFAULT;
|
|
SetMenuItemInfo(hmenu, i, MF_BYPOSITION, &mii);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT CFSDropTarget::_DragDropMenu(FSDRAGDROPMENUPARAM *pddm)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY; // assume error
|
|
DWORD dwEffectOut = 0; // assume no-ope.
|
|
if (pddm->hmenu)
|
|
{
|
|
UINT idCmd;
|
|
UINT idCmdFirst = DDIDM_EXTFIRST;
|
|
HDXA hdxa = HDXA_Create();
|
|
HDCA hdca = DCA_Create();
|
|
if (hdxa && hdca)
|
|
{
|
|
// Enumerate the DD handlers and let them append menu items.
|
|
for (DWORD i = 0; i < pddm->ck; i++)
|
|
{
|
|
DCA_AddItemsFromKey(hdca, pddm->rghk[i], STRREG_SHEX_DDHANDLER);
|
|
}
|
|
|
|
idCmdFirst = HDXA_AppendMenuItems(hdxa, pddm->pdtobj, pddm->ck,
|
|
pddm->rghk, _GetIDList(), pddm->hmenu, 0,
|
|
DDIDM_EXTFIRST, DDIDM_EXTLAST, 0, hdca);
|
|
}
|
|
|
|
// modifier keys held down to force operations that are not permitted (for example
|
|
// alt to force a shortcut from the start menu, which does not have SFGAO_CANLINK)
|
|
// can result in no default items on the context menu. however in displaying the
|
|
// cursor overlay in this case we fall back to DROPEFFECT_COPY. a left drag then
|
|
// tries to invoke the default menu item (user thinks its copy) but theres no default.
|
|
|
|
// this function selects a default menu item to match the default effect if there
|
|
// is no default item already.
|
|
_FixUpDefaultItem(pddm->hmenu, pddm->dwDefEffect);
|
|
|
|
// 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 (_hwnd)
|
|
SetForegroundWindow(_hwnd);
|
|
}
|
|
else
|
|
{
|
|
// Note that SHTrackPopupMenu calls DAD_DragLeave().
|
|
idCmd = SHTrackPopupMenu(pddm->hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
|
|
pddm->pt.x, pddm->pt.y, 0, _hwnd, 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)
|
|
{
|
|
hr = 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,
|
|
_hwnd,
|
|
(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);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
for (int nItem = 0; nItem < ARRAYSIZE(c_IDFSEffects); ++nItem)
|
|
{
|
|
if (idCmd == c_IDFSEffects[nItem].uID)
|
|
{
|
|
dwEffectOut = c_IDFSEffects[nItem].dwEffect;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
if (hdca)
|
|
DCA_Destroy(hdca);
|
|
|
|
if (hdxa)
|
|
HDXA_Destroy(hdxa);
|
|
|
|
pddm->idCmd = idCmd;
|
|
}
|
|
|
|
*pddm->pdwEffect = dwEffectOut;
|
|
|
|
return hr;
|
|
}
|
|
|
|
void _MapName(void *hNameMap, LPTSTR pszPath)
|
|
{
|
|
if (hNameMap)
|
|
{
|
|
SHNAMEMAPPING *pNameMapping;
|
|
for (int i = 0; (pNameMapping = SHGetNameMappingPtr((HDSA)hNameMap, i)) != NULL; i++)
|
|
{
|
|
if (lstrcmpi(pszPath, pNameMapping->pszOldPath) == 0)
|
|
{
|
|
lstrcpy(pszPath, pNameMapping->pszNewPath);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// convert double null list of files to array of pidls
|
|
|
|
int FileListToIDArray(LPCTSTR pszFiles, void *hNameMap, LPITEMIDLIST **pppidl)
|
|
{
|
|
int i = 0;
|
|
int nItems = CountFiles(pszFiles);
|
|
LPITEMIDLIST *ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, nItems * sizeof(*ppidl));
|
|
if (ppidl)
|
|
{
|
|
*pppidl = ppidl;
|
|
|
|
while (*pszFiles)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
lstrcpy(szPath, pszFiles);
|
|
|
|
_MapName(hNameMap, szPath);
|
|
|
|
ppidl[i] = SHSimpleIDListFromPath(szPath);
|
|
|
|
pszFiles += lstrlen(pszFiles) + 1;
|
|
i++;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
// move items to the new drop location
|
|
|
|
void CFSDropTarget::_MoveSelectIcons(IDataObject *pdtobj, IFolderView* pfv, void *hNameMap, LPCTSTR pszFiles, BOOL fMove, HDROP hDrop)
|
|
{
|
|
LPITEMIDLIST *ppidl = NULL;
|
|
int cidl;
|
|
|
|
if (pszFiles)
|
|
{
|
|
cidl = FileListToIDArray(pszFiles, hNameMap, &ppidl);
|
|
}
|
|
else
|
|
{
|
|
cidl = CreateMoveCopyList(hDrop, hNameMap, &ppidl);
|
|
}
|
|
|
|
if (ppidl)
|
|
{
|
|
if (pfv)
|
|
PositionItems(pfv, (LPCITEMIDLIST*)ppidl, cidl, pdtobj, fMove ? &_ptDrop : NULL);
|
|
|
|
FreeIDListArray(ppidl, cidl);
|
|
}
|
|
}
|
|
|
|
// this is the ILIsParent which matches up the desktop with the desktop directory.
|
|
BOOL AliasILIsParent(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
LPITEMIDLIST pidlUse1 = SHLogILFromFSIL(pidl1);
|
|
if (pidlUse1)
|
|
pidl1 = pidlUse1;
|
|
|
|
LPITEMIDLIST pidlUse2 = SHLogILFromFSIL(pidl2);
|
|
if (pidlUse2)
|
|
pidl2 = pidlUse2;
|
|
|
|
BOOL fSame = ILIsParent(pidl1, pidl2, TRUE);
|
|
|
|
ILFree(pidlUse1); // NULL is OK here
|
|
ILFree(pidlUse2);
|
|
|
|
return fSame;
|
|
}
|
|
|
|
// in:
|
|
// pszDestDir destination dir for new file names
|
|
// pszDestSpecs double null list of destination specs
|
|
//
|
|
// returns:
|
|
// double null list of fully qualified destination file names to be freed
|
|
// with LocalFree()
|
|
//
|
|
|
|
LPTSTR RemapDestNamesW(LPCTSTR pszDestDir, LPCWSTR pszDestSpecs)
|
|
{
|
|
UINT cbDestSpec = lstrlen(pszDestDir) * sizeof(TCHAR) + sizeof(TCHAR);
|
|
LPCWSTR pszTemp;
|
|
UINT cbAlloc = sizeof(TCHAR); // for double NULL teriminaion of entire string
|
|
|
|
// compute length of buffer to aloc
|
|
for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenW(pszTemp) + 1)
|
|
{
|
|
// +1 for null teriminator
|
|
cbAlloc += cbDestSpec + lstrlenW(pszTemp) * sizeof(TCHAR) + sizeof(TCHAR);
|
|
}
|
|
|
|
LPTSTR pszRet = (LPTSTR)LocalAlloc(LPTR, cbAlloc);
|
|
if (pszRet)
|
|
{
|
|
LPTSTR pszDest = pszRet;
|
|
|
|
for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenW(pszTemp) + 1)
|
|
{
|
|
// PathCombine requires dest buffer of MAX_PATH size or it'll rip in call
|
|
// to PathCanonicalize (IsBadWritePtr)
|
|
TCHAR szTempDest[MAX_PATH];
|
|
PathCombine(szTempDest, pszDestDir, pszTemp);
|
|
lstrcpy(pszDest, szTempDest);
|
|
pszDest += lstrlen(pszDest) + 1;
|
|
|
|
ASSERT((UINT)((BYTE *)pszDest - (BYTE *)pszRet) < cbAlloc);
|
|
ASSERT(*pszDest == 0); // zero init alloc
|
|
}
|
|
ASSERT((LPTSTR)((BYTE *)pszRet + cbAlloc - sizeof(TCHAR)) >= pszDest);
|
|
ASSERT(*pszDest == 0); // zero init alloc
|
|
|
|
}
|
|
return pszRet;
|
|
}
|
|
|
|
LPTSTR RemapDestNamesA(LPCTSTR pszDestDir, LPCSTR pszDestSpecs)
|
|
{
|
|
UINT cbDestSpec = lstrlen(pszDestDir) * sizeof(TCHAR) + sizeof(TCHAR);
|
|
LPCSTR pszTemp;
|
|
LPTSTR pszRet;
|
|
UINT cbAlloc = sizeof(TCHAR); // for double NULL teriminaion of entire string
|
|
|
|
// compute length of buffer to aloc
|
|
for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenA(pszTemp) + 1)
|
|
{
|
|
// +1 for null teriminator
|
|
cbAlloc += cbDestSpec + lstrlenA(pszTemp) * sizeof(TCHAR) + sizeof(TCHAR);
|
|
}
|
|
|
|
pszRet = (LPTSTR)LocalAlloc(LPTR, cbAlloc);
|
|
if (pszRet)
|
|
{
|
|
LPTSTR pszDest = pszRet;
|
|
|
|
for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlenA(pszTemp) + 1)
|
|
{
|
|
// PathCombine requires dest buffer of MAX_PATH size or it'll rip in call
|
|
// to PathCanonicalize (IsBadWritePtr)
|
|
TCHAR szTempDest[MAX_PATH];
|
|
WCHAR wszTemp[MAX_PATH];
|
|
SHAnsiToUnicode(pszTemp, wszTemp, ARRAYSIZE(wszTemp));
|
|
PathCombine(szTempDest, pszDestDir, wszTemp);
|
|
lstrcpy(pszDest, szTempDest);
|
|
pszDest += lstrlen(pszDest) + 1;
|
|
|
|
ASSERT((UINT)((BYTE *)pszDest - (BYTE *)pszRet) < cbAlloc);
|
|
ASSERT(*pszDest == 0); // zero init alloc
|
|
}
|
|
ASSERT((LPTSTR)((BYTE *)pszRet + cbAlloc - sizeof(TCHAR)) >= pszDest);
|
|
ASSERT(*pszDest == 0); // zero init alloc
|
|
|
|
}
|
|
return pszRet;
|
|
}
|
|
|
|
LPTSTR _GetDestNames(IDataObject *pdtobj, LPCTSTR pszPath)
|
|
{
|
|
LPTSTR pszDestNames = NULL;
|
|
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {g_cfFileNameMapW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
if (S_OK == pdtobj->GetData(&fmte, &medium))
|
|
{
|
|
pszDestNames = RemapDestNamesW(pszPath, (LPWSTR)GlobalLock(medium.hGlobal));
|
|
ReleaseStgMediumHGLOBAL(medium.hGlobal, &medium);
|
|
}
|
|
else
|
|
{
|
|
fmte.cfFormat = g_cfFileNameMapA;
|
|
if (S_OK == pdtobj->GetData(&fmte, &medium))
|
|
{
|
|
pszDestNames = RemapDestNamesA(pszPath, (LPSTR)GlobalLock(medium.hGlobal));
|
|
ReleaseStgMediumHGLOBAL(medium.hGlobal, &medium);
|
|
}
|
|
}
|
|
return pszDestNames;
|
|
}
|
|
|
|
BOOL _IsInSameFolder(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
STGMEDIUM medium = {0};
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
for (UINT i = 0; i < pida->cidl; i++)
|
|
{
|
|
LPITEMIDLIST pidl = IDA_FullIDList(pida, i);
|
|
if (pidl)
|
|
{
|
|
// if we're doing keyboard cut/copy/paste
|
|
// to and from the same directories
|
|
// This is needed for common desktop support - BobDay/EricFlo
|
|
if (AliasILIsParent(pidlFolder, pidl))
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
LPCTSTR _RootSpecialCase(LPCTSTR pszFiles, LPTSTR pszSrc, LPTSTR pszDest)
|
|
{
|
|
if ((1 == CountFiles(pszFiles)) &&
|
|
PathIsRoot(pszFiles))
|
|
{
|
|
SHFILEINFO sfi;
|
|
|
|
// NOTE: don't use SHGFI_USEFILEATTRIBUTES because the simple IDList
|
|
// support for \\server\share produces the wrong name
|
|
if (SHGetFileInfo(pszFiles, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME))
|
|
{
|
|
if (!(PCS_FATAL & PathCleanupSpec(pszDest, sfi.szDisplayName)))
|
|
{
|
|
PathAppend(pszDest, sfi.szDisplayName); // sub dir name based on source root path
|
|
PathCombine(pszSrc, pszFiles, TEXT("*.*")); // all files on source
|
|
pszFiles = pszSrc;
|
|
}
|
|
}
|
|
}
|
|
return pszFiles;
|
|
}
|
|
|
|
void CFSDropTarget::_MoveCopy(IDataObject *pdtobj, IFolderView* pfv, HDROP hDrop)
|
|
{
|
|
#ifdef DEBUG
|
|
if (_hwnd == NULL)
|
|
{
|
|
TraceMsg(TF_GENERAL, "_MoveCopy() without an hwnd which will prevent displaying insert disk UI");
|
|
}
|
|
#endif // DEBUG
|
|
|
|
DRAGINFO di = { sizeof(di) };
|
|
if (DragQueryInfo(hDrop, &di))
|
|
{
|
|
TCHAR szDest[MAX_PATH] = {0}; // zero init for dbl null termination
|
|
|
|
_GetPath(szDest);
|
|
|
|
switch (_idCmd)
|
|
{
|
|
case DDIDM_MOVE:
|
|
|
|
if (_fSameHwnd)
|
|
{
|
|
_MoveSelectIcons(pdtobj, pfv, NULL, NULL, TRUE, hDrop);
|
|
break;
|
|
}
|
|
|
|
// fall through...
|
|
|
|
case DDIDM_COPY:
|
|
{
|
|
TCHAR szAltSource[MAX_PATH] = {0}; // zero init for dbl null termination
|
|
LPCTSTR pszSource = _RootSpecialCase(di.lpFileList, szAltSource, szDest);
|
|
|
|
SHFILEOPSTRUCT fo =
|
|
{
|
|
_hwnd,
|
|
(DDIDM_COPY == _idCmd) ? FO_COPY : FO_MOVE,
|
|
pszSource,
|
|
szDest,
|
|
FOF_WANTMAPPINGHANDLE | FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR
|
|
};
|
|
if (fo.wFunc == FO_MOVE && IsFolderSecurityModeOn())
|
|
{
|
|
fo.fFlags |= FOF_NOCOPYSECURITYATTRIBS;
|
|
}
|
|
|
|
// if they are in the same hwnd or to and from
|
|
// the same directory, turn on the automatic rename on collision flag
|
|
if (_fSameHwnd ||
|
|
((DDIDM_COPY == _idCmd) && _IsInSameFolder(_GetIDList(), pdtobj)))
|
|
{
|
|
// do rename on collision for copy;
|
|
fo.fFlags |= FOF_RENAMEONCOLLISION;
|
|
}
|
|
|
|
// see if there is a rename mapping from recycle bin (or someone else)
|
|
|
|
LPTSTR pszDestNames = _GetDestNames(pdtobj, szDest);
|
|
if (pszDestNames)
|
|
{
|
|
fo.pTo = pszDestNames;
|
|
fo.fFlags |= FOF_MULTIDESTFILES;
|
|
fo.fFlags &= ~FOF_ALLOWUNDO; // HACK, this came from the recycle bin, don't allow undo
|
|
}
|
|
|
|
{
|
|
static UINT s_cfFileOpFlags = 0;
|
|
if (0 == s_cfFileOpFlags)
|
|
s_cfFileOpFlags = RegisterClipboardFormat(TEXT("FileOpFlags"));
|
|
|
|
fo.fFlags = (FILEOP_FLAGS)DataObj_GetDWORD(pdtobj, s_cfFileOpFlags, fo.fFlags);
|
|
}
|
|
|
|
// Check if there were any errors
|
|
if (SHFileOperation(&fo) == 0 && !fo.fAnyOperationsAborted)
|
|
{
|
|
if (_fBkDropTarget)
|
|
ShellFolderView_SetRedraw(_hwnd, 0);
|
|
|
|
SHChangeNotifyHandleEvents(); // force update now
|
|
if (_fBkDropTarget)
|
|
{
|
|
_MoveSelectIcons(pdtobj, pfv, fo.hNameMappings, pszDestNames, _fDragDrop, hDrop);
|
|
ShellFolderView_SetRedraw(_hwnd, TRUE);
|
|
}
|
|
}
|
|
|
|
if (fo.hNameMappings)
|
|
SHFreeNameMappings(fo.hNameMappings);
|
|
|
|
if (pszDestNames)
|
|
{
|
|
LocalFree((HLOCAL)pszDestNames);
|
|
|
|
// HACK, this usually comes from the bitbucket
|
|
// but in our shell, we don't handle the moves from the source
|
|
if (DDIDM_MOVE == _idCmd)
|
|
BBCheckRestoredFiles(pszSource);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
SHFree(di.lpFileList);
|
|
}
|
|
}
|
|
|
|
const UINT c_rgFolderShortcutTargets[] = {
|
|
CSIDL_STARTMENU,
|
|
CSIDL_COMMON_STARTMENU,
|
|
CSIDL_PROGRAMS,
|
|
CSIDL_COMMON_PROGRAMS,
|
|
CSIDL_NETHOOD,
|
|
};
|
|
|
|
BOOL _ShouldCreateFolderShortcut(LPCTSTR pszFolder)
|
|
{
|
|
return PathIsEqualOrSubFolderOf(pszFolder, c_rgFolderShortcutTargets, ARRAYSIZE(c_rgFolderShortcutTargets));
|
|
}
|
|
|
|
void CFSDropTarget::_DoDrop(IDataObject *pdtobj, IFolderView* pfv)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Sleep(10 * 1000); // to debug async case
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
_GetPath(szPath);
|
|
SHCreateDirectory(NULL, szPath); // if this fails we catch it later
|
|
|
|
switch (_idCmd)
|
|
{
|
|
case DDIDM_SYNCCOPY:
|
|
case DDIDM_SYNCCOPYTYPE:
|
|
if (_IsBriefcaseTarget())
|
|
{
|
|
IBriefcaseStg *pbrfstg;
|
|
if (SUCCEEDED(CreateBrfStgFromPath(szPath, _hwnd, &pbrfstg)))
|
|
{
|
|
hr = pbrfstg->AddObject(pdtobj, NULL,
|
|
(DDIDM_SYNCCOPYTYPE == _idCmd) ? AOF_FILTERPROMPT : AOF_DEFAULT,
|
|
_hwnd);
|
|
pbrfstg->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Perform a sneakernet addition to the briefcase
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
// Is there a briefcase root in this pdtobj?
|
|
IBriefcaseStg *pbrfstg;
|
|
if (SUCCEEDED(CreateBrfStgFromIDList(IDA_GetIDListPtr(pida, (UINT)-1), _hwnd, &pbrfstg)))
|
|
{
|
|
hr = pbrfstg->AddObject(pdtobj, szPath,
|
|
(DDIDM_SYNCCOPYTYPE == _idCmd) ? AOF_FILTERPROMPT : AOF_DEFAULT,
|
|
_hwnd);
|
|
pbrfstg->Release();
|
|
}
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DDIDM_COPY:
|
|
case DDIDM_MOVE:
|
|
{
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
hr = pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_MoveCopy(pdtobj, pfv, (HDROP)medium.hGlobal);
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DDIDM_LINK:
|
|
{
|
|
int i = 0;
|
|
LPITEMIDLIST *ppidl = NULL;
|
|
|
|
if (_fBkDropTarget)
|
|
{
|
|
i = DataObj_GetHIDACount(pdtobj);
|
|
ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, sizeof(*ppidl) * i);
|
|
}
|
|
|
|
// _grfKeyStateLast of 0 means this was a simulated drop
|
|
UINT uCreateFlags = _grfKeyStateLast && !(_dwEffectFolder & DROPEFFECT_LINK) ? SHCL_USETEMPLATE : 0;
|
|
|
|
if (_ShouldCreateFolderShortcut(szPath))
|
|
uCreateFlags |= SHCL_MAKEFOLDERSHORTCUT;
|
|
|
|
ShellFolderView_SetRedraw(_hwnd, FALSE);
|
|
// passing ppidl == NULL is correct in failure case
|
|
hr = SHCreateLinks(_hwnd, szPath, pdtobj, uCreateFlags, ppidl);
|
|
if (ppidl)
|
|
{
|
|
if (pfv)
|
|
PositionItems(pfv, (LPCITEMIDLIST*)ppidl, i, pdtobj, &_ptDrop);
|
|
|
|
FreeIDListArray(ppidl, i);
|
|
}
|
|
ShellFolderView_SetRedraw(_hwnd, TRUE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && _dwEffect)
|
|
{
|
|
DataObj_SetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, _dwEffect);
|
|
DataObj_SetDWORD(pdtobj, g_cfPerformedDropEffect, _dwEffect);
|
|
}
|
|
|
|
SHChangeNotifyHandleEvents(); // force update now
|
|
}
|
|
|
|
DWORD CALLBACK CFSDropTarget::_DoDropThreadProc(void *pv)
|
|
{
|
|
DROPTHREADPARAMS *pdtp = (DROPTHREADPARAMS *)pv;
|
|
|
|
IDataObject *pdtobj;
|
|
if (SUCCEEDED(CoGetInterfaceAndReleaseStream(pdtp->pstmDataObj, IID_PPV_ARG(IDataObject, &pdtobj))))
|
|
{
|
|
IFolderView* pfv;
|
|
if (FAILED(CoGetInterfaceAndReleaseStream(pdtp->pstmFolderView, IID_PPV_ARG(IFolderView, &pfv))))
|
|
pfv = NULL;
|
|
|
|
pdtp->pThis->_DoDrop(pdtobj, pfv);
|
|
|
|
if (pfv)
|
|
pfv->Release();
|
|
|
|
pdtp->pstmFolderView = NULL; // stream now invalid; CoGetInterfaceAndReleaseStream already released it
|
|
pdtobj->Release();
|
|
}
|
|
|
|
pdtp->pstmDataObj = NULL; // stream now invalid; CoGetInterfaceAndReleaseStream already released it
|
|
_FreeThreadParams(pdtp);
|
|
|
|
CoFreeUnusedLibraries();
|
|
return 0;
|
|
}
|
|
|
|
// REARCHITECT: view and drop related helpers, these use the ugly old private defview messages
|
|
// we should replace the usage of this stuff with IShellFolderView programming
|
|
|
|
|
|
// create the pidl array that contains the destination file names. this is
|
|
// done by taking the source file names, and translating them through the
|
|
// name mapping returned by the copy engine.
|
|
//
|
|
//
|
|
// in:
|
|
// hDrop HDROP containing files recently moved/copied
|
|
// hNameMap used to translate names
|
|
//
|
|
// out:
|
|
// *pppidl id array of length return value
|
|
// # of items in pppida
|
|
|
|
//
|
|
// WARNING! You must use the provided HDROP. Do not attempt to ask the
|
|
// data object for a HDROP or HIDA or WS_FTP will break! They don't like
|
|
// it if you ask them for HDROP/HIDA, move the files to a new location
|
|
// (via the copy engine), and then ask them for HDROP/HIDA a second time.
|
|
// They notice that "Hey, those files I downloaded last time are gone!"
|
|
// and then get confused.
|
|
//
|
|
STDAPI_(int) CreateMoveCopyList(HDROP hDrop, void *hNameMap, LPITEMIDLIST **pppidl)
|
|
{
|
|
int nItems = 0;
|
|
|
|
if (hDrop)
|
|
{
|
|
nItems = DragQueryFile(hDrop, (UINT)-1, NULL, 0);
|
|
*pppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, nItems * sizeof(*pppidl));
|
|
if (*pppidl)
|
|
{
|
|
for (int i = nItems - 1; i >= 0; i--)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath));
|
|
_MapName(hNameMap, szPath);
|
|
(*pppidl)[i] = SHSimpleIDListFromPath(szPath);
|
|
}
|
|
}
|
|
}
|
|
return nItems;
|
|
}
|
|
|
|
// this is really not related to CFSFolder. it is generic over any view
|
|
// REARCHITECT: convert view hwnd programming to site pointer
|
|
|
|
STDAPI_(void) PositionFileFromDrop(HWND hwnd, LPCTSTR pszFile, DROPHISTORY *pdh)
|
|
{
|
|
LPITEMIDLIST pidl = SHSimpleIDListFromPath(pszFile);
|
|
if (pidl)
|
|
{
|
|
LPITEMIDLIST pidlNew = ILFindLastID(pidl);
|
|
HWND hwndView = ShellFolderViewWindow(hwnd);
|
|
SFM_SAP sap;
|
|
|
|
SHChangeNotifyHandleEvents();
|
|
|
|
// Fill in some easy SAP fields first.
|
|
sap.uSelectFlags = SVSI_SELECT;
|
|
sap.fMove = TRUE;
|
|
sap.pidl = pidlNew;
|
|
|
|
// Now compute the x,y coordinates.
|
|
// If we have a drop history, use it to determine the
|
|
// next point.
|
|
|
|
if (pdh)
|
|
{
|
|
// fill in the anchor point first...
|
|
if (!pdh->fInitialized)
|
|
{
|
|
ITEMSPACING is;
|
|
|
|
ShellFolderView_GetDropPoint(hwnd, &pdh->ptOrigin);
|
|
|
|
pdh->pt = pdh->ptOrigin; // Compute the first point.
|
|
|
|
// Compute the point deltas.
|
|
if (ShellFolderView_GetItemSpacing(hwnd, &is))
|
|
{
|
|
pdh->cxItem = is.cxSmall;
|
|
pdh->cyItem = is.cySmall;
|
|
pdh->xDiv = is.cxLarge;
|
|
pdh->yDiv = is.cyLarge;
|
|
pdh->xMul = is.cxSmall;
|
|
pdh->yMul = is.cySmall;
|
|
}
|
|
else
|
|
{
|
|
pdh->cxItem = g_cxIcon;
|
|
pdh->cyItem = g_cyIcon;
|
|
pdh->xDiv = pdh->yDiv = pdh->xMul = pdh->yMul = 1;
|
|
}
|
|
|
|
// First point gets special flags.
|
|
sap.uSelectFlags |= SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED;
|
|
|
|
pdh->fInitialized = TRUE; // We be initialized.
|
|
}
|
|
// if we have no list of offsets, then just inc by icon size..
|
|
else if ( !pdh->pptOffset )
|
|
{
|
|
// Simple computation of the next point.
|
|
pdh->pt.x += pdh->cxItem;
|
|
pdh->pt.y += pdh->cyItem;
|
|
}
|
|
|
|
// do this after the above stuff so that we always get our position relative to the anchor
|
|
// point, if we use the anchor point as the first one things get screwy...
|
|
if (pdh->pptOffset)
|
|
{
|
|
// Transform the old offset to our coordinates.
|
|
pdh->pt.x = ((pdh->pptOffset[pdh->iItem].x * pdh->xMul) / pdh->xDiv) + pdh->ptOrigin.x;
|
|
pdh->pt.y = ((pdh->pptOffset[pdh->iItem].y * pdh->yMul) / pdh->yDiv) + pdh->ptOrigin.y;
|
|
}
|
|
|
|
sap.pt = pdh->pt; // Copy the next point from the drop history.
|
|
}
|
|
else
|
|
{
|
|
// Preinitialize this puppy in case the folder view doesn't
|
|
// know what the drop point is (e.g., if it didn't come from
|
|
// a drag/drop but rather from a paste or a ChangeNotify.)
|
|
sap.pt.x = 0x7FFFFFFF; // "don't know"
|
|
sap.pt.y = 0x7FFFFFFF;
|
|
|
|
// Get the drop point, conveniently already in
|
|
// defview's screen coordinates.
|
|
//
|
|
// pdv->bDropAnchor should be TRUE at this point,
|
|
// see DefView's GetDropPoint() for details.
|
|
|
|
ShellFolderView_GetDropPoint(hwnd, &sap.pt);
|
|
|
|
// Only point gets special flags.
|
|
sap.uSelectFlags |= SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED;
|
|
}
|
|
|
|
SendMessage(hwndView, SVM_SELECTANDPOSITIONITEM, 1, (LPARAM)&sap);
|
|
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Class used to scale and position items for drag and drops. Handles
|
|
// scaling between different sized views.
|
|
//
|
|
|
|
//
|
|
// Bug 165413 (edwardp 8/16/00) Convert IShellFolderView usage in CItemPositioning to IFolderView
|
|
//
|
|
|
|
class CItemPositioning
|
|
{
|
|
// Methods
|
|
public:
|
|
CItemPositioning(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ppt);
|
|
|
|
void DragSetPoints(void);
|
|
void DropPositionItems(void);
|
|
|
|
private:
|
|
|
|
typedef enum
|
|
{
|
|
DPIWP_AUTOARRANGE,
|
|
DPIWP_DATAOBJ,
|
|
} DPIWP;
|
|
|
|
BOOL _DragShouldPositionItems(void);
|
|
BOOL _DragGetPoints(POINT* apts);
|
|
void _DragPositionPoints(POINT* apts);
|
|
void _DragScalePoints(POINT* apts);
|
|
|
|
POINT* _DropGetPoints(DPIWP dpiwp, STGMEDIUM* pMediam);
|
|
void _DropFreePoints(DPIWP dpiwp, POINT* apts, STGMEDIUM* pmedium);
|
|
void _DropPositionPoints(POINT* apts);
|
|
void _DropScalePoints(POINT* apts);
|
|
void _DropPositionItemsWithPoints(DPIWP dpiwp);
|
|
void _DropPositionItems(POINT* apts);
|
|
|
|
void _ScalePoints(POINT* apts, POINT ptFrom, POINT ptTo);
|
|
POINT* _SkipAnchorPoint(POINT* apts);
|
|
|
|
// Data
|
|
private:
|
|
IFolderView* _pfv;
|
|
LPCITEMIDLIST* _apidl;
|
|
UINT _cidl;
|
|
IDataObject* _pdtobj;
|
|
POINT* _ppt;
|
|
};
|
|
|
|
CItemPositioning::CItemPositioning(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ppt)
|
|
{
|
|
ASSERT(pifv);
|
|
ASSERT(apidl);
|
|
ASSERT(cidl);
|
|
ASSERT(pdtobj);
|
|
|
|
_pfv = pifv; // No need to addref as long as CPostionItems is only used locally.
|
|
_apidl = apidl;
|
|
_cidl = cidl;
|
|
_pdtobj = pdtobj; // No need to addref as long as CPostionItems is only used locally.
|
|
_ppt = ppt;
|
|
}
|
|
|
|
|
|
void CItemPositioning::DragSetPoints(void)
|
|
{
|
|
if (_DragShouldPositionItems())
|
|
{
|
|
POINT* apts = (POINT*) GlobalAlloc(GPTR, sizeof(POINT) * (_cidl + 1));
|
|
|
|
if (apts)
|
|
{
|
|
if (_DragGetPoints(apts))
|
|
{
|
|
_DragPositionPoints(_SkipAnchorPoint(apts));
|
|
_DragScalePoints(_SkipAnchorPoint(apts));
|
|
|
|
if (FAILED(DataObj_SetGlobal(_pdtobj, g_cfOFFSETS, apts)))
|
|
GlobalFree((HGLOBAL)apts);
|
|
}
|
|
else
|
|
{
|
|
GlobalFree((HGLOBAL)apts);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CItemPositioning::_DragShouldPositionItems()
|
|
{
|
|
// Don't position multiple items if they come from a view that doesn't allow
|
|
// positioning. The position information is not likely to be usefull in this
|
|
// case.
|
|
// Always position single items so they show up at the drop point.
|
|
// Don't bother with position data for 100 or more items.
|
|
|
|
return ((S_OK == _pfv->GetSpacing(NULL)) || 1 == _cidl) && _cidl < 100;
|
|
}
|
|
|
|
BOOL CItemPositioning::_DragGetPoints(POINT* apts)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
|
|
// The first point is the anchor.
|
|
apts[0] = *_ppt;
|
|
|
|
for (UINT i = 0; i < _cidl; i++)
|
|
{
|
|
if (FAILED(_pfv->GetItemPosition(_apidl[i], &apts[i + 1])))
|
|
{
|
|
if (1 == _cidl)
|
|
{
|
|
apts[i + 1].x = _ppt->x;
|
|
apts[i + 1].y = _ppt->y;
|
|
}
|
|
else
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
void CItemPositioning::_DragPositionPoints(POINT* apts)
|
|
{
|
|
for (UINT i = 0; i < _cidl; i++)
|
|
{
|
|
apts[i].x -= _ppt->x;
|
|
apts[i].y -= _ppt->y;
|
|
}
|
|
}
|
|
|
|
void CItemPositioning::_DragScalePoints(POINT* apts)
|
|
{
|
|
POINT ptFrom;
|
|
POINT ptTo;
|
|
|
|
_pfv->GetSpacing(&ptFrom);
|
|
_pfv->GetDefaultSpacing(&ptTo);
|
|
|
|
if (ptFrom.x != ptTo.x || ptFrom.y != ptTo.y)
|
|
_ScalePoints(apts, ptFrom, ptTo);
|
|
}
|
|
|
|
void CItemPositioning::DropPositionItems(void)
|
|
{
|
|
if (S_OK == _pfv->GetAutoArrange())
|
|
{
|
|
_DropPositionItemsWithPoints(DPIWP_AUTOARRANGE);
|
|
}
|
|
else if (S_OK == _pfv->GetSpacing(NULL) && _ppt)
|
|
{
|
|
_DropPositionItemsWithPoints(DPIWP_DATAOBJ);
|
|
}
|
|
else
|
|
{
|
|
_DropPositionItems(NULL);
|
|
}
|
|
}
|
|
|
|
void CItemPositioning::_DropPositionItemsWithPoints(DPIWP dpiwp)
|
|
{
|
|
STGMEDIUM medium;
|
|
POINT* apts = _DropGetPoints(dpiwp, &medium);
|
|
|
|
if (apts)
|
|
{
|
|
if (DPIWP_DATAOBJ == dpiwp)
|
|
{
|
|
_DropScalePoints(_SkipAnchorPoint(apts));
|
|
_DropPositionPoints(_SkipAnchorPoint(apts));
|
|
}
|
|
|
|
_DropPositionItems(_SkipAnchorPoint(apts));
|
|
|
|
_DropFreePoints(dpiwp, apts, &medium);
|
|
}
|
|
else if (_ppt)
|
|
{
|
|
POINT *ppts;
|
|
|
|
ppts = (POINT *)LocalAlloc(LPTR, _cidl * sizeof(POINT));
|
|
|
|
if (ppts)
|
|
{
|
|
POINT pt;
|
|
|
|
_pfv->GetDefaultSpacing(&pt);
|
|
|
|
for (UINT i = 0; i < _cidl; i++)
|
|
{
|
|
ppts[i].x = (-g_cxIcon / 2) + (i * pt.x);
|
|
ppts[i].y = (-g_cyIcon / 2) + (i * pt.y);
|
|
}
|
|
_DropScalePoints(ppts);
|
|
_DropPositionPoints(ppts);
|
|
_DropPositionItems(ppts);
|
|
|
|
LocalFree(ppts);
|
|
}
|
|
else
|
|
{
|
|
_DropPositionItems(NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_DropPositionItems(NULL);
|
|
}
|
|
}
|
|
|
|
void CItemPositioning::_DropPositionItems(POINT* apts)
|
|
{
|
|
// Drop the first item with special selection flags.
|
|
LPCITEMIDLIST pidl = ILFindLastID(_apidl[0]);
|
|
_pfv->SelectAndPositionItems(1, &pidl, apts, SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED);
|
|
|
|
// Drop the rest of the items.
|
|
if (_cidl > 1)
|
|
{
|
|
LPCITEMIDLIST* apidl = (LPCITEMIDLIST*)LocalAlloc(GPTR, sizeof(LPCITEMIDLIST) * (_cidl - 1));
|
|
|
|
if (apidl)
|
|
{
|
|
for (UINT i = 1; i < _cidl; i++)
|
|
apidl[i - 1] = ILFindLastID(_apidl[i]);
|
|
|
|
_pfv->SelectAndPositionItems(_cidl - 1, apidl, (apts) ? &apts[1] : NULL, SVSI_SELECT);
|
|
|
|
LocalFree(apidl);
|
|
}
|
|
}
|
|
}
|
|
|
|
POINT* CItemPositioning::_DropGetPoints(DPIWP dpiwp, STGMEDIUM* pmedium)
|
|
{
|
|
POINT* pptRet = NULL;
|
|
|
|
if (DPIWP_DATAOBJ == dpiwp)
|
|
{
|
|
FORMATETC fmte = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
|
|
if (SUCCEEDED(_pdtobj->GetData(&fmte, pmedium)))
|
|
{
|
|
if (pmedium->hGlobal)
|
|
{
|
|
POINT *pptSrc;
|
|
|
|
pptSrc = (POINT *)GlobalLock(pmedium->hGlobal);
|
|
|
|
if (pptSrc)
|
|
{
|
|
pptRet = (POINT*)LocalAlloc(GPTR, (_cidl + 1) * sizeof(POINT));
|
|
|
|
if (pptRet)
|
|
{
|
|
for (UINT i = 0; i <= _cidl; i++)
|
|
{
|
|
pptRet[i] = pptSrc[i];
|
|
}
|
|
}
|
|
|
|
GlobalUnlock(pptSrc);
|
|
}
|
|
}
|
|
|
|
ReleaseStgMedium(pmedium);
|
|
}
|
|
}
|
|
else if (DPIWP_AUTOARRANGE == dpiwp)
|
|
{
|
|
if (_ppt)
|
|
{
|
|
pptRet = (POINT*)LocalAlloc(GPTR, (_cidl + 1) * sizeof(POINT));
|
|
|
|
if (pptRet)
|
|
{
|
|
// skip first point to simulate data object use of first point
|
|
|
|
for (UINT i = 1; i <= _cidl; i++)
|
|
{
|
|
pptRet[i] = *_ppt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return pptRet;
|
|
}
|
|
|
|
void CItemPositioning::_DropFreePoints(DPIWP dpiwp, POINT* apts, STGMEDIUM* pmedium)
|
|
{
|
|
LocalFree(apts);
|
|
}
|
|
|
|
void CItemPositioning::_DropScalePoints(POINT* apts)
|
|
{
|
|
POINT ptFrom;
|
|
POINT ptTo;
|
|
|
|
_pfv->GetDefaultSpacing(&ptFrom);
|
|
_pfv->GetSpacing(&ptTo);
|
|
|
|
if (ptFrom.x != ptTo.x || ptFrom.y != ptTo.y)
|
|
_ScalePoints(apts, ptFrom, ptTo);
|
|
}
|
|
|
|
void CItemPositioning::_DropPositionPoints(POINT* apts)
|
|
{
|
|
for (UINT i = 0; i < _cidl; i++)
|
|
{
|
|
apts[i].x += _ppt->x;
|
|
apts[i].y += _ppt->y;
|
|
}
|
|
}
|
|
|
|
void CItemPositioning::_ScalePoints(POINT* apts, POINT ptFrom, POINT ptTo)
|
|
{
|
|
for (UINT i = 0; i < _cidl; i++)
|
|
{
|
|
apts[i].x = MulDiv(apts[i].x, ptTo.x, ptFrom.x);
|
|
apts[i].y = MulDiv(apts[i].y, ptTo.y, ptFrom.y);
|
|
}
|
|
}
|
|
|
|
POINT* CItemPositioning::_SkipAnchorPoint(POINT* apts)
|
|
{
|
|
return &apts[1];
|
|
}
|
|
|
|
|
|
|
|
STDAPI_(void) SetPositionItemsPoints(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ptDrag)
|
|
{
|
|
CItemPositioning cpi(pifv, apidl, cidl, pdtobj, ptDrag);
|
|
cpi.DragSetPoints();
|
|
}
|
|
|
|
STDAPI_(void) PositionItems(IFolderView* pifv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ptDrop)
|
|
{
|
|
CItemPositioning cip(pifv, apidl, cidl, pdtobj, ptDrop);
|
|
cip.DropPositionItems();
|
|
}
|
|
|
|
//
|
|
// Don't use PositionItems_DontUse. Instead convert to PositionItems.
|
|
// PositionItems_DontUse will be removed.
|
|
//
|
|
// Bug#163533 (edwardp 8/15/00) Remove this code.
|
|
|
|
STDAPI_(void) PositionItems_DontUse(HWND hwndOwner, UINT cidl, const LPITEMIDLIST *ppidl, IDataObject *pdtobj, POINT *pptOrigin, BOOL fMove, BOOL fUseExactOrigin)
|
|
{
|
|
if (!ppidl || !IsWindow(hwndOwner))
|
|
return;
|
|
|
|
SFM_SAP *psap = (SFM_SAP *)GlobalAlloc(GPTR, sizeof(SFM_SAP) * cidl);
|
|
if (psap)
|
|
{
|
|
UINT i, cxItem, cyItem;
|
|
int xMul, yMul, xDiv, yDiv;
|
|
STGMEDIUM medium;
|
|
POINT *pptItems = NULL;
|
|
POINT pt;
|
|
ITEMSPACING is;
|
|
// select those objects;
|
|
// this had better not fail
|
|
HWND hwnd = ShellFolderViewWindow(hwndOwner);
|
|
|
|
if (fMove)
|
|
{
|
|
FORMATETC fmte = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
if (SUCCEEDED(pdtobj->GetData(&fmte, &medium)) &&
|
|
medium.hGlobal)
|
|
{
|
|
pptItems = (POINT *)GlobalLock(medium.hGlobal);
|
|
pptItems++; // The first point is the anchor
|
|
}
|
|
else
|
|
{
|
|
// By default, drop at (-g_cxIcon/2, -g_cyIcon/2), and increase
|
|
// x and y by icon dimension for each icon
|
|
pt.x = ((-3 * g_cxIcon) / 2) + pptOrigin->x;
|
|
pt.y = ((-3 * g_cyIcon) / 2) + pptOrigin->y;
|
|
medium.hGlobal = NULL;
|
|
}
|
|
|
|
if (ShellFolderView_GetItemSpacing(hwndOwner, &is))
|
|
{
|
|
xDiv = is.cxLarge;
|
|
yDiv = is.cyLarge;
|
|
xMul = is.cxSmall;
|
|
yMul = is.cySmall;
|
|
cxItem = is.cxSmall;
|
|
cyItem = is.cySmall;
|
|
}
|
|
else
|
|
{
|
|
xDiv = yDiv = xMul = yMul = 1;
|
|
cxItem = g_cxIcon;
|
|
cyItem = g_cyIcon;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < cidl; i++)
|
|
{
|
|
if (ppidl[i])
|
|
{
|
|
psap[i].pidl = ILFindLastID(ppidl[i]);
|
|
psap[i].fMove = fMove;
|
|
if (fMove)
|
|
{
|
|
if (fUseExactOrigin)
|
|
{
|
|
psap[i].pt = *pptOrigin;
|
|
}
|
|
else
|
|
{
|
|
if (pptItems)
|
|
{
|
|
psap[i].pt.x = ((pptItems[i].x * xMul) / xDiv) + pptOrigin->x;
|
|
psap[i].pt.y = ((pptItems[i].y * yMul) / yDiv) + pptOrigin->y;
|
|
}
|
|
else
|
|
{
|
|
pt.x += cxItem;
|
|
pt.y += cyItem;
|
|
psap[i].pt = pt;
|
|
}
|
|
}
|
|
}
|
|
|
|
// do regular selection from all of the rest of the items
|
|
psap[i].uSelectFlags = SVSI_SELECT;
|
|
}
|
|
}
|
|
|
|
// do this special one for the first only
|
|
psap[0].uSelectFlags = SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED;
|
|
|
|
SendMessage(hwnd, SVM_SELECTANDPOSITIONITEM, cidl, (LPARAM)psap);
|
|
|
|
if (fMove && medium.hGlobal)
|
|
ReleaseStgMediumHGLOBAL(NULL, &medium);
|
|
|
|
GlobalFree(psap);
|
|
}
|
|
}
|