|
|
#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); } }
|