Windows2000/private/shell/shell32/brfcase.c
2020-09-30 17:12:32 +02:00

2577 lines
73 KiB
C

// Copyright (c) Microsoft Corporation 1993-1994
// File: brfcase.c
// This file contains the briefcase-style file synchronization code
// made integral with the shell.
// History:
// 08-03-93 ScottH Created
// 01-27-94 ScottH Changed for OLE 2-style and moniker ID list
#include "shellprv.h"
#pragma hdrstop
#include <limits.h>
#include <brfcasep.h>
#include "fstreex.h"
#include "brfcase.h"
#include "datautil.h"
#include "prop.h"
// forward decl.
STDMETHODIMP Briefcase_GetDetailsOf(BRFEXP * pbrfexp, IBriefcaseStg *pbrfstg,
HWND hwnd, HANDLE hMutexDelay,
LPCITEMIDLIST pidl, UINT iColumn,
SHELLDETAILS *pDetails );
HRESULT BrfStg_CreateInstance(LPCITEMIDLIST pidl, HWND hwnd, void **ppvOut)
{
HRESULT hres = E_OUTOFMEMORY;
TCHAR szFolder[MAX_PATH];
// Create an instance of IBriefcaseStg
if (SHGetPathFromIDList(pidl, szFolder))
{
IBriefcaseStg *pbrfstg;
hres = CoCreateInstance(&CLSID_Briefcase, NULL, CLSCTX_INPROC_SERVER, &IID_IBriefcaseStg, &pbrfstg);
if (SUCCEEDED(hres))
{
hres = pbrfstg->lpVtbl->Initialize(pbrfstg, szFolder, hwnd);
if (SUCCEEDED(hres))
{
hres = pbrfstg->lpVtbl->QueryInterface(pbrfstg, &IID_IBriefcaseStg, ppvOut);
}
pbrfstg->lpVtbl->Release(pbrfstg);
}
}
return hres; // S_OK or E_NOINTERFACE
}
// CFSBrfIDLData
UINT g_cfBriefObj = 0;
UINT _GetBriefObjCF()
{
if (g_cfBriefObj == 0)
{
g_cfBriefObj = RegisterClipboardFormat(CFSTR_BRIEFOBJECT);
}
return g_cfBriefObj;
}
/*
Purpose: Gets the root path of the briefcase storage and copies
it into the buffer.
This function obtains the briefcase storage root by
binding to an IShellFolder (briefcase) instance of the
pidl. This parent is be an CFSBrfFolder *, so we can
call the IBriefcaseStg::GetExtraInfo member function.
Returns: standard result
Cond: --
*/
HRESULT GetBriefcaseRoot(LPCITEMIDLIST pidl, LPTSTR pszBuf, int cchBuf)
{
IBriefcaseStg *pbrfstg;
HRESULT hres = BrfStg_CreateInstance(pidl, NULL, &pbrfstg);
if (SUCCEEDED(hres))
{
hres = pbrfstg->lpVtbl->GetExtraInfo(pbrfstg, NULL, GEI_ROOT, (WPARAM)cchBuf, (LPARAM)pszBuf);
pbrfstg->lpVtbl->Release(pbrfstg);
}
return hres;
}
// Creates an IDL of the root path of the briefcase storage.
BOOL ILGetBriefcaseRoot(IBriefcaseStg *pbrfstg, LPITEMIDLIST *ppidlRoot)
{
BOOL bRet = FALSE;
TCHAR sz[MAX_PATH];
if (SUCCEEDED(pbrfstg->lpVtbl->GetExtraInfo(pbrfstg, NULL, GEI_ROOT, (WPARAM)ARRAYSIZE(sz), (LPARAM)sz)))
{
bRet = (NULL != (*ppidlRoot = ILCreateFromPath(sz)));
}
return bRet;
}
/*
Purpose: Packages a BriefObj struct into pmedium from a HIDA.
Returns: standard
Cond: --
*/
HRESULT CFSBrfIDLData_GetBriefObj(IDataObject *pdtobj, LPSTGMEDIUM pmedium)
{
HRESULT hres = E_OUTOFMEMORY;
LPITEMIDLIST pidl = ILCreate();
if (pidl)
{
STGMEDIUM medium;
UINT cFiles, cbSize;
PBRIEFOBJ pbo;
if (DataObj_GetHIDA(pdtobj, &medium))
{
ASSERT(medium.hGlobal);
cFiles = HIDA_GetCount(medium.hGlobal);
// "cFiles+1" includes the briefpath...
cbSize = SIZEOF(BriefObj) + MAX_PATH * SIZEOF(TCHAR) * (cFiles + 1) + 1;
pbo = GlobalAlloc(GPTR, cbSize);
if (pbo)
{
LPITEMIDLIST pidlT;
LPTSTR pszFiles = (LPTSTR)((LPBYTE)pbo + _IOffset(BriefObj, data));
UINT i;
pbo->cbSize = cbSize;
pbo->cItems = cFiles;
pbo->cbListSize = MAX_PATH*SIZEOF(TCHAR)*cFiles + 1;
pbo->ibFileList = _IOffset(BriefObj, data);
for (i = 0; i < cFiles; i++)
{
pidlT = HIDA_FillIDList(medium.hGlobal, i, pidl);
if (NULL == pidlT)
break; // out of memory
pidl = pidlT;
SHGetPathFromIDList(pidl, pszFiles);
pszFiles += lstrlen(pszFiles)+1;
}
*pszFiles = TEXT('\0');
if (i < cFiles)
{
// Out of memory, fail
ASSERT(NULL == pidlT);
}
else
{
// Make pszFiles point to beginning of szBriefPath buffer
pszFiles++;
pbo->ibBriefPath = (UINT) ((LPBYTE)pszFiles - (LPBYTE)pbo);
pidlT = HIDA_FillIDList(medium.hGlobal, 0, pidl);
if (pidlT)
{
pidl = pidlT;
hres = GetBriefcaseRoot(pidl, pszFiles, MAX_PATH);
pmedium->tymed = TYMED_HGLOBAL;
pmedium->hGlobal = pbo;
// Indicate that the caller should release hmem.
pmedium->pUnkForRelease = NULL;
}
}
}
HIDA_ReleaseStgMedium(NULL, &medium);
}
ILFree(pidl);
}
return hres;
}
// IDataObject::GetData
STDMETHODIMP CFSBrfIDLData_GetData(IDataObject *pdtobj,
LPFORMATETC pformatetcIn,
LPSTGMEDIUM pmedium) // put data in here
{
HRESULT hres = E_INVALIDARG;
if (pformatetcIn->cfFormat == _GetBriefObjCF() && (pformatetcIn->tymed & TYMED_HGLOBAL))
{
hres = CFSBrfIDLData_GetBriefObj(pdtobj, pmedium);
}
else
{
hres = CFSIDLData_GetData(pdtobj, pformatetcIn, pmedium);
}
return hres;
}
// IDataObject::QueryGetData
STDMETHODIMP CFSBrfIDLData_QueryGetData(IDataObject *pdtobj,
LPFORMATETC pformatetc)
{
if (pformatetc->cfFormat == _GetBriefObjCF() && (pformatetc->tymed & TYMED_HGLOBAL))
return NOERROR;
return CFSIDLData_QueryGetData(pdtobj, pformatetc);
}
const IDataObjectVtbl c_CFSBrfIDLDataVtbl = {
CIDLData_QueryInterface, CIDLData_AddRef, CIDLData_Release,
CFSBrfIDLData_GetData, // special member function
CIDLData_GetDataHere,
CFSBrfIDLData_QueryGetData, // special member function
CIDLData_GetCanonicalFormatEtc,
CIDLData_SetData,
CIDLData_EnumFormatEtc,
CIDLData_Advise,
CIDLData_Unadvise,
CIDLData_EnumAdvise
};
enum
{
ICOL_BRIEFCASE_NAME = 0,
ICOL_BRIEFCASE_ORIGIN,
ICOL_BRIEFCASE_STATUS,
ICOL_BRIEFCASE_SIZE,
ICOL_BRIEFCASE_TYPE,
ICOL_BRIEFCASE_MODIFIED,
};
const COL_DATA s_briefcase_cols[] = {
{ICOL_BRIEFCASE_NAME, IDS_NAME_COL, 20, LVCFMT_LEFT, &SCID_NAME},
{ICOL_BRIEFCASE_ORIGIN, IDS_ORIGINAL_COL, 24, LVCFMT_LEFT, &SCID_ORIGINALLOCATION},
{ICOL_BRIEFCASE_STATUS, IDS_STATUS_COL, 18, LVCFMT_LEFT, &SCID_STATUS},
{ICOL_BRIEFCASE_SIZE, IDS_SIZE_COL, 8, LVCFMT_LEFT, &SCID_SIZE},
{ICOL_BRIEFCASE_TYPE, IDS_TYPE_COL, 18, LVCFMT_LEFT, &SCID_TYPE},
{ICOL_BRIEFCASE_MODIFIED, IDS_MODIFIED_COL, 18, LVCFMT_LEFT, &SCID_WRITETIME}
};
#define MAX_NAME 32
#define HACK_IGNORETYPE 0x04000000
TCHAR g_szDetailsUnknown[MAX_NAME] = TEXT("");
typedef struct
{
TCHAR szOrigin[MAX_PATH];
TCHAR szStatus[MAX_NAME];
BOOL bDetermined:1;
BOOL bUpToDate:1;
BOOL bDeleted:1;
} BRFINFO, * PBRFINFO;
typedef struct
{
LPITEMIDLIST pidl; // Indexed value
BRFINFO bi;
} BRFINFOHDR, * PBRFINFOHDR;
// Values for BrfExp_FindNextState
#define FNS_UNDETERMINED 1
#define FNS_STALE 2
#define FNS_DELETED 3
#ifdef DEBUG
void BrfExp_EnterCS(PBRFEXP this);
void BrfExp_LeaveCS(PBRFEXP this);
#define BrfExp_AssertInCS(this) ASSERT(0 < (this)->cCSRef)
#define BrfExp_AssertNotInCS(this) ASSERT(0 == (this)->cCSRef)
#else
#define BrfExp_EnterCS(this) EnterCriticalSection(&(this)->cs)
#define BrfExp_LeaveCS(this) LeaveCriticalSection(&(this)->cs)
#define BrfExp_AssertInCS(this)
#define BrfExp_AssertNotInCS(this)
#endif
DWORD CALLBACK BrfExp_CalcThread(PBRFEXP this);
#ifdef DEBUG
void BrfExp_EnterCS(PBRFEXP this)
{
EnterCriticalSection(&this->cs);
this->cCSRef++;
}
void BrfExp_LeaveCS(PBRFEXP this)
{
BrfExp_AssertInCS(this);
this->cCSRef--;
LeaveCriticalSection(&this->cs);
}
#endif
// Brfview functions: Expensive cache stuff
/*
Purpose: Comparison function for the DPA list
Returns: standard
Cond: --
*/
int CALLBACK BrfExp_CompareIDs(void *pv1, void *pv2, LPARAM lParam)
{
IShellFolder *psf = ((PBRFEXP)lParam)->psf;
PBRFINFOHDR pbihdr1 = (PBRFINFOHDR)pv1;
PBRFINFOHDR pbihdr2 = (PBRFINFOHDR)pv2;
HRESULT hres = psf->lpVtbl->CompareIDs(psf, HACK_IGNORETYPE,
pbihdr1->pidl, pbihdr2->pidl);
ASSERT(SUCCEEDED(hres));
return (short)SCODE_CODE(GetScode(hres)); // (the short cast is important!)
}
/*
Purpose: Create the secondary thread for the expensive cache
Returns: TRUE on success
Cond: --
*/
BOOL BrfExp_CreateThread(PBRFEXP this)
{
BOOL bRet = FALSE;
DWORD idThread;
// The semaphore is used to determine whether anything
// needs to be refreshed in the cache.
this->hSemPending = CreateSemaphore(NULL, 0, INT_MAX, NULL);
if (this->hSemPending)
{
#ifdef DEBUG
this->cStale = 0;
this->cUndetermined = 0;
this->cDeleted = 0;
#endif
ASSERT(NULL == this->hEventDie);
this->hEventDie = CreateEvent(NULL, FALSE, FALSE, NULL);
if (this->hEventDie)
{
// Create the thread that will calculate expensive data
this->hThreadPaint = CreateThread(NULL, 0, (LPVOID)BrfExp_CalcThread, this, CREATE_SUSPENDED, &idThread);
if (this->hThreadPaint)
{
ResumeThread(this->hThreadPaint);
bRet = TRUE;
}
else
{
CloseHandle(this->hEventDie);
this->hEventDie = NULL;
CloseHandle(this->hSemPending);
this->hSemPending = NULL;
}
}
else
{
CloseHandle(this->hSemPending);
this->hSemPending = NULL;
}
}
return bRet;
}
/*
Purpose: Initialize the cache
Returns: TRUE on success
Cond: --
*/
BOOL BrfExp_Init(PBRFEXP this, IBriefcaseStg *pbrfstg, HWND hwndMain, HANDLE hMutexDelay)
{
BOOL bRet = FALSE;
ASSERT(pbrfstg);
#ifndef WINNT
// The critical section should have been initialized when this
// IShellView's IShellFolder interface was created.
ReinitializeCriticalSection(&this->cs);
#endif
BrfExp_EnterCS(this);
{
if (this->hdpa)
{
bRet = TRUE;
}
else
{
LoadString(HINST_THISDLL, IDS_DETAILSUNKNOWN, g_szDetailsUnknown, SIZEOF(g_szDetailsUnknown));
this->hwndMain = hwndMain;
this->hMutexDelay = hMutexDelay;
this->idpaStaleCur = 0;
this->idpaUndeterminedCur = 0;
this->idpaDeletedCur = 0;
this->hdpa = DPA_Create(8);
if (this->hdpa)
{
bRet = BrfExp_CreateThread(this);
if (bRet)
{
this->pbrfstg = pbrfstg;
pbrfstg->lpVtbl->AddRef(pbrfstg);
}
else
{
// Failed
DPA_Destroy(this->hdpa);
this->hdpa = NULL;
}
}
}
}
BrfExp_LeaveCS(this);
return bRet;
}
/*
Purpose: Clean up the cache of expensive data
Returns: --
Cond: IMPORTANT!! The caller must guarantee it is not
holding the BrfExp critical section before calling
otherwise we can deadlock during MsgWaitObjectsSendMessage below.
*/
void BrfExp_Free(PBRFEXP this)
{
BrfExp_EnterCS(this);
{
if (this->hEventDie)
{
if (this->hThreadPaint)
{
HANDLE hThread = this->hThreadPaint;
SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);
// Signal the secondary thread to end
SetEvent(this->hEventDie);
// Make sure we are not in the critical section when
// we wait for the secondary thread to exit. Without
// this check, hitting F5 twice in a row could deadlock.
BrfExp_LeaveCS(this);
{
// Wait for the threads to exit
BrfExp_AssertNotInCS(this);
WaitForSendMessageThread(hThread, INFINITE);
}
BrfExp_EnterCS(this);
DebugMsg(DM_TRACE, TEXT("Briefcase Secondary thread ended"));
CloseHandle(this->hThreadPaint);
this->hThreadPaint = NULL;
}
CloseHandle(this->hEventDie);
this->hEventDie = NULL;
}
if (this->hdpa)
{
int idpa = DPA_GetPtrCount(this->hdpa);
while (--idpa >= 0)
{
PBRFINFOHDR pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(this->hdpa, idpa);
ILFree(pbihdr->pidl);
LocalFree((HLOCAL)pbihdr);
}
DPA_Destroy(this->hdpa);
this->hdpa = NULL;
}
if (this->hSemPending)
{
CloseHandle(this->hSemPending);
this->hSemPending = NULL;
}
if (this->pbrfstg)
{
this->pbrfstg->lpVtbl->Release(this->pbrfstg);
this->pbrfstg = NULL;
}
}
BrfExp_LeaveCS(this);
}
/*
Purpose: Resets the expensive data cache
Returns: --
Cond: IMPORTANT!! The caller must guarantee it is not
holding the BrfExp critical section before calling
otherwise we can deadlock during MsgWaitObjectsSendMessage below.
*/
void BrfExp_Reset(PBRFEXP this)
{
BrfExp_AssertNotInCS(this);
BrfExp_EnterCS(this);
{
IBriefcaseStg *pbrfstg = this->pbrfstg;
if (FALSE == this->bFreePending && pbrfstg)
{
HWND hwndMain = this->hwndMain;
HANDLE hMutex = this->hMutexDelay;
pbrfstg->lpVtbl->AddRef(pbrfstg);
// Since we won't be in the critical section when we
// wait for the paint thread to exit, set this flag to
// avoid nasty re-entrant calls.
this->bFreePending = TRUE;
// Reset by freeing and reinitializing.
BrfExp_LeaveCS(this);
{
BrfExp_Free(this);
BrfExp_Init(this, pbrfstg, hwndMain, hMutex);
}
BrfExp_EnterCS(this);
this->bFreePending = FALSE;
pbrfstg->lpVtbl->Release(pbrfstg);
}
}
BrfExp_LeaveCS(this);
}
/*
Purpose: Finds a cached name structure and returns a copy of
it in *pbi.
Returns: TRUE if the pidl was found in the cache
FALSE otherwise
Cond: --
*/
BOOL BrfExp_FindCachedName(PBRFEXP this, LPCITEMIDLIST pidl, PBRFINFO pbi)
{
BOOL bRet = FALSE;
ASSERT(pbi);
BrfExp_EnterCS(this);
{
if (this->hdpa)
{
int idpa;
BRFINFOHDR bihdrT;
// Was the pidl found?
bihdrT.pidl = ILFindLastID(pidl);
idpa = DPA_Search(this->hdpa, (LPVOID)&bihdrT, 0, BrfExp_CompareIDs, (LPARAM)this, DPAS_SORTED);
if (DPA_ERR != idpa)
{
// Yes
PBRFINFOHDR pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(this->hdpa, idpa);
ASSERT(pbihdr);
*pbi = pbihdr->bi;
bRet = TRUE;
}
}
}
BrfExp_LeaveCS(this);
return bRet;
}
/*
Purpose: Deletes a cached name structure.
Returns: TRUE if the pidl was found in the cache
FALSE otherwise
Cond: --
*/
BOOL BrfExp_DeleteCachedName(PBRFEXP this, LPCITEMIDLIST pidl)
{
BOOL bRet = FALSE;
BrfExp_EnterCS(this);
{
if (this->hdpa)
{
int idpa;
BRFINFOHDR bihdrT;
// Was the pidl found?
bihdrT.pidl = ILFindLastID(pidl);
idpa = DPA_Search(this->hdpa, (LPVOID)&bihdrT, 0, BrfExp_CompareIDs, (LPARAM)this, DPAS_SORTED);
if (DPA_ERR != idpa)
{
// Yes
#ifdef DEBUG
PBRFINFOHDR pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(this->hdpa, idpa);
ASSERT(pbihdr);
this->cDeleted--;
if (!pbihdr->bi.bDetermined)
this->cUndetermined--;
else if (!pbihdr->bi.bUpToDate)
this->cStale--;
#endif
// Keep index pointers current
if (this->idpaStaleCur >= idpa)
this->idpaStaleCur--;
if (this->idpaUndeterminedCur >= idpa)
this->idpaUndeterminedCur--;
if (this->idpaDeletedCur >= idpa)
this->idpaDeletedCur--;
DPA_DeletePtr(this->hdpa, idpa);
bRet = TRUE;
}
}
}
BrfExp_LeaveCS(this);
return bRet;
}
/*
Purpose: Finds the next cached name structure that matches the
requested state.
Returns: TRUE if an item was found in the cache
FALSE otherwise
Cond: --
*/
BOOL BrfExp_FindNextState(PBRFEXP this,
UINT uState, // FNS_UNDETERMINED, FNS_STALE or FNS_DELETED
PBRFINFOHDR pbihdrOut) // Structure with filled in values
{
BOOL bRet = FALSE;
ASSERT(pbihdrOut);
BrfExp_EnterCS(this);
{
if (this->hdpa)
{
HDPA hdpa = this->hdpa;
int idpaCur;
int idpa;
int cdpaMax;
PBRFINFOHDR pbihdr;
cdpaMax = DPA_GetPtrCount(hdpa);
switch (uState)
{
case FNS_UNDETERMINED:
// Iterate thru the entire list starting at idpa. We roll this
// loop out to be two loops: the first iterates the last portion
// of the list, the second iterates the first portion if the former
// failed to find anything.
idpaCur = this->idpaUndeterminedCur + 1;
for (idpa = idpaCur; idpa < cdpaMax; idpa++)
{
pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(hdpa, idpa);
if (!pbihdr->bi.bDetermined)
{
goto Found; // Found it
}
}
ASSERT(idpaCur <= cdpaMax);
for (idpa = 0; idpa < idpaCur; idpa++)
{
pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(hdpa, idpa);
if (!pbihdr->bi.bDetermined)
{
goto Found; // Found it
}
}
ASSERT(0 == this->cUndetermined);
break;
case FNS_STALE:
// Iterate thru the entire list starting at idpa. We roll this
// loop out to be two loops: the first iterates the last portion
// of the list, the second iterates the first portion if the former
// failed to find anything.
idpaCur = this->idpaStaleCur + 1;
for (idpa = idpaCur; idpa < cdpaMax; idpa++)
{
pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(hdpa, idpa);
if (!pbihdr->bi.bUpToDate)
{
goto Found; // Found it
}
}
ASSERT(idpaCur <= cdpaMax);
for (idpa = 0; idpa < idpaCur; idpa++)
{
pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(hdpa, idpa);
if (!pbihdr->bi.bUpToDate)
{
goto Found; // Found it
}
}
ASSERT(0 == this->cStale);
break;
case FNS_DELETED:
// Iterate thru the entire list starting at idpa. We roll this
// loop out to be two loops: the first iterates the last portion
// of the list, the second iterates the first portion if the former
// failed to find anything.
idpaCur = this->idpaDeletedCur + 1;
for (idpa = idpaCur; idpa < cdpaMax; idpa++)
{
pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(hdpa, idpa);
if (pbihdr->bi.bDeleted)
{
goto Found; // Found it
}
}
ASSERT(idpaCur <= cdpaMax);
for (idpa = 0; idpa < idpaCur; idpa++)
{
pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(hdpa, idpa);
if (pbihdr->bi.bDeleted)
{
goto Found; // Found it
}
}
ASSERT(0 == this->cDeleted);
break;
default:
ASSERT(0); // should never get here
break;
}
goto Done;
Found:
ASSERT(0 <= idpa && idpa < cdpaMax);
// Found the next item of the requested state
switch (uState)
{
case FNS_UNDETERMINED:
this->idpaUndeterminedCur = idpa;
break;
case FNS_STALE:
this->idpaStaleCur = idpa;
break;
case FNS_DELETED:
this->idpaDeletedCur = idpa;
break;
}
*pbihdrOut = *pbihdr;
pbihdrOut->pidl = ILClone(pbihdr->pidl);
if (pbihdrOut->pidl)
bRet = TRUE;
}
Done:;
}
BrfExp_LeaveCS(this);
return bRet;
}
/* Purpose: Recalculates a cached name structure. This can be an expensive operation. */
void BrfExp_CalcCachedName(PBRFEXP this, LPCITEMIDLIST pidl, PBRFINFO pbi)
{
BrfExp_EnterCS(this);
if (this->hdpa && this->pbrfstg)
{
int idpa;
BRFINFOHDR bihdrT;
LPIDFOLDER pidf = (LPIDFOLDER)ILFindLastID(pidl);
IBriefcaseStg *pbrfstg = this->pbrfstg;
pbrfstg->lpVtbl->AddRef(pbrfstg);
// Make sure we're out of the critical section when we call
// the expensive functions!
BrfExp_LeaveCS(this);
{
TCHAR szTmp[MAX_PATH];
FS_CopyName(pidf, szTmp, MAX_PATH);
pbrfstg->lpVtbl->GetExtraInfo(pbrfstg, szTmp,
GEI_ORIGIN, (WPARAM)ARRAYSIZE(pbi->szOrigin), (LPARAM)pbi->szOrigin);
pbrfstg->lpVtbl->GetExtraInfo(pbrfstg, szTmp,
GEI_STATUS, (WPARAM)ARRAYSIZE(pbi->szStatus), (LPARAM)pbi->szStatus);
}
BrfExp_EnterCS(this);
pbrfstg->lpVtbl->Release(pbrfstg);
// Check again if we are valid
if (this->hdpa)
{
// Is the pidl still around so we can update it?
bihdrT.pidl = (LPITEMIDLIST)pidf;
idpa = DPA_Search(this->hdpa, (LPVOID)&bihdrT, 0, BrfExp_CompareIDs, (LPARAM)this, DPAS_SORTED);
if (DPA_ERR != idpa)
{
// Yes; update it
PBRFINFOHDR pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(this->hdpa, idpa);
ASSERT(!pbihdr->bi.bUpToDate || !pbihdr->bi.bDetermined)
// This entry may have been marked for deletion while the
// expensive calculations were in process above. Check for
// it now.
if (pbihdr->bi.bDeleted)
{
BrfExp_DeleteCachedName(this, pidl);
}
else
{
pbihdr->bi = *pbi;
pbihdr->bi.bUpToDate = TRUE;
pbihdr->bi.bDetermined = TRUE;
#ifdef DEBUG
if (!pbi->bDetermined)
this->cUndetermined--;
else if (!pbi->bUpToDate)
this->cStale--;
else
ASSERT(0);
#endif
}
}
}
}
BrfExp_LeaveCS(this);
}
// Finds a cached name structure and marks it stale
void BrfExp_CachedNameIsStale(PBRFEXP this,
LPCITEMIDLIST pidl,
BOOL bDeleted)
{
BrfExp_EnterCS(this);
{
if (this->hdpa)
{
int idpa;
BRFINFOHDR bihdrT;
// Was the pidl found?
bihdrT.pidl = ILFindLastID(pidl);
idpa = DPA_Search(this->hdpa, (LPVOID)&bihdrT, 0, BrfExp_CompareIDs, (LPARAM)this, DPAS_SORTED);
if (DPA_ERR != idpa)
{
// Yes; mark it stale
PBRFINFOHDR pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(this->hdpa, idpa);
ASSERT(pbihdr);
// Is this cached name pending calculation yet?
if (pbihdr->bi.bDetermined && pbihdr->bi.bUpToDate &&
!pbihdr->bi.bDeleted)
{
// No; signal the calculation thread
if (bDeleted)
{
pbihdr->bi.bDeleted = TRUE;
#ifdef DEBUG
this->cDeleted++;
#endif
}
else
{
pbihdr->bi.bUpToDate = FALSE;
#ifdef DEBUG
this->cStale++;
#endif
}
// Notify the calculating thread of an item that is pending
// calculation
ReleaseSemaphore(this->hSemPending, 1, NULL);
}
else if (bDeleted)
{
// Yes; but mark for deletion anyway
pbihdr->bi.bDeleted = TRUE;
#ifdef DEBUG
this->cDeleted++;
#endif
}
}
}
}
BrfExp_LeaveCS(this);
}
/*
Purpose: Marks all cached name structures stale
Returns: --
Cond: --
*/
void BrfExp_AllNamesAreStale(PBRFEXP this)
{
BrfExp_EnterCS(this);
{
if (this->pbrfstg)
{
UINT uFlags;
// Dirty the briefcase storage cache
this->pbrfstg->lpVtbl->Notify(this->pbrfstg, NULL, NOE_DIRTYALL, &uFlags, NULL);
}
}
BrfExp_LeaveCS(this);
// (It is important that we call BrfExp_Reset outside of the critical
// section. Otherwise, we can deadlock when this function is called
// while the secondary thread is calculating (hit F5 twice in a row).)
// Clear the entire expensive data cache
BrfExp_Reset(this);
}
/*
Purpose: Adds a new item with default values to the extra info list
Returns: TRUE on success
Cond: --
*/
BOOL BrfExp_AddCachedName(
PBRFEXP this,
LPCITEMIDLIST pidl,
PBRFINFO pbi,
IBriefcaseStg *pbrfstg,
HWND hwndMain,
HANDLE hMutexDelay)
{
BOOL bRet = FALSE;
ASSERT(pbi);
BrfExp_EnterCS(this);
{
if (this->hdpa || BrfExp_Init(this, pbrfstg, hwndMain, hMutexDelay))
{
PBRFINFOHDR pbihdr;
ASSERT(this->hdpa);
pbihdr = (void*)LocalAlloc(LPTR, SIZEOF(*pbihdr));
if (pbihdr)
{
pbihdr->pidl = ILClone(ILFindLastID(pidl));
if (pbihdr->pidl)
{
int idpa = DPA_AppendPtr(this->hdpa, pbihdr);
if (DPA_ERR != idpa)
{
pbihdr->bi.bUpToDate = FALSE;
pbihdr->bi.bDetermined = FALSE;
pbihdr->bi.bDeleted = FALSE;
lstrcpy(pbihdr->bi.szOrigin, g_szDetailsUnknown);
lstrcpy(pbihdr->bi.szStatus, g_szDetailsUnknown);
#ifdef DEBUG
this->cUndetermined++;
#endif
DPA_Sort(this->hdpa, BrfExp_CompareIDs, (LPARAM)this);
// Notify the calculating thread of an item that is pending
// calculation
ReleaseSemaphore(this->hSemPending, 1, NULL);
*pbi = pbihdr->bi;
bRet = TRUE;
}
else
{
// Failed. Cleanup
ILFree(pbihdr->pidl);
LocalFree((HLOCAL)pbihdr);
}
}
else
{
// Failed. Cleanup
LocalFree((HLOCAL)pbihdr);
}
}
}
}
BrfExp_LeaveCS(this);
return bRet;
}
/*
Purpose: Thread to process the expensive calculations for
details view.
Returns: 0
Cond: --
*/
DWORD CALLBACK BrfExp_CalcThread(PBRFEXP this)
{
BRFINFOHDR bihdr;
HANDLE rghObjPending[2] = {this->hEventDie, this->hSemPending};
HANDLE rghObjDelay[2] = {this->hEventDie, this->hMutexDelay};
DWORD dwRet;
DebugMsg(DM_TRACE, TEXT("Briefcase: Entering paint thread"));
while (TRUE)
{
// Wait for an end event or for a job to do
dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjPending), rghObjPending, FALSE, INFINITE);
if (WAIT_OBJECT_0 == dwRet)
{
// Exit thread
break;
}
else
{
#ifdef DEBUG
BrfExp_EnterCS(this);
{
ASSERT(0 < this->cUndetermined ||
0 < this->cStale ||
0 < this->cDeleted);
}
BrfExp_LeaveCS(this);
#endif
// Now wait for an end event or for the delay-calculation mutex
dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjDelay), rghObjDelay, FALSE, INFINITE);
if (WAIT_OBJECT_0 == dwRet)
{
// Exit thread
break;
}
else
{
// Address deleted entries first
if (BrfExp_FindNextState(this, FNS_DELETED, &bihdr))
{
BrfExp_DeleteCachedName(this, bihdr.pidl);
ILFree(bihdr.pidl);
}
// Calculate undetermined entries before stale entries
// to fill the view as quickly as possible
else if (BrfExp_FindNextState(this, FNS_UNDETERMINED, &bihdr) ||
BrfExp_FindNextState(this, FNS_STALE, &bihdr))
{
BrfExp_CalcCachedName(this, bihdr.pidl, &bihdr.bi);
ShellFolderView_RefreshObject(this->hwndMain, &bihdr.pidl);
ILFree(bihdr.pidl);
}
else
{
ASSERT(0); // Should never get here
}
ReleaseMutex(this->hMutexDelay);
}
}
}
DebugMsg(DM_TRACE, TEXT("Briefcase: Exiting paint thread"));
return 0;
}
// BrfView functions:
// Merge the briefcase menu with the defview's menu bar.
HRESULT BrfView_MergeMenu(PBRFVIEW this, LPQCMINFO pinfo)
{
// Merge the briefcase menu onto the menu that CDefView created.
if (pinfo->hmenu)
{
HMENU hmSync = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_BRIEFCASE));
if (hmSync)
{
Shell_MergeMenus(pinfo->hmenu, hmSync, pinfo->indexMenu,
pinfo->idCmdFirst,
pinfo->idCmdLast, MM_SUBMENUSHAVEIDS);
DestroyMenu(hmSync);
}
}
return NOERROR;
}
/*
Purpose: Get the data object of the root folder of this briefcase.
Returns: standard hresult
Cond: --
*/
HRESULT BrfView_GetRootObject(PBRFVIEW this, HWND hwnd, IDataObject **ppdtobj)
{
HRESULT hres = E_OUTOFMEMORY;
LPITEMIDLIST pidl = ILClone(this->pidlRoot);
if (pidl)
{
LPITEMIDLIST pidlLast = ILClone(ILFindLastID(pidl));
if (pidlLast)
{
ILRemoveLastID(pidl);
hres = CIDLData_CreateFromIDArray2(&c_CFSBrfIDLDataVtbl,
pidl, 1, &pidlLast, (IDataObject **)ppdtobj);
ILFree(pidlLast);
}
ILFree(pidl);
}
return hres;
}
HRESULT BrfView_GetSelectedObjects(IShellFolder *psf, HWND hwnd, IDataObject **ppdtobj)
{
HRESULT hres = E_FAIL;
LPCITEMIDLIST * apidl;
UINT cidl = (UINT) ShellFolderView_GetSelectedObjects(hwnd, &apidl);
if (cidl > 0 && apidl)
{
hres = psf->lpVtbl->GetUIObjectOf(psf, hwnd, cidl, apidl,
&IID_IDataObject, 0, ppdtobj);
// We are not supposed to free apidl
}
else if (-1 == cidl)
{
hres = E_OUTOFMEMORY;
}
if (SUCCEEDED(hres))
{
hres = ResultFromShort((SHORT)cidl);
}
return hres;
}
/*
Purpose: DVM_WINDOWCREATED handler
Returns: --
Cond: --
*/
void BrfView_OnCreate(PBRFVIEW this, HWND hwndMain, HWND hwndView)
{
SHChangeNotifyEntry fsne;
BrfExp_Init(this->pbrfexp, this->pbrfstg, hwndMain, this->hMutexDelay);
// Register an extra SHChangeNotifyRegister for our pidl to try to catch things
// like UpdateDir
fsne.pidl = this->pidl;
fsne.fRecursive = FALSE;
this->uSCNRExtra = SHChangeNotifyRegister(hwndView, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel, SHCNE_DISKEVENTS, WM_DSV_FSNOTIFY, 1, &fsne);
}
// DVM_QUERYFSNOTIFY handler
HRESULT BrfView_OnQueryFSNotify(PBRFVIEW this, SHChangeNotifyEntry * pfsne)
{
// Register to receive global events
pfsne->pidl = NULL;
pfsne->fRecursive = TRUE;
return NOERROR;
}
/*
Purpose: DVM_WINDOWDESTROY handler
Returns: --
Cond: --
*/
void BrfView_OnDestroy(PBRFVIEW this, HWND hwndView)
{
BrfExp_Free(this->pbrfexp);
//need to release pbrfstg as well
if(this->pbrfstg)
{
this->pbrfstg->lpVtbl->Release(this->pbrfstg);
this->pbrfstg = NULL;
}
if (this->uSCNRExtra)
SHChangeNotifyDeregister(this->uSCNRExtra);
}
/*
Purpose: WM_COMMAND handler
Returns: NOERROR
Cond: --
*/
HRESULT BrfView_Command(PBRFVIEW this, IShellFolder *psf, HWND hwnd, UINT uID)
{
IDataObject *pdtobj;
switch (uID)
{
case FSIDM_UPDATEALL:
// Update the entire briefcase
if (SUCCEEDED(BrfView_GetRootObject(this, hwnd, &pdtobj)))
{
this->pbrfstg->lpVtbl->UpdateObject(this->pbrfstg, pdtobj, hwnd);
pdtobj->lpVtbl->Release(pdtobj);
}
break;
case FSIDM_UPDATESELECTION:
// Update the selected objects
if (SUCCEEDED(BrfView_GetSelectedObjects(psf, hwnd, &pdtobj)))
{
this->pbrfstg->lpVtbl->UpdateObject(this->pbrfstg, pdtobj, hwnd);
pdtobj->lpVtbl->Release(pdtobj);
}
break;
case FSIDM_SPLIT:
// Split the selected objects
if (SUCCEEDED(BrfView_GetSelectedObjects(psf, hwnd, &pdtobj)))
{
this->pbrfstg->lpVtbl->ReleaseObject(this->pbrfstg, pdtobj, hwnd);
pdtobj->lpVtbl->Release(pdtobj);
}
break;
}
return NOERROR;
}
// DVM_GETDETAILSOF handler
HRESULT BrfView_OnGetDetailsOf(PBRFVIEW this, HWND hwndMain, UINT iColumn, PDETAILSINFO lpDetails)
{
return Briefcase_GetDetailsOf(this->pbrfexp, this->pbrfstg, hwndMain, this->hMutexDelay,
lpDetails->pidl, iColumn, (SHELLDETAILS *)&lpDetails->fmt);
}
void BrfView_ForceRefresh(HWND hwndMain)
{
IShellView *psv;
IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwndMain);
if (psb && SUCCEEDED(psb->lpVtbl->QueryActiveShellView(psb, &psv)))
{
// Yes; refresh the window
ASSERT(psv);
DebugMsg(DM_TRACE, TEXT("Briefcase: Forced refresh of briefcase window"));
psv->lpVtbl->Refresh(psv);
psv->lpVtbl->Release(psv);
}
}
/*
Purpose: Secondary DVM_FSNOTIFY handler
Returns: S_OK (NOERROR) to forward the event onto the defview window proc
S_FALSE to retain the event from the defview
Cond: --
*/
HRESULT BrfView_HandleFSNotifyForDefView(PBRFVIEW this, HWND hwndMain,
LONG lEvent, LPCITEMIDLIST * ppidl, LPTSTR pszBuf)
{
HRESULT hres;
switch (lEvent)
{
case SHCNE_RENAMEITEM:
case SHCNE_RENAMEFOLDER:
if (!ILIsParent(this->pidl, ppidl[0], TRUE))
{
// move to this folder
hres = BrfView_HandleFSNotifyForDefView(this, hwndMain, SHCNE_CREATE, &ppidl[1], pszBuf);
}
else if (!ILIsParent(this->pidl, ppidl[1], TRUE))
{
// move from this folder
hres = BrfView_HandleFSNotifyForDefView(this, hwndMain, SHCNE_DELETE, &ppidl[0], pszBuf);
}
else
{
// have the defview handle it
BrfExp_CachedNameIsStale(this->pbrfexp, ppidl[0], TRUE);
hres = NOERROR;
}
break;
case SHCNE_DELETE:
case SHCNE_RMDIR:
BrfExp_CachedNameIsStale(this->pbrfexp, ppidl[0], TRUE);
hres = NOERROR;
break;
default:
hres = NOERROR;
break;
}
return hres;
}
/*
Purpose: Converts a shell change notify event to a briefcase
storage event.
Returns: see above
Cond: --
*/
LONG NOEFromSHCNE( LONG lEvent)
{
switch (lEvent)
{
case SHCNE_RENAMEITEM: return NOE_RENAME;
case SHCNE_RENAMEFOLDER: return NOE_RENAMEFOLDER;
case SHCNE_CREATE: return NOE_CREATE;
case SHCNE_MKDIR: return NOE_CREATEFOLDER;
case SHCNE_DELETE: return NOE_DELETE;
case SHCNE_RMDIR: return NOE_DELETEFOLDER;
case SHCNE_UPDATEITEM: return NOE_DIRTY;
case SHCNE_UPDATEDIR: return NOE_DIRTYFOLDER;
default: return 0;
}
}
/*
Purpose: DVM_FSNOTIFY handler
Returns: S_OK (NOERROR) to forward the event onto the defview window proc
S_FALSE to retain the event from the defview
Cond: Note the briefcase receives global events
*/
HRESULT BrfView_OnFSNotify(PBRFVIEW this, HWND hwndMain, LONG lEvent, LPCITEMIDLIST * ppidl)
{
HRESULT hres;
TCHAR szPath[MAX_PATH*2];
if (lEvent == SHCNE_UPDATEIMAGE || lEvent == SHCNE_FREESPACE)
{
return S_FALSE;
}
if (ppidl && SHGetPathFromIDList(ppidl[0], szPath))
{
UINT uFlags;
LONG lEventNOE;
if ((SHCNE_RENAMEFOLDER == lEvent) || (SHCNE_RENAMEITEM == lEvent))
{
ASSERT(ppidl[1]);
ASSERT(ARRAYSIZE(szPath) >= lstrlen(szPath)*2); // rough estimate
// Tack the new name after the old name, separated by the null
SHGetPathFromIDList(ppidl[1], &szPath[lstrlen(szPath)+1]);
}
// Tell the briefcase the path has potentially changed
lEventNOE = NOEFromSHCNE(lEvent);
this->pbrfstg->lpVtbl->Notify(this->pbrfstg, szPath, lEventNOE, &uFlags, hwndMain);
// Was this item marked?
if (uFlags & NF_ITEMMARKED)
{
// Yes; mark it stale in the expensive cache
BrfExp_CachedNameIsStale(this->pbrfexp, ppidl[0], FALSE);
}
// Does the window need to be refreshed?
if (uFlags & NF_REDRAWWINDOW)
{
// Yes
BrfView_ForceRefresh(hwndMain);
}
// Did this event occur in this folder?
if (NULL == ppidl ||
ILIsParent(this->pidl, ppidl[0], TRUE) ||
(((SHCNE_RENAMEITEM == lEvent) || (SHCNE_RENAMEFOLDER == lEvent)) && ILIsParent(this->pidl, ppidl[1], TRUE)) ||
(SHCNE_UPDATEDIR == lEvent && ILIsEqual(this->pidl, ppidl[0])))
{
// Yes; deal with it
hres = BrfView_HandleFSNotifyForDefView(this, hwndMain, lEvent, ppidl, szPath);
}
else
{
// No
hres = S_FALSE;
}
}
else
{
ASSERT(0);
hres = S_FALSE;
}
return hres;
}
/*
Purpose: DVM_NOTIFYCOPYHOOK handler
Returns: IDCANCEL to cancel the operation
NOERROR to allow the operation
Cond: --
*/
HRESULT BrfView_OnNotifyCopyHook(PBRFVIEW this, HWND hwndMain, const COPYHOOKINFO * pchi)
{
HRESULT hres = NOERROR;
DebugMsg(DM_TRACE, TEXT("Briefcase: BrfView_ViewCallBack: DVM_NOTIFYCOPYHOOK is sent (wFunc=%d, pszSrc=%s)"),
pchi->wFunc, pchi->pszSrcFile);
// Is this a pertinent operation?
if (FO_MOVE == pchi->wFunc ||
FO_RENAME == pchi->wFunc ||
FO_DELETE == pchi->wFunc)
{
// Yes; don't allow the briefcase root or a parent folder to get moved
// while the briefcase is still open. (The database is locked while
// the briefcase is open, and will fail the move/rename operation
// in an ugly way.)
LPITEMIDLIST pidl = ILCreateFromPath(pchi->pszSrcFile);
if (pidl)
{
// Is the folder that is being moved or renamed a parent or equal
// of the Briefcase root?
if (ILIsParent(pidl, this->pidlRoot, FALSE) ||
ILIsEqual(pidl, this->pidlRoot))
{
// Yes; don't allow it until the briefcase is closed.
int ids;
if (FO_MOVE == pchi->wFunc ||
FO_RENAME == pchi->wFunc)
{
ids = IDS_MOVEBRIEFCASE;
}
else
{
ASSERT(FO_DELETE == pchi->wFunc);
ids = IDS_DELETEBRIEFCASE;
}
ShellMessageBox(
HINST_THISDLL,
hwndMain,
MAKEINTRESOURCE(ids),
NULL, // copy the title from the owner window.
MB_OK|MB_ICONINFORMATION);
hres = IDCANCEL;
}
ILFree(pidl);
}
}
return hres;
}
// DVM_INITMENUPOPUP handler
HRESULT BrfView_InitMenuPopup(PBRFVIEW this, HWND hwnd, UINT idCmdFirst, int nIndex, HMENU hmenu)
{
BOOL bEnabled = ShellFolderView_GetSelectedCount(hwnd);
EnableMenuItem(hmenu, idCmdFirst+FSIDM_UPDATESELECTION, bEnabled ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(hmenu, idCmdFirst+FSIDM_SPLIT, bEnabled ? MF_ENABLED : MF_GRAYED);
return NOERROR;
}
// DVM_INSERTITEM handler
HRESULT BrfView_OnInsertItem(PBRFVIEW this, HWND hwndMain, LPCITEMIDLIST pidl)
{
HRESULT hres;
TCHAR szPath[MAX_PATH];
if (SHGetPathFromIDList(pidl, szPath))
{
// Always hide the desktop.ini and the database file.
LPTSTR pszName = PathFindFileName(szPath);
if (0 == lstrcmpi(pszName, c_szDesktopIni) ||
0 == lstrcmpi(pszName, this->szDBName))
hres = S_FALSE; // don't add
else
hres = S_OK;
}
else
hres = S_OK; // Let it be added...
return hres;
}
// DVM_SELCHANGE handler
HRESULT BrfView_OnSelChange(PBRFVIEW this, HWND hwndMain, UINT idCmdFirst)
{
IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwndMain);
if (psb)
{
psb->lpVtbl->SendControlMsg(psb, FCW_TOOLBAR, TB_ENABLEBUTTON,
idCmdFirst + FSIDM_UPDATESELECTION,
(LPARAM)(ShellFolderView_GetSelectedCount(hwndMain)), NULL);
}
return E_FAIL; // (we did not update the status area)
}
// This function creates an instance of IShellView.
// The BrfView class does very little. It aggregates to defviewx.c.
HRESULT BrfView_CreateInstance(IShellFolder2 *psf, LPCITEMIDLIST pidl,
PBRFEXP pbrfexp, HWND hwnd, void ** ppvOut)
{
BrfView bv;
HRESULT hres;
*ppvOut = NULL; // assume the worst
// Create an instance of the briefcase storage BEFORE creating
// the defview. Otherwise the hMuteDelay will not be set
// before the paint thread is created.
hres = BrfStg_CreateInstance(pidl, hwnd, &bv.pbrfstg);
if (SUCCEEDED(hres))
{
bv.pidl = pidl;
bv.pbrfexp = pbrfexp;
bv.uSCNRExtra = 0;
// Get the global delay mutex
bv.pbrfstg->lpVtbl->GetExtraInfo(bv.pbrfstg, NULL, GEI_DELAYHANDLE, 0, (LPARAM)&bv.hMutexDelay);
// Get the database name
bv.pbrfstg->lpVtbl->GetExtraInfo(bv.pbrfstg, NULL, GEI_DATABASENAME, ARRAYSIZE(bv.szDBName), (LPARAM)bv.szDBName);
if (ILGetBriefcaseRoot(bv.pbrfstg, &bv.pidlRoot))
{
SFV_CREATE sSFV;
sSFV.cbSize = sizeof(sSFV);
sSFV.pshf = (IShellFolder *)psf;
sSFV.psvOuter = NULL;
sSFV.psfvcb = BrfView_CreateSFVCB((IShellFolder *)psf, &bv);
hres = SHCreateShellFolderView(&sSFV, (IShellView **)ppvOut);
if (sSFV.psfvcb)
sSFV.psfvcb->lpVtbl->Release(sSFV.psfvcb);
if (FAILED(hres))
{
if (bv.pidlRoot)
ILFree(bv.pidlRoot);
}
}
else
{
hres = E_OUTOFMEMORY;
}
if (FAILED(hres))
bv.pbrfstg->lpVtbl->Release(bv.pbrfstg);
}
ASSERT(FAILED(hres) ? (*ppvOut == NULL) : TRUE);
return hres; // NOERROR or E_NOINTERFACE
}
// CFSBrfFolder : class definition
typedef struct _CFSBrfFolder
{
CFSFolder fsf; // file system folder
BRFEXP brfexp; // extra goo
} CFSBrfFolder;
typedef struct
{
IShellDetails sd;
LONG cRef;
HWND hwndMain;
CFSBrfFolder * pbfsf;
IBriefcaseStg * pbrfstg;
HANDLE hMutexDelay;
} CFSBrfDetails;
STDMETHODIMP CFSBrfDetails_QueryInterface(IShellDetails *psd, REFIID riid, void **ppvObj)
{
CFSBrfDetails *this = IToClass(CFSBrfDetails, sd, psd);
if (IsEqualIID(riid, &IID_IShellDetails) ||
IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = &this->sd;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
this->cRef++;
return NOERROR;
}
STDMETHODIMP_(ULONG) CFSBrfDetails_AddRef(IShellDetails *psd)
{
CFSBrfDetails *this = IToClass(CFSBrfDetails, sd, psd);
return InterlockedIncrement(&this->cRef);
}
STDMETHODIMP_(ULONG) CFSBrfDetails_Release(IShellDetails *psd)
{
CFSBrfDetails *this = IToClass(CFSBrfDetails, sd, psd);
if (InterlockedDecrement(&this->cRef))
return this->cRef;
if (this->pbrfstg)
this->pbrfstg->lpVtbl->Release(this->pbrfstg);
CFSFolder_Release(&this->pbfsf->fsf.sf);
LocalFree((HLOCAL)this);
return 0;
}
STDMETHODIMP CFSBrfDetails_GetDetailsOf(IShellDetails *psd, LPCITEMIDLIST pidl,
UINT iColumn, SHELLDETAILS *pDetails)
{
CFSBrfDetails *this = IToClass(CFSBrfDetails, sd, psd);
// delay create the Briefcase storage....
if ( this->pbrfstg == NULL )
{
HRESULT hr = BrfStg_CreateInstance( this->pbfsf->fsf._pidl, this->hwndMain, &this->pbrfstg );
if ( FAILED( hr ))
return hr;
this->pbrfstg->lpVtbl->GetExtraInfo(this->pbrfstg, NULL, GEI_DELAYHANDLE, 0, (LPARAM)&this->hMutexDelay);
}
return Briefcase_GetDetailsOf( &this->pbfsf->brfexp, this->pbrfstg,
this->hwndMain, this->hMutexDelay,
pidl, iColumn, pDetails );
}
STDMETHODIMP Briefcase_GetDetailsOf(BRFEXP * pbrfexp, IBriefcaseStg *pbrfstg,
HWND hwnd, HANDLE hMutexDelay,
LPCITEMIDLIST pidl, UINT iColumn,
SHELLDETAILS *pDetails )
{
HRESULT hres = S_OK;
LPIDFOLDER pidf = (LPIDFOLDER)pidl;
TCHAR szTemp[MAX_PATH];
pDetails->str.uType = STRRET_CSTR;
pDetails->str.cStr[0] = 0;
if (iColumn >= ARRAYSIZE(s_briefcase_cols))
return E_NOTIMPL;
if (!pidf)
{
pDetails->fmt = s_briefcase_cols[iColumn].iFmt;
pDetails->cxChar = s_briefcase_cols[iColumn].cchCol;
return ResToStrRet(s_briefcase_cols[iColumn].ids, &pDetails->str);
}
switch (iColumn)
{
case ICOL_BRIEFCASE_NAME:
FS_CopyName(pidf, szTemp, ARRAYSIZE(szTemp));
hres = StringToStrRet(szTemp, &pDetails->str);
break;
case ICOL_BRIEFCASE_ORIGIN:
case ICOL_BRIEFCASE_STATUS:
{
BRFINFO bi;
LPTSTR lpsz;
// Did we find extra info for this file or
// was the new item added to the extra info list?
if (BrfExp_FindCachedName(pbrfexp, pidl, &bi) ||
BrfExp_AddCachedName(pbrfexp, pidl, &bi, pbrfstg,
hwnd, hMutexDelay))
{
// Yes; take what's in there
if (ICOL_BRIEFCASE_ORIGIN == iColumn)
lpsz = bi.szOrigin;
else
{
ASSERT(ICOL_BRIEFCASE_STATUS == iColumn);
lpsz = bi.szStatus;
}
hres = StringToStrRet(lpsz, &pDetails->str);
}
}
break;
case ICOL_BRIEFCASE_SIZE:
if (!FS_IsFolder(pidf))
{
ShortSizeFormat(pidf->fs.dwSize, szTemp);
hres = StringToStrRet(szTemp, &pDetails->str);
}
break;
case ICOL_BRIEFCASE_TYPE:
FS_GetTypeName(pidf, szTemp, ARRAYSIZE(szTemp));
hres = StringToStrRet(szTemp, &pDetails->str);
break;
case ICOL_BRIEFCASE_MODIFIED:
DosTimeToDateTimeString(pidf->fs.dateModified, pidf->fs.timeModified, szTemp, ARRAYSIZE(szTemp), pDetails->fmt & LVCFMT_DIRECTION_MASK);
hres = StringToStrRet(szTemp, &pDetails->str);
break;
}
return hres;
}
STDMETHODIMP CFSBrfDetails_ColumnClick(IShellDetails *psd, UINT iColumn)
{
// tell defview to do the work....
return S_FALSE;
}
IShellDetailsVtbl c_FSBrfDetailVtbl =
{
CFSBrfDetails_QueryInterface, CFSBrfDetails_AddRef, CFSBrfDetails_Release,
CFSBrfDetails_GetDetailsOf,
CFSBrfDetails_ColumnClick,
};
HRESULT CFSBrfDetails_Create(CFSBrfFolder *pbfsf, HWND hwndMain, void **ppvOut)
{
CFSBrfDetails *psd = (void*)LocalAlloc(LPTR, SIZEOF(CFSBrfDetails));
if (!psd)
return E_OUTOFMEMORY;
psd->sd.lpVtbl = &c_FSBrfDetailVtbl;
psd->cRef = 1;
psd->hwndMain = hwndMain;
psd->pbfsf = pbfsf;
psd->pbrfstg = NULL;
ASSERT(psd->pbfsf->fsf.sf.lpVtbl->AddRef == CFSFolder_AddRef);
CFSFolder_AddRef(&psd->pbfsf->fsf.sf);
*ppvOut = psd;
return S_OK;
}
/* Entry of "drop thread" for briefcase.
The one special thing this does differently is
call the IBriefcaseStg->AddObject to handle the
copy case.
*/
DWORD CALLBACK BrfDropTargetThreadProc(void *pv)
{
FSTHREADPARAM *pfsthp = (FSTHREADPARAM *)pv;
// Is this a sync copy operation?
if ((DROPEFFECT_COPY == pfsthp->dwEffect) && pfsthp->bSyncCopy)
{
// Yes; add the object to the briefcase storage and
// let it handle the file operation
if (pfsthp->pDataObj == NULL)
{
CoGetInterfaceAndReleaseStream(pfsthp->pstmDataObj, &IID_IDataObject, (void **)&pfsthp->pDataObj);
pfsthp->pstmDataObj = NULL;
}
if (pfsthp->pDataObj)
{
IBriefcaseStg *pbrfstg;
if (SUCCEEDED(BrfStg_CreateInstance(pfsthp->pidl, pfsthp->hwndOwner, &pbrfstg)))
{
UINT uFlags = (DDIDM_SYNCCOPYTYPE == pfsthp->idCmd) ? AOF_FILTERPROMPT : AOF_DEFAULT;
pbrfstg->lpVtbl->AddObject(pbrfstg, pfsthp->pDataObj, NULL, uFlags, pfsthp->hwndOwner);
pbrfstg->lpVtbl->Release(pbrfstg);
}
SHChangeNotifyHandleEvents(); // force update now
DataObj_SetDWORD(pfsthp->pDataObj, g_cfPerformedDropEffect, pfsthp->dwEffect);
DataObj_SetDWORD(pfsthp->pDataObj, g_cfLogicalPerformedDropEffect, pfsthp->dwEffect);
}
FreeFSThreadParam(pfsthp);
}
else
{
FileDropTargetThreadProc(pfsthp);
}
return 0;
}
/*
Purpose: Returns TRUE if the object is from the same briefcase
as pidl.
Returns: see above
Cond: --
*/
BOOL IsFromSameBriefcase(LPCTSTR pszBriefPath, LPCTSTR pszPath, LPCITEMIDLIST pidl)
{
BOOL bRet;
TCHAR szPathTgt[MAX_PATH];
int cch;
SHGetPathFromIDList(pidl, szPathTgt);
cch = PathCommonPrefix(pszPath, szPathTgt, NULL);
bRet = (0 < cch && lstrlen(pszBriefPath) <= cch);
return bRet;
}
// TRUE if any folders are in hdrop
BOOL DroppingAnyFolders(HDROP hDrop)
{
UINT i;
TCHAR szPath[MAX_PATH];
for (i = 0; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); i++)
{
if (PathIsDirectory(szPath))
return TRUE;
}
return FALSE;
}
// Determines the correct operation based on the package being
// dropped into the briefcase.
// Returns: dwDefEffect
DWORD PickDefBriefOperation(CIDLDropTarget *pdroptgt, IDataObject *pdtobj,
DWORD grfKeyState, BOOL *pbSyncCopy,
UINT *pidMenu, DWORD *pdwEffect)
{
HRESULT hres;
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
DWORD dwDefEffect;
BOOL bSyncCopy;
UINT idMenu;
// Are these objects file-system objects?
hres = pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte);
if (S_OK == hres)
{
// Yes; are they from within a briefcase?
STGMEDIUM medium;
FORMATETC fmteBrief = {(CLIPFORMAT)_GetBriefObjCF(), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
if (S_OK == pdtobj->lpVtbl->QueryGetData(pdtobj, &fmteBrief))
{
// Yes; are they from the same briefcase as the target?
if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmteBrief, &medium)))
{
PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalLock(medium.hGlobal);
TCHAR szBriefPath[MAX_PATH];
TCHAR szPath[MAX_PATH];
lstrcpy(szBriefPath, BOBriefcasePath(pbo));
// Picking the first file will suffice.
lstrcpy(szPath, BOFileList(pbo));
GlobalUnlock(medium.hGlobal);
if (IsFromSameBriefcase(szBriefPath, szPath, pdroptgt->pidl))
{
// Yes; don't allow the user to create a sync copy.
// Just use the default NDD menu and commands
bSyncCopy = FALSE;
*pdwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK;
dwDefEffect = CFSIDLDropTarget_GetDefaultEffect(pdroptgt, grfKeyState, pdwEffect, NULL);
idMenu = POPUP_NONDEFAULTDD;
}
else
{
// No; allow the sync copy
ReleaseStgMedium(&medium);
goto AllowSync;
}
ReleaseStgMedium(&medium);
}
else
{
// Failure case
bSyncCopy = FALSE;
*pdwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK;
dwDefEffect = CFSIDLDropTarget_GetDefaultEffect(pdroptgt, grfKeyState, pdwEffect, NULL);
idMenu = POPUP_NONDEFAULTDD;
}
}
else
{
// No; allow the sync copy
AllowSync:
bSyncCopy = TRUE;
*pdwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK;
// We pick the default operation with following logic:
// If CTRL key is down -> "sync copy"
// else if SHIFT key is down -> "move"
// else -> "sync copy"
// (For the briefcase, it does not matter whether this is
// across volumes.)
if (grfKeyState & MK_CONTROL)
dwDefEffect = DROPEFFECT_COPY;
else if (grfKeyState & MK_SHIFT)
dwDefEffect = DROPEFFECT_MOVE;
else
dwDefEffect = DROPEFFECT_COPY;
if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium)))
{
HDROP hDrop = GlobalLock(medium.hGlobal);
// Are any folders being dropped?
if (DroppingAnyFolders(hDrop))
idMenu = POPUP_BRIEFCASE_FOLDER_NONDEFAULTDD; // Yes
else
idMenu = POPUP_BRIEFCASE_NONDEFAULTDD; // No
GlobalUnlock(medium.hGlobal);
ReleaseStgMedium(&medium);
}
else
{
idMenu = POPUP_NONDEFAULTDD;
}
}
}
else
{
// check to see if it is one of the other file types..
BOOL fContents = ((pdroptgt->dwData & (DTID_CONTENTS | DTID_FDESCA)) == (DTID_CONTENTS | DTID_FDESCA) ||
(pdroptgt->dwData & (DTID_CONTENTS | DTID_FDESCW)) == (DTID_CONTENTS | DTID_FDESCW));
if (fContents || (pdroptgt->dwData & DTID_HIDA))
{
if (pdroptgt->dwData & DTID_HIDA)
{
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 ((pdroptgt->dwData & (DTID_PREFERREDEFFECT | DTID_HIDA)) ==
DTID_PREFERREDEFFECT)
{
dwDefEffect = pdroptgt->dwEffectPreferred;
}
else if (pdroptgt->dwData & DTID_FD_LINKUI)
{
dwDefEffect = DROPEFFECT_LINK;
}
else
{
dwDefEffect = DROPEFFECT_COPY;
}
idMenu = POPUP_BRIEFCASE_FILECONTENTS;
}
bSyncCopy = TRUE;
}
else
{
// This used to allow Link even when nothing was on the clipboard
// (or just text, for example). The briefcase can't link to such things,
// so I can't see why we would allow links.
bSyncCopy = FALSE;
*pdwEffect = DROPEFFECT_NONE;
dwDefEffect = DROPEFFECT_NONE;
idMenu = POPUP_NONDEFAULTDD;
}
}
if (pbSyncCopy)
*pbSyncCopy = bSyncCopy;
if (pidMenu)
*pidMenu = idMenu;
return dwDefEffect;
}
// IDropTarget::DragEnter
STDMETHODIMP CFSBrfIDLDropTarget_DragEnter(IDropTarget *pdropt,
IDataObject *pdtobj,
DWORD grfKeyState,
POINTL pt,
DWORD *pdwEffect)
{
CIDLDropTarget *this = IToClass(CIDLDropTarget, dropt, pdropt);
DWORD dwDefault;
// Let the base-class process it first
CIDLDropTarget_DragEnter(pdropt, pdtobj, grfKeyState, pt, pdwEffect);
dwDefault = PickDefBriefOperation(this, pdtobj, grfKeyState,
NULL, NULL, pdwEffect);
// The cursor always indicates the default action.
*pdwEffect = dwDefault;
this->dwEffectLastReturned = *pdwEffect;
return S_OK;
}
// IDropTarget::DragOver
STDMETHODIMP CFSBrfIDLDropTarget_DragOver(IDropTarget *pdropt,
DWORD grfKeyState,
POINTL pt,
DWORD *pdwEffect)
{
CIDLDropTarget *this = IToClass(CIDLDropTarget, dropt, pdropt);
if (this->grfKeyStateLast != grfKeyState)
{
DWORD dwDefault;
this->grfKeyStateLast = grfKeyState;
dwDefault = PickDefBriefOperation(this, this->pdtobj, grfKeyState,
NULL, NULL, pdwEffect);
// The cursor always indicates the default action.
*pdwEffect = dwDefault;
this->dwEffectLastReturned = *pdwEffect;
}
else
{
*pdwEffect = this->dwEffectLastReturned;
}
return NOERROR;
}
/*
Purpose: IDropTarget::Drop
Handle drops into the briefcase folder. Unlike normal
file-system containers, a briefcase folder ALWAYS
interprets a drop as a "synchronized copy" by default.
In this case, we add the dropped object(s) to the
briefcase storage and let the storage handle the action.
Returns: standard hresult
Cond: --
*/
STDMETHODIMP CFSBrfIDLDropTarget_Drop(IDropTarget *pdropt, IDataObject *pdtobj,
DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
CIDLDropTarget *this = IToClass(CIDLDropTarget, dropt, pdropt);
HRESULT hres;
DWORD dwDefEffect;
HKEY hkeyBaseProgID;
HKEY hkeyProgID;
UINT idMenu;
BOOL bSyncCopy;
DRAGDROPMENUPARAM ddm;
IUnknown_Set((IUnknown **)&this->pdtobj, (IUnknown *)pdtobj);
dwDefEffect = PickDefBriefOperation(this, pdtobj, grfKeyState, &bSyncCopy, &idMenu, pdwEffect);
// Get the hkeyProgID and hkeyBaseProgID
SHGetClassKey(this->pidl, &hkeyProgID, &hkeyBaseProgID);
ddm.dwDefEffect = dwDefEffect;
ddm.pdtobj = pdtobj;
ddm.pt = pt;
ddm.pdwEffect = pdwEffect;
ddm.hkeyProgID = hkeyProgID;
ddm.hkeyBase = hkeyBaseProgID;
ddm.idMenu = idMenu;
ddm.grfKeyState = grfKeyState;
hres = CIDLDropTarget_DragDropMenuEx(this, &ddm);
SHCloseClassKey(hkeyProgID);
SHCloseClassKey(hkeyBaseProgID);
// Continue with the operation (based on return value of DragDropMenu)?
if (S_FALSE == hres)
{
if (idMenu == POPUP_BRIEFCASE_FILECONTENTS &&
!((*pdwEffect & DROPEFFECT_LINK) && (this->dwData & DTID_HIDA)))
{
TCHAR szPath[MAX_PATH];
BOOL fIsBkDropTarget = ShellFolderView_IsBkDropTarget(this->hwndOwner, &this->dropt);
CIDLDropTarget_GetPath(this, szPath);
hres = FS_CreateFileFromClip(this->hwndOwner, szPath, pdtobj, pt, pdwEffect, fIsBkDropTarget);
}
else
{
FSTHREADPARAM *pfsthp = (void*)LocalAlloc(LPTR, SIZEOF(*pfsthp));
if (pfsthp)
{
CoMarshalInterThreadInterfaceInStream(&IID_IDataObject, (IUnknown *)pdtobj, &pfsthp->pstmDataObj);
ASSERT(pfsthp->pDataObj == NULL);
pfsthp->dwEffect = *pdwEffect;
pfsthp->fSameHwnd = ShellFolderView_IsDropOnSource(this->hwndOwner, &this->dropt);
pfsthp->bSyncCopy = bSyncCopy;
pfsthp->idCmd = ddm.idCmd;
pfsthp->hwndOwner = this->hwndOwner;
pfsthp->grfKeyState = this->grfKeyStateLast;
pfsthp->pidl = ILClone(this->pidl);
CIDLDropTarget_GetPath(this, pfsthp->szPath);
ShellFolderView_GetAnchorPoint(this->hwndOwner, FALSE, &pfsthp->ptDrop);
if (DataObj_CanGoAsync(pdtobj))
{
// create another thread to avoid blocking the source thread.
if (SHCreateThread(BrfDropTargetThreadProc, pfsthp, CTF_COINIT, NULL))
{
hres = NOERROR;
}
else
{
FreeFSThreadParam(pfsthp);
hres = E_OUTOFMEMORY;
}
}
else
{
BrfDropTargetThreadProc(pfsthp); // do this on this thread
}
}
}
}
CIDLDropTarget_DragLeave(pdropt);
return hres;
}
const IDropTargetVtbl c_CFSBrfDropTargetVtbl =
{
CIDLDropTarget_QueryInterface, CIDLDropTarget_AddRef, CIDLDropTarget_Release,
CFSBrfIDLDropTarget_DragEnter,
CFSBrfIDLDropTarget_DragOver,
CIDLDropTarget_DragLeave,
CFSBrfIDLDropTarget_Drop, // special member function
};
// IShellFolder::Release
STDMETHODIMP CFSBrfFolderUnk_QueryInterface(IUnknown *punk, REFIID riid, void **ppv)
{
CFSBrfFolder * this = IToClass(CFSBrfFolder, fsf.iunk, punk);
if (IsEqualIID(riid, &IID_IUnknown))
{
*ppv = &this->fsf.iunk;
}
else if (IsEqualIID(riid, &IID_IShellFolder))
{
*ppv = &this->fsf.sf;
}
else if (IsEqualIID(riid, &IID_IPersist) ||
IsEqualIID(riid, &IID_IPersistFolder) ||
IsEqualIID(riid, &IID_IPersistFolder2) ||
IsEqualIID(riid, &IID_IPersistFolder3))
{
*ppv = &this->fsf.pf;
}
else if (IsEqualIID(riid, &IID_INeedRealCFSFolder))
{
*ppv = this; // return unreffed pointer
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
((IUnknown*)(*ppv))->lpVtbl->AddRef(*ppv);
return S_OK;
}
STDMETHODIMP_(ULONG) CFSBrfFolderUnk_Release(IUnknown *punk)
{
CFSBrfFolder *this = IToClass(CFSBrfFolder, fsf.iunk, punk);
if (InterlockedDecrement(&this->fsf.cRef))
return this->fsf.cRef;
CFSFolder_Reset(&this->fsf);
DeleteCriticalSection(&this->brfexp.cs);
LocalFree((HLOCAL)this);
return 0;
}
// IShellFolder::CompareIDs
// BUGBUG: share more code with fstreex.c CFSFolder_CompareIDs()
STDMETHODIMP CFSBrfFolder_CompareIDs(IShellFolder2 *psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
CFSBrfFolder *this = IToClass(CFSBrfFolder, fsf.sf, psf);
HRESULT hres;
short nCmp;
LPCIDFOLDER pidf1 = FS_IsValidID(pidl1);
LPCIDFOLDER pidf2 = FS_IsValidID(pidl2);
if (!pidf1 || !pidf2)
{
ASSERT(0);
return E_INVALIDARG;
}
hres = FS_CompareFolderness(pidf1, pidf2);
if (hres != ResultFromShort(0))
return hres;
switch (lParam & SHCIDS_COLUMNMASK)
{
case ICOL_BRIEFCASE_SIZE:
if (pidf1->fs.dwSize < pidf2->fs.dwSize)
return ResultFromShort(-1);
if (pidf1->fs.dwSize > pidf2->fs.dwSize)
return ResultFromShort(1);
goto DoDefault;
case ICOL_BRIEFCASE_TYPE:
nCmp = _CompareFileTypes((IShellFolder *)psf, pidf1, pidf2);
if (nCmp)
return ResultFromShort(nCmp);
goto DoDefault;
case ICOL_BRIEFCASE_MODIFIED:
hres = FS_CompareModifiedDate(pidf1, pidf2);
if (!hres)
goto DoDefault;
break;
case ICOL_BRIEFCASE_NAME:
// We need to treat this differently from others bacause
// pidf1/2 might not be simple.
hres = FS_CompareNamesCase(pidf1, pidf2);
// REVIEW: (Possible performance gain with some extra code)
// We should probably aviod bindings by walking down
// the IDList here instead of calling this helper function.
if (hres == ResultFromShort(0))
{
hres = ILCompareRelIDs((IShellFolder *)psf, pidl1, pidl2);
}
goto DoDefaultModification;
case ICOL_BRIEFCASE_ORIGIN:
case ICOL_BRIEFCASE_STATUS:
{
BRFINFO bi1, bi2;
BOOL bVal1 = BrfExp_FindCachedName(&this->brfexp, pidl1, &bi1);
BOOL bVal2 = BrfExp_FindCachedName(&this->brfexp, pidl2, &bi2);
// Do we have this info in our cache?
if (!bVal1 || !bVal2)
{
// No; one or both of them are missing. Have unknowns gravitate
// to the bottom of the list.
// (Don't bother adding them)
if (!bVal1 && !bVal2)
hres = ResultFromShort(0);
else if (!bVal1)
hres = ResultFromShort(1);
else
hres = ResultFromShort(-1);
}
else
{
// Found the info; do a comparison
if (ICOL_BRIEFCASE_ORIGIN == (lParam & SHCIDS_COLUMNMASK))
{
hres = ResultFromShort(lstrcmp(bi1.szOrigin, bi2.szOrigin));
}
else
{
ASSERT(ICOL_BRIEFCASE_STATUS == (lParam & SHCIDS_COLUMNMASK));
hres = ResultFromShort(lstrcmp(bi1.szStatus, bi2.szStatus));
}
}
}
break;
default:
DoDefault:
// Sort it based on the primary (long) name -- ignore case.
{
TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
FS_CopyName(pidf1, szName1, ARRAYSIZE(szName1));
FS_CopyName(pidf2, szName2, ARRAYSIZE(szName2));
hres = ResultFromShort(lstrcmpi(szName1, szName2));
}
DoDefaultModification:
if (hres == S_OK && (lParam & SHCIDS_ALLFIELDS)) {
// Must sort by modified date to pick up any file changes!
hres = FS_CompareModifiedDate(pidf1, pidf2);
if (!hres)
hres = FS_CompareAttribs(pidf1, pidf2);
}
}
return hres;
}
// IShellFolder::CreateViewObject
STDMETHODIMP CFSBrfFolder_CreateViewObject(IShellFolder2 *psf, HWND hwnd, REFIID riid, void **ppvOut)
{
CFSBrfFolder *this = IToClass(CFSBrfFolder, fsf.sf, psf);
if (IsEqualIID(riid, &IID_IShellView))
{
// Use the default shell view
return BrfView_CreateInstance(psf, this->fsf._pidl, &this->brfexp, hwnd, ppvOut);
}
else if (IsEqualIID(riid, &IID_IDropTarget))
{
// Create an IDropTarget interface instance with our
// own vtable
return CIDLDropTarget_Create(hwnd, &c_CFSBrfDropTargetVtbl,
this->fsf._pidl, (IDropTarget **)ppvOut);
}
else if (IsEqualIID(riid, &IID_IContextMenu))
{
return CFSFolder_CreateViewObject(psf, hwnd, riid, ppvOut);
}
else if (IsEqualIID(riid, &IID_IShellDetails))
{
return CFSBrfDetails_Create(this, hwnd, ppvOut);
}
else
{
*ppvOut = NULL;
return E_NOINTERFACE;
}
}
// IShellFolder::GetAttributesOf
STDMETHODIMP CFSBrfFolder_GetAttributesOf(IShellFolder2 *psf,
UINT cidl, LPCITEMIDLIST *apidl,
ULONG * prgfInOut)
{
CFSBrfFolder *this = IToClass(CFSBrfFolder, fsf.sf, psf);
// Validate this pidl?
if (*prgfInOut & SFGAO_VALIDATE)
{
// Yes; dirty the briefcase storage entry by sending an update
// notification
DebugMsg(DM_TRACE, TEXT("Briefcase: Receiving F5, dirty entire briefcase storage"));
BrfExp_AllNamesAreStale(&this->brfexp);
DebugMsg(DM_TRACE, TEXT("Briefcase: Finished staling everything"));
}
// Pass onto standard CFSFolder class member function
return CFSFolder_GetAttributesOf(psf, cidl, apidl, prgfInOut);
}
// IShellFolder::GetUIObjectOf
STDMETHODIMP CFSBrfFolder_GetUIObjectOf(IShellFolder2 *psf, HWND hwnd, UINT cidl,
LPCITEMIDLIST * apidl, REFIID riid,
UINT * prgfInOut, void **ppvOut)
{
CFSFolder *this = IToClass(CFSFolder, sf, psf);
*ppvOut = NULL;
if (IsEqualIID(riid, &IID_IExtractIconA) ||
IsEqualIID(riid, &IID_IExtractIconW) ||
IsEqualIID(riid, &IID_IContextMenu) ||
IsEqualIID(riid, &IID_IQueryAssociations) ||
IsEqualIID(riid, &IID_IDropTarget))
{
return CFSFolder_GetUIObjectOf(psf, hwnd, cidl, apidl, riid, prgfInOut, ppvOut);
}
else if (cidl > 0 && IsEqualIID(riid, &IID_IDataObject))
{
// Create an IDataObject interface instance with our
// own vtable because we support the CFSTR_BRIEFOBJECT clipboard format
return CIDLData_CreateFromIDArray2(&c_CFSBrfIDLDataVtbl, this->_pidl, cidl, apidl, (IDataObject **)ppvOut);
}
return E_INVALIDARG;
}
IShellFolder2Vtbl c_FSBrfFolderVtbl =
{
CFSFolder_QueryInterface, CFSFolder_AddRef, CFSFolder_Release,
CFSFolder_ParseDisplayName,
CFSFolder_EnumObjects,
CFSFolder_BindToObject,
CFSFolder_BindToStorage,
CFSBrfFolder_CompareIDs,
CFSBrfFolder_CreateViewObject, // special member function
CFSBrfFolder_GetAttributesOf,
CFSBrfFolder_GetUIObjectOf, // special member function
CFSFolder_GetDisplayNameOf,
CFSFolder_SetNameOf,
// NOTE: we don't support IShellFolder2 in our QI
// CFSFolder_EnumSearches,
FindFileOrFolders_GetDefaultSearchGUID,
};
const IUnknownVtbl c_FSBrfFolderUnkVtbl =
{
CFSBrfFolderUnk_QueryInterface, CFSFolderUnk_AddRef, CFSBrfFolderUnk_Release,
};
extern IPersistFolder3Vtbl c_CFSFolderPFVtbl;
// CFSBrfFolder constructor
STDAPI CFSBrfFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
{
CFSBrfFolder *this = (void*)LocalAlloc(LPTR, SIZEOF(CFSBrfFolder));
if (this)
{
HRESULT hres;
this->fsf.iunk.lpVtbl = &c_FSBrfFolderUnkVtbl;
this->fsf.sf.lpVtbl = &c_FSBrfFolderVtbl;
this->fsf.pf.lpVtbl = &c_CFSFolderPFVtbl;
this->fsf.punkOuter = &this->fsf.iunk;
this->fsf.cRef = 1;
this->fsf._clsidBind = CLSID_BriefcaseFolder;
InitializeCriticalSection(&this->brfexp.cs);
this->brfexp.psf = (IShellFolder *)&this->fsf.sf;
hres = this->fsf.sf.lpVtbl->QueryInterface(&this->fsf.sf, riid, ppvOut);
this->fsf.sf.lpVtbl->Release(&this->fsf.sf);
return hres;
}
*ppvOut = NULL;
return E_OUTOFMEMORY;
}
// dead export, don't know if anyone calls this
STDAPI_(void) Desktop_UpdateBriefcaseOnEvent(HWND hwndMain, UINT uEvent)
{
}