1194 lines
40 KiB
C
1194 lines
40 KiB
C
|
#include "shellprv.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "bookmk.h"
|
||
|
#include "fstreex.h"
|
||
|
#include "datautil.h"
|
||
|
#include "copy.h"
|
||
|
#include "_security.h"
|
||
|
|
||
|
STDAPI_(void) FS_PositionFileFromDrop(HWND hwnd, LPCTSTR pszFile, PDROPHISTORY pdh)
|
||
|
{
|
||
|
LPITEMIDLIST pidl = SHSimpleIDListFromPath(pszFile);
|
||
|
if (pidl) {
|
||
|
LPITEMIDLIST pidlNew = ILFindLastID(pidl);
|
||
|
SFM_SAP sap;
|
||
|
HWND hwndView;
|
||
|
|
||
|
SHChangeNotifyHandleEvents();
|
||
|
|
||
|
hwndView = DV_HwndMain2HwndView(hwnd);
|
||
|
|
||
|
|
||
|
// 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);
|
||
|
|
||
|
|
||
|
// Compute the first point.
|
||
|
|
||
|
|
||
|
pdh->pt = pdh->ptOrigin;
|
||
|
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
|
||
|
// We be initialized.
|
||
|
|
||
|
pdh->fInitialized = TRUE;
|
||
|
}
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Copy the next point from the drop history.
|
||
|
|
||
|
sap.pt = pdh->pt;
|
||
|
} 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FS_FreeMoveCopyList(LPITEMIDLIST* ppidl, UINT cidl)
|
||
|
{
|
||
|
UINT i;
|
||
|
// free everything
|
||
|
for (i = 0; i < cidl; i++) {
|
||
|
ILFree(ppidl[i]);
|
||
|
}
|
||
|
LocalFree(ppidl);
|
||
|
}
|
||
|
|
||
|
void FS_PositionItems(HWND hwndOwner, UINT cidl, const LPITEMIDLIST* ppidl, IDataObject* pdtobj, POINT* pptOrigin, BOOL fMove)
|
||
|
{
|
||
|
SFM_SAP* psap;
|
||
|
|
||
|
if (!ppidl || !IsWindow(hwndOwner))
|
||
|
return;
|
||
|
|
||
|
psap = 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 = DV_HwndMain2HwndView(hwndOwner);
|
||
|
|
||
|
if (fMove) {
|
||
|
FORMATETC fmte = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &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 (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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void FS_MapName(void* hNameMappings, LPTSTR pszPath)
|
||
|
{
|
||
|
int i;
|
||
|
LPSHNAMEMAPPING pNameMapping;
|
||
|
|
||
|
if (!hNameMappings)
|
||
|
return;
|
||
|
|
||
|
for (i = 0; (pNameMapping = SHGetNameMappingPtr(hNameMappings, i)) != NULL; i++) {
|
||
|
if (lstrcmpi(pszPath, pNameMapping->pszOldPath) == 0) {
|
||
|
lstrcpy(pszPath, pNameMapping->pszNewPath);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// convert null separated/terminated file list to array of pidls
|
||
|
int FileListToPidlList(LPCTSTR lpszFiles, void* hNameMappings, LPITEMIDLIST** pppidl)
|
||
|
{
|
||
|
int nItems = CountFiles(lpszFiles);
|
||
|
int i = 0;
|
||
|
LPITEMIDLIST* ppidl = (void*)LocalAlloc(LPTR, nItems * SIZEOF(LPITEMIDLIST));
|
||
|
if (ppidl) {
|
||
|
*pppidl = ppidl;
|
||
|
|
||
|
while (*lpszFiles) {
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
lstrcpy(szPath, lpszFiles);
|
||
|
FS_MapName(hNameMappings, szPath);
|
||
|
|
||
|
ppidl[i] = SHSimpleIDListFromPath(szPath);
|
||
|
|
||
|
lpszFiles += lstrlen(lpszFiles) + 1;
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
// 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:
|
||
|
// pszDir desitination of operation
|
||
|
// pdtobj HIDA containg data object
|
||
|
// hNameMappings used to translate names
|
||
|
|
||
|
// out:
|
||
|
// *pppidl id array of length return value
|
||
|
// # of items in pppida
|
||
|
|
||
|
int FS_CreateMoveCopyList(IDataObject* pdtobj, void* hNameMappings, LPITEMIDLIST** pppidl)
|
||
|
{
|
||
|
int nItems = 0;
|
||
|
STGMEDIUM medium;
|
||
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
|
||
|
HRESULT hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium);
|
||
|
if (SUCCEEDED(hres)) {
|
||
|
HDROP hDrop = medium.hGlobal;
|
||
|
nItems = DragQueryFile(hDrop, (UINT)-1, NULL, 0);
|
||
|
*pppidl = (void*)LocalAlloc(LPTR, nItems * SIZEOF(LPITEMIDLIST));
|
||
|
if (*pppidl) {
|
||
|
int i;
|
||
|
for (i = nItems - 1; i >= 0; i--) {
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath));
|
||
|
FS_MapName(hNameMappings, szPath);
|
||
|
(*pppidl)[i] = SHSimpleIDListFromPath(szPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ReleaseStgMedium(&medium);
|
||
|
}
|
||
|
return nItems;
|
||
|
}
|
||
|
|
||
|
|
||
|
// in:
|
||
|
// pszPath destination path of the operation
|
||
|
// pszFiles source files list, may be NULL
|
||
|
// hNameMappings name mappings result from the copy operaton
|
||
|
|
||
|
|
||
|
void FS_MoveSelectIcons(FSTHREADPARAM* pfsthp, void* hNameMappings, LPCTSTR pszFiles, BOOL fMove)
|
||
|
{
|
||
|
LPITEMIDLIST* ppidl = NULL;
|
||
|
int cidl;
|
||
|
|
||
|
if (pszFiles) {
|
||
|
cidl = FileListToPidlList(pszFiles, hNameMappings, &ppidl);
|
||
|
} else {
|
||
|
cidl = FS_CreateMoveCopyList(pfsthp->pDataObj, hNameMappings, &ppidl);
|
||
|
}
|
||
|
|
||
|
if (ppidl) {
|
||
|
FS_PositionItems(pfsthp->hwndOwner, cidl, ppidl, pfsthp->pDataObj, &pfsthp->ptDrop, fMove);
|
||
|
FS_FreeMoveCopyList(ppidl, cidl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this is the ILIsParent which matches up the desktop with the desktop directory.
|
||
|
BOOL FSILIsParent(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
||
|
{
|
||
|
LPITEMIDLIST pidlUse1, pidlUse2;
|
||
|
BOOL fSame;
|
||
|
|
||
|
pidlUse1 = SHLogILFromFSIL(pidl1);
|
||
|
if (pidlUse1)
|
||
|
pidl1 = pidlUse1;
|
||
|
|
||
|
pidlUse2 = SHLogILFromFSIL(pidl2);
|
||
|
if (pidlUse2)
|
||
|
pidl2 = pidlUse2;
|
||
|
|
||
|
fSame = ILIsParent(pidl1, pidl2, TRUE);
|
||
|
|
||
|
if (pidlUse1)
|
||
|
ILFree(pidlUse1);
|
||
|
if (pidlUse2)
|
||
|
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;
|
||
|
LPTSTR pszRet;
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
pszRet = 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];
|
||
|
#ifdef UNICODE
|
||
|
PathCombine(szTempDest, pszDestDir, pszTemp);
|
||
|
#else
|
||
|
char szTemp[MAX_PATH];
|
||
|
SHUnicodeToAnsi(pszTemp, szTemp, ARRAYSIZE(szTemp));
|
||
|
PathCombine(szTempDest, pszDestDir, szTemp);
|
||
|
#endif
|
||
|
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 = 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];
|
||
|
#ifdef UNICODE
|
||
|
WCHAR wszTemp[MAX_PATH];
|
||
|
SHAnsiToUnicode(pszTemp, wszTemp, ARRAYSIZE(wszTemp));
|
||
|
PathCombine(szTempDest, pszDestDir, wszTemp);
|
||
|
#else
|
||
|
PathCombine(szTempDest, pszDestDir, pszTemp);
|
||
|
#endif
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
BOOL HandleSneakernetDrop(FSTHREADPARAM* pfsthp, LPCITEMIDLIST pidlParent, LPCTSTR pszTarget);
|
||
|
|
||
|
void _HandleMoveOrCopy(FSTHREADPARAM* pfsthp, HDROP hDrop, LPCTSTR pszPath)
|
||
|
{
|
||
|
DRAGINFO di;
|
||
|
|
||
|
// BUGBUG: we don't deal with non TCHAR stuff here
|
||
|
di.uSize = SIZEOF(di);
|
||
|
if (!DragQueryInfo(hDrop, &di)) {
|
||
|
// NOTE: Win95/NT4 dont have this fix, you will fault if you hit this case!
|
||
|
AssertMsg(FALSE, TEXT("hDrop contains the opposite TCHAR (UNICODE when on ANSI)"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
switch (pfsthp->dwEffect) {
|
||
|
case DROPEFFECT_MOVE:
|
||
|
|
||
|
if (pfsthp->fSameHwnd) {
|
||
|
FS_MoveSelectIcons(pfsthp, NULL, NULL, TRUE);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// fall through...
|
||
|
|
||
|
case DROPEFFECT_COPY:
|
||
|
{
|
||
|
SHFILEOPSTRUCT fo = {
|
||
|
pfsthp->hwndOwner,
|
||
|
(pfsthp->dwEffect == DROPEFFECT_COPY) ? FO_COPY : FO_MOVE,
|
||
|
di.lpFileList,
|
||
|
pszPath,
|
||
|
FOF_WANTMAPPINGHANDLE | FOF_ALLOWUNDO
|
||
|
};
|
||
|
LPTSTR pszDestNames = NULL;
|
||
|
STGMEDIUM medium;
|
||
|
FORMATETC fmte = {g_cfFileNameMapW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
HRESULT hres;
|
||
|
|
||
|
// if they are in the same hwnd or to and from
|
||
|
// the same directory, turn on the automatic rename on collision flag
|
||
|
if (pfsthp->fSameHwnd) {
|
||
|
fo.fFlags |= FOF_RENAMEONCOLLISION;
|
||
|
} else {
|
||
|
LPIDA pida = DataObj_GetHIDA(pfsthp->pDataObj, &medium);
|
||
|
if (pida) {
|
||
|
LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1);
|
||
|
|
||
|
// make sure stuff immediately under the desktop
|
||
|
// compares ok to stuff under the desktop directory
|
||
|
if (pidlParent) {
|
||
|
INT i;
|
||
|
BOOL fMoveToSame = FALSE;
|
||
|
|
||
|
for (i = 0; i < (INT)pida->cidl; i++) {
|
||
|
LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, (UINT)i);
|
||
|
LPITEMIDLIST pidlAbs = ILCombine(pidlParent, pidl);
|
||
|
if (!pidlAbs)
|
||
|
continue;
|
||
|
|
||
|
// if we're doing keyboard cut/copy/paste
|
||
|
// to and from the same directories
|
||
|
// This is needed for common desktop support - BobDay/EricFlo
|
||
|
if (FSILIsParent(pfsthp->pidl, pidlAbs)) {
|
||
|
if (pfsthp->dwEffect == DROPEFFECT_MOVE) {
|
||
|
// if they're the same, do nothing on move
|
||
|
fMoveToSame = TRUE;
|
||
|
} else {
|
||
|
// do rename on collision for copy;
|
||
|
fo.fFlags |= FOF_RENAMEONCOLLISION;
|
||
|
}
|
||
|
}
|
||
|
ILFree(pidlAbs);
|
||
|
}
|
||
|
|
||
|
if (fMoveToSame)
|
||
|
goto DoneWithData;
|
||
|
|
||
|
// Handle sneaker-net for briefcase; did briefcase
|
||
|
// handle it?
|
||
|
if (HandleSneakernetDrop(pfsthp, pidlParent, pszPath)) {
|
||
|
// Yes; don't do anything
|
||
|
DebugMsg(TF_FSTREE, TEXT("Briefcase handled drop"));
|
||
|
HIDA_ReleaseStgMedium(pida, &medium);
|
||
|
goto Exit;
|
||
|
}
|
||
|
}
|
||
|
DoneWithData:
|
||
|
HIDA_ReleaseStgMedium(pida, &medium);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// see if there is a rename mapping from recycle bin (or someone else)
|
||
|
ASSERT(fmte.cfFormat == g_cfFileNameMapW);
|
||
|
|
||
|
hres = pfsthp->pDataObj->lpVtbl->GetData(pfsthp->pDataObj, &fmte, &medium);
|
||
|
if (hres != S_OK) {
|
||
|
fmte.cfFormat = g_cfFileNameMapA;
|
||
|
hres = pfsthp->pDataObj->lpVtbl->GetData(pfsthp->pDataObj, &fmte, &medium);
|
||
|
}
|
||
|
|
||
|
if (hres == S_OK) {
|
||
|
DebugMsg(TF_FSTREE, TEXT("Got rename mapping"));
|
||
|
|
||
|
if (fmte.cfFormat == g_cfFileNameMapW)
|
||
|
pszDestNames = RemapDestNamesW(pszPath, (LPWSTR)GlobalLock(medium.hGlobal));
|
||
|
else
|
||
|
pszDestNames = RemapDestNamesA(pszPath, (LPSTR)GlobalLock(medium.hGlobal));
|
||
|
|
||
|
if (pszDestNames) {
|
||
|
fo.pTo = pszDestNames;
|
||
|
fo.fFlags |= FOF_MULTIDESTFILES;
|
||
|
// HACK, this came from the recycle bin, don't allow undo
|
||
|
fo.fFlags &= ~FOF_ALLOWUNDO;
|
||
|
#ifdef DEBUG
|
||
|
{
|
||
|
UINT cFrom = 0, cTo = 0;
|
||
|
LPCTSTR pszTemp;
|
||
|
for (pszTemp = fo.pTo; *pszTemp; pszTemp += lstrlen(pszTemp) + 1)
|
||
|
cTo++;
|
||
|
for (pszTemp = fo.pFrom; *pszTemp; pszTemp += lstrlen(pszTemp) + 1)
|
||
|
cFrom++;
|
||
|
|
||
|
AssertMsg(cFrom == cTo, TEXT("dest count does not equal source"));
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
ReleaseStgMediumHGLOBAL(medium.hGlobal, &medium);
|
||
|
}
|
||
|
|
||
|
// Check if there were any errors
|
||
|
if (SHFileOperation(&fo) == 0 && !fo.fAnyOperationsAborted) {
|
||
|
if (pfsthp->fBkDropTarget)
|
||
|
ShellFolderView_SetRedraw(pfsthp->hwndOwner, 0);
|
||
|
|
||
|
SHChangeNotifyHandleEvents(); // force update now
|
||
|
if (pfsthp->fBkDropTarget) {
|
||
|
FS_MoveSelectIcons(pfsthp, fo.hNameMappings, pszDestNames, pfsthp->fDragDrop);
|
||
|
ShellFolderView_SetRedraw(pfsthp->hwndOwner, 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 (pfsthp->dwEffect == DROPEFFECT_MOVE)
|
||
|
BBCheckRestoredFiles(di.lpFileList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
Exit:
|
||
|
SHFree(di.lpFileList);
|
||
|
}
|
||
|
|
||
|
STDAPI_(VOID) FreeFSThreadParam(FSTHREADPARAM* pfsthp)
|
||
|
{
|
||
|
ASSERT(NULL != pfsthp);
|
||
|
ASSERT(NULL != pfsthp->pDataObj);
|
||
|
|
||
|
ATOMICRELEASE(pfsthp->pDataObj);
|
||
|
ATOMICRELEASE(pfsthp->pstmDataObj);
|
||
|
|
||
|
ILFree(pfsthp->pidl);
|
||
|
|
||
|
LocalFree((HLOCAL)pfsthp);
|
||
|
}
|
||
|
|
||
|
const UINT c_rgFolderShortcutTargets[] = {
|
||
|
CSIDL_STARTMENU,
|
||
|
CSIDL_COMMON_STARTMENU,
|
||
|
CSIDL_PROGRAMS,
|
||
|
CSIDL_COMMON_PROGRAMS,
|
||
|
CSIDL_NETHOOD,
|
||
|
-1
|
||
|
};
|
||
|
|
||
|
|
||
|
BOOL _ShouldCreateFolderShortcut(LPCTSTR pszFolder)
|
||
|
{
|
||
|
return PathIsEqualOrSubFolderOf(c_rgFolderShortcutTargets, pszFolder);
|
||
|
}
|
||
|
|
||
|
|
||
|
// This is the entry of "drop thread"
|
||
|
DWORD CALLBACK FileDropTargetThreadProc(void* pv)
|
||
|
{
|
||
|
FSTHREADPARAM* pfsthp = (FSTHREADPARAM*)pv;
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
// Sleep(10 * 1000); // to debug async case
|
||
|
|
||
|
if (pfsthp->pDataObj == NULL) {
|
||
|
CoGetInterfaceAndReleaseStream(pfsthp->pstmDataObj, &IID_IDataObject, (void**)&pfsthp->pDataObj);
|
||
|
pfsthp->pstmDataObj = NULL;
|
||
|
}
|
||
|
|
||
|
if (pfsthp->pDataObj) {
|
||
|
STGMEDIUM medium;
|
||
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
|
||
|
// If the link is the only choice and this is a default drag & drop,
|
||
|
// and it is not forced by the user, we should tell the user.
|
||
|
|
||
|
if (((pfsthp->grfKeyState & (MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_ALT)) ==
|
||
|
MK_LBUTTON) && pfsthp->fLinkOnly) {
|
||
|
|
||
|
// Note that we can not pass hwnd, because it might
|
||
|
// not be activated.
|
||
|
|
||
|
|
||
|
// BUGBUG: This blocks the main UI thread. Other shell windows don't paint
|
||
|
// when this is up. -BryanSt It's necessary to move this call to a background
|
||
|
// thread always.
|
||
|
UINT idMBox = ShellMessageBox(HINST_THISDLL, pfsthp->hwndOwner,
|
||
|
MAKEINTRESOURCE(IDS_WOULDYOUCREATELINK),
|
||
|
MAKEINTRESOURCE(IDS_LINKTITLE),
|
||
|
MB_YESNO | MB_ICONQUESTION);
|
||
|
|
||
|
ASSERT(pfsthp->dwEffect == DROPEFFECT_LINK);
|
||
|
|
||
|
if (idMBox != IDYES)
|
||
|
pfsthp->dwEffect = 0;
|
||
|
}
|
||
|
|
||
|
switch (pfsthp->dwEffect) {
|
||
|
case DROPEFFECT_MOVE:
|
||
|
case DROPEFFECT_COPY:
|
||
|
hr = pfsthp->pDataObj->lpVtbl->GetData(pfsthp->pDataObj, &fmte, &medium);
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
AssertMsg((NULL != pfsthp->hwndOwner), TEXT("You are calling _HandleMoveOrCopy() with out an hwnd which will prevent us from displaying insert disk UI"));
|
||
|
|
||
|
_HandleMoveOrCopy(pfsthp, (HDROP)medium.hGlobal, pfsthp->szPath);
|
||
|
ReleaseStgMedium(&medium);
|
||
|
}
|
||
|
break;
|
||
|
case DROPEFFECT_LINK:
|
||
|
{
|
||
|
int i;
|
||
|
UINT uCreateFlags = 0;
|
||
|
LPITEMIDLIST* ppidl;
|
||
|
|
||
|
if (pfsthp->fBkDropTarget) {
|
||
|
i = DataObj_GetHIDACount(pfsthp->pDataObj);
|
||
|
ppidl = (void*)LocalAlloc(LPTR, SIZEOF(LPITEMIDLIST) * i);
|
||
|
} else
|
||
|
ppidl = NULL;
|
||
|
|
||
|
if (pfsthp->grfKeyState)
|
||
|
uCreateFlags = SHCL_USETEMPLATE;
|
||
|
|
||
|
if (_ShouldCreateFolderShortcut(pfsthp->szPath))
|
||
|
uCreateFlags |= SHCL_MAKEFOLDERSHORTCUT;
|
||
|
|
||
|
ShellFolderView_SetRedraw(pfsthp->hwndOwner, FALSE);
|
||
|
// passing ppidl == NULL is correct in failure case
|
||
|
hr = SHCreateLinks(pfsthp->hwndOwner, pfsthp->szPath, pfsthp->pDataObj, uCreateFlags, ppidl);
|
||
|
if (ppidl) {
|
||
|
FS_PositionItems(pfsthp->hwndOwner, i, ppidl, pfsthp->pDataObj, &pfsthp->ptDrop, TRUE);
|
||
|
FS_FreeMoveCopyList(ppidl, i);
|
||
|
}
|
||
|
ShellFolderView_SetRedraw(pfsthp->hwndOwner, TRUE);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr) && pfsthp->dwEffect) {
|
||
|
DataObj_SetDWORD(pfsthp->pDataObj, g_cfLogicalPerformedDropEffect, pfsthp->dwEffect);
|
||
|
DataObj_SetDWORD(pfsthp->pDataObj, g_cfPerformedDropEffect, pfsthp->dwEffect);
|
||
|
}
|
||
|
|
||
|
SHChangeNotifyHandleEvents(); // force update now
|
||
|
}
|
||
|
|
||
|
FreeFSThreadParam(pfsthp);
|
||
|
CoFreeUnusedLibraries();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
STDAPI_(BOOL) AllRegisteredPrograms(HDROP hDrop)
|
||
|
{
|
||
|
UINT i;
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
|
||
|
for (i = 0; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); i++) {
|
||
|
if (!PathIsRegisteredProgram(szPath))
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#ifdef SYNC_BRIEFCASE
|
||
|
|
||
|
BOOL IsBriefcaseRoot(IDataObject* pDataObj)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
STGMEDIUM medium;
|
||
|
LPIDA pida = DataObj_GetHIDA(pDataObj, &medium);
|
||
|
if (pida) {
|
||
|
// Is there a briefcase root in this pDataObj?
|
||
|
IShellFolder2* psf;
|
||
|
LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1);
|
||
|
if (pidlParent &&
|
||
|
SUCCEEDED(SHBindToObject(NULL, &IID_IShellFolder2, pidlParent, (void**)&psf))) {
|
||
|
UINT i;
|
||
|
for (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->lpVtbl->Release(psf);
|
||
|
}
|
||
|
HIDA_ReleaseStgMedium(pida, &medium);
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Returns:
|
||
|
// If the data object does NOT contain HDROP -> "none"
|
||
|
// else if the source is root or registered progam -> "link"
|
||
|
// else if this is within a volume -> "move"
|
||
|
// else if this is a briefcase -> "move"
|
||
|
// else -> "copy"
|
||
|
|
||
|
DWORD _PickDefFSOperation(CIDLDropTarget* this)
|
||
|
{
|
||
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
DWORD dwDefEffect = 0; // assume no HDROP
|
||
|
STGMEDIUM medium;
|
||
|
|
||
|
if (SUCCEEDED(this->pdtobj->lpVtbl->GetData(this->pdtobj, &fmte, &medium))) {
|
||
|
TCHAR szPath[MAX_PATH], szFolder[MAX_PATH];
|
||
|
|
||
|
CIDLDropTarget_GetPath(this, szFolder);
|
||
|
DragQueryFile(medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)); // focused item
|
||
|
|
||
|
// Determine the default operation depending on the item.
|
||
|
if (PathIsRoot(szPath) || AllRegisteredPrograms(medium.hGlobal)) {
|
||
|
dwDefEffect = DROPEFFECT_LINK;
|
||
|
} else if (PathIsSameRoot(szPath, szFolder)) {
|
||
|
dwDefEffect = DROPEFFECT_MOVE;
|
||
|
} else if (IsBriefcaseRoot(this->pdtobj)) {
|
||
|
// a briefcase is in the data object
|
||
|
// default to "move" even if across volumes
|
||
|
DebugMsg(TF_FSTREE, TEXT("FS::Drop the object is the briefcase"));
|
||
|
dwDefEffect = DROPEFFECT_MOVE;
|
||
|
} else {
|
||
|
dwDefEffect = DROPEFFECT_COPY;
|
||
|
}
|
||
|
ReleaseStgMedium(&medium);
|
||
|
} else // if (SUCCEEDED(...))
|
||
|
{
|
||
|
// GetData failed. Let's see if QueryGetData failed or not.
|
||
|
|
||
|
if (SUCCEEDED(this->pdtobj->lpVtbl->QueryGetData(this->pdtobj, &fmte))) {
|
||
|
// this means this data object has HDROP but can't
|
||
|
// provide it until it is dropped. Let's assume we are copying.
|
||
|
dwDefEffect = DROPEFFECT_COPY;
|
||
|
}
|
||
|
}
|
||
|
return dwDefEffect;
|
||
|
}
|
||
|
|
||
|
|
||
|
// make sure that the default effect is among the allowed effects
|
||
|
|
||
|
DWORD _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;
|
||
|
}
|
||
|
|
||
|
// {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};
|
||
|
|
||
|
|
||
|
// This function returns the default effect.
|
||
|
// This function also modified *pdwEffect to indicate "available" operation.
|
||
|
|
||
|
DWORD CFSIDLDropTarget_GetDefaultEffect(CIDLDropTarget* this, DWORD grfKeyState, LPDWORD pdwEffectInOut, UINT* pidMenu)
|
||
|
{
|
||
|
DWORD dwDefEffect;
|
||
|
UINT idMenu = POPUP_NONDEFAULTDD;
|
||
|
DWORD dwEffectAvail = 0;
|
||
|
|
||
|
|
||
|
// First try file system operation (HDROP).
|
||
|
|
||
|
if (this->dwData & DTID_HDROP) {
|
||
|
|
||
|
// If HDROP exists, ignore the rest of formats.
|
||
|
|
||
|
dwEffectAvail |= DROPEFFECT_COPY | DROPEFFECT_MOVE;
|
||
|
|
||
|
|
||
|
// We don't support 'links' from HDROP (only from HIDA).
|
||
|
// This is a known limitation and we have no plan to implement
|
||
|
// it for Win95.
|
||
|
|
||
|
if (this->dwData & DTID_HIDA)
|
||
|
dwEffectAvail |= DROPEFFECT_LINK;
|
||
|
|
||
|
dwDefEffect = _PickDefFSOperation(this);
|
||
|
|
||
|
ASSERT(dwDefEffect);
|
||
|
} else {
|
||
|
BOOL fContents = ((this->dwData & (DTID_CONTENTS | DTID_FDESCA)) == (DTID_CONTENTS | DTID_FDESCA) ||
|
||
|
(this->dwData & (DTID_CONTENTS | DTID_FDESCW)) == (DTID_CONTENTS | DTID_FDESCW));
|
||
|
|
||
|
if (fContents || (this->dwData & DTID_HIDA)) {
|
||
|
if (this->dwData & DTID_HIDA) {
|
||
|
dwEffectAvail = DROPEFFECT_LINK;
|
||
|
dwDefEffect = DROPEFFECT_LINK;
|
||
|
}
|
||
|
|
||
|
if (fContents) {
|
||
|
|
||
|
// 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 ((this->dwData & (DTID_PREFERREDEFFECT | DTID_HIDA)) ==
|
||
|
DTID_PREFERREDEFFECT) {
|
||
|
dwEffectAvail = this->dwEffectPreferred;
|
||
|
// dwDefEffect will be set below
|
||
|
} else if (this->dwData & DTID_FD_LINKUI) {
|
||
|
dwEffectAvail = DROPEFFECT_LINK;
|
||
|
dwDefEffect = DROPEFFECT_LINK;
|
||
|
} else {
|
||
|
dwEffectAvail |= DROPEFFECT_COPY | DROPEFFECT_MOVE;
|
||
|
dwDefEffect = DROPEFFECT_COPY;
|
||
|
}
|
||
|
idMenu = POPUP_FILECONTENTS;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// BUGBUG this should be moved to OLE's clipboard/dataobject code
|
||
|
// (ie anybody provides these formats and OLE provides FILECONTENTS)
|
||
|
// this will be more important as random containers get implemented
|
||
|
|
||
|
if (!dwEffectAvail) {
|
||
|
BOOL fPackage = FALSE;
|
||
|
if (this->dwData & DTID_EMBEDDEDOBJECT) {
|
||
|
FORMATETC fmte = {g_cfObjectDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
STGMEDIUM medium;
|
||
|
ASSERT(NULL != this->pdtobj);
|
||
|
if (SUCCEEDED(this->pdtobj->lpVtbl->GetData(this->pdtobj, &fmte, &medium))) {
|
||
|
// we've got an object descriptor
|
||
|
OBJECTDESCRIPTOR* pOD = GlobalLock(medium.hGlobal);
|
||
|
if (pOD) {
|
||
|
if (IsEqualGUID(&CLSID_OldPackage, &pOD->clsid) ||
|
||
|
IsEqualGUID(&CLSID_CPackage, &pOD->clsid)) {
|
||
|
dwEffectAvail |= DROPEFFECT_COPY | DROPEFFECT_MOVE;
|
||
|
dwDefEffect = DROPEFFECT_COPY;
|
||
|
idMenu = POPUP_EMBEDDEDOBJECT;
|
||
|
fPackage = TRUE;
|
||
|
}
|
||
|
GlobalUnlock(medium.hGlobal);
|
||
|
}
|
||
|
ReleaseStgMedium(&medium);
|
||
|
}
|
||
|
}
|
||
|
if (!fPackage) {
|
||
|
|
||
|
// Try scrap and doc-shortcut
|
||
|
|
||
|
if (this->dwData & DTID_OLELINK) {
|
||
|
dwEffectAvail |= DROPEFFECT_LINK;
|
||
|
dwDefEffect = DROPEFFECT_LINK;
|
||
|
idMenu = POPUP_SCRAP;
|
||
|
}
|
||
|
|
||
|
if (this->dwData & DTID_OLEOBJ) {
|
||
|
dwEffectAvail |= DROPEFFECT_COPY | DROPEFFECT_MOVE;
|
||
|
dwDefEffect = DROPEFFECT_COPY;
|
||
|
idMenu = POPUP_SCRAP;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pdwEffectInOut &= dwEffectAvail;
|
||
|
|
||
|
|
||
|
// Alter the default effect depending on modifier keys.
|
||
|
|
||
|
switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT)) {
|
||
|
case MK_CONTROL: dwDefEffect = DROPEFFECT_COPY; break;
|
||
|
case MK_SHIFT: dwDefEffect = DROPEFFECT_MOVE; break;
|
||
|
case MK_SHIFT | MK_CONTROL: dwDefEffect = DROPEFFECT_LINK; break;
|
||
|
case MK_ALT: dwDefEffect = DROPEFFECT_LINK; break;
|
||
|
default:
|
||
|
|
||
|
// no modifier keys:
|
||
|
// if the data object contains a preferred drop effect, try to use it
|
||
|
|
||
|
if (this->dwData & DTID_PREFERREDEFFECT) {
|
||
|
DWORD dwPreferred = this->dwEffectPreferred & dwEffectAvail;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pidMenu)
|
||
|
*pidMenu = idMenu;
|
||
|
|
||
|
DebugMsg(TF_FSTREE, TEXT("CFSDT::GetDefaultEffect dwD=%x, *pdw=%x, idM=%d"),
|
||
|
dwDefEffect, *pdwEffectInOut, idMenu);
|
||
|
|
||
|
return _LimitDefaultEffect(dwDefEffect, *pdwEffectInOut);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL IsInsideBriefcase(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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
Purpose: Determines if pidl listed in the hida is a briefcase
|
||
|
on removable media
|
||
|
|
||
|
Returns: TRUE if the above is true
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL IsFromSneakernetBriefcase(LPCITEMIDLIST pidlSource, LPCTSTR pszTarget)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
TCHAR szSource[MAX_PATH];
|
||
|
|
||
|
if (SHGetPathFromIDList(pidlSource, szSource)) {
|
||
|
// is source on removable device?
|
||
|
if (IsRemovableDrive(DRIVEID(szSource))) {
|
||
|
// is the target fixed media?
|
||
|
if (PathIsUNC(pszTarget) || !IsRemovableDrive(DRIVEID(pszTarget))) {
|
||
|
bRet = IsInsideBriefcase(pidlSource);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL HandleSneakernetDrop(FSTHREADPARAM* pfsthp, LPCITEMIDLIST pidlParent, LPCTSTR pszTarget)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
ASSERT(pidlParent);
|
||
|
ASSERT(pszTarget);
|
||
|
|
||
|
// Is it being dragged from a mobile briefcase?
|
||
|
if ((DROPEFFECT_COPY == pfsthp->dwEffect) && pfsthp->bSyncCopy) {
|
||
|
// Yes
|
||
|
IBriefcaseStg* pbrfstg;
|
||
|
|
||
|
// Perform a sneakernet addition to the briefcase
|
||
|
if (SUCCEEDED(BrfStg_CreateInstance(pidlParent, pfsthp->hwndOwner, &pbrfstg))) {
|
||
|
// (Even if AddObject fails, return TRUE to prevent caller
|
||
|
// from handling this)
|
||
|
bRet = (S_FALSE != pbrfstg->lpVtbl->AddObject(pbrfstg, pfsthp->pDataObj, pszTarget,
|
||
|
(DDIDM_SYNCCOPYTYPE == pfsthp->idCmd) ? AOF_FILTERPROMPT : AOF_DEFAULT,
|
||
|
pfsthp->hwndOwner));
|
||
|
pbrfstg->lpVtbl->Release(pbrfstg);
|
||
|
}
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
extern BOOL DroppingAnyFolders(HDROP hDrop);
|
||
|
|
||
|
void SneakernetHook(IDataObject* pdtobj, LPCTSTR pszTarget, UINT* pidMenu, BOOL* pbSyncCopy)
|
||
|
{
|
||
|
// Is this the sneakernet case?
|
||
|
STGMEDIUM medium;
|
||
|
LPIDA pida;
|
||
|
|
||
|
// (Default: leave *pidMenu as it is passed in)
|
||
|
*pbSyncCopy = FALSE; // Default
|
||
|
|
||
|
pida = DataObj_GetHIDA(pdtobj, &medium);
|
||
|
if (pida) {
|
||
|
LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1);
|
||
|
if (pidlParent) {
|
||
|
if (IsFromSneakernetBriefcase(pidlParent, pszTarget)) {
|
||
|
// Yes; show the non-default briefcase cm
|
||
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||
|
STGMEDIUM mediumT;
|
||
|
|
||
|
if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &mediumT))) {
|
||
|
if (DroppingAnyFolders(mediumT.hGlobal))
|
||
|
*pidMenu = POPUP_BRIEFCASE_FOLDER_NONDEFAULTDD; // Yes
|
||
|
else
|
||
|
*pidMenu = POPUP_BRIEFCASE_NONDEFAULTDD; // No
|
||
|
|
||
|
*pbSyncCopy = TRUE;
|
||
|
ReleaseStgMedium(&mediumT);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
HIDA_ReleaseStgMedium(pida, &medium);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// BUGBUG:
|
||
|
// 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;
|
||
|
|
||
|
// NT #391669: pszHTML isn't terminated, so terminate it now.
|
||
|
LPSTR pszCopiedHTML = (LPSTR)LocalAlloc(LPTR, cbHTMLSize + 1);
|
||
|
if (pszCopiedHTML) {
|
||
|
LPSTR szBase;
|
||
|
LPSTR szImgSrc;
|
||
|
LPSTR szTemp;
|
||
|
DWORD dwLen = dwSize;
|
||
|
BOOL bRet = TRUE;
|
||
|
LPSTR szImgSrcOrig;
|
||
|
|
||
|
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
|
||
|
|
||
|
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
|
||
|
szImgSrc = StrStrIA(szTemp, "IMG");
|
||
|
if (szImgSrc != NULL) {
|
||
|
szImgSrc = StrStrIA(szImgSrc, "SRC");
|
||
|
if (szImgSrc != NULL) {
|
||
|
szImgSrcOrig = szImgSrc;
|
||
|
szImgSrc = StrChrA(szImgSrc, '\"');
|
||
|
if (szImgSrc) {
|
||
|
szImgSrc++; // Skip over the quote at the beginning of the src path.
|
||
|
szTemp = StrChrA(szImgSrc, '\"'); // Find the end of the path.
|
||
|
} else {
|
||
|
LPSTR pszTemp1;
|
||
|
LPSTR pszTemp2;
|
||
|
|
||
|
szImgSrc = StrChrA(szImgSrcOrig, '=');
|
||
|
szImgSrc++; // Skip past the equals to the first char in the path.
|
||
|
// Someday we may need to handle spaces between '=' and the path.
|
||
|
|
||
|
pszTemp1 = StrChrA(szImgSrc, ' '); // Since the path doesn't have quotes around it, assume a space will terminate it.
|
||
|
pszTemp2 = StrChrA(szImgSrc, '>'); // 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(szImgSrc, URLIS_URL) && szBase) {
|
||
|
if (SUCCEEDED(UrlCombineA(szBase, szImgSrc, szImg, &dwLen, 0)))
|
||
|
fSucceeded = TRUE;
|
||
|
} else {
|
||
|
if (lstrlenA(szImgSrc) <= (int)dwSize)
|
||
|
lstrcpyA(szImg, szImgSrc);
|
||
|
|
||
|
fSucceeded = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LocalFree(pszCopiedHTML);
|
||
|
}
|
||
|
|
||
|
return fSucceeded;
|
||
|
}
|