mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3450 lines
95 KiB
3450 lines
95 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
|
|
// BUGBUG
|
|
//
|
|
// DroppingAnyFolders() complains that it was prototyped in a previous block,
|
|
// which it wasn't. Maybe its because this source file is actually #included
|
|
// in fstreex.c... I don't know. Smells like a bogus warning to me.
|
|
|
|
#pragma warning(disable: 4217)
|
|
|
|
DWORD WaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: This function creates an instance of IBriefcaseStg.
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfStg_CreateInstance(
|
|
LPCITEMIDLIST pidl,
|
|
HWND hwnd,
|
|
LPVOID FAR* ppvOut)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
TCHAR szFolder[MAX_PATH];
|
|
|
|
// Create an instance of IBriefcaseStg
|
|
if (SHGetPathFromIDList(pidl, szFolder))
|
|
{
|
|
LPBRIEFCASESTG pbrfstg;
|
|
hres = SHCoCreateInstance(NULL, &CLSID_Briefcase, NULL, &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
|
|
//===========================================================================
|
|
|
|
static UINT s_cfBriefObj = 0;
|
|
|
|
extern HRESULT CIDLData_Clone(LPDATAOBJECT pdtobjIn, UINT * acf, UINT ccf, LPDATAOBJECT *ppdtobjOut);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Clones the IDataObject
|
|
|
|
Returns: standard result
|
|
Cond: --
|
|
*/
|
|
HRESULT CIDLData_CloneForBriefcaseMoveCopy(
|
|
LPDATAOBJECT pdtobjIn,
|
|
LPDATAOBJECT *ppdtobjOut)
|
|
{
|
|
UINT acf[] = { g_cfHIDA, g_cfOFFSETS, CF_HDROP, g_cfFileNameMap, s_cfBriefObj };
|
|
return CIDLData_Clone(pdtobjIn, acf, ARRAYSIZE(acf), ppdtobjOut);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
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 LPFSBRFFOLDER, so we can
|
|
call the IBriefcaseStg::GetExtraInfo member function.
|
|
|
|
Returns: standard result
|
|
Cond: --
|
|
*/
|
|
HRESULT GetBriefcaseRoot(
|
|
LPCITEMIDLIST pidl,
|
|
LPTSTR pszBuf,
|
|
int cchBuf)
|
|
{
|
|
HRESULT hres;
|
|
LPBRIEFCASESTG pbrfstg;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Creates an IDL of the root path of the briefcase storage.
|
|
|
|
Returns: TRUE on success
|
|
Cond: --
|
|
*/
|
|
BOOL ILGetBriefcaseRoot(
|
|
LPBRIEFCASESTG pbrfstg,
|
|
LPCITEMIDLIST pidl, // IDL of a folder
|
|
LPITEMIDLIST * ppidlRoot)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
TCHAR sz[MAX_PATH];
|
|
|
|
if (SUCCEEDED(pbrfstg->lpVtbl->GetExtraInfo(pbrfstg, NULL, GEI_ROOT, (WPARAM)ARRAYSIZE(sz), (LPARAM)sz)))
|
|
{
|
|
// Create ppidlRoot as a IDFOLDER, not as an ordinary PIDL.
|
|
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 = (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;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDataObject::GetData
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfIDLData_GetData(
|
|
LPDATAOBJECT pdtobj,
|
|
LPFORMATETC pformatetcIn,
|
|
LPSTGMEDIUM pmedium) // put data in here
|
|
{
|
|
HRESULT hres = E_INVALIDARG;
|
|
|
|
if (pformatetcIn->cfFormat == s_cfBriefObj && (pformatetcIn->tymed & TYMED_HGLOBAL))
|
|
{
|
|
hres = CFSBrfIDLData_GetBriefObj(pdtobj, pmedium);
|
|
}
|
|
else
|
|
{
|
|
hres = CFSIDLData_GetData(pdtobj, pformatetcIn, pmedium);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDataObject::QueryGetData
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfIDLData_QueryGetData(
|
|
LPDATAOBJECT pdtobj,
|
|
LPFORMATETC pformatetc)
|
|
{
|
|
if (pformatetc->cfFormat == s_cfBriefObj && (pformatetc->tymed & TYMED_HGLOBAL))
|
|
return NOERROR;
|
|
|
|
return CFSIDLData_QueryGetData(pdtobj, pformatetc);
|
|
}
|
|
|
|
|
|
#pragma data_seg(DATASEG_READONLY)
|
|
|
|
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
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// BrfView class
|
|
//---------------------------------------------------------------------------
|
|
|
|
enum
|
|
{
|
|
ICOL_BRIEFCASE_NAME = 0,
|
|
ICOL_BRIEFCASE_ORIGIN,
|
|
ICOL_BRIEFCASE_STATUS,
|
|
ICOL_BRIEFCASE_SIZE,
|
|
ICOL_BRIEFCASE_TYPE,
|
|
ICOL_BRIEFCASE_MODIFIED,
|
|
ICOL_BRIEFCASE_MAX, // Make sure this is the last enum item
|
|
};
|
|
|
|
#pragma data_seg(DATASEG_READONLY)
|
|
|
|
const COL_DATA s_briefcase_cols[] = {
|
|
{ICOL_BRIEFCASE_NAME, IDS_NAME_COL, 20, LVCFMT_LEFT},
|
|
{ICOL_BRIEFCASE_ORIGIN, IDS_ORIGINAL_COL, 24, LVCFMT_LEFT},
|
|
{ICOL_BRIEFCASE_STATUS, IDS_STATUS_COL, 18, LVCFMT_LEFT},
|
|
{ICOL_BRIEFCASE_SIZE, IDS_SIZE_COL, 8, LVCFMT_LEFT},
|
|
{ICOL_BRIEFCASE_TYPE, IDS_TYPE_COL, 18, LVCFMT_LEFT},
|
|
{ICOL_BRIEFCASE_MODIFIED, IDS_MODIFIED_COL, 18, LVCFMT_LEFT},
|
|
};
|
|
|
|
const TBBUTTON c_tbBrfCase[] = {
|
|
{ 0, FSIDM_UPDATEALL, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
|
|
{ 1, FSIDM_UPDATESELECTION, 0, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
|
|
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP , {0,0}, 0L, -1 },
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
#define MAX_NAME 32
|
|
#define DPA_ERR (-1)
|
|
#define DPA_APPEND 0x7fff
|
|
|
|
#define HACK_IGNORETYPE 0x08000000
|
|
|
|
TCHAR g_szDetailsUnknown[MAX_NAME] = TEXT("");
|
|
|
|
typedef struct _BrfInfo
|
|
{
|
|
TCHAR szOrigin[MAX_PATH];
|
|
TCHAR szStatus[MAX_NAME];
|
|
BOOL bDetermined:1;
|
|
BOOL bUpToDate:1;
|
|
BOOL bDeleted:1;
|
|
} BRFINFO, * PBRFINFO;
|
|
|
|
typedef struct _BrfInfoHdr
|
|
{
|
|
LPITEMIDLIST pidl; // Indexed value
|
|
BRFINFO bi;
|
|
} BRFINFOHDR, * PBRFINFOHDR;
|
|
|
|
|
|
// Values for BrfExp_FindNextState
|
|
#define FNS_UNDETERMINED 1
|
|
#define FNS_STALE 2
|
|
#define FNS_DELETED 3
|
|
|
|
// This structure is for the cache of brfinfohdr's
|
|
typedef struct _BrfExpensiveList
|
|
{
|
|
HWND hwndMain;
|
|
LPSHELLFOLDER psf;
|
|
LPBRIEFCASESTG pbrfstg;
|
|
HDPA hdpa; // Cache of expensive data
|
|
int idpaStaleCur;
|
|
int idpaUndeterminedCur;
|
|
int idpaDeletedCur;
|
|
HANDLE hSemPending; // Pending semaphore
|
|
CRITICAL_SECTION cs;
|
|
HANDLE hEventDie;
|
|
HANDLE hThreadPaint;
|
|
HANDLE hMutexDelay; // Not owned by BrfExp
|
|
BOOL bFreePending;
|
|
#ifdef DEBUG
|
|
UINT cUndetermined;
|
|
UINT cStale;
|
|
UINT cDeleted;
|
|
UINT cCSRef;
|
|
#endif
|
|
|
|
} BRFEXP, * PBRFEXP;
|
|
|
|
|
|
#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
|
|
|
|
typedef struct _BrfView
|
|
{
|
|
IShellView sv; // 1st base class
|
|
LPSHELLVIEW psv; // Saved shellview member functions
|
|
LPBRIEFCASESTG pbrfstg;
|
|
LPITEMIDLIST pidlRoot; // Root of briefcase
|
|
LPCITEMIDLIST pidl;
|
|
PBRFEXP pbrfexp;
|
|
HANDLE hMutexDelay;
|
|
TCHAR szDBName[MAX_PATH];
|
|
|
|
} BrfView, * PBRFVIEW;
|
|
|
|
|
|
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(
|
|
LPVOID pv1,
|
|
LPVOID pv2,
|
|
LPARAM lParam)
|
|
{
|
|
LPSHELLFOLDER 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, LONG_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 (INVALID_HANDLE_VALUE != 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
|
|
{
|
|
// Failure (set to NULL for easier processing at termination)
|
|
this->hEventDie = NULL;
|
|
|
|
CloseHandle(this->hSemPending);
|
|
this->hSemPending = NULL;
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Initialize the cache
|
|
|
|
Returns: TRUE on success
|
|
Cond: --
|
|
*/
|
|
BOOL BrfExp_Init(
|
|
PBRFEXP this,
|
|
LPBRIEFCASESTG pbrfstg,
|
|
HWND hwndMain,
|
|
HANDLE hMutexDelay)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
Assert(pbrfstg);
|
|
|
|
// The critical section should have been initialized when this
|
|
// IShellView's IShellFolder interface was created.
|
|
// ReinitializeCriticalSection(&this->cs);
|
|
|
|
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("sh TR - Secondary thread ended"));
|
|
|
|
CloseHandle(this->hThreadPaint);
|
|
this->hThreadPaint = NULL;
|
|
}
|
|
|
|
CloseHandle(this->hEventDie);
|
|
this->hEventDie = NULL;
|
|
}
|
|
|
|
if (this->hdpa)
|
|
{
|
|
int idpa = DPA_GetPtrCount(this->hdpa);
|
|
PBRFINFOHDR pbihdr;
|
|
|
|
while (--idpa >= 0)
|
|
{
|
|
pbihdr = (PBRFINFOHDR)DPA_FastGetPtr(this->hdpa, idpa);
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Deleting item '%s'"), (LPTSTR)FS_GetName((LPIDFOLDER)pbihdr->pidl));
|
|
|
|
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);
|
|
{
|
|
LPBRIEFCASESTG 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) // Structure with filled in values
|
|
{
|
|
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--;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Deleting item '%s'"), (LPTSTR)FS_GetName((LPIDFOLDER)bihdrT.pidl));
|
|
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.
|
|
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
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);
|
|
LPBRIEFCASESTG 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);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Finds a cached name structure and marks it stale
|
|
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
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
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Item '%s' is marked for deletion. Pending."), (LPTSTR)FS_GetName((LPIDFOLDER)pbihdr->pidl));
|
|
this->cDeleted++;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pbihdr->bi.bUpToDate = FALSE;
|
|
#ifdef DEBUG
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Item '%s' is stale. Pending."), (LPTSTR)FS_GetName((LPIDFOLDER)pbihdr->pidl));
|
|
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
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Item '%s' is also marked for deletion."), (LPTSTR)FS_GetName((LPIDFOLDER)pbihdr->pidl));
|
|
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,
|
|
LPBRIEFCASESTG 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_InsertPtr(this->hdpa, DPA_APPEND, pbihdr);
|
|
if (DPA_ERR != idpa)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Adding item '%s'."), (LPTSTR)FS_GetName((LPIDFOLDER)pidl));
|
|
|
|
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("sh TR - 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("sh TR - Exiting paint thread"));
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// BrfView functions:
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Merge the briefcase menu with the defview's menu bar.
|
|
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_MergeMenu(
|
|
PBRFVIEW this,
|
|
LPQCMINFO pinfo)
|
|
{
|
|
HMENU hmMain = pinfo->hmenu;
|
|
|
|
// Merge the briefcase menu onto the menu that CDefView created.
|
|
if (hmMain)
|
|
{
|
|
HMENU hmSync;
|
|
|
|
hmSync = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_BRIEFCASE));
|
|
if (hmSync)
|
|
{
|
|
Shell_MergeMenus(hmMain, 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,
|
|
LPDATAOBJECT * 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, (LPDATAOBJECT FAR*)ppdtobj);
|
|
ILFree(pidlLast);
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Get the selected data objects
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_GetSelectedObjects(
|
|
LPSHELLFOLDER psf,
|
|
HWND hwnd,
|
|
LPDATAOBJECT * ppdtobj)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
UINT cidl;
|
|
LPCITEMIDLIST * apidl;
|
|
|
|
cidl = 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)
|
|
{
|
|
BrfExp_Init(this->pbrfexp, this->pbrfstg, hwndMain, this->hMutexDelay);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: DVM_QUERYFSNOTIFY handler
|
|
|
|
Returns: standard result
|
|
Cond: --
|
|
*/
|
|
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);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: WM_COMMAND handler
|
|
|
|
Returns: NOERROR
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_Command(
|
|
PBRFVIEW this,
|
|
LPSHELLFOLDER psf,
|
|
HWND hwnd,
|
|
UINT uID)
|
|
{
|
|
LPDATAOBJECT 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;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: DVM_GETDETAILSOF handler
|
|
|
|
Returns: E_NOTIMPL if iColumn is greater than supported range
|
|
otherwise NOERROR
|
|
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_OnGetDetailsOf(
|
|
PBRFVIEW this,
|
|
HWND hwndMain,
|
|
UINT iColumn,
|
|
PDETAILSINFO lpDetails)
|
|
{
|
|
LPIDFOLDER pidf = (LPIDFOLDER)lpDetails->pidl;
|
|
#ifdef UNICODE
|
|
TCHAR szTemp[MAX_PATH];
|
|
#endif
|
|
|
|
if (iColumn >= ICOL_BRIEFCASE_MAX)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
lpDetails->str.uType = STRRET_CSTR;
|
|
lpDetails->str.cStr[0] = '\0';
|
|
|
|
if (!pidf)
|
|
{
|
|
#ifdef UNICODE
|
|
LoadString(HINST_THISDLL, s_briefcase_cols[iColumn].ids,
|
|
szTemp, ARRAYSIZE(szTemp));
|
|
|
|
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
|
|
if ( lpDetails->str.pOleStr != NULL ) {
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr, szTemp);
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
#else
|
|
LoadString(HINST_THISDLL, s_briefcase_cols[iColumn].ids,
|
|
lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
|
|
#endif
|
|
lpDetails->fmt = s_briefcase_cols[iColumn].iFmt;
|
|
lpDetails->cxChar = s_briefcase_cols[iColumn].cchCol;
|
|
return NOERROR;
|
|
}
|
|
|
|
switch (iColumn)
|
|
{
|
|
case ICOL_BRIEFCASE_NAME:
|
|
#ifdef UNICODE
|
|
ualstrcpyn(szTemp, FS_GetName(pidf), ARRAYSIZE(szTemp));
|
|
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
|
|
if ( lpDetails->str.pOleStr != NULL ) {
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr, szTemp);
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
#else
|
|
lpDetails->str.uType = STRRET_OFFSET;
|
|
lpDetails->str.uOffset = FS_GetName(pidf) - (LPNTSTR)(pidf);
|
|
#endif
|
|
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(this->pbrfexp, lpDetails->pidl, &bi) ||
|
|
BrfExp_AddCachedName(this->pbrfexp, lpDetails->pidl, &bi, this->pbrfstg,
|
|
hwndMain, this->hMutexDelay))
|
|
{
|
|
// Yes; take what's in there
|
|
if (ICOL_BRIEFCASE_ORIGIN == iColumn)
|
|
{
|
|
lpsz = bi.szOrigin;
|
|
}
|
|
else
|
|
{
|
|
Assert(ICOL_BRIEFCASE_STATUS == iColumn);
|
|
lpsz = bi.szStatus;
|
|
}
|
|
}
|
|
#ifdef UNICODE
|
|
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(lpsz)+1)*SIZEOF(TCHAR));
|
|
if ( lpDetails->str.pOleStr != NULL ) {
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr, lpsz);
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
#else
|
|
lstrcpyn(lpDetails->str.cStr, lpsz, ARRAYSIZE(lpDetails->str.cStr));
|
|
#endif
|
|
|
|
}
|
|
break;
|
|
|
|
case ICOL_BRIEFCASE_SIZE:
|
|
if (!FS_IsFolder(pidf))
|
|
{
|
|
#ifdef UNICODE
|
|
ShortSizeFormat(pidf->fs.dwSize, szTemp);
|
|
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
|
|
if ( lpDetails->str.pOleStr != NULL ) {
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr, szTemp);
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
#else
|
|
ShortSizeFormat(pidf->fs.dwSize, lpDetails->str.cStr);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case ICOL_BRIEFCASE_TYPE:
|
|
#ifdef UNICODE
|
|
FS_GetTypeName(pidf, szTemp, ARRAYSIZE(szTemp));
|
|
|
|
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
|
|
if ( lpDetails->str.pOleStr != NULL ) {
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr, szTemp);
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
#else
|
|
FS_GetTypeName(pidf, lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
|
|
#endif
|
|
break;
|
|
|
|
case ICOL_BRIEFCASE_MODIFIED:
|
|
#ifdef UNICODE
|
|
BldDateTimeString(pidf->fs.dateModified, pidf->fs.timeModified, szTemp);
|
|
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
|
|
if ( lpDetails->str.pOleStr != NULL ) {
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr, szTemp);
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
#else
|
|
BldDateTimeString(pidf->fs.dateModified, pidf->fs.timeModified, lpDetails->str.cStr);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: DVM_COLUMNCLICK handler
|
|
|
|
Returns: NOERROR
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_OnColumnClick(
|
|
PBRFVIEW this,
|
|
HWND hwndMain,
|
|
UINT iColumn)
|
|
{
|
|
Assert(iColumn < ICOL_BRIEFCASE_MAX);
|
|
|
|
ShellFolderView_ReArrange(hwndMain, iColumn);
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Force the window to refresh
|
|
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
void BrfView_ForceRefresh(
|
|
HWND hwndMain)
|
|
{
|
|
LPSHELLBROWSER psb = FileCabinet_GetIShellBrowser(hwndMain);
|
|
LPSHELLVIEW psv;
|
|
|
|
// Did we get the shellview?
|
|
Assert(psb);
|
|
if (psb &&
|
|
SUCCEEDED(psb->lpVtbl->QueryActiveShellView(psb, &psv)))
|
|
{
|
|
// Yes; refresh the window
|
|
Assert(psv);
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - 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) // buffer to save stack space
|
|
{
|
|
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: ResultFromScode(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("sh TR - 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 = ResultFromScode(IDCANCEL);
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: DVM_GETBUTTONS handler
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
void BrfView_OnGetButtons(
|
|
PBRFVIEW this,
|
|
HWND hwndMain,
|
|
UINT idCmdFirst,
|
|
LPTBBUTTON ptbbutton)
|
|
{
|
|
UINT i;
|
|
UINT iBtnOffset;
|
|
IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwndMain);
|
|
TBADDBITMAP ab;
|
|
|
|
// add the toolbar button bitmap, get it's offset
|
|
ab.hInst = HINST_THISDLL;
|
|
ab.nID = IDB_BRF_TB_SMALL; // std bitmaps
|
|
psb->lpVtbl->SendControlMsg(psb, FCW_TOOLBAR, TB_ADDBITMAP, 2, (LPARAM)&ab, &iBtnOffset);
|
|
|
|
for (i = 0; i < ARRAYSIZE(c_tbBrfCase); i++)
|
|
{
|
|
ptbbutton[i] = c_tbBrfCase[i];
|
|
|
|
if (!(c_tbBrfCase[i].fsStyle & TBSTYLE_SEP))
|
|
{
|
|
ptbbutton[i].idCommand += idCmdFirst;
|
|
ptbbutton[i].iBitmap += iBtnOffset;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: DVM_INITMENUPOPUP handler
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_InitMenuPopup(
|
|
PBRFVIEW this,
|
|
HWND hwnd,
|
|
UINT idCmdFirst,
|
|
int nIndex,
|
|
HMENU hmenu)
|
|
{
|
|
BOOL bEnabled;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: DVM_INSERTITEM handler
|
|
|
|
Returns: S_OK to allow the object to be inserted
|
|
S_FALSE to prevent it
|
|
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_OnInsertItem(
|
|
PBRFVIEW this,
|
|
HWND hwndMain,
|
|
LPCITEMIDLIST pidl,
|
|
DVSELCHANGEINFO * pdvsci)
|
|
{
|
|
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;
|
|
else
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
hres = S_OK; // Let it be added...
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: DVM_SELCHANGE handler
|
|
|
|
Returns: standard hresult
|
|
E_FAIL means we did not update the status area
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_OnSelChange(
|
|
PBRFVIEW this,
|
|
HWND hwndMain,
|
|
UINT idCmdFirst,
|
|
PDVSELCHANGEINFO pdvsci)
|
|
{
|
|
IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwndMain);
|
|
|
|
// Enable/disable toolbar button
|
|
Assert(psb);
|
|
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)
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Default View callback handler
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT CALLBACK BrfView_ViewCallback(
|
|
LPSHELLVIEW psvOuter,
|
|
LPSHELLFOLDER psf,
|
|
HWND hwndMain,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psvOuter);
|
|
HRESULT hres = NOERROR;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case DVM_WINDOWCREATED:
|
|
BrfView_OnCreate(this, hwndMain, (HWND)wParam);
|
|
break;
|
|
|
|
case DVM_WINDOWDESTROY:
|
|
BrfView_OnDestroy(this, (HWND)wParam);
|
|
break;
|
|
|
|
case DVM_MERGEMENU:
|
|
BrfView_MergeMenu(this, (LPQCMINFO)lParam);
|
|
break;
|
|
|
|
case DVM_INVOKECOMMAND:
|
|
BrfView_Command(this, psf, hwndMain, (UINT)wParam);
|
|
break;
|
|
|
|
case DVM_GETHELPTEXT:
|
|
case DVM_GETTOOLTIPTEXT:
|
|
LoadString(HINST_THISDLL, LOWORD(wParam) + (DVM_GETHELPTEXT == uMsg ? IDS_MH_FSIDM_FIRST : IDS_TT_FSIDM_FIRST), (LPTSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DVM_INITMENUPOPUP:
|
|
BrfView_InitMenuPopup(this, hwndMain, LOWORD(wParam), HIWORD(wParam), (HMENU)lParam);
|
|
break;
|
|
|
|
case DVM_GETBUTTONINFO: {
|
|
LPTBINFO ptbinfo = (LPTBINFO)lParam;
|
|
ptbinfo->cbuttons = ARRAYSIZE(c_tbBrfCase);
|
|
ptbinfo->uFlags = TBIF_PREPEND;
|
|
}
|
|
break;
|
|
|
|
case DVM_GETBUTTONS:
|
|
BrfView_OnGetButtons(this, hwndMain, LOWORD(wParam), (LPTBBUTTON)lParam);
|
|
break;
|
|
|
|
case DVM_SELCHANGE:
|
|
hres = BrfView_OnSelChange(this, hwndMain, LOWORD(wParam), (PDVSELCHANGEINFO)lParam);
|
|
break;
|
|
|
|
case DVM_QUERYFSNOTIFY:
|
|
hres = BrfView_OnQueryFSNotify(this, (SHChangeNotifyEntry *)lParam);
|
|
break;
|
|
|
|
case DVM_FSNOTIFY:
|
|
hres = BrfView_OnFSNotify(this, hwndMain, (LONG)lParam, (LPCITEMIDLIST *)wParam);
|
|
break;
|
|
|
|
case DVM_GETDETAILSOF:
|
|
hres = BrfView_OnGetDetailsOf(this, hwndMain, (UINT)wParam, (PDETAILSINFO)lParam);
|
|
break;
|
|
|
|
case DVM_COLUMNCLICK:
|
|
hres = BrfView_OnColumnClick(this, hwndMain, (UINT)wParam);
|
|
break;
|
|
|
|
case DVM_QUERYCOPYHOOK:
|
|
Assert(hres==NOERROR);
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - BrfView_ViewCallBack: DVM_QUERYCOPYHOOK is sent"));
|
|
break;
|
|
|
|
case DVM_NOTIFYCOPYHOOK:
|
|
hres = BrfView_OnNotifyCopyHook(this, hwndMain, (const COPYHOOKINFO *)lParam);
|
|
break;
|
|
|
|
case DVM_INSERTITEM:
|
|
hres = BrfView_OnInsertItem(this, hwndMain, (LPCITEMIDLIST)wParam, (DVSELCHANGEINFO *)lParam);
|
|
break;
|
|
|
|
case DVM_DEFVIEWMODE:
|
|
*(FOLDERVIEWMODE *)lParam = FVM_DETAILS;
|
|
break;
|
|
|
|
default:
|
|
hres = E_FAIL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Remote IShellView member functions
|
|
//
|
|
// Unlike the other classes that we support, our IShellView
|
|
// class is a wrapper to the shell's functions.
|
|
//---------------------------------------------------------------------------
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::QueryInterface
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_QueryInterface(
|
|
LPSHELLVIEW psv,
|
|
REFIID riid,
|
|
LPVOID FAR* ppvOut)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->QueryInterface(this->psv, riid, ppvOut);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::AddRef
|
|
|
|
Returns: new reference count
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP_(UINT) BrfView_AddRef(
|
|
LPSHELLVIEW psv)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->AddRef(this->psv);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::Release
|
|
|
|
Returns: new reference count
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP_(UINT) BrfView_Release(
|
|
LPSHELLVIEW psv)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
UINT cRef;
|
|
|
|
cRef = this->psv->lpVtbl->Release(this->psv);
|
|
|
|
if (0 == cRef)
|
|
{
|
|
if (this->pbrfstg)
|
|
{
|
|
this->pbrfstg->lpVtbl->Release(this->pbrfstg);
|
|
}
|
|
|
|
if (this->pidlRoot)
|
|
{
|
|
ILFree(this->pidlRoot);
|
|
}
|
|
|
|
// Don't close the delay mutex! It is owned by the briefcase
|
|
// storage.
|
|
LocalFree((HLOCAL)this);
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::GetWindow
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_GetWindow(
|
|
LPSHELLVIEW psv,
|
|
HWND * phwnd)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->GetWindow(this->psv, phwnd);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::ContextSensitiveHelp
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_ContextSensitiveHelp(
|
|
LPSHELLVIEW psv,
|
|
BOOL bEnterMode)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->ContextSensitiveHelp(this->psv, bEnterMode);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::TranslateAccelerator
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_TranslateAccelerator(
|
|
LPSHELLVIEW psv,
|
|
LPMSG pmsg)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->TranslateAccelerator(this->psv, pmsg);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::EnableModeless
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_EnableModeless(
|
|
LPSHELLVIEW psv,
|
|
BOOL bEnable)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->EnableModeless(this->psv, bEnable);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::UIActivate
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_UIActivate(
|
|
LPSHELLVIEW psv,
|
|
UINT uState)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->UIActivate(this->psv, uState);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::Refresh
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_Refresh(
|
|
LPSHELLVIEW psv)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->Refresh(this->psv);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::CreateViewWindow
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_CreateViewWindow(
|
|
LPSHELLVIEW psv,
|
|
LPSHELLVIEW psvPrevView,
|
|
LPCFOLDERSETTINGS pfs,
|
|
LPSHELLBROWSER psb,
|
|
LPRECT prcView,
|
|
HWND * phwnd)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
HRESULT hres;
|
|
|
|
hres = this->psv->lpVtbl->CreateViewWindow(this->psv, psvPrevView, pfs,
|
|
psb, prcView, phwnd);
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::DestroyViewWindow
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_DestroyViewWindow(
|
|
LPSHELLVIEW psv)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->DestroyViewWindow(this->psv);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::GetCurrentInfo
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_GetCurrentInfo(
|
|
LPSHELLVIEW psv,
|
|
LPFOLDERSETTINGS pfs)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->GetCurrentInfo(this->psv, pfs);
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::AddPropertySheetPages
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP BrfView_AddPropertySheetPages(
|
|
LPSHELLVIEW psv,
|
|
DWORD dwReserved,
|
|
LPFNADDPROPSHEETPAGE pfn,
|
|
LPARAM lParam)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->AddPropertySheetPages(this->psv, dwReserved,
|
|
pfn, lParam);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::SaveViewState
|
|
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP_(UINT) BrfView_SaveViewState(
|
|
LPSHELLVIEW psv)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->SaveViewState(this->psv);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellView::Release
|
|
|
|
Returns: new reference count
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP_(UINT) BrfView_SelectItem(
|
|
LPSHELLVIEW psv,
|
|
LPCITEMIDLIST pidlItem,
|
|
UINT uFlags)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->SelectItem(this->psv, pidlItem, uFlags);
|
|
}
|
|
|
|
|
|
STDMETHODIMP BrfView_GetItemObject(
|
|
LPSHELLVIEW psv,
|
|
UINT uItem,
|
|
REFIID riid,
|
|
LPVOID *ppv)
|
|
{
|
|
PBRFVIEW this = IToClass(BrfView, sv, psv);
|
|
|
|
return this->psv->lpVtbl->GetItemObject(this->psv, uItem, riid, ppv);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Remote class : Vtables
|
|
//---------------------------------------------------------------------------
|
|
|
|
#pragma data_seg(DATASEG_READONLY)
|
|
|
|
IShellViewVtbl c_BrfViewVtbl =
|
|
{
|
|
BrfView_QueryInterface,
|
|
BrfView_AddRef,
|
|
BrfView_Release,
|
|
|
|
BrfView_GetWindow,
|
|
BrfView_ContextSensitiveHelp,
|
|
BrfView_TranslateAccelerator,
|
|
BrfView_EnableModeless,
|
|
BrfView_UIActivate,
|
|
BrfView_Refresh,
|
|
|
|
BrfView_CreateViewWindow,
|
|
BrfView_DestroyViewWindow,
|
|
BrfView_GetCurrentInfo,
|
|
BrfView_AddPropertySheetPages,
|
|
BrfView_SaveViewState,
|
|
BrfView_SelectItem,
|
|
BrfView_GetItemObject,
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: This function creates an instance of IShellView.
|
|
The BrfView class does very little. It aggregates
|
|
to defviewx.c.
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
HRESULT BrfView_CreateInstance(
|
|
LPSHELLFOLDER psf,
|
|
LPCITEMIDLIST pidl,
|
|
PBRFEXP pbrfexp,
|
|
HWND hwnd,
|
|
LPVOID FAR* ppvOut)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
PBRFVIEW this;
|
|
|
|
this = (void*)LocalAlloc(LPTR, SIZEOF(*this));
|
|
|
|
if (this)
|
|
{
|
|
// This is stupid that we have to do all this aggregation to
|
|
// allocate our own instance data.
|
|
this->sv.lpVtbl = &c_BrfViewVtbl;
|
|
|
|
// 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, &this->pbrfstg);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Get the global delay mutex
|
|
this->pbrfstg->lpVtbl->GetExtraInfo(this->pbrfstg, NULL, GEI_DELAYHANDLE,
|
|
0, (LPARAM)&this->hMutexDelay);
|
|
|
|
// Get the database name
|
|
this->pbrfstg->lpVtbl->GetExtraInfo(this->pbrfstg, NULL, GEI_DATABASENAME,
|
|
ARRAYSIZE(this->szDBName), (LPARAM)this->szDBName);
|
|
|
|
if (ILGetBriefcaseRoot(this->pbrfstg, pidl, &this->pidlRoot))
|
|
{
|
|
CSFV csfv;
|
|
|
|
// Piggy-back off the shell's default view object.
|
|
csfv.cbSize = SIZEOF(csfv);
|
|
csfv.pshf = psf;
|
|
csfv.psvOuter = &this->sv;
|
|
csfv.pidl = pidl;
|
|
csfv.lEvents = SHCNE_DISKEVENTS | SHCNE_ASSOCCHANGED | SHCNE_GLOBALEVENTS;
|
|
csfv.pfnCallback = BrfView_ViewCallback;
|
|
csfv.fvm = 0;
|
|
|
|
hres = SHCreateShellFolderViewEx(&csfv, &this->psv);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
this->pidl = pidl;
|
|
this->pbrfexp = pbrfexp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
if (this->pidlRoot)
|
|
ILFree(this->pidlRoot);
|
|
|
|
if (this->pbrfstg)
|
|
this->pbrfstg->lpVtbl->Release(this->pbrfstg);
|
|
|
|
LocalFree((HLOCAL)this);
|
|
this = NULL;
|
|
}
|
|
}
|
|
|
|
*ppvOut = this;
|
|
|
|
return hres; // S_OK or E_NOINTERFACE
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
// CFSBrfFolder : class definition
|
|
//===========================================================================
|
|
|
|
typedef struct _CFSBrfFolder // bf
|
|
{
|
|
CFSFolder fsf;
|
|
BRFEXP brfexp;
|
|
} CFSBrfFolder, FAR* LPFSBRFFOLDER;
|
|
|
|
|
|
//===========================================================================
|
|
// CFSBrfIDLDropTarget
|
|
//===========================================================================
|
|
|
|
// Parameter to the "Drop" thread.
|
|
//
|
|
typedef struct _BRFDROPTHREAD
|
|
{
|
|
FSTHREADPARAM fsthp; // This must be the first field in this struct
|
|
} BRFDROPTHREAD, * PBRFDROPTHREAD;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Entry of "drop thread" for briefcase.
|
|
|
|
The one special thing this does differently is
|
|
call the IBriefcaseStg->AddObject to handle the
|
|
copy case.
|
|
|
|
Returns: --
|
|
|
|
Cond: This function frees pv.
|
|
*/
|
|
DWORD CALLBACK CFSBrfDropTarget_DropThreadInit(LPVOID pv)
|
|
{
|
|
PBRFDROPTHREAD pbrfdt = (PBRFDROPTHREAD)pv;
|
|
LPFSTHREADPARAM pfsthp = &pbrfdt->fsthp;
|
|
Assert((LPVOID)pfsthp == (LPVOID)pbrfdt);
|
|
|
|
// 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
|
|
LPBRIEFCASESTG pbrfstg;
|
|
UINT uFlags;
|
|
|
|
if (DDIDM_SYNCCOPYTYPE == pfsthp->idCmd)
|
|
uFlags = AOF_FILTERPROMPT;
|
|
else
|
|
uFlags = AOF_DEFAULT;
|
|
|
|
if (SUCCEEDED(BrfStg_CreateInstance(pfsthp->pfsdtgt->pidl, pfsthp->pfsdtgt->hwndOwner, &pbrfstg)))
|
|
{
|
|
pbrfstg->lpVtbl->AddObject(pbrfstg, pfsthp->pDataObj, NULL, uFlags, pfsthp->pfsdtgt->hwndOwner);
|
|
pbrfstg->lpVtbl->Release(pbrfstg);
|
|
}
|
|
SHChangeNotifyHandleEvents(); // force update now
|
|
|
|
pfsthp->pDataObj->lpVtbl->Release(pfsthp->pDataObj);
|
|
pfsthp->pfsdtgt->dropt.lpVtbl->Release(&pfsthp->pfsdtgt->dropt);
|
|
LocalFree((HLOCAL)pbrfdt);
|
|
}
|
|
else
|
|
{
|
|
// No; let the default handler do it
|
|
CFSDropTarget_DropThreadInit(pfsthp);
|
|
|
|
// (CFSDropTarget_DropThreadInit frees pftthp/pbrfdt)
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Returns TRUE if any folders are in hdrop
|
|
|
|
Returns: see above
|
|
Cond: --
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Determines the correct operation based on the package being
|
|
dropped into the briefcase.
|
|
|
|
Returns: dwDefEffect
|
|
Cond: --
|
|
*/
|
|
DWORD PickDefBriefOperation(
|
|
LPIDLDROPTARGET pdroptgt,
|
|
LPDATAOBJECT pdtobj,
|
|
DWORD grfKeyState,
|
|
LPBOOL pbSyncCopy,
|
|
UINT * pidMenu,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
HRESULT hres;
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
FORMATETC fmteBrief = {s_cfBriefObj, 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;
|
|
|
|
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
|
|
SHReleaseStgMedium(&medium);
|
|
goto AllowSync;
|
|
}
|
|
|
|
SHReleaseStgMedium(&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);
|
|
SHReleaseStgMedium(&medium);
|
|
}
|
|
else
|
|
idMenu = POPUP_NONDEFAULTDD;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDropTarget::DragEnter
|
|
|
|
Returns: standard result
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfIDLDropTarget_DragEnter(
|
|
IDropTarget *pdropt,
|
|
IDataObject *pdtobj,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
LPIDLDROPTARGET 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);
|
|
|
|
#if 1
|
|
// The cursor always indicates the default action.
|
|
*pdwEffect = dwDefault;
|
|
#else
|
|
// The cursor indicates the default action only if left-dragged.
|
|
if (grfKeyState & MK_LBUTTON)
|
|
{
|
|
*pdwEffect = dwDefault;
|
|
}
|
|
#endif
|
|
|
|
this->dwEffectLastReturned = *pdwEffect;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IDropTarget::DragOver
|
|
|
|
Returns: standard result
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfIDLDropTarget_DragOver(
|
|
LPDROPTARGET pdropt,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
LPDATAOBJECT pdtobj = this->pdtobj;
|
|
|
|
if (this->grfKeyStateLast != grfKeyState)
|
|
{
|
|
DWORD dwDefault;
|
|
|
|
this->grfKeyStateLast = grfKeyState;
|
|
|
|
dwDefault = PickDefBriefOperation(this, pdtobj, grfKeyState,
|
|
NULL, NULL, pdwEffect);
|
|
|
|
#if 1
|
|
// The cursor always indicates the default action.
|
|
*pdwEffect = dwDefault;
|
|
#else
|
|
// The cursor indicates the default action only if left-dragged.
|
|
if (grfKeyState & MK_LBUTTON)
|
|
{
|
|
*pdwEffect = dwDefault;
|
|
}
|
|
#endif
|
|
|
|
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(
|
|
LPDROPTARGET pdropt,
|
|
LPDATAOBJECT pdtobj,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
HRESULT hres;
|
|
DWORD dwDefEffect;
|
|
HKEY hkeyBaseProgID;
|
|
HKEY hkeyProgID;
|
|
UINT idMenu;
|
|
BOOL bSyncCopy;
|
|
DRAGDROPMENUPARAM ddm;
|
|
|
|
this->pdtobj = pdtobj;
|
|
|
|
dwDefEffect = PickDefBriefOperation(this, pdtobj, grfKeyState,
|
|
&bSyncCopy, &idMenu, pdwEffect);
|
|
|
|
// Get the hkeyProgID and hkeyBaseProgID
|
|
SHGetClassKey((LPIDFOLDER)this->pidl, &hkeyProgID, NULL, FALSE);
|
|
SHGetBaseClassKey((LPIDFOLDER)this->pidl, &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)
|
|
{
|
|
// Yes; note that we need to create another thread to avoid
|
|
// blocking the source thread.
|
|
//
|
|
PBRFDROPTHREAD pbrfdt = (void*)LocalAlloc(LPTR, SIZEOF(*pbrfdt));
|
|
if (pbrfdt)
|
|
{
|
|
DWORD idThread;
|
|
HANDLE hthread;
|
|
BOOL bIsOurs = CIDLData_IsOurs(pdtobj);
|
|
|
|
// If this is copy or move operation (i.e., file operation)
|
|
// clone the data object with appropriate formats and force
|
|
// secondary thread (CIDLData_IsOurs will succeed). This will
|
|
// solve thread-lock problem AND scrap-left-open probelm.
|
|
// (SatoNa)
|
|
//
|
|
if (!bIsOurs &&
|
|
(*pdwEffect == DROPEFFECT_MOVE || *pdwEffect == DROPEFFECT_COPY))
|
|
{
|
|
LPDATAOBJECT pdtobjClone = NULL;
|
|
if (SUCCEEDED(CIDLData_CloneForBriefcaseMoveCopy(pdtobj, &pdtobjClone)))
|
|
{
|
|
pdtobj->lpVtbl->Release(pdtobj);
|
|
pdtobj = pdtobjClone;
|
|
bIsOurs = TRUE;
|
|
}
|
|
}
|
|
|
|
pdtobj->lpVtbl->AddRef(pdtobj);
|
|
this->dropt.lpVtbl->AddRef(pdropt);
|
|
pbrfdt->fsthp.pfsdtgt = this;
|
|
pbrfdt->fsthp.pDataObj = pdtobj;
|
|
pbrfdt->fsthp.dwEffect = *pdwEffect;
|
|
pbrfdt->fsthp.fSameHwnd = ShellFolderView_IsDropOnSource(this->hwndOwner, &this->dropt);
|
|
pbrfdt->fsthp.bSyncCopy = bSyncCopy;
|
|
pbrfdt->fsthp.idCmd = ddm.idCmd;
|
|
ShellFolderView_GetAnchorPoint(this->hwndOwner, FALSE, &pbrfdt->fsthp.ptDrop);
|
|
|
|
// If this data object is our own (it means it is from our own
|
|
// drag&drop loop), create a thread and do it asynchronously.
|
|
// Otherwise (it means this is from OLE), do it synchronously.
|
|
//
|
|
if (bIsOurs)
|
|
{
|
|
// Process it asynchronously.
|
|
if (NULL != (hthread = CreateThread(NULL, 0, CFSBrfDropTarget_DropThreadInit, pbrfdt, 0, &idThread)))
|
|
{
|
|
// We don't need to communicate with this thread any more.
|
|
// Close the handle and let it run and terminate itself.
|
|
//
|
|
// Notes: In this case, pszCopy will be freed by the thread.
|
|
//
|
|
CloseHandle(hthread);
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// Thread creation failed, we should release thread parameter.
|
|
pdtobj->lpVtbl->Release(pdtobj);
|
|
this->dropt.lpVtbl->Release(pdropt);
|
|
LocalFree((HLOCAL)pbrfdt);
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Process it synchronously.
|
|
//
|
|
CFSBrfDropTarget_DropThreadInit(pbrfdt);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
#pragma data_seg(DATASEG_READONLY)
|
|
|
|
IDropTargetVtbl c_CFSBrfDropTargetVtbl =
|
|
{
|
|
CIDLDropTarget_QueryInterface,
|
|
CIDLDropTarget_AddRef,
|
|
CIDLDropTarget_Release,
|
|
CFSBrfIDLDropTarget_DragEnter,
|
|
CFSBrfIDLDropTarget_DragOver,
|
|
CIDLDropTarget_DragLeave,
|
|
CFSBrfIDLDropTarget_Drop, // special member function
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
|
|
//===========================================================================
|
|
// CFSBrfFolder
|
|
//===========================================================================
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolder::Release
|
|
|
|
Returns: reference count
|
|
Cond: --
|
|
*/
|
|
ULONG STDMETHODCALLTYPE CFSBrfFolder_Release(
|
|
LPSHELLFOLDER psf)
|
|
{
|
|
register LPFSBRFFOLDER this = IToClass(CFSBrfFolder, fsf, psf);
|
|
this->fsf.cRef--;
|
|
if (this->fsf.cRef > 0)
|
|
{
|
|
return this->fsf.cRef;
|
|
}
|
|
|
|
if (this->fsf.pidl)
|
|
{
|
|
ILFree(this->fsf.pidl);
|
|
}
|
|
|
|
DeleteCriticalSection(&this->brfexp.cs);
|
|
|
|
LocalFree((HLOCAL)this);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolder::BindToObject
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfFolder_BindToObject(
|
|
LPSHELLFOLDER psf,
|
|
LPCITEMIDLIST pidl,
|
|
LPBC pbc,
|
|
REFIID riid,
|
|
LPVOID FAR* ppvOut)
|
|
{
|
|
LPFSFOLDER this = IToClass(CFSFolder, sf, psf);
|
|
if (FS_IsValidID(pidl))
|
|
{
|
|
// Bind to a folder inside a briefcase folder as
|
|
// a briefcase folder too.
|
|
//
|
|
return FSBindToObject(&CLSID_Briefcase, this->pidl, pidl, pbc, riid, ppvOut);
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolder::CompareIDs
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfFolder_CompareIDs(
|
|
LPSHELLFOLDER psf,
|
|
LPARAM lParam,
|
|
LPCITEMIDLIST pidl1,
|
|
LPCITEMIDLIST pidl2)
|
|
{
|
|
HRESULT hres;
|
|
short nCmp;
|
|
LPFSBRFFOLDER this = IToClass(CFSBrfFolder, fsf, psf);
|
|
LPIDFOLDER pidf1 = (LPIDFOLDER)pidl1;
|
|
LPIDFOLDER pidf2 = (LPIDFOLDER)pidl2;
|
|
UINT iCol = (lParam & ~HACK_IGNORETYPE);
|
|
|
|
if (!FS_IsValidID(pidl1) || !FS_IsValidID(pidl2))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// We should ignore type, if one of ID has unknown type (SHID_FS).
|
|
//
|
|
if ( !(lParam & HACK_IGNORETYPE) &&
|
|
FS_GetType(pidf1) != SHID_FS && FS_GetType(pidf2) != SHID_FS &&
|
|
FS_GetType(pidf1) != SHID_FS_UNICODE && FS_GetType(pidf2) != SHID_FS_UNICODE
|
|
)
|
|
{
|
|
// Always put the folders first
|
|
if (FS_IsFolder(pidf1))
|
|
{
|
|
if (!FS_IsFolder(pidf2))
|
|
{
|
|
return ResultFromShort(-1);
|
|
}
|
|
}
|
|
else if (FS_IsFolder(pidf2))
|
|
{
|
|
return ResultFromShort(1);
|
|
}
|
|
}
|
|
|
|
switch (iCol)
|
|
{
|
|
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(psf, pidf1, pidf2);
|
|
if (nCmp)
|
|
return ResultFromShort(nCmp);
|
|
goto DoDefault;
|
|
|
|
case ICOL_BRIEFCASE_MODIFIED:
|
|
if ((DWORD)MAKELONG(pidf1->fs.timeModified, pidf1->fs.dateModified) <
|
|
(DWORD)MAKELONG(pidf2->fs.timeModified, pidf2->fs.dateModified))
|
|
{
|
|
return ResultFromShort(-1);
|
|
}
|
|
if ((DWORD)MAKELONG(pidf1->fs.timeModified, pidf1->fs.dateModified) >
|
|
(DWORD)MAKELONG(pidf2->fs.timeModified, pidf2->fs.dateModified))
|
|
{
|
|
return ResultFromShort(1);
|
|
}
|
|
goto DoDefault;
|
|
|
|
case ICOL_BRIEFCASE_NAME:
|
|
// We need to treat this differently from others bacause
|
|
// pidf1/2 might not be simple.
|
|
hres = FS_CompareItemIDs((LPSHITEMID)pidf1, (LPSHITEMID)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(psf, pidl1, pidl2);
|
|
}
|
|
|
|
break;
|
|
|
|
case ICOL_BRIEFCASE_ORIGIN:
|
|
case ICOL_BRIEFCASE_STATUS: {
|
|
BRFINFO bi1;
|
|
BRFINFO bi2;
|
|
BOOL bVal1;
|
|
BOOL bVal2;
|
|
|
|
bVal1 = BrfExp_FindCachedName(&this->brfexp, pidl1, &bi1);
|
|
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 == iCol)
|
|
{
|
|
hres = ResultFromShort(lstrcmp(bi1.szOrigin, bi2.szOrigin));
|
|
}
|
|
else
|
|
{
|
|
Assert(ICOL_BRIEFCASE_STATUS == iCol);
|
|
hres = ResultFromShort(lstrcmp(bi1.szStatus, bi2.szStatus));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DoDefault:
|
|
// Sort it based on the primary (long) name -- ignore case.
|
|
hres = ResultFromShort(ualstrcmpi(FS_GetName(pidf1), FS_GetName(pidf2)));
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolder::CreateViewObject
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfFolder_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd, REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
LPFSBRFFOLDER this = IToClass(CFSBrfFolder, fsf, 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, (LPDROPTARGET *)ppvOut);
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IContextMenu))
|
|
{
|
|
return CFSFolder_CreateViewObject(psf, hwnd, riid, ppvOut);
|
|
}
|
|
else
|
|
{
|
|
*ppvOut = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolder::GetAttributesOf
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfFolder_GetAttributesOf(
|
|
LPSHELLFOLDER psf,
|
|
UINT cidl,
|
|
LPCITEMIDLIST * apidl,
|
|
ULONG * prgfInOut)
|
|
{
|
|
LPFSBRFFOLDER this = IToClass(CFSBrfFolder, fsf, psf);
|
|
|
|
// Validate this pidl?
|
|
if (*prgfInOut & SFGAO_VALIDATE)
|
|
{
|
|
// Yes; dirty the briefcase storage entry by sending an update
|
|
// notification
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Receiving F5, dirty entire briefcase storage"));
|
|
|
|
BrfExp_AllNamesAreStale(&this->brfexp);
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Finished staling everything"));
|
|
}
|
|
|
|
// Pass onto standard CFSFolder class member function
|
|
return CFSFolder_GetAttributesOf(psf, cidl, apidl, prgfInOut);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IShellFolder::GetUIObjectOf
|
|
|
|
Returns: standard
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP CFSBrfFolder_GetUIObjectOf(
|
|
LPSHELLFOLDER psf,
|
|
HWND hwndOwner,
|
|
UINT cidl,
|
|
LPCITEMIDLIST * apidl,
|
|
REFIID riid,
|
|
UINT * prgfInOut,
|
|
LPVOID * ppvOut)
|
|
{
|
|
LPFSFOLDER this = IToClass(CFSFolder, sf, psf);
|
|
|
|
s_cfBriefObj = RegisterClipboardFormat(CFSTR_BRIEFOBJECT);
|
|
|
|
if (IsEqualIID(riid, &IID_IExtractIcon)
|
|
#ifdef UNICODE
|
|
|| IsEqualIID(riid, &IID_IExtractIconA)
|
|
#endif
|
|
)
|
|
{
|
|
// Defer to CFSFolder...
|
|
//
|
|
return CFSFolder_GetUIObjectOf(psf, hwndOwner, cidl, apidl, riid,
|
|
prgfInOut, ppvOut);
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IContextMenu))
|
|
{
|
|
// Defer to CFSFolder...
|
|
//
|
|
return CFSFolder_GetUIObjectOf(psf, hwndOwner, 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, (LPDATAOBJECT FAR*)ppvOut);
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IDropTarget))
|
|
{
|
|
// Defer to CFSFolder... (CFSBrfFolder_CreateViewObject
|
|
// ends up getting called.)
|
|
return CFSFolder_GetUIObjectOf(psf, hwndOwner, cidl, apidl, riid,
|
|
prgfInOut, ppvOut);
|
|
}
|
|
else
|
|
{
|
|
*ppvOut = NULL;
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma data_seg(DATASEG_READONLY)
|
|
|
|
IShellFolderVtbl c_FSBrfFolderVtbl =
|
|
{
|
|
CDefShellFolder_QueryInterface,
|
|
CFSFolder_AddRef,
|
|
CFSBrfFolder_Release, // special member function
|
|
CFSFolder_ParseDisplayName,
|
|
CFSFolder_EnumObjects,
|
|
CFSBrfFolder_BindToObject, // special member function
|
|
CDefShellFolder_BindToStorage,
|
|
CFSBrfFolder_CompareIDs,
|
|
CFSBrfFolder_CreateViewObject, // special member function
|
|
CFSBrfFolder_GetAttributesOf,
|
|
CFSBrfFolder_GetUIObjectOf, // special member function
|
|
CFSFolder_GetDisplayNameOf,
|
|
CFSFolder_SetNameOf,
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: CFSBrfFolder constructor
|
|
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT CFSBrfFolder_CreateFromIDList(
|
|
LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
LPFSBRFFOLDER this = (void*)LocalAlloc(LPTR, SIZEOF(CFSBrfFolder));
|
|
if (this)
|
|
{
|
|
HRESULT hres;
|
|
this->fsf.sf.lpVtbl = &c_FSBrfFolderVtbl;
|
|
this->fsf.cRef = 1;
|
|
this->fsf.pidl = ILClone(pidl);
|
|
if (this->fsf.pidl)
|
|
{
|
|
// Pre-initialize the expensive-cache here. The rest of
|
|
// its initialization will happen when IShellView is
|
|
// created.
|
|
InitializeCriticalSection(&this->brfexp.cs);
|
|
this->brfexp.psf = (LPSHELLFOLDER)this;
|
|
|
|
Assert(this->fsf.sf.lpVtbl->QueryInterface == CDefShellFolder_QueryInterface);
|
|
hres = CDefShellFolder_QueryInterface(&this->fsf.sf, riid, ppvOut);
|
|
}
|
|
else
|
|
{
|
|
*ppvOut = NULL;
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
Assert(this->fsf.sf.lpVtbl->Release == CFSBrfFolder_Release);
|
|
CFSBrfFolder_Release(&this->fsf.sf);
|
|
return hres;
|
|
}
|
|
else
|
|
{
|
|
*ppvOut = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
|
|
void WINAPI Desktop_UpdateBriefcaseOnEvent(HWND hwndMain, UINT uEvent)
|
|
{
|
|
#ifdef UPDATE_ON_HOT_DOCKING
|
|
|
|
HRESULT hres;
|
|
LPBRIEFCASESTG pbrfstg;
|
|
LPBRIEFCASESTG pbrfstgUpdate;
|
|
TCHAR szPath[MAXPATHLEN];
|
|
|
|
// Create an instance of IBriefcaseStg and enumerate all the briefcases
|
|
// on the system.
|
|
if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_Briefcase, NULL, &IID_IBriefcaseStg, &pbrfstg)))
|
|
{
|
|
hres = pbrfstg->lpVtbl->FindFirst(pbrfstg, szPath, ARRAYSIZE(szPath));
|
|
while (S_OK == hres)
|
|
{
|
|
// Open this briefcase storage
|
|
if (SUCCEEDED(pbrfstg->lpVtbl->QueryInterface(pbrfstg, &IID_IBriefcaseStg,
|
|
&pbrfstgUpdate)))
|
|
{
|
|
if (SUCCEEDED(pbrfstgUpdate->lpVtbl->Initialize(pbrfstgUpdate, szPath, hwndMain)))
|
|
{
|
|
// Update the briefcase on this event
|
|
pbrfstgUpdate->lpVtbl->UpdateOnEvent(pbrfstgUpdate, uEvent, hwndMain);
|
|
}
|
|
pbrfstgUpdate->lpVtbl->Release(pbrfstgUpdate);
|
|
}
|
|
|
|
// Find next briefcase on system
|
|
hres = pbrfstg->lpVtbl->FindNext(pbrfstg, szPath, ARRAYSIZE(szPath));
|
|
}
|
|
pbrfstg->lpVtbl->Release(pbrfstg);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|