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

3681 lines
105 KiB
C++

/*
This file contains the Find Files IShellFolder (CDFFolder) and IShellBrowser
(CDFBrowse) implementations
*/
#include "shellprv.h"
#pragma hdrstop
extern "C" {
#include <regstr.h>
#include <fsmenu.h>
#include "ids.h" // Why isn't this gotten from shellprv.h???
#include "findhlp.h" // "
#include "pidl.h" // "
#include "shitemid.h" // "
#include "defview.h" // "
#include "fstreex.h" // for views.h
#include "views.h"
}
#include "docfind.h"
#include "docfindx.h"
#include "cowsite.h"
#include "exdisp.h"
#include "shguidp.h"
#include "dfcmd.h"
#include "dbgmem.h"
#include "prop.h" // COL_DATA
// HACK! Listview doesn't support more than 100000000 items, so if our
// client returns more than that, just stop after that point.
// Instead of 100000000, we use the next lower 64K boundary. This keeps
// us away from strange boundary cases (where a +1 might push us over the
// top), and it keeps the Alpha happy.
#define MAX_LISTVIEWITEMS (100000000 & ~0xFFFF)
#define SANE_ITEMCOUNT(c) ((int)min(c, MAX_LISTVIEWITEMS))
#ifndef DEBUG
#define DF_USE_EXCEPTION
#endif
// NTRAID 277332: To enable the Save Results logic, uncomment this line: [scotthan]
// #define DOCFIND_SAVERESULTS_ENABLED
#ifdef DEBUG // For leak detection
EXTERN_C void GetAndRegisterLeakDetection(void);
EXTERN_C BOOL g_fInitTable;
EXTERN_C LEAKDETECTFUNCS LeakDetFunctionTable;
#endif
#define _FILESEARCHBAND_ // use WIN32 file/folder search band.
#define ID_LISTVIEW 1
#define WM_DF_THREADNOTIFY (WM_USER + 42)
#define WM_DF_FSNOTIFY (WM_USER + 43)
#define WM_DF_THREADNOTIFYASYNC (WM_USER + 44)
#define DF_THREADNOTIFY_COMPLETE ((LPARAM)-1)
#define DF_THREADNOTIFY_NORMAL ((LPARAM)0)
#define DF_THREADNOTIFY_ASYNC ((LPARAM)42)
#define DF_THREADNOTIFY_REDRAW ((LPARAM)43)
#define DF_MAX_MATCHFILES 10000
const UINT c_auDFMenuIDs[] = {
FCIDM_MENU_FILE,
FCIDM_MENU_EDIT,
FCIDM_MENU_VIEW,
IDM_MENU_OPTIONS,
FCIDM_MENU_HELP
};
const TCHAR s_szDocFind[]= TEXT("DocFind");
const TCHAR s_szFlags[] = TEXT("Flags");
const TCHAR s_szDocSpecMRU[] = REGSTR_PATH_EXPLORER TEXT("\\Doc Find Spec MRU");
#ifdef WINNT
// Unicode descriptor:
// Structure written at the end of NT-generated find stream serves dual purpose.
// 1. Contains an NT-specific signature to identify stream as NT-generated.
// Appears as "NTFF" (NT Find File) in ASCII dump of file.
// 2. Contains an offset to the unicode-formatted criteria section.
// The following diagram shows the find criteria/results stream format including
// the NT-specific unicode criteria and descriptor.
// +-+ --------------
// | DFHEADER structure | . .
// +-+ . .
// | DF Criteria records (ANSI) | Win95 .
// +-+ . .
// | DF Results (PIDL) [optional] | . NT
// +-+ ------- .
// +----->| DF Criteria records (Unicode) [NT only] | .
// | +-+ .
// | | Unicode Descriptor | .
// | ++ -------
// | / \
// | / \
// | +-----------------+---------+
// +---| Offset (64-bit) | "NTFF" |
// +-----------------+---------+
const DWORD c_NTsignature = 0x4646544E; // "NTFF" in ASCII file dump.
typedef struct _dfc_unicode_desc {
ULARGE_INTEGER oUnicodeCriteria; // Offset of unicode find criteria.
DWORD NTsignature; // Signature of NT-generated find file.
} DFC_UNICODE_DESC;
#endif
static const DWORD aFindHelpIDs[] = {
IDD_PAGELIST, NO_HELP,
IDD_ANIMATE, NO_HELP,
IDD_STATUS, NO_HELP,
IDD_START, IDH_FINDFILENAME_FINDNOW,
IDD_STOP, IDH_FINDFILENAME_STOP,
IDD_NEWSEARCH, IDH_FINDFILENAME_NEWSEARCH,
ID_LISTVIEW, IDH_FINDFILENAME_STATUSSCREEN,
0, 0
};
// Setup the column number definitions. - This may need to be converted
// into somethings that is filter related...
#ifdef WINNT
const UINT s_auMapDFColToFSCol[] = {0, (UINT)-1, (UINT)-1, 1, 2, 3, 4, 5, 6}; // More items than are needed but...
#else
// no rank column on win9x
const UINT s_auMapDFColToFSCol[] = {0, (UINT)-1, 1, 2, 3, 4, 5, 6}; // More items than are needed but...c
#endif
BOOL DocFind_StartFind(HWND hwndDlg, LPFINDTHREAD pft);
BOOL DocFind_StopFind(HWND hwndDlg);
BOOL DocFind_FindNext(HWND hwndDlg);
// IContextMenuCB implementation
class CDFContextMenuBase : public IContextMenuCB , public CObjectWithSite
{
public:
CDFContextMenuBase();
virtual ~CDFContextMenuBase();
// *** IUnknown methods ***
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
STDMETHOD_(ULONG,AddRef)();
STDMETHOD_(ULONG,Release)();
// *** IContextMenuCB methods ***
STDMETHOD(CallBack)(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg,
WPARAM wParam, LPARAM lParam) PURE;
private:
LONG _cRef;
};
CDFContextMenuBase::CDFContextMenuBase()
{
TraceMsg(TF_DOCFIND, "CDFContextMenuBase ctor");
_cRef = 1;
}
CDFContextMenuBase::~CDFContextMenuBase()
{
TraceMsg(TF_DOCFIND, "CDFContextMenuBase dtor");
}
STDMETHODIMP CDFContextMenuBase::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CDFContextMenuBase, IContextMenuCB), // IID_IContextMenuCB
QITABENT(CDFContextMenuBase, IObjectWithSite), // IID_IObjectWithSite
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
ULONG CDFContextMenuBase::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CDFContextMenuBase::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
class CDFContextMenuCB : public CDFContextMenuBase
{
public:
// *** IContextMenuCB methods ***
STDMETHOD(CallBack)(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg,
WPARAM wParam, LPARAM lParam);
};
// in fstreex.h
EXTERN_C DWORD CALLBACK _CFSFolder_PropertiesThread(void* pps); // bugbug: not actually a void
// static const QCMINFO_IDMAP
static const struct {
UINT max;
struct {
UINT id;
UINT fFlags;
} list[2];
} idMap = {
2,
{
{FSIDM_FOLDER_SEP, QCMINFO_PLACE_BEFORE},
{FSIDM_VIEW_SEP, QCMINFO_PLACE_AFTER},
},
};
STDMETHODIMP CDFContextMenuCB::CallBack(IShellFolder *psf, HWND hwndOwner,
IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT hres = NOERROR;
switch (uMsg)
{
case DFM_MERGECONTEXTMENU:
if (!(wParam & CMF_VERBSONLY))
{
LPQCMINFO pqcm = (LPQCMINFO)lParam;
if (pdtobj)
{
if (!(wParam & CMF_DVFILE))
{
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_FSVIEW_ITEM, 0, pqcm);
}
}
else
{
UINT idStart = pqcm->idCmdFirst;
UINT idBGMain = 0, idBGPopup = 0;
IDocFindFolder *pdfFolder;
IDocFindFileFilter *pdfff;
if (SUCCEEDED(psf->QueryInterface(IID_IDocFindFolder, (void **)&pdfFolder)))
{
if (SUCCEEDED(pdfFolder->GetDocFindFilter(&pdfff)))
{
pdfff->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup);
CDefFolderMenu_MergeMenu(HINST_THISDLL, idBGMain, idBGPopup, pqcm);
DeleteMenu(pqcm->hmenu, SFVIDM_EDIT_PASTE, MF_BYCOMMAND);
DeleteMenu(pqcm->hmenu, SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND);
DeleteMenu(pqcm->hmenu, SFVIDM_MISC_REFRESH, MF_BYCOMMAND);
IDocFindControllerNotify *pdcn;
if (S_OK == pdfFolder->GetControllerNotifyObject(&pdcn))
{
pdcn->Release();
}
else
{
DeleteMenu(pqcm->hmenu, idStart+FSIDM_SAVESEARCH, MF_BYCOMMAND);
}
pdfff->Release();
}
pdfFolder->Release();
}
}
pqcm->pIdMap = (QCMINFO_IDMAP *)&idMap;
}
break;
case DFM_INVOKECOMMAND:
{
// Check if this is from item context menu
if (pdtobj)
{
switch(wParam)
{
case DFM_CMD_LINK:
hres = SHCreateLinks(hwndOwner, NULL, pdtobj, SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL);
break;
case DFM_CMD_DELETE:
// TODO: Do this stuff for CShellBrowser case as well [t-ashram]
{
IDocFindBrowser *pdfb = NULL;
if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IDocFindBrowser, (void **)&pdfb)))
{
pdfb->DeferProcessUpdateDir();
}
// convert to DFM_INVOKCOMMANDEX to get flags bits
hres = DeleteFilesInDataObject(hwndOwner, 0, pdtobj);
if (pdfb)
{
pdfb->EndDeferProcessUpdateDir();
pdfb->Release();
}
}
break;
case DFM_CMD_PROPERTIES:
// We need to pass an empty IDlist to combine with.
SHLaunchPropSheet(_CFSFolder_PropertiesThread, pdtobj,
(LPCTSTR)lParam, NULL, (void *)&c_idlDesktop);
break;
default:
// BUGBUG: if GetAttributesOf did not specify the SFGAO_ bit
// that corresponds to this default DFM_CMD, then we should
// fail it here instead of returning S_FALSE. Otherwise,
// accelerator keys (cut/copy/paste/etc) will get here, and
// defcm tries to do the command with mixed results.
// BUGBUG: if GetAttributesOf did not specify SFGAO_CANLINK
// or SFGAO_CANDELETE or SFGAO_HASPROPERTIES, then the above
// implementations of these DFM_CMD commands are wrong...
// Let the defaults happen for this object
hres = (S_FALSE);
break;
}
}
else
{
IShellFolderView *psfv;
// No.
switch(wParam)
{
case FSIDM_SAVESEARCH:
{
IDocFindFolder *pdfFolder;
if (SUCCEEDED(psf->QueryInterface(IID_IDocFindFolder, (void **)&pdfFolder)))
{
IDocFindControllerNotify *pdcn;
if (S_OK == pdfFolder->GetControllerNotifyObject(&pdcn))
{
pdcn->SaveSearch();
pdcn->Release();
}
pdfFolder->Release();
}
break;
}
case FSIDM_SORTBYNAME:
case FSIDM_SORTBYSIZE:
case FSIDM_SORTBYTYPE:
case FSIDM_SORTBYDATE:
case FSIDM_SORTBYLOCATION:
if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_DefView, IID_IShellFolderView, (void **)&psfv)))
{
psfv->Rearrange(DFSortIDToICol((int) wParam));
psfv->Release();
}
break;
default:
// This is one of view menu items, use the default code.
hres = (S_FALSE);
break;
}
}
}
break;
case DFM_GETHELPTEXT: // ansi version
case DFM_GETHELPTEXTW:
{
UINT id = LOWORD(wParam) + IDS_MH_FSIDM_FIRST;
if (uMsg == DFM_GETHELPTEXTW)
LoadStringW(HINST_THISDLL, id, (LPWSTR)lParam, HIWORD(wParam));
else
LoadStringA(HINST_THISDLL, id, (LPSTR)lParam, HIWORD(wParam));
}
break;
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
class CDFFolderContextMenuItemCB : public CDFContextMenuBase
{
public:
// *** IContextMenuCB methods ***
STDMETHOD(CallBack)(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg,
WPARAM wParam, LPARAM lParam);
private:
CDFFolderContextMenuItemCB(IDocFindFolder* pdfFolder);
~CDFFolderContextMenuItemCB();
friend IContextMenu* CDFFolderContextMenuItem_Create(HWND hwndOwner, IDocFindFolder* pdfFolder);
IDocFindFolder *_pdfFolder;
};
CDFFolderContextMenuItemCB::CDFFolderContextMenuItemCB(IDocFindFolder* pdfFolder)
{
ASSERT(pdfFolder);
_pdfFolder = pdfFolder;
_pdfFolder->AddRef();
}
CDFFolderContextMenuItemCB::~CDFFolderContextMenuItemCB()
{
_pdfFolder->Release();
}
STDMETHODIMP CDFFolderContextMenuItemCB::CallBack(IShellFolder *psf, HWND hwndOwner,
IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT hres = NOERROR;
switch (uMsg)
{
case DFM_MERGECONTEXTMENU:
if (!(wParam & CMF_VERBSONLY))
{
LPQCMINFO pqcm = (LPQCMINFO)lParam;
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DOCFIND_ITEM_MERGE, 0, pqcm);
}
break;
case DFM_INVOKECOMMAND:
switch(wParam)
{
case FSIDM_OPENCONTAININGFOLDER:
{
IShellFolderView *psfv;
if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_DefView, IID_IShellFolderView, (void **)&psfv)))
{
DFB_HandleOpenContainingFolder(psfv, _pdfFolder);
psfv->Release();
}
break;
}
}
break;
case DFM_GETHELPTEXT:
case DFM_GETHELPTEXTW:
// probably need to implement these...
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
IContextMenu* CDFFolderContextMenuItem_Create(HWND hwndOwner, IDocFindFolder* pdfFolder)
{
IContextMenu* pcm = NULL;
// We want a quick IContextMenu implementation -- an empty defcm looks easiest
IContextMenuCB* pcmcb = new CDFFolderContextMenuItemCB(pdfFolder);
if (pcmcb)
{
CDefFolderMenu_CreateEx((LPCITEMIDLIST)NULL, hwndOwner,
0, NULL, NULL, pcmcb, NULL, NULL, &pcm);
pcmcb->Release();
}
return pcm;
}
// CDFFolder: class definition
// in docfind.h
// CDFFolder : Helper Functions
void cdecl DocFind_SetStatusText(HWND hwndStatus, int iField, UINT ids,...)
{
TCHAR sz2[MAX_PATH+32]; // leave slop for message + max path name
va_list ArgList;
if (hwndStatus)
{
va_start(ArgList, ids);
LPTSTR psz = _ConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(ids), &ArgList);
if (psz)
{
// a-msadek; needed only for BiDi Win95 loc
// Mirroring will take care of that over NT5 & BiDi Win98
if(g_bBiDiW95Loc)
{
StrCpyN(sz2, psz, ARRAYSIZE(sz2)); // for some reason, this has always copied to sz2
sz2[0] = sz2[1] = TEXT('\t'); // localizers must be leaving 2 spaces for these tabs...
}
else
StrCpyN(sz2, psz, ARRAYSIZE(sz2));
LocalFree(psz);
}
else
{
sz2[0] = 0;
}
va_end(ArgList);
if (iField < 0)
{
// a-msadek; needed only for BiDi Win95 loc
// Mirroring will take care of that over NT5 & BiDi Win98
if(g_bBiDiW95Loc)
{
SendMessage(hwndStatus, SB_SETTEXT, SBT_RTLREADING | SBT_NOBORDERS | 255, (LPARAM)(LPTSTR)sz2);
}
else
SendMessage(hwndStatus, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)(LPTSTR)sz2);
}
else
SendMessage(hwndStatus, SB_SETTEXT, iField, (LPARAM)(LPTSTR)sz2);
UpdateWindow(hwndStatus);
}
}
HRESULT CDFFolder::AddFolderToFolderList(LPITEMIDLIST pidl, BOOL fCheckForDup, int * piFolder)
{
HRESULT hres = ERROR_SUCCESS;
DFFolderListItem *pdffli;
int i;
int cbPidl;
cbPidl = ILGetSize(pidl);
if (fCheckForDup)
{
EnterCriticalSection(&_csSearch);
for (i = DPA_GetPtrCount(this->hdpaPidf)-1; i >= 0; i--)
{
pdffli = (DFFolderListItem *)DPA_FastGetPtr( this->hdpaPidf, i);
if (pdffli && (pdffli->cbPidl == cbPidl) && (ILIsEqual(&pdffli->idl, pidl)))
{
LeaveCriticalSection(&_csSearch);
*piFolder = i;
return S_OK;
}
}
LeaveCriticalSection(&_csSearch);
}
// For now it is path based, but...
pdffli = (DFFolderListItem *)LocalAlloc(LPTR, SIZEOF(DFFolderListItem) - sizeof(ITEMIDLIST) + cbPidl);
if (pdffli == NULL)
{
*piFolder = -1;
return (E_OUTOFMEMORY);
}
// lpddfli->psf = NULL;
// pdffli->fUpdateDir = FALSE;
memcpy(&pdffli->idl, pidl, cbPidl);
pdffli->iImage = -1; // Don't know the image yet for this one...
pdffli->cbPidl = cbPidl;
EnterCriticalSection(&_csSearch);
// Now add this item to our DPA...
i = DPA_AppendPtr( this->hdpaPidf, pdffli);
LeaveCriticalSection(&_csSearch);
if (i == -1)
{
LocalFree((HLOCAL)pdffli);
hres = E_OUTOFMEMORY;
}
*piFolder = i;
/* If this is a network ID list then register a path -> pidl mapping, therefore
/ avoiding having to create simple ID lists, which don't work correctly when
/ being compared against real ID lists. */
if (IsIDListInNameSpace(pidl, &CLSID_NetworkPlaces))
{
TCHAR szPath[ MAX_PATH ];
SHGetPathFromIDList(pidl, szPath);
NPTRegisterNameToPidlTranslation(szPath, pidl);
}
return hres;
}
void CDFFolder::_AddESFItemToSaveStateList(ESFItem *pesfi)
{
ESFSaveStateItem essi;
essi.dwState = pesfi->dwState & CDFITEM_STATE_MASK;
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA);
ASSERT(phdfd && (phdfd->wFlags & DFDF_EXTRADATA));
essi.dwItemID = phdfd->dwItemID;
DSA_AppendItem(this->_hdsaSaveStateForIDs, (void *)&essi);
if (essi.dwState & LVIS_SELECTED)
_cSaveStateSelected++;
}
HRESULT CDFFolder::RememberSelectedItems()
{
int i;
ESFItem *pesfi;
EnterCriticalSection(&_csSearch);
// Currently has list of pidls...
for (i = DPA_GetPtrCount(this->hdpaItems); i-- > 0; )
{
// Pidl at start of structure...
pesfi = (ESFItem*)DPA_FastGetPtr(this->hdpaItems, i);
if (pesfi != NULL)
{
if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED))
_AddESFItemToSaveStateList(pesfi);
}
}
LeaveCriticalSection(&_csSearch);
return S_OK;
}
STDMETHODIMP CDFFolder::ClearItemList()
{
int i;
ESFItem *pesfi;
// Clear out any async enumerators we may have
SetAsyncEnum(NULL);
_cAsyncItems = 0; // clear out our count of items...
pdfff->ReleaseQuery();
// Also tell the filter to release everything...
EnterCriticalSection(&_csSearch);
if (this->hdpaItems == NULL)
{
LeaveCriticalSection(&_csSearch);
return S_OK; // Nothing to do
}
// Currently has list of pidls...
for (i = DPA_GetPtrCount(this->hdpaItems); i-- > 0; )
{
// Pidl at start of structure...
pesfi = (ESFItem*)DPA_FastGetPtr(this->hdpaItems, i);
if (pesfi != NULL)
LocalFree((HLOCAL)pesfi);
}
_fSearchComplete = FALSE;
DPA_DeleteAllPtrs(this->hdpaItems);
LeaveCriticalSection(&_csSearch);
return S_OK;
}
STDMETHODIMP CDFFolder::ClearFolderList()
{
int i;
DFFolderListItem *pdffli;
EnterCriticalSection(&_csSearch);
if (this->hdpaPidf == NULL)
{
LeaveCriticalSection(&_csSearch);
return S_OK; // Nothing to do
}
for (i = DPA_GetPtrCount(this->hdpaPidf); i-- > 0; )
{
pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, i);
if (pdffli != NULL)
{
// Release the IShellFolder if we have one
if (pdffli->psf != NULL)
pdffli->psf->Release();
// And delete the item
if (LocalFree((HLOCAL)pdffli))
{
ASSERT(FALSE); // Something bad happened!
return E_FAIL;
}
}
}
DPA_DeleteAllPtrs(this->hdpaPidf);
LeaveCriticalSection(&_csSearch);
return S_OK;
}
// CDFFolder : Constructor
CDFFolder::CDFFolder()
{
_cRef = 1;
ASSERT(_pidl == NULL);
_iGetIDList = -1;
*_szEmptyText = 0;
ASSERT(this->pdfff == NULL);
// Create the heap for the folder lists.
this->hdpaPidf = DPA_CreateEx(64, GetProcessHeap());
// Create the DPA and DSA for the item list.
this->cbItem = sizeof (ESFItem);
this->hdpaItems = DPA_CreateEx(64, GetProcessHeap());
this->_hdsaSaveStateForIDs = DSA_Create(sizeof(ESFSaveStateItem), 16);
// initialize our LV selection objects...
_dflvrSel.SetOwner(this, LVIS_SELECTED);
_dflvrCut.SetOwner(this, LVIS_CUT);
InitializeCriticalSection(&_csSearch);
TraceMsg(TF_DOCFIND, "CDFFolder ctor");
}
CDFFolder::~CDFFolder()
{
ASSERT(_cRef==0);
// We will need to call our function to Free our items in our
// Folder list. We will use the same function that we use to
// clear it when we do a new search
ClearItemList();
ClearFolderList();
ClearSaveStateList();
EnterCriticalSection(&_csSearch);
DPA_Destroy(hdpaPidf);
DPA_Destroy(hdpaItems);
hdpaPidf = NULL;
hdpaItems = NULL;
LeaveCriticalSection(&_csSearch);
DSA_Destroy(_hdsaSaveStateForIDs);
if (pdfff)
pdfff->Release();
DeleteCriticalSection(&_csSearch);
TraceMsg(TF_DOCFIND, "CDFFolder dtor");
}
HRESULT CDFFolder_Create(void **ppvOut, IDocFindFileFilter *pdfff)
{
CDFFolder *pdffNew;
HRESULT hr = E_OUTOFMEMORY;
*ppvOut = NULL;
pdffNew = new CDFFolder();
if (pdffNew)
{
pdffNew->pdfff = pdfff; // Also handle case they do not pass one in...
if (pdffNew->pdfff == NULL)
pdffNew->pdfff = CreateDefaultDocFindFilter();
// Check for out of memory conditions...
if ( (pdffNew->pdfff == NULL) || (pdffNew->hdpaPidf == NULL) || (pdffNew->hdpaItems == NULL))
{
if (pdffNew->hdpaPidf)
DPA_Destroy(pdffNew->hdpaPidf);
if (pdffNew->hdpaItems)
DPA_Destroy(pdffNew->hdpaItems);
delete pdffNew->pdfff;
delete pdffNew;
pdffNew = NULL;
}
else
{
hr = NOERROR;
}
}
*ppvOut = pdffNew;
return hr;
}
STDAPI CDocFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
HRESULT hr;
CDFFolder *pdff;
hr = CDFFolder_Create((void **)&pdff, NULL);
if (SUCCEEDED(hr))
{
hr = pdff->QueryInterface(riid, ppv);
pdff->Release();
}
else
{
*ppv = NULL;
}
return hr;
}
//Should combine with above
STDAPI CComputerFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
HRESULT hr;
CDFFolder *pdff;
IDocFindFileFilter *pdfff = CreateDefaultComputerFindFilter();
if (!pdfff)
return E_FAIL;
// Warning This call takes ownership of pdfff - so don't release
hr = CDFFolder_Create((void **)&pdff, pdfff);
if (SUCCEEDED(hr))
{
hr = pdff->QueryInterface(riid, ppv);
pdff->Release();
}
else
{
*ppv = NULL;
}
return hr;
}
// CDFFolder : Now part of IDocFindFolder
HRESULT CDFFolder::MapFSPidlToDFPidl(LPITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl)
{
LPITEMIDLIST pidlParent = ILCloneParent(pidl);
*ppidl = NULL;
if (pidlParent)
{
EnterCriticalSection(&_csSearch);
// Now loop through our DPA list and see if we can find a matach
for (int i = 0; i <DPA_GetPtrCount(this->hdpaPidf); i++ )
{
DFFolderListItem *pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, i);
if (pdffli != NULL && ILIsEqual(pidlParent, &pdffli->idl))
{
// We found the right one
// so no lets transform the ID into one of our own
// to return. Note: we must catch the case where the
// original one passed in was a simple pidl and do
// the appropriate thing.
LPITEMIDLIST pidlReal;
// If this is not a FS folder, just clone it.
if (fMapToReal)
{
// We need to make sure we have an IShellFolder to
// work with...
IShellFolder *psf = DocFind_GetObjectsIFolder(this, pdffli, NULL);
if (psf)
{
SHGetRealIDL(psf, (LPITEMIDLIST)ILFindLastID(pidl), &pidlReal);
psf->Release();
}
else
{
pidlReal = ILClone((LPITEMIDLIST)ILFindLastID(pidl));
}
}
else
pidlReal = ILClone((LPITEMIDLIST)ILFindLastID(pidl));
if (pidlReal)
// return the appended
*ppidl = DocFind_AppendIFolder(pidlReal, i);
// exit regardless of success.
break;
}
}
LeaveCriticalSection(&_csSearch);
ILFree(pidlParent);
}
return *ppidl ? S_OK : S_FALSE;
}
// _UpdateItemList()
// Called before saving folder list. Needed especially in Asynch search,
// (CI). We lazily pull item data from RowSet only when list view asks
// for it. When we are leaving the search folder, we pull all items
// creating all necessary folder lists. This ensure when saving folder
// list, all are included.
// remark : Fix bug#338714.
HRESULT CDFFolder::_UpdateItemList()
{
ESFItem *pesfi;
int cItems;
int i;
USHORT cb = 0;
if (SUCCEEDED(GetItemCount(&cItems)))
{
for (i=0; i < cItems; i++)
{
HRESULT hres = GetItem(i, &pesfi);
if (hres == DB_S_ENDOFROWSET)
break;
}
}
return S_OK;
}
// CDFFolder : External function to Save results out to file.
// Now part of IDocfindFolder
HRESULT CDFFolder::SaveFolderList(IStream *pstm)
{
// We First pull all the items from RowSet (in Asynch case)
_UpdateItemList();
// Then, We serialize all of our PIDLS for each folder in our list
DFFolderListItem *pdffli;
USHORT cb;
int i;
EnterCriticalSection(&_csSearch);
// Now loop through our DPA list and see if we can find a matach
for (i = 0; i <DPA_GetPtrCount(this->hdpaPidf); i++ )
{
pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, i);
if (EVAL(pdffli))
ILSaveToStream(pstm, &pdffli->idl);
else
break;
}
LeaveCriticalSection(&_csSearch);
// Now out a zero size item..
cb = 0;
pstm->Write((TCHAR *)&cb, SIZEOF(cb), NULL);
return(TRUE);
}
// CDFFolder : External function to Restore results out to file.
// Now part of IDocfindFolder
HRESULT CDFFolder::RestoreFolderList(IStream *pstm)
{
// loop through and all all of the folders to our list...
LPITEMIDLIST pidl = NULL;
HRESULT hres;
for (;;)
{
int i;
hres = ILLoadFromStream(pstm, &pidl); // frees pidl for us
if (pidl == NULL)
break; // end of the list
AddFolderToFolderList(pidl, FALSE, &i);
}
ILFree(pidl); // don't forget to free last pidl
return hres;
}
HRESULT CDFFolder::SaveItemList(IStream *pstm)
{
// We First serialize all of our PIDLS for each item in our list
ESFItem *pesfi;
int cItems;
int i;
USHORT cb = 0;
if (SUCCEEDED(GetItemCount(&cItems)))
{
// And Save the items that are in the list
for (i=0; i < cItems; i++)
{
HRESULT hres = GetItem(i, &pesfi);
if (hres == DB_S_ENDOFROWSET)
break;
if (SUCCEEDED(hres) && pesfi)
ILSaveToStream(pstm, &pesfi->idl);
}
}
// Write out a Trailing NULL size to say end of pidl list...
pstm->Write(&cb, SIZEOF(cb), NULL);
return S_OK;
}
HRESULT CDFFolder::RestoreItemList(IStream *pstm, int *pcItems)
{
// And the pidls that are associated with the object
int cItems = 0;
LPITEMIDLIST pidl = NULL; // don't free previous one
ESFItem *pesfi;
for (; ;)
{
if (FAILED(ILLoadFromStream(pstm, &pidl)) || (pidl == NULL))
break;
if (FAILED(AddPidl(cItems, pidl, (UINT)-1, &pesfi)) || !pesfi)
break;
cItems++;
}
if (pidl)
ILFree(pidl); // Free the last one read in
*pcItems = cItems;
return S_OK;
}
// CDFFolder : External function to Restore results out to file.
// Now part of IDocfindFolder
HRESULT CDFFolder::GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent)
{
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
*ppidlParent = NULL;
if (phdfd)
{
EnterCriticalSection(&_csSearch);
// Now get to the item associated with that folder out of our dpa.
ASSERT(hdpaPidf && DPA_GetPtrCount(hdpaPidf) > phdfd->iFolder);
DFFolderListItem *pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, phdfd->iFolder);
if (pdffli)
{
// BUGBUG: we are returning pidl from a structure that may be freed on a different thread. should clone
*ppidlParent = &pdffli->idl;
LeaveCriticalSection(&_csSearch);
return S_OK;
}
LeaveCriticalSection(&_csSearch);
}
else
AssertMsg(FALSE, TEXT("CDF::GetParentsPIDL() passed invalid DocFind pidl"));
return S_FALSE;
}
HRESULT CDFFolder::SetControllerNotifyObject(IDocFindControllerNotify *pdfcn)
{
ATOMICRELEASE(_pdfcn);
_pdfcn = pdfcn;
if (_pdfcn)
{
_pdfcn->AddRef();
}
return S_OK;
}
HRESULT CDFFolder::GetControllerNotifyObject(IDocFindControllerNotify **ppdfcn)
{
*ppdfcn = _pdfcn;
if (_pdfcn)
_pdfcn->AddRef();
return _pdfcn ? S_OK : S_FALSE;
}
// CDFFolder : Members
// AddRef
STDMETHODIMP_(ULONG) CDFFolder::AddRef()
{
TraceMsg(TF_DOCFIND, "cdff.AddRef %d",_cRef+1);
return (++_cRef);
}
// Release
STDMETHODIMP_(ULONG) CDFFolder::Release()
{
ASSERT(_cRef >= 1); // Don't release more than we can!
_cRef--;
TraceMsg(TF_DOCFIND,"cdff.Release %d",_cRef);
if (_cRef > 0)
{
return _cRef;
}
delete this;
return 0;
}
STDMETHODIMP CDFFolder::ParseDisplayName(HWND hwndOwner,
LPBC pbc, LPOLESTR pwzDisplayName,
ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG * pdwAttributes)
{
return E_NOTIMPL;
}
STDMETHODIMP CDFFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
{
// We do not want the def view to enumerate us, instead we
// will tell defview to call us...
*ppenum = NULL; // No enumerator
return S_FALSE; // no enumerator (not error)
}
IShellFolder *DocFind_GetObjectsIFolder(IDocFindFolder *pdff, DFFolderListItem *pdffli, LPCITEMIDLIST pidl)
{
// First Validate the pidl that was passed in.
// is what we expect - cb, Folder number, followed by a mkid
if (pdffli == NULL)
{
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
if (phdfd)
pdff->GetFolderListItem(phdfd->iFolder, &pdffli);
else
AssertMsg(FALSE, TEXT("DocFind_GetObjectsIFolder() passed invalid DocFind pidl"));
}
// Now see if we need to build an IFolder for this object.
if (pdffli && pdffli->psf == NULL)
SHBindToObject(NULL, IID_IShellFolder, &pdffli->idl, (void **)&pdffli->psf);
if (pdffli && pdffli->psf)
pdffli->psf->AddRef();
return pdffli ? pdffli->psf : NULL;
}
STDMETHODIMP CDFFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut)
{
HRESULT hr = E_INVALIDARG;
// We need to parse the ID and see which sub-folder it belongs to
// then we will see if we have an IShellFolder for the object. If not
// we will construct it and then extract our part off of the IDLIST and
// then forward it...
IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl);
if (psfItem)
{
hr = psfItem->BindToObject(pidl, pbc, riid, ppvOut);
psfItem->Release();
}
return hr;
}
// Little helper function for bellow
HRESULT CDFFolder::_CompareFolderIndexes(int iFolder1, int iFolder2)
{
TCHAR szPath1[MAX_PATH];
TCHAR szPath2[MAX_PATH];
EnterCriticalSection(&_csSearch);
DFFolderListItem *pdffli1 = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf,
iFolder1);
DFFolderListItem *pdffli2 = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf,
iFolder2);
if ((pdffli1 != NULL) && (pdffli2 != NULL))
{
SHGetPathFromIDList(&pdffli1->idl, szPath1);
SHGetPathFromIDList(&pdffli2->idl, szPath2);
LeaveCriticalSection(&_csSearch);
return(ResultFromShort(lstrcmpi(szPath1, szPath2)));
}
LeaveCriticalSection(&_csSearch);
// If we got here there is an error!
return (E_INVALIDARG);
}
STDMETHODIMP CDFFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
HRESULT hres = (E_INVALIDARG);
IShellFolder *psf1;
LPITEMIDLIST pidl1Last;
int iFolder1;
int iFolder2;
// Note there are times in the defview that may call us back looking for
// a fully qualified pidl in pidl1, so we need to detect this case and
// only process the last part.
pidl1Last = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)pidl1);
iFolder1 = DF_IFOLDER(pidl1Last);
iFolder2 = DF_IFOLDER(pidl2);
// We need to handle the case our self if the sort is by
// the containing folder. But otherwise we simply forward the informat
// to the FSFolders compare function. This will only work if both ids
// are file system objects, but if this is not the case, then what???
if (lParam == IDFCOL_PATH)
{
if (iFolder1 != iFolder2)
return _CompareFolderIndexes(iFolder1, iFolder2);
// If we get here the paths are the same so set the sort order
// to name...
lParam = IDFCOL_NAME;
}
else if (lParam == IDFCOL_RANK)
{
PCHIDDENDOCFINDDATA phdfd1 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl1, IDLHID_DOCFINDDATA);
PCHIDDENDOCFINDDATA phdfd2 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl2, IDLHID_DOCFINDDATA);
// Could be mixed if so put ones without rank at the end...
if (phdfd1 && (phdfd1->wFlags & DFDF_EXTRADATA))
{
if (phdfd2 && (phdfd2->wFlags & DFDF_EXTRADATA))
{
// Have both...
if (phdfd1->ulRank < phdfd2->ulRank)
return ResultFromShort(-1);
if (phdfd1->ulRank > phdfd2->ulRank)
return ResultFromShort(1);
}
else
return ResultFromShort(1);
}
else if (phdfd2)
return ResultFromShort(-1);
// Otherwise next sort on name...
lParam = IDFCOL_NAME;
}
// For now we just forward this to the function the that CFSFolder
// uses. This is not clean but...
psf1 = DocFind_GetObjectsIFolder(this, NULL, pidl1Last);
if (psf1)
{
hres = psf1->CompareIDs(s_auMapDFColToFSCol[lParam], pidl1Last, pidl2);
psf1->Release();
if ((hres == 0) && (lParam == IDFCOL_NAME) && (iFolder1 != iFolder2))
{
// They compared the same and by name so make sure truelly the
// same path as this is used in processing notifications.
return _CompareFolderIndexes(iFolder1, iFolder2);
}
return hres;
}
else
return E_INVALIDARG;
}
STDMETHODIMP CDFFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppvOut)
{
HRESULT hr;
// We need to parse the ID and see which sub-folder it belongs to
// then we will see if we have an IShellFolder for the object. If not
// we will construct it and then extract our part off of the IDLIST and
// then forward it...
if (IsEqualIID(riid, IID_IShellView))
{
SFV_CREATE sSFV;
sSFV.cbSize = sizeof(sSFV);
sSFV.pshf = this;
sSFV.psvOuter = NULL;
sSFV.psfvcb = DocFind_CreateSFVCB(SAFECAST(this, IShellFolder*), this);
if (sSFV.psfvcb)
{
hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppvOut);
sSFV.psfvcb->Release();
}
else
hr = E_OUTOFMEMORY;
}
else if (IsEqualIID(riid, IID_IContextMenu))
{
IContextMenuCB *pcmcb = new CDFContextMenuCB();
if (pcmcb)
{
hr = CDefFolderMenu_CreateEx(NULL, hwnd,
0, NULL, this, pcmcb, NULL, NULL, (IContextMenu * *)ppvOut);
pcmcb->Release();
}
else
hr = E_OUTOFMEMORY;
}
else
{
*ppvOut = NULL;
hr = E_NOINTERFACE;
}
return hr;
}
STDMETHODIMP CDFFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG * prgfInOut)
{
HRESULT hr = E_FAIL;
// We need to simply forward this to the the IShellfolder of the
// first one. We will pass him all of them as I know he does not
// process the others...
// call off to the containing folder
if (cidl == 0)
{
// They are asking for capabilities, so tell them that we support Rename...
// Review: See what other ones we need to return.
*prgfInOut = SFGAO_CANRENAME;
hr = S_OK;
}
else
{
IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, ILFindLastID(*apidl));
if (psfItem)
{
hr = psfItem->GetAttributesOf(cidl, apidl, prgfInOut);
psfItem->Release();
}
}
return hr;
}
// Helper function to Map the Sort Id to IColumn
int DFSortIDToICol(int uCmd)
{
switch (uCmd)
{
case FSIDM_SORTBYNAME:
return IDFCOL_NAME;
case FSIDM_SORTBYLOCATION:
return IDFCOL_PATH;
default:
#ifdef WINNT
return uCmd - FSIDM_SORT_FIRST - IDFCOL_NAME + 2; // make room for location and rank
#else
// win9x doesn't have rank
return uCmd - FSIDM_SORT_FIRST - IDFCOL_NAME + 1; // make room for location
#endif
}
}
// To be called back from within CDefFolderMenuE - Currently only used
// Some helper functions
STDMETHODIMP CDFFolder::SetItemsChangedSinceSort()
{
this->fItemsChangedSinceSort = TRUE;
return S_OK;
}
STDMETHODIMP CDFFolder::GetItemCount(INT *pcItems)
{
ASSERT(pcItems);
DBCOUNTITEM cItems = 0;
EnterCriticalSection(&_csSearch);
if (this->hdpaItems)
cItems = DPA_GetPtrCount(this->hdpaItems);
LeaveCriticalSection(&_csSearch);
// If async, then we may not have grown our dpa yet... but in mixed case we have so take
// max of the two...
if (_pDFEnumAsync)
{
if (_cAsyncItems > cItems)
cItems = _cAsyncItems;
}
*pcItems = SANE_ITEMCOUNT(cItems);
return S_OK;
};
STDMETHODIMP CDFFolder::GetItem(int iItem, ESFItem **ppItem)
{
HRESULT hres = E_FAIL; // just to init, use anything
ESFItem *pesfi;
IDFEnum *pidfenum;
GetAsyncEnum(&pidfenum);
DWORD dwItemID = (UINT)-1;
EnterCriticalSection(&_csSearch);
int i = DPA_GetPtrCount(this->hdpaItems);
pesfi = (ESFItem *) DPA_GetPtr(this->hdpaItems, iItem);
LeaveCriticalSection(&_csSearch);
// Mondo hack to better handle Async searching (ROWSET), we are not sure if we
// can trust the PIDL of the row as new rows may have been inserted...
// Only do this if we are not looking at the previous item..
if (pesfi && pidfenum && !_fSearchComplete && (iItem != _iGetIDList))
{
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA);
// As we can now have mixed results only blow away if this is an async guy...
if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA))
{
pidfenum->GetItemID(iItem, &dwItemID);
if (dwItemID != phdfd->dwItemID)
{
// Overlload, pass NULL to ADDPIDL to tell system to free that item
if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED))
_AddESFItemToSaveStateList(pesfi);
AddPidl(iItem, 0, NULL, NULL);
pesfi = NULL;
}
}
}
_iGetIDList = iItem; // remember the last one we retrieved...
if (!pesfi && (iItem >= 0))
{
// See if this is the async case
if (pidfenum)
{
LPITEMIDLIST pidlT;
hres = pidfenum->GetItemIDList(SANE_ITEMCOUNT(iItem), &pidlT);
if (SUCCEEDED(hres) && hres != DB_S_ENDOFROWSET)
{
AddPidl(iItem, pidlT, dwItemID, &pesfi);
// See if this item should show up as selected...
if (dwItemID == (UINT)-1)
pidfenum->GetItemID(iItem, &dwItemID);
GetStateFromSaveStateList(dwItemID, &pesfi->dwState);
}
}
}
*ppItem = pesfi;
if (hres != DB_S_ENDOFROWSET)
hres = pesfi? S_OK : E_FAIL;
return hres;
}
STDMETHODIMP CDFFolder::DeleteItem(int iItem)
{
HRESULT hres = E_FAIL;
if (!_fInRefresh)
{
ESFItem *pesfi;
hres = E_INVALIDARG;
// make sure the item is in dpa (if using cI)
if (SUCCEEDED(GetItem(iItem, &pesfi)) && pesfi)
{
EnterCriticalSection(&_csSearch);
DPA_DeletePtr(hdpaItems, iItem);
LeaveCriticalSection(&_csSearch);
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA);
if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA))
{
//we are deleting async item...
_cAsyncItems--;
}
if (pesfi->dwState &= LVIS_SELECTED)
{
// Need to update the count of items selected...
_dflvrSel.DecrementIncludedCount();
}
LocalFree((HLOCAL)pesfi);
hres = S_OK;
}
}
return hres;
}
STDMETHODIMP CDFFolder::ValidateItems(int iItem, int cItems, BOOL bSearchComplete)
{
ESFItem *pesfi;
IDFEnum *pidfenum;
int cItemsInList;
int iLVFirst;
int cLVItems;
GetAsyncEnum(&pidfenum);
if (!pidfenum || _fAllAsyncItemsCached)
return S_OK; // nothing to validate.
DWORD dwItemID = (UINT)-1;
GetItemCount(&cItemsInList);
// force reload of rows
pidfenum->Reset();
iLVFirst = ListView_GetTopIndex(_hwndLV);
cLVItems = ListView_GetCountPerPage(_hwndLV);
if (iItem == -1)
{
iItem = iLVFirst;
cItems = cLVItems;
}
// to avoid failing to update an item...
if (bSearchComplete)
_iGetIDList = -1;
while ((iItem < cItemsInList) && cItems)
{
EnterCriticalSection(&_csSearch);
pesfi = (ESFItem *) DPA_GetPtr(this->hdpaItems, iItem);
LeaveCriticalSection(&_csSearch);
if (!pesfi) // Assume that if we have not gotten this one we are in the clear...
break;
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA);
if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA))
{
pidfenum->GetItemID(iItem, &dwItemID);
if (dwItemID != phdfd->dwItemID)
{
ESFItem *pItem; // dummy to make GetItem happy
// Oops don't match,
if (InRange(iItem, iLVFirst, iLVFirst+cLVItems))
{
if (SUCCEEDED(GetItem(iItem, &pItem)))
{
ListView_RedrawItems(_hwndLV, iItem, iItem);
}
}
else
{
AddPidl(iItem, NULL, 0, NULL);
}
}
}
else
{
break; // stop after we reach first non ci item
}
iItem++;
cItems--;
}
_fSearchComplete = bSearchComplete;
DebugMsg(TF_DOCFIND, TEXT("sh TR - CDFFolder::ValidateItems - OK"));
return S_OK;
}
STDMETHODIMP CDFFolder::AddPidl(int i, LPITEMIDLIST pidl, DWORD dwItemID, ESFItem **ppcdfi)
{
HRESULT hr = S_OK;
ESFItem* pesfi;
if (!pidl)
{
EnterCriticalSection(&_csSearch);
pesfi = (ESFItem*)DPA_GetPtr(this->hdpaItems, i);
if (pesfi)
{
LocalFree((HLOCAL)pesfi);
DPA_SetPtr(this->hdpaItems, i, NULL);
}
LeaveCriticalSection(&_csSearch);
if (ppcdfi)
*ppcdfi = NULL;
return S_OK;
}
int cb = ILGetSize(pidl);
pesfi = (ESFItem*)LocalAlloc(LMEM_FIXED, sizeof(ESFItem) - sizeof(ITEMIDLIST) + cb);
if (pesfi)
{
pesfi->dwMask = 0;
pesfi->dwState = 0;
// pesfi->dwItemID = dwItemID;
pesfi->iIcon = -1;
memcpy(&pesfi->idl, pidl, cb);
EnterCriticalSection(&_csSearch);
BOOL bRet = DPA_SetPtr(this->hdpaItems, i, (void *)pesfi);
LeaveCriticalSection(&_csSearch);
if (!bRet)
{
LocalFree((HLOCAL)pesfi);
pesfi = NULL;
hr = E_FAIL;
}
}
if (ppcdfi)
*ppcdfi = pesfi;
return hr;
}
STDMETHODIMP CDFFolder::SetAsyncEnum(IDFEnum *pdfEnumAsync)
{
if (_pDFEnumAsync)
_pDFEnumAsync->Release();
_pDFEnumAsync = pdfEnumAsync;
if (pdfEnumAsync)
pdfEnumAsync->AddRef();
return S_OK;
}
STDMETHODIMP CDFFolder::CacheAllAsyncItems()
{
ESFItem *pesfi;
IDFEnum *pidfenum;
int i, maxItems;
if (_fAllAsyncItemsCached)
return S_OK; // Allready done it...
GetAsyncEnum(&pidfenum);
if (!pidfenum)
return S_FALSE; // nothing to do...
// Probably the easiest thing to do is to simply walk through all of the items...
maxItems = SANE_ITEMCOUNT(_cAsyncItems);
for (i=0;i < maxItems; i++)
{
GetItem(i, &pesfi);
}
_fAllAsyncItemsCached = TRUE;
return S_OK;
}
BOOL CDFFolder::AllAsyncItemsCached()
{
return _fAllAsyncItemsCached;
}
STDMETHODIMP CDFFolder::GetAsyncEnum(IDFEnum **ppdfEnumAsync)
{
*ppdfEnumAsync = _pDFEnumAsync;
return S_OK;
}
STDMETHODIMP CDFFolder::SetAsyncCount(DBCOUNTITEM cCount)
{
_cAsyncItems = cCount;
_fAllAsyncItemsCached = FALSE;
return S_OK;
}
STDMETHODIMP CDFFolder::ClearSaveStateList()
{
DSA_DeleteAllItems(this->_hdsaSaveStateForIDs);
_cSaveStateSelected = 0;
return S_OK;
}
STDMETHODIMP CDFFolder::GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState)
{
ESFSaveStateItem *pessi;
int i;
for (i = DSA_GetItemCount(this->_hdsaSaveStateForIDs); i-- > 0; )
{
// Pidl at start of structure...
pessi = (ESFSaveStateItem*)DSA_GetItemPtr(this->_hdsaSaveStateForIDs, i);
if (pessi->dwItemID == dwItemID)
{
*pdwState = pessi->dwState;
if (pessi->dwState & LVIS_SELECTED)
{
// Remember the counts of items that we have touched...
_dflvrSel.IncrementIncludedCount();
_cSaveStateSelected--;
}
// Any items we retrieve we can get rid of...
DSA_DeleteItem(this->_hdsaSaveStateForIDs, i);
return S_OK;
}
}
return S_FALSE;
}
STDMETHODIMP CDFFolder::GetFolderListItemCount(INT *pcItemCount)
{
*pcItemCount = 0;
EnterCriticalSection(&_csSearch);
if (this->hdpaPidf)
*pcItemCount = DPA_GetPtrCount(this->hdpaPidf);
LeaveCriticalSection(&_csSearch);
return S_OK;
}
STDMETHODIMP CDFFolder::GetFolderListItem(int iItem, DFFolderListItem **ppdffi)
{
EnterCriticalSection(&_csSearch);
*ppdffi = (DFFolderListItem *) DPA_GetPtr(this->hdpaPidf, iItem);
LeaveCriticalSection(&_csSearch);
return *ppdffi ? S_OK : E_FAIL;
}
// Now define a simple wrapper IContext menu which allows us to catch
// special things like Create Link...
// Also this is a DELEGATING icontextmenu implementation. We could rip
// the delegating stuff into a base class which might be useful somewhere...
// CDefFolderMenu class
#define MAX_CM_WRAP 2
class CDFMenuWrap : public IContextMenu3, public CObjectWithSite
{
public:
// *** IUnknown methods ***
virtual STDMETHODIMP QueryInterface(REFIID,void **);
virtual STDMETHODIMP_(ULONG) AddRef(void);
virtual STDMETHODIMP_(ULONG) Release(void);
// *** IContextMenu methods ***
virtual STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags);
virtual STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
virtual STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax);
// *** IContextMenu2 methods ***
virtual STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
// *** IContextMenu3 methods ***
virtual STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
//*** IObjectWithSite ***
STDMETHOD(SetSite)(IUnknown *punkSite); // override
private:
// This would be a completely generic IContextMenu delegating object except:
// 1) There's this silly _pdtobj hack which is the reason it was first written and
// 2) The constructor function only takes 2 pcms -- easily modifiable however
friend IContextMenu* DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2);
CDFMenuWrap(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2);
~CDFMenuWrap();
void _InsertMenu(IContextMenu* pcm);
UINT _cRef; // Reference count
IDataObject * _pdtobj; // Data object
UINT _count;
UINT _idFirst;
UINT _idOffsets[MAX_CM_WRAP]; // The END of each range (BEGINing of next range is +1)
IContextMenu *_pcmItem[MAX_CM_WRAP]; // The contextmenu for the item
IContextMenu2 *_pcm2Item[MAX_CM_WRAP]; // The contextmenu for the item
IContextMenu3 *_pcm3Item[MAX_CM_WRAP]; // The contextmenu for the item
};
CDFMenuWrap::CDFMenuWrap(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2)
{
_cRef = 1;
ASSERT(pdo);
pdo->AddRef();
_pdtobj = pdo;
_InsertMenu(pcm1);
_InsertMenu(pcm2);
ASSERT(_count>0); // we better have something or we're worthless
}
void CDFMenuWrap::_InsertMenu(IContextMenu* pcm)
{
if (pcm)
{
if (_count >= MAX_CM_WRAP)
{
TraceMsg(TF_ERROR, "Someone's inserting more contextmenus than CDFMenuWrap can handle...");
}
else
{
pcm->AddRef();
_pcmItem[_count] = pcm;
pcm->QueryInterface(IID_IContextMenu2, (void**)&_pcm2Item[_count]);
pcm->QueryInterface(IID_IContextMenu3, (void**)&_pcm3Item[_count]);
_count++;
}
}
}
CDFMenuWrap::~CDFMenuWrap()
{
ASSERT(_cRef == 0);
for (UINT i = 0 ; i < _count ; i++)
{
ATOMICRELEASE(_pcmItem[i]);
ATOMICRELEASE(_pcm2Item[i]);
ATOMICRELEASE(_pcm3Item[i]);
}
if (_pdtobj)
_pdtobj->Release();
}
STDMETHODIMP CDFMenuWrap::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENTMULTI(CDFMenuWrap, IContextMenu, IContextMenu3), // IID_IContextMenu
QITABENTMULTI(CDFMenuWrap, IContextMenu2, IContextMenu3), // IID_IContextMenu2
QITABENT(CDFMenuWrap, IContextMenu3), // IID_IContextMenu3
QITABENT(CDFMenuWrap, IObjectWithSite), // IID_IObjectWithSite
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CDFMenuWrap::AddRef()
{
_cRef++;
TraceMsg(TF_DOCFIND, "dfmw.AddRef %d",_cRef);
return _cRef;
}
STDMETHODIMP_(ULONG) CDFMenuWrap::Release()
{
ASSERT(_cRef >= 1); // Don't release more than we can!
_cRef--;
TraceMsg(TF_DOCFIND, "dfmw.Release %d",_cRef);
if (_cRef > 0)
{
return _cRef;
}
delete this;
return 0;
}
STDMETHODIMP CDFMenuWrap::SetSite(IUnknown *punkSite)
{
// let all the kids know
for (UINT i = 0 ; i < _count ; i++)
{
IUnknown_SetSite(_pcmItem[i], punkSite);
}
return CObjectWithSite::SetSite(punkSite);
}
STDMETHODIMP CDFMenuWrap::QueryContextMenu(
HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)
{
HRESULT hres;
// simply foward this to the one we are wrapping...
TraceMsg(TF_DOCFIND,"dfmw.queryContextMenu, iMenu=%d, iFirst=%d, iLast=%d",
indexMenu, idCmdFirst, idCmdLast);
_idFirst = idCmdFirst;
for (UINT i = 0 ; i < _count && idCmdFirst < idCmdLast; i++)
{
hres = _pcmItem[i]->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
if (SUCCEEDED(hres))
{
_idOffsets[i] = idCmdFirst - _idFirst + (UINT)ShortFromResult(hres);
idCmdFirst = idCmdFirst + (UINT)ShortFromResult(hres) + 1;
}
else
{
if (0 == i)
_idOffsets[i] = 0;
else
_idOffsets[i] = _idOffsets[i-1];
}
}
return hres;
}
STDMETHODIMP CDFMenuWrap::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
{
HRESULT hres;
TraceMsg(TF_DOCFIND, "dfmw.InvokeCommand, verbstr=%lx", lpici->lpVerb);
// This is sortof Gross, but we will attempt to pickoff the Link command
// which looks like the pcmitem will be SHARED_FILE_LINK....
// NOTE: This assumes the filesys context menu is FIRST in our array
if ((UINT_PTR)lpici->lpVerb == SHARED_FILE_LINK ||
(!IS_INTRESOURCE(lpici->lpVerb) && (lstrcmpiA(lpici->lpVerb, "link") == 0)))
{
// Note: old code used to check pdtobj, but we don't create this
// object unless we get one of them, so why check?
ASSERT(_pdtobj);
return SHCreateLinks(lpici->hwnd, NULL, _pdtobj,
SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL);
}
for (UINT i = 0 ; i < _count ; i++)
{
if (IS_INTRESOURCE(lpici->lpVerb))
{
UINT idCmd = (UINT)LOWORD((DWORD_PTR)lpici->lpVerb);
if (idCmd <= _idOffsets[i])
{
// adjust id to be in proper range for this pcm
if (i>0)
{
lpici->lpVerb = MAKEINTRESOURCEA(idCmd - _idOffsets[i-1] - 1);
}
hres = _pcmItem[i]->InvokeCommand(lpici);
return hres;
}
}
else
{
// I guess we try until it works
hres = _pcmItem[i]->InvokeCommand(lpici);
if (SUCCEEDED(hres))
return hres;
}
}
TraceMsg(TF_ERROR, "Someone's passing CDFMenuWrap::InvokeCommand an id we didn't insert...");
return E_FAIL;
}
STDMETHODIMP CDFMenuWrap::GetCommandString(
UINT_PTR idCmd, UINT wFlags, UINT * pmf,
LPSTR pszName, UINT cchMax)
{
for (UINT i = 0 ; i < _count ; i++)
{
if (idCmd <= _idOffsets[i])
{
// adjust id to be in proper range for this pcm
if (i>0)
{
idCmd = idCmd - _idOffsets[i-1] - 1;
}
return _pcmItem[i]->GetCommandString(idCmd, wFlags, pmf, pszName, cchMax);
}
}
TraceMsg(TF_ERROR, "Someone's passing CDFMenuWrap::GetCommandString an id we didn't insert...");
return E_FAIL;
}
STDMETHODIMP CDFMenuWrap::HandleMenuMsg(
UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
}
STDMETHODIMP CDFMenuWrap::HandleMenuMsg2(
UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
{
HRESULT hres = E_FAIL;
UINT idCmd;
// Find the menu command id -- it's packed differently depending on the message
switch (uMsg) {
case WM_MEASUREITEM:
idCmd = ((MEASUREITEMSTRUCT *)lParam)->itemID;
break;
case WM_DRAWITEM:
idCmd = ((LPDRAWITEMSTRUCT)lParam)->itemID;
break;
case WM_INITMENUPOPUP:
idCmd = GetMenuItemID((HMENU)wParam, 0);
break;
case WM_MENUSELECT:
idCmd = (UINT) LOWORD(wParam);
break;
case WM_MENUCHAR:
case WM_NEXTMENU:
idCmd = GetMenuItemID((HMENU)lParam, 0);
break;
default:
return E_FAIL;
}
// make sure it's in our range
if (idCmd >= _idFirst)
{
idCmd -= _idFirst;
for (UINT i = 0 ; i < _count ; i++)
{
if (idCmd <= _idOffsets[i])
{
if (_pcm3Item[i])
hres = _pcm3Item[i]->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
else if (_pcm2Item[i] && NULL == plResult)
hres = _pcm2Item[i]->HandleMenuMsg(uMsg, wParam, lParam);
break;
}
}
}
return hres;
}
HRESULT DFWrapIContextMenu(HWND hwndOwner, IShellFolder *psfItem, LPCITEMIDLIST pidl,
IContextMenu* pcmExtra, void **ppvInOut)
{
HRESULT hres;
IContextMenu *pcmWrap = NULL;
IContextMenu *pcmFree = (IContextMenu*)*ppvInOut;
IDataObject* pdo;
hres = psfItem->GetUIObjectOf(hwndOwner,
1, &pidl, IID_IDataObject, NULL, (void **) &pdo);
if (SUCCEEDED(hres))
{
pcmWrap = DFWrapIContextMenus(pdo, pcmFree, pcmExtra);
if (!pcmWrap)
hres = E_OUTOFMEMORY;
pdo->Release();
}
pcmFree->Release();
*ppvInOut = pcmWrap;
return hres;
}
IContextMenu* DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2)
{
CDFMenuWrap * pcm = new CDFMenuWrap(pdo, pcm1, pcm2);
return SAFECAST(pcm, IContextMenu*);
}
STDMETHODIMP CDFFolder::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CDFFolder, IShellFolder2), //IID_ISHELLFolder2
QITABENTMULTI(CDFFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
QITABENT(CDFFolder, IDocFindFolder), //IID_IDocFindFolder
QITABENT(CDFFolder, IShellIcon), //IID_IShellIcon
QITABENT(CDFFolder, IPersistFolder2), //IID_IPersistFolder2
QITABENTMULTI(CDFFolder, IPersistFolder, IPersistFolder2), //IID_IPersistFolder
QITABENTMULTI(CDFFolder, IPersist, IPersistFolder2), //IID_IPersist
QITABENT(CDFFolder, IShellIconOverlay), //IID_IShellIconOverlay
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
// IPersistFolder2 implementation
STDMETHODIMP CDFFolder::GetClassID(CLSID *pClassID)
{
*pClassID = CLSID_DocFindFolder;
return S_OK;
}
STDMETHODIMP CDFFolder::Initialize(LPCITEMIDLIST pidl)
{
if (_pidl)
ILFree(_pidl);
return SHILClone(pidl, &_pidl);
}
STDMETHODIMP CDFFolder::GetCurFolder(LPITEMIDLIST *ppidl)
{
return GetCurFolderImpl(_pidl, ppidl);
}
// helper function to sort the selected ID list by something that
// makes file operations work reasonably OK, when both an object and it's
// parent is in the list...
int CALLBACK CDFFolder_SortForFileOp(void *lp1, void *lp2, LPARAM lparam)
{
// Since I do recursion, If I get the Folder index number from the
// last element of each and sort by them such that the higher numbers
// come first, should solve the problem fine...
LPITEMIDLIST pidl1 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)lp1);
LPITEMIDLIST pidl2 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)lp2);
return DF_IFOLDER(pidl2) - DF_IFOLDER(pidl1);
}
LPITEMIDLIST CDFFolder::_GetFullPidlForItem(LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlParent;
if (S_OK == GetParentsPIDL(pidl, &pidlParent))
{
return ILCombine(pidlParent, pidl);
}
return NULL;
}
STDMETHODIMP CDFFolder::GetUIObjectOf(HWND hwndOwner,
UINT cidl, LPCITEMIDLIST * apidl,
REFIID riid, UINT * prgfInOut, void **ppvOut)
{
HRESULT hres = E_INVALIDARG;
*ppvOut = NULL;
// If Count of items passed in is == 1 simply pass to the appropriate
// folder
if (cidl == 1)
{
// Note we may have been passed in a complex item so find the last
// id.
ASSERT(ILIsEmpty(_ILNext(*apidl))); // should be a single level PIDL!
LPCITEMIDLIST pidl = ILFindLastID(*apidl); // BUGBUG, this is bogus
IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl);
if (psfItem)
{
hres = psfItem->GetUIObjectOf(hwndOwner, 1, &pidl, riid, prgfInOut, ppvOut);
// if we are doing context menu, then we will wrap this
// interface in a wrapper object, that we can then pick
// off commands like link to process specially
if (SUCCEEDED(hres) && IsEqualIID(riid, IID_IContextMenu))
{
// we also let the net/file guy add in a context menu if they want to
IContextMenu* pcmExtra = NULL;
pdfff->GetItemContextMenu(hwndOwner, SAFECAST(this, IDocFindFolder*), &pcmExtra);
hres = DFWrapIContextMenu(hwndOwner, psfItem, pidl, pcmExtra, ppvOut);
ATOMICRELEASE(pcmExtra);
}
psfItem->Release();
}
return hres; // error...
}
if (IsEqualIID(riid, IID_IContextMenu))
{
// Is there any selection?
if (cidl == 0)
{
hres = E_INVALIDARG;
}
else
{
IContextMenuCB *pcmcb;
// So we have at least two items in the list.
// Try to create a menu object that we process ourself
// Yes, do context menu.
HKEY ahkeys[3] = {0, 0, 0};
LPITEMIDLIST pidlFull = _GetFullPidlForItem(apidl[0]);
if (pidlFull)
{
// Get the hkeyProgID and hkeyBaseProgID from the first item.
SHGetClassKey(pidlFull, &ahkeys[0], &ahkeys[1]);
ILFree(pidlFull);
}
RegOpenKey(HKEY_CLASSES_ROOT, TEXT("AllFilesystemObjects"), &ahkeys[2]);
pcmcb = new CDFContextMenuCB();
if (pcmcb)
{
hres = CDefFolderMenu_Create2Ex(NULL, hwndOwner,
cidl, apidl, this, pcmcb,
ARRAYSIZE(ahkeys), ahkeys,
(IContextMenu **)ppvOut);
pcmcb->Release();
}
SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys));
}
}
else if (cidl && IsEqualIID(riid, IID_IDataObject))
{
// We need to generate a data object that each item as being
// fully qualified. This is a pain, but...
// This is a really gross use of memory!
UINT i;
HDPA hdpa = DPA_Create(0);
if (!hdpa)
return hres;
if (!DPA_Grow(hdpa, cidl))
{
DPA_Destroy(hdpa);
return hres;
}
for (i = 0; i < cidl; i++)
{
LPITEMIDLIST pidl = _GetFullPidlForItem(apidl[i]);
if (pidl)
DPA_InsertPtr(hdpa, i, pidl);
}
// In order to make file manipulation functions work properly we
// need to sort the elements to make sure if an element and one
// of it's parents are in the list, that the element comes
// before it's parents...
DPA_Sort(hdpa, CDFFolder_SortForFileOp, 0);
hres = CFSFolder_CreateDataObject(&c_idlDesktop, cidl, (LPCITEMIDLIST*)DPA_GetPtrPtr(hdpa),
(IDataObject **)ppvOut);
// now to cleanup what we created.
for (i = 0; i < cidl; i++)
{
ILFree((LPITEMIDLIST)DPA_FastGetPtr(hdpa, i));
}
DPA_Destroy(hdpa);
}
return hres;
}
STDMETHODIMP CDFFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwRes, LPSTRRET pStrRet)
{
HRESULT hr = E_INVALIDARG;
IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl);
if (psfItem)
{
hr = psfItem->GetDisplayNameOf(pidl, dwRes, pStrRet);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDFFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
LPCOLESTR lpszName, DWORD dwRes, LPITEMIDLIST *ppidlOut)
{
HRESULT hr = E_INVALIDARG;
IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl);
if (psfItem)
{
hr = psfItem->SetNameOf(hwnd, pidl, lpszName, dwRes, ppidlOut);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDFFolder::GetDefaultSearchGUID(LPGUID lpGuid)
{
return pdfff->GetDefaultSearchGUID(SAFECAST(this, IShellFolder2*), lpGuid);
}
STDMETHODIMP CDFFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
{
return pdfff->EnumSearches(SAFECAST(this, IShellFolder2*), ppenum);
}
HRESULT CDFFolder::_QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
{
HRESULT hr = E_FAIL;
IShellFolder *psf = DocFind_GetObjectsIFolder(this, NULL, pidl);
if (psf)
{
hr = psf->QueryInterface(riid, ppv);
psf->Release();
}
return hr;
}
STDMETHODIMP CDFFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
{
return E_NOTIMPL;
}
STDMETHODIMP CDFFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
{
return E_NOTIMPL;
}
STDMETHODIMP CDFFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{
IShellFolder2 *psf2;
HRESULT hr = _QueryItemInterface(pidl, IID_IShellFolder2, (void **)&psf2);
if (SUCCEEDED(hr))
{
hr = psf2->GetDetailsEx(pidl, pscid, pv);
psf2->Release();
}
return hr;
}
STDMETHODIMP CDFFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
{
// first let the filters have a crack at this...
IDocFindFileFilter *pdfff;
HRESULT hres = GetDocFindFilter(&pdfff);
if (SUCCEEDED(hres))
{
EnterCriticalSection(&_csSearch);
hres = pdfff->GetDetailsOf(hdpaPidf, pidl, &iColumn, pdi);
LeaveCriticalSection(&_csSearch);
pdfff->Release();
// special case to go to async guy...
#ifdef IF_ADD_MORE_COLS
if (hres == S_FALSE)
{
if (_pDFEnumAsync)
{
// OK lets ask it for the information...
// First we need to get which row it is in.
CDFItem *pesfi;
if (SUCCEEDED(GetItem(_iGetIDList, &pesfi))
&& pesfi
&& ((&pesfi->idl == pidl) || ILIsEqual(pidl, &pesfi->idl)))
{
// Yep it was ours so return the index quickly..
return _pDFEnumAsync->GetExtendedDetailsOf(pidl, iColumn, pdi);
}
}
else
hres = S_OK; // No extended data so ignore it for now...
}
#endif
}
return hres;
}
STDMETHODIMP CDFFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
{
return E_NOTIMPL;
}
STDMETHODIMP CDFFolder::SetDocFindFilter(IDocFindFileFilter *pdfffNew)
{
// TODO: add type checked version of IUnknown_Set to avoid code like this [t-ashram]
if (pdfff)
pdfff->Release();
pdfff = pdfffNew;
if (pdfff)
pdfff->AddRef();
return S_OK;
}
STDMETHODIMP CDFFolder::GetDocFindFilter(IDocFindFileFilter **ppdfff)
{
*ppdfff = pdfff;
if (pdfff)
pdfff->AddRef();
return pdfff ? S_OK : E_FAIL;
}
// CDFFolder::IShellIcon : Members
// GetIconOf
STDMETHODIMP CDFFolder::GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex)
{
IShellIcon * psiItem;
HRESULT hres = _QueryItemInterface(pidl, IID_IShellIcon, (void **)&psiItem);
if (SUCCEEDED(hres))
{
hres = psiItem->GetIconOf(pidl, flags, piIndex);
psiItem->Release();
}
return hres;
}
// CDFFolder::IShellIconOverlay : Members
STDMETHODIMP CDFFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex)
{
IShellIconOverlay * psioItem;
HRESULT hres = _QueryItemInterface(pidl, IID_IShellIconOverlay, (void **)&psioItem);
if (SUCCEEDED(hres))
{
hres = psioItem->GetOverlayIndex(pidl, pIndex);
psioItem->Release();
}
return hres;
}
STDMETHODIMP CDFFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex)
{
return E_NOTIMPL;
}
STDMETHODIMP CDFFolder::RestoreSearchFromSaveFile(LPITEMIDLIST pidlSaveFile, IShellFolderView *psfv)
{
// See if we can restore most of the search from here...
IStream *pstm;
ULONG cbRead;
TCHAR szPathName[MAX_PATH];
LARGE_INTEGER dlibMove = {0, 0};
DFHEADER dfh;
int cItems = 0;
HRESULT hr;
// First try to open the file
SHGetPathFromIDList(pidlSaveFile, szPathName);
if (FAILED(hr = SHCreateStreamOnFile(szPathName, STGM_READ, &pstm)))
return hr;
// Now Read in the header
// Note: in theory I should test the size read by the size of the
// smaller headers, but if the number of bytes read is smaller than
// the few new things added then there is nothing to restore anyway...
// Note: Win95/NT4 incorrectly failed newer versions of this structure.
// Which is bogus since the struct was backward compatible (that's what
// the offsets are for). We fix for NT5 and beyond, but downlevel
// systems are forever broken. Hopefully this feature is rarely enough
// used (and never mailed) that nobody will notice we're broken.
if (FAILED(pstm->Read(&dfh, SIZEOF(DFHEADER), &cbRead)) ||
(cbRead != SIZEOF(DFHEADER)) || (dfh.wSig != DOCFIND_SIG))
{
// Not the correct format...
pstm->Release();
return E_FAIL;
}
#if 0
//BUGBUG:: need to add support for remembering which view we are in and then restoring it.
if (dfh.wVer < 3)
{
// The ViewMode was added in version 3.
dfh.ViewMode = FVM_DETAILS;
}
#endif
// Now try to read in the criteria...
#ifdef WINNT
DFC_UNICODE_DESC desc;
WORD fCharType = 0;
// Check the stream's signature to see if it was generated by Win95 or NT.
dlibMove.QuadPart = 0 - SIZEOF(desc);
pstm->Seek(dlibMove, STREAM_SEEK_END, NULL);
pstm->Read(&desc, SIZEOF(desc), &cbRead);
if (cbRead > 0 && desc.NTsignature == c_NTsignature)
{
// NT-generated stream. Read in Unicode criteria.
fCharType = DFC_FMT_UNICODE;
dlibMove.QuadPart = desc.oUnicodeCriteria.QuadPart;
}
else
{
// Win95-generated stream. Read in ANSI criteria.
fCharType = DFC_FMT_ANSI;
dlibMove.LowPart = dfh.oCriteria;
}
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
pdfff->RestoreCriteria(pstm, dfh.cCriteria, fCharType);
#else
// Now Read in the criteria
dlibMove.LowPart = dfh.oCriteria;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
pdfff->RestoreCriteria(pstm, dfh.cCriteria, DFC_FMT_ANSI);
#endif
// Now read in the results
dlibMove.LowPart = dfh.oResults;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
if (dfh.wVer > 1)
{
// only restore this way if version 2 data....
// Now Restore away the folder list
RestoreFolderList(pstm);
RestoreItemList(pstm, &cItems);
if (cItems > 0)
psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
}
// and close the file
pstm->Release();
return S_OK;
}
STDMETHODIMP CDFFolder::SetEmptyText(LPCTSTR pszText)
{
if (pszText && 0 == lstrcmp(_szEmptyText, pszText))
return S_OK ;
if (pszText)
lstrcpyn(_szEmptyText, pszText, ARRAYSIZE(_szEmptyText));
else
*_szEmptyText = 0 ;
if (IsWindow(_hwndLV))
SendMessage(_hwndLV, LVM_RESETEMPTYTEXT, 0, 0L);
return S_OK ;
}
BOOL CDFFolder::IsSlow()
{
BOOL bRet = FALSE;
if (IsWindow(_hwndLV))
{
LONG lStyle = GetWindowLong(_hwndLV, GWL_STYLE) & LVS_TYPEMASK;
bRet = lStyle == LVS_ICON || lStyle == LVS_SMALLICON;
}
return bRet;
}
// this is the same as shfindfiles except without the gross hack
BOOL RealFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile)
{
HRESULT hres;
IWebBrowser2* pwb2 = NULL;
// First create the top level browser...
hres = CoCreateInstance(CLSID_ShellBrowserWindow, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void **)&pwb2);
if (SUCCEEDED( hres ) && pwb2)
{
SA_BSTR bstrClsid;
VARIANT varClsid;
VARIANT varEmpty = {0};
SHStringFromGUIDW(CLSID_FileSearchBand, bstrClsid.wsz, ARRAYSIZE(bstrClsid.wsz));
bstrClsid.cb = lstrlenW(bstrClsid.wsz) * sizeof(WCHAR);
varClsid.vt = VT_BSTR;
varClsid.bstrVal = bstrClsid.wsz;
// show a search bar
hres = pwb2->ShowBrowserBar(&varClsid, &varEmpty, &varEmpty);
if (SUCCEEDED(hres))
{
// Grab the band's IUnknown from browser property.
VARIANT varFsb ;
BSTR bstrProperty = NULL ;
WCHAR wszProperty[GUIDSTR_MAX+1] ;
EVAL( SUCCEEDED( SHStringFromGUIDW( CLSID_FileSearchBand, wszProperty, ARRAYSIZE(wszProperty) ) ) ) ;
VariantInit( &varFsb ) ;
if( (bstrProperty = SysAllocString( wszProperty )) != NULL &&
SUCCEEDED( pwb2->GetProperty( bstrProperty, &varFsb ) ) )
{
if( VT_UNKNOWN == varFsb.vt && varFsb.punkVal )
{
// QI for IFileSearchBand, which we'll use to program the search band's
// search type (files or folders), inititial scope, and/or saved query file.
IFileSearchBand* pfsb ;
if( SUCCEEDED( varFsb.punkVal->QueryInterface( IID_IFileSearchBand, (PVOID*)&pfsb ) ) )
{
WCHAR wszSearch[GUIDSTR_MAX+1] ;
BSTR bstrSearch ;
// Get the File/Folders search guid in string form
EVAL( SUCCEEDED( SHStringFromGUIDW( SRCID_SFileSearch, wszSearch, ARRAYSIZE(wszSearch) ) ) ) ;
if( (bstrSearch = SysAllocString( wszSearch )) != NULL )
{
VARIANT varQueryFile ;
VARIANT varScope ;
VariantInit( &varQueryFile ) ;
VariantInit( &varScope ) ;
// assign initial scope
if( pidlFolder )
InitVariantFromIDList( &varScope, pidlFolder ) ;
// assign query file from which to restore search
else if( pidlSaveFile )
InitVariantFromIDList( &varQueryFile, pidlSaveFile ) ;
pfsb->SetSearchParameters( &bstrSearch, VARIANT_TRUE, &varScope, &varQueryFile ) ;
}
pfsb->Release() ;
}
}
VariantClear( &varFsb ) ;
}
SysFreeString( bstrProperty ) ;
if (SUCCEEDED(hres))
hres = pwb2->put_Visible(TRUE);
}
pwb2->Release();
}
else
hres = E_FAIL;
return hres;
}
// This is the main external entry point to start a search. This will
// create a new thread to process the
STDAPI_(BOOL) SHFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile)
{
// are we allowed?
if (SHRestricted(REST_NOFIND))
return FALSE;
// We Need a hack to allow Find to work for cases like
// Rest of network and workgroups to map to find computer instead
// This is rather gross, but what the heck. It is also assumed that
// the pidl is of the type that we know about (either File or network)
if (pidlFolder)
{
BYTE bType;
bType = SIL_GetType(ILFindLastID(pidlFolder));
if ((bType == SHID_NET_NETWORK) ||
(bType == SHID_NET_RESTOFNET) ||
(bType == SHID_NET_DOMAIN))
return SHFindComputer(pidlFolder, pidlSaveFile);
}
return RealFindFiles(pidlFolder, pidlSaveFile);
}
// This is the main external entry point to start a search. This will
// create a new thread to process the
STDAPI_(BOOL) SHFindComputer(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile)
{
IContextMenu *pcm;
HRESULT hres = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu, (void **)&pcm);
if (SUCCEEDED(hres))
{
CMINVOKECOMMANDINFO ici = {0};
ici.cbSize = SIZEOF(ici);
ici.lpParameters = "{996E1EB1-B524-11d1-9120-00A0C98BA67D}"; // Search Guid of Find Computers
ici.nShow = SW_NORMAL;
hres = pcm->InvokeCommand(&ici);
pcm->Release();
}
return SUCCEEDED(hres);
}
HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts)
{
VARIANT var;
HRESULT hres = InitVariantFromIDList(&var, pidl);
if (SUCCEEDED(hres))
{
hres = psfv->SelectItem(&var, dwOpts);
VariantClear(&var);
}
return hres;
}
HRESULT OpenContainingFolderAndGetShellFolderView(LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv)
{
*ppsfv = NULL;
IWebBrowserApp *pauto;
HRESULT hres = SHGetIDispatchForFolder(pidlFolder, &pauto);
if (SUCCEEDED(hres))
{
// WARNING! This needs to change to LONG_PTR once the MIDL
// compiler supports it.
HWND hwnd;
if (SUCCEEDED(pauto->get_HWND((LONG*)&hwnd)))
{
// Make sure we make this the active window
SetForegroundWindow(hwnd);
ShowWindow(hwnd, SW_SHOWNORMAL);
}
// We have IDispatch for window, now try to get one for
// the folder object...
IDispatch *pautoDoc;
hres = pauto->get_Document(&pautoDoc);
if (SUCCEEDED(hres))
{
hres = pautoDoc->QueryInterface(IID_IShellFolderViewDual, (void **)ppsfv);
pautoDoc->Release();
}
pauto->Release();
}
return hres;
}
void DFB_HandleOpenContainingFolder(IShellFolderView* psfv, IDocFindFolder* pdff)
{
LPCITEMIDLIST *ppidls; // pointer to a list of pidls.
UINT cpidls; // Count of pidls that were returned.
// Ok lets ask the view for the list of selected objects.
psfv->GetSelectedObjects(&ppidls, &cpidls);
if (cpidls > 0)
{
UINT i;
for (i = 0; i < cpidls; i++)
{
LPITEMIDLIST pidl;
LPITEMIDLIST pidlT;
IShellFolderViewDual *psfvDual;
// See if we have already processed this one.
if (ppidls[i] == NULL)
continue;
// Now get the parent of it.
pdff->GetParentsPIDL(ppidls[i], &pidl);
if (SUCCEEDED(OpenContainingFolderAndGetShellFolderView(pidl, &psfvDual)))
{
UINT j;
SelectPidlInSFV(psfvDual, ppidls[i], SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
// Now see if there are any other items selected in
// this same cabinet. If so we might as well process
// them here and save the work of trying to open it
// again and the like!
for (j=i+1; j < cpidls; j++)
{
if (ppidls[j] == NULL)
continue;
// Now see if it has the same parent as we
// are processing...
pdff->GetParentsPIDL(ppidls[j], &pidlT);
if (pidlT == pidl)
{
SelectPidlInSFV(psfvDual, ppidls[j], SVSI_SELECT);
ppidls[j] = NULL; // dont process again.
}
}
psfvDual->Release();
}
}
}
// Free the memory associated with the item list.
if (ppidls != NULL)
LocalFree((HLOCAL)ppidls);
}
#ifdef DOCFIND_SAVERESULTS_ENABLED
// Hook procedure to allow us to our Save Results checkbox to the Save file dialog
UINT_PTR APIENTRY DocFindSaveDialogHookProc(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
DFBSAVEINFO * pdfb;
switch ( msg )
{
case WM_INITDIALOG:
pdfb = (DFBSAVEINFO*)((LPOPENFILENAME)lParam)->lCustData;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pdfb);
CheckDlgButton( hWnd, IDD_SAVERESULTS,
(pdfb->dwFlags & DFOO_SAVERESULTS) ? BST_CHECKED : BST_UNCHECKED);
break;
case WM_NOTIFY:
switch (((LPOFNOTIFY)lParam)->hdr.code)
{
case CDN_FILEOK:
{
// See if the Save results option changed.
BOOL fChecked;
pdfb = (DFBSAVEINFO*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
fChecked = (BOOL) SendDlgItemMessage( hWnd, IDD_SAVERESULTS, BM_GETCHECK, 0, 0 );
if ((fChecked && ((pdfb->dwFlags & DFOO_SAVERESULTS) == 0)) ||
(!fChecked && (pdfb->dwFlags & DFOO_SAVERESULTS)))
{
pdfb->dwFlags ^= DFOO_SAVERESULTS;
HKEY hkeyExp = SHGetExplorerHkey(HKEY_LOCAL_MACHINE, TRUE);
if (hkeyExp)
SHSetValue(hkeyExp, s_szDocFind, s_szFlags, REG_BINARY, &pdfb->dwFlags, SIZEOF(pdfb->dwFlags));
}
}
// Returning zero signifies that it is ok to exit the common dialog...
return FALSE;
}
break;
}
return ( FALSE );
}
#endif //DOCFIND_SAVERESULTS_ENABLED
// DFBSave
// Save away the current search to a file on the desktop.
// For now the name will be automatically generated.
void DFB_Save(IDocFindFileFilter* pdfff, HWND hwndOwner, DFBSAVEINFO * pSaveInfo,
IShellView* psv, IDocFindFolder* pDocfindFolder, IUnknown *pObject)
{
TCHAR szFilePath[MAX_PATH];
IStream * pstm;
DFHEADER dfh;
TCHAR szTemp[MAX_PATH];
SHORT cb;
HRESULT hres;
LARGE_INTEGER dlibMove = {0, 0};
ULARGE_INTEGER libCurPos;
FOLDERSETTINGS fs;
LPCTSTR pszLastPath = c_szNULL; // Null string to begin with.
// See if the search already has a file name associated with it. If so
// we will save it in it, else we will create a new file on the desktop
if (pdfff->FFilterChanged() == S_FALSE)
{
// Lets blow away the save file
ILFree(pSaveInfo->pidlSaveFile);
pSaveInfo->pidlSaveFile = NULL;
}
// If it still looks like we want to continue to use a save file then
// continue.
if (pSaveInfo->pidlSaveFile)
{
SHGetPathFromIDList(pSaveInfo->pidlSaveFile, szFilePath);
}
else
{
LPTSTR lpsz;
LPTSTR pszTitle;
TCHAR szShortName[12];
// First get the path name to the Desktop.
SHGetSpecialFolderPath(NULL, szFilePath, CSIDL_PERSONAL, TRUE);
// and update the title
// we now do this before getting a filename because we generate
// the file name from the title
pdfff->GenerateTitle(&pszTitle, TRUE);
if (pszTitle)
{
// Now add on the extension.
lstrcpyn(szTemp, pszTitle, MAX_PATH - (lstrlen(szFilePath) + 1 + 4 + 1+3));
lstrcat(szTemp, TEXT(".fnd"));
LocalFree(pszTitle); // And free the title string.
}
// Now loop through and replace all of the invalid characters with _'s
// we special case a few of the characters...
for (lpsz = szTemp; *lpsz; lpsz = CharNext(lpsz))
{
if (PathGetCharType(*lpsz) & (GCT_INVALID|GCT_WILD|GCT_SEPARATOR))
{
switch (*lpsz) {
case TEXT(':'):
*lpsz = TEXT('-');
break;
case TEXT('*'):
*lpsz = TEXT('@');
break;
case TEXT('?'):
*lpsz = TEXT('!');
break;
default:
*lpsz = TEXT('_');
}
}
}
LoadString(HINST_THISDLL, IDS_FIND_SHORT_NAME, szShortName, ARRAYSIZE(szShortName));
if (!PathYetAnotherMakeUniqueName(szFilePath, szFilePath, szShortName, szTemp))
return;
}
// Now lets bring up the save as dialog...
TCHAR szFilter[MAX_PATH];
TCHAR szTitle[MAX_PATH];
TCHAR szFilename[MAX_PATH];
LPTSTR psz;
OPENFILENAME ofn = { 0 };
LoadString(g_hinst, IDS_FINDFILESFILTER, szFilter, MAX_PATH);
LoadString(g_hinst, IDS_FINDSAVERESULTSTITLE, szTitle, MAX_PATH);
//Strip out the # and make them Nulls for SaveAs Dialog
psz = szFilter;
while (*psz)
{
if (*psz == TEXT('#'))
*psz = TEXT('\0');
psz++;
}
lstrcpy(szFilename, PathFindFileName(szFilePath));
PathRemoveFileSpec(szFilePath);
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwndOwner;
ofn.hInstance = g_hinst;
ofn.lpstrFilter = szFilter;
ofn.lpstrFile = szFilename;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = szFilePath;
ofn.lpstrTitle = szTitle;
ofn.lpstrDefExt = TEXT("fnd");
ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST |
OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
#ifdef DOCFIND_SAVERESULTS_ENABLED
ofn.lpTemplateName= MAKEINTRESOURCE(DLG_DOCFIND_SAVE);
ofn.lpfnHook= DocFindSaveDialogHookProc;
ofn.lCustData = (LPARAM)pSaveInfo;
ofn.Flags |= (OFN_ENABLETEMPLATE | OFN_ENABLEHOOK) ;
#else //DOCFIND_SAVERESULTS_ENABLED
ofn.lpTemplateName = NULL;
ofn.lpfnHook= NULL;
ofn.lCustData = NULL;
#endif //DOCFIND_SAVERESULTS_ENABLED
if (!GetSaveFileName(&ofn))
return;
if (FAILED(SHCreateStreamOnFile(szFilename, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, &pstm)))
return;
// remember the file that we saved away to...
ILFree(pSaveInfo->pidlSaveFile);
pSaveInfo->pidlSaveFile = ILCreateFromPath(szFilename);
// Now setup and write out header information
ZeroMemory(&dfh, sizeof(dfh));
dfh.wSig = DOCFIND_SIG;
dfh.wVer = DF_CURFILEVER;
dfh.dwFlags = pSaveInfo->dwFlags;
dfh.wSortOrder = (WORD)pSaveInfo->SortMode;
dfh.wcbItem = SIZEOF(DFITEM);
dfh.oCriteria = SIZEOF(dfh);
// dfh.cCriteria = sizeof(s_aIndexes) / sizeof(SHORT);
// dfh.oResults =;
// Not used anymore...
dfh.cResults = -1;
// Note: Later we may convert this to DOCFILE where the
// criteria is stored as properties.
// Get the current Folder Settings
if (SUCCEEDED(psv->GetCurrentInfo(&fs)))
dfh.ViewMode = fs.ViewMode;
else
dfh.ViewMode = FVM_DETAILS;
// Now call the filter object to save out his own set of criterias
dlibMove.LowPart = dfh.oCriteria;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
hres = pdfff->SaveCriteria(pstm, DFC_FMT_ANSI);
if (SUCCEEDED(hres))
dfh.cCriteria = GetScode(hres);
// Now setup to output the results
dlibMove.LowPart = 0;
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos); // Get current pos
dfh.oResults = libCurPos.LowPart;
// Now Let our file folder serialize his results out here also...
// But only if the option is set to do so...
cb = 0;
#ifdef DOCFIND_SAVERESULTS_ENABLED
if (pSaveInfo->dwFlags & DFOO_SAVERESULTS)
{
pDocfindFolder->SaveFolderList(pstm);
pDocfindFolder->SaveItemList(pstm);
}
else
#endif DOCFIND_SAVERESULTS_ENABLED
{
// Write out a Trailing NULL for Folder list
pstm->Write(&cb, SIZEOF(cb), NULL);
// And item list.
pstm->Write(&cb, SIZEOF(cb), NULL);
}
// END of DFHEADER_WIN95 information
// BEGIN of NT5 information:
// Now setup to output the history stream
if (pObject)
{
dlibMove.LowPart = 0;
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos); // Get current pos
dfh.oHistory = libCurPos.LowPart;
if (FAILED(SavePersistHistory(pObject, pstm)))
{
// On failure we might as well just pretend we didn't save this bit of data.
// Do we need an error message -- the ui won't be right when relaunched...
dfh.oHistory = 0;
dlibMove.LowPart = libCurPos.LowPart;
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
}
}
// In NT the below was done AT THE END OF THE STREAM instead of
// revving the DFHEADER struct. (Okay, DFHEADEREX, since Win95
// already broke DFHEADER back compat by in improper version check)
// This could have been done by putting a propery signatured
// DFHEADEREX that had proper versioning so we could add information
// to. Unfortunately another hardcoded struct was tacked on to
// the end of the stream... Next time, please fix the problem
// instead of work around it.
// What this boils down to is we cannot put any information
// after the DFC_UNICODE_DESC section, so might as well
// always do this SaveCriteria section last...
#ifdef WINNT
{
// See comment at top of file for DFC_UNICODE_DESC.
DFC_UNICODE_DESC desc;
// Get the current location in stream. This is the offset where
// we'll write the unicode find criteria. Save this
// value (along with NT-specific signature) in the descriptor
dlibMove.LowPart = 0;
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos);
desc.oUnicodeCriteria.QuadPart = libCurPos.QuadPart;
desc.NTsignature = c_NTsignature;
// Append the Unicode version of the find criteria.
hres = pdfff->SaveCriteria(pstm, DFC_FMT_UNICODE);
// Append the unicode criteria descriptor to the end of the file.
pstm->Write(&desc, SIZEOF(desc), NULL);
}
#endif
// don't put any code between the above DFC_UNICDE_DESC section
// and this back-patch of the dfh header...
// Finally output the header information at the start of the file
// and close the file
pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
pstm->Write((LPTSTR)&dfh, SIZEOF(dfh), NULL);
pstm->Release();
SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL);
SHChangeNotify(SHCNE_FREESPACE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL);
}
void DocFind_SetObjectCount(IShellView *psv, int cItems, DWORD dwFlags)
{
IShellFolderView *psfv;
if (SUCCEEDED(psv->QueryInterface(IID_IShellFolderView, (void **)&psfv)))
{
psfv->SetObjectCount(cItems, dwFlags);
psfv->Release();
}
}
// Broke out from class to share with old and new code
BOOL DFB_handleUpdateDir(IDocFindFolder *pdfFolder, LPITEMIDLIST pidl, BOOL fCheckSubDirs)
{
// 1. Start walk through list of dirs. Find list of directories effected
// and mark them
// 2. Walk the list of items that we have and mark each of the items that
// that are in our list of directories and then do a search...
int iPidf;
BOOL fCurrentItemsMayBeImpacted = FALSE;
DFFolderListItem *pdffli;
int iItem;
INT cPidf;
ESFItem *pesfi;
IShellFolder *psfDesktop;
SHGetDesktopFolder(&psfDesktop);
// First see which directories are effected...
pdfFolder->GetFolderListItemCount(&cPidf);
for (iPidf = 0; iPidf < cPidf; iPidf++ )
{
if (SUCCEEDED(pdfFolder->GetFolderListItem(iPidf, &pdffli))
&& !pdffli->fUpdateDir) // We may have already impacted these...
{
pdffli->fUpdateDir = ILIsParent(pidl, &pdffli->idl, FALSE);
fCurrentItemsMayBeImpacted |= pdffli->fUpdateDir;
}
}
if (fCurrentItemsMayBeImpacted)
{
// Now we need to walk through the whole list and remove any entries
// that are no longer there...
if (SUCCEEDED(pdfFolder->GetItemCount(&iItem)))
{
for (--iItem; iItem >= 0; iItem--)
{
WORD iFolder;
if (FAILED(pdfFolder->GetItem(iItem, &pesfi)) || pesfi == NULL)
continue;
iFolder = DF_IFOLDER(&pesfi->idl);
// See if item may be impacted...
if (SUCCEEDED(pdfFolder->GetFolderListItem(iFolder, &pdffli)) && pdffli->fUpdateDir)
pesfi->dwState |= CDFITEM_STATE_MAYBEDELETE;
}
}
}
return fCurrentItemsMayBeImpacted;
}
void DFB_UpdateOrMaybeAddPidl(IDocFindFolder *pdfFolder, IShellFolderView *psfv, int code,
LPITEMIDLIST pidl, LPITEMIDLIST pidlOld)
{
IShellFolder *psf;
LPITEMIDLIST pidlT;
BOOL fMatch = FALSE;
UINT iItem;
ESFItem *pesfi;
HRESULT hres;
// First see if we should try to do an update...
if (pidlOld)
{
if (pdfFolder->MapFSPidlToDFPidl(pidl, TRUE, &pidlT) == S_OK)
{
// SFV owns apidl[1] after this
pdfFolder->SetItemsChangedSinceSort();
hres = psfv->UpdateObject(pidlOld, pidlT, &iItem);
ILFree(pidlT); // In either case simply blow away our generated pidl...
if (SUCCEEDED(hres))
return;
}
}
if (SUCCEEDED(SHBindToIDListParent(pidl, IID_IShellFolder, (void **)&psf, (LPCITEMIDLIST *) &pidlT)))
{
// See if this item matches the filter...
IDocFindFileFilter *pdfff;
if (SUCCEEDED(pdfFolder->GetDocFindFilter(&pdfff))) {
fMatch = pdfff->FDoesItemMatchFilter(NULL, NULL, psf, pidl) != 0;
pdfff->Release();
}
psf->Release();
if (fMatch)
{
LPITEMIDLIST pidlT;
if (pdfFolder->MapFSPidlToDFPidl(pidl, TRUE, &pidlT) != S_OK)
{
// The folder has not been added before now...
int iFolder;
LPITEMIDLIST pidlFolder;
TCHAR szPath[MAX_PATH]; // Don't add stuff in trashcan...
SHGetPathFromIDList(pidl, szPath);
if (IsFileInBitBucket(szPath))
return; // yes, don't add it to the list...
PathRemoveFileSpec(szPath);
pidlFolder = ILCreateFromPath(szPath);
if (!pidlFolder)
return;
hres = pdfFolder->AddFolderToFolderList(pidlFolder, TRUE, &iFolder);
ILFree(pidlFolder);
if (FAILED(hres))
return;
if (pdfFolder->MapFSPidlToDFPidl(pidl, TRUE, &pidlT) != S_OK)
return;
}
// There are times we get notified twice. To handle this
// see if the item is already in our list. If so punt...
// SFV owns apidl[1] after this
pdfFolder->SetItemsChangedSinceSort();
if (SUCCEEDED(psfv->UpdateObject(pidlT, pidlT, &iItem)))
{
ILFree(pidlT);
return; // We were able to add the item without problem...
}
// Normal case would be here to add the object
// We need to add this to our dpa and dsa...
if (SUCCEEDED(pdfFolder->GetItemCount((INT *)&iItem))) {
pdfFolder->AddPidl(iItem, pidlT, (UINT)-1, &pesfi);
ILFree(pidlT);
if (pesfi)
psfv->SetObjectCount(++iItem, SFVSOC_NOSCROLL);
}
}
}
}
void DFB_handleRMDir(IDocFindFolder *pdfFolder, IShellFolderView *psfv, LPITEMIDLIST pidl)
{
BOOL fCurrentItemsMayBeImpacted = FALSE;
DFFolderListItem *pdffli;
int iItem;
INT cItems;
ESFItem *pesfi;
// First see which directories are effected...
pdfFolder->GetFolderListItemCount(&cItems);
for (iItem = 0; iItem < cItems; iItem++ )
{
if (SUCCEEDED(pdfFolder->GetFolderListItem(iItem, &pdffli)))
{
pdffli->fDeleteDir = ILIsParent(pidl, &pdffli->idl, FALSE);
fCurrentItemsMayBeImpacted |= pdffli->fDeleteDir;
}
else
{
#ifdef DEBUG
INT cItem;
pdfFolder->GetFolderListItemCount(&cItem);
TraceMsg(TF_WARNING, "NULL pdffli in _handleRMDir (iItem == %d, ItemCount()==%d)!!!", iItem, cItems);
#endif
}
}
if (fCurrentItemsMayBeImpacted)
{
// Now we need to walk through the whole list and remove any entries
// that are no longer there...
if (SUCCEEDED(pdfFolder->GetItemCount(&iItem))) {
for (--iItem; iItem >= 0; iItem--)
{
WORD iFolder;
if (FAILED(pdfFolder->GetItem(iItem, &pesfi)) || pesfi == NULL)
continue;
iFolder = DF_IFOLDER(&pesfi->idl);
// See if item may be impacted...
if (SUCCEEDED(pdfFolder->GetFolderListItem(iFolder, &pdffli))
&& pdffli->fDeleteDir) {
psfv->RemoveObject(&pesfi->idl, (UINT*)&cItems);
}
}
}
}
}
UINT GetControlCharWidth(HWND hwnd)
{
SIZE siz;
HDC hdc = GetDC(HWND_DESKTOP);
HFONT hfOld = SelectFont(hdc, FORWARD_WM_GETFONT(hwnd, SendMessage));
GetTextExtentPoint(hdc, TEXT("0"), 1, &siz);
SelectFont(hdc, hfOld);
ReleaseDC(HWND_DESKTOP, hdc);
return siz.cx;
}
// Context menu stuff
TCHAR const c_szFindExtensions[] = TEXT("FindExtensions");
IContextMenu * WINAPI SHFind_InitMenuPopup(HMENU hmenu, HWND hwndOwner, UINT idCmdFirst, UINT idCmdLast)
{
IContextMenu * pcm = NULL;
HKEY hkFind = SHGetExplorerSubHkey(HKEY_LOCAL_MACHINE, c_szFindExtensions, FALSE);
if (hkFind) {
if (SUCCEEDED(CDefFolderMenu_CreateHKeyMenu(hwndOwner, hkFind, &pcm))) {
int iItems = GetMenuItemCount(hmenu);
// nuke all old entries
while (iItems--) {
DeleteMenu(hmenu, iItems, MF_BYPOSITION);
}
pcm->QueryContextMenu(hmenu, 0, idCmdFirst, idCmdLast, CMF_NODEFAULT|CMF_INCLUDESTATIC|CMF_FINDHACK);
iItems = GetMenuItemCount(hmenu);
if (!iItems) {
TraceMsg(TF_DOCFIND, "no menus in find extension, blowing away context menu");
pcm->Release();
pcm = NULL;
}
}
RegCloseKey(hkFind);
}
return pcm;
}
// This whole class is so "Find Computer" can redirect from a static item to this one.
// The FindFile stuff below is DEAD CODE. BUT, it may be resurrected (see HACKHACK
// comment in defcm's _Static_Add function.
class CShellFindExt : public IShellExtInit, public IContextMenu
{
public:
// *** IUnknown methods ***
virtual STDMETHODIMP QueryInterface(THIS_ REFIID riid, void **ppvObj);
virtual STDMETHODIMP_(ULONG) AddRef();
virtual STDMETHODIMP_(ULONG) Release();
// *** IShellExtInit methods ***
virtual STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj,
HKEY hkeyProgID);
// *** IContextMenu methods ***
virtual STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast,
UINT uFlags);
virtual STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici);
virtual STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT wFlags, UINT *pwReserved, LPSTR pszName,
UINT cchMax);
// constructor
CShellFindExt();
private:
UINT cRef;
LPITEMIDLIST pidl;
};
typedef CShellFindExt* LPSHELLFINDEXT;
STDMETHODIMP CShellFindExt::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CShellFindExt, IShellExtInit), //IID_IDocFindFolder
QITABENT(CShellFindExt, IContextMenu), //IID_IContextMenu
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CShellFindExt::Release()
{
ASSERT(this->cRef >= 1); // Don't release more than we can!
this->cRef--;
if (this->cRef > 0)
{
return this->cRef;
}
ILFree(this->pidl);
delete this;
return 0;
}
STDMETHODIMP_(ULONG) CShellFindExt::AddRef()
{
this->cRef++;
return this->cRef;
}
CShellFindExt::CShellFindExt()
{
this->cRef = 1;
}
STDMETHODIMP CShellFindExt::Initialize(LPCITEMIDLIST pidlFolder,
IDataObject *pdtobj,
HKEY hkeyProgID)
{
return NOERROR;
}
STDMETHODIMP CShellFindExt::QueryContextMenu(HMENU hmenu, UINT indexMenu,
UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
int iCommands = 0;
int idMax = idCmdFirst;
HMENU hmMerge = SHLoadPopupMenu(HINST_THISDLL, POPUP_FINDEXT_POPUPMERGE);
if (hmMerge) {
MENUITEMINFO mii;
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_DATA;
mii.dwItemData = Shell_GetCachedImageIndex(c_szShell32Dll, EIRESID(IDI_DOCFIND), 0);
// setting the icon index
TraceMsg(TF_DOCFIND, "CSFE::QueryContextMenu: %d is the icon index being set", mii.dwItemData);
if (mii.dwItemData != -1)
SetMenuItemInfo(hmMerge, FSIDM_FINDFILES, FALSE, &mii);
if (!(GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS) ||
!SHRestricted(REST_HASFINDCOMPUTERS) ) {
// Need to remove the Find Computer as we dont have a network
DeleteMenu(hmMerge, FSIDM_FINDCOMPUTER, MF_BYCOMMAND);
} else {
mii.dwItemData = Shell_GetCachedImageIndex(c_szShell32Dll, EIRESID(IDI_COMPFIND),0);
if (mii.dwItemData != -1)
SetMenuItemInfo(hmMerge, FSIDM_FINDCOMPUTER, FALSE, &mii);
}
idMax = Shell_MergeMenus(hmenu, hmMerge, indexMenu,
idCmdFirst, idCmdLast,
0);
DestroyMenu(hmMerge);
}
return ResultFromShort(idMax - idCmdFirst);
}
STDMETHODIMP CShellFindExt::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
LPITEMIDLIST pidl;
UINT_PTR id;
TraceMsg(TF_DOCFIND, "CSFE::InvokeCommand %d", pici->lpVerb);
id = (UINT_PTR)pici->lpVerb;
if (pici->lpParameters)
{
CLSID guid;
if (SUCCEEDED(GUIDFromStringA(pici->lpParameters, &guid)) && IsEqualGUID(guid, SRCID_SFindComputer))
id = FSIDM_FINDCOMPUTER;
}
switch (id) {
case FSIDM_FINDFILES:
if (pici->lpDirectory)
{
#ifdef UNICODE
WCHAR szDirectory[MAX_PATH];
LPCWSTR lpDirectory;
if (pici->cbSize < CMICEXSIZE_NT4
|| (pici->fMask & CMIC_MASK_UNICODE) != CMIC_MASK_UNICODE)
{
MultiByteToWideChar(CP_ACP, 0,
pici->lpDirectory, -1,
szDirectory, ARRAYSIZE(szDirectory));
lpDirectory = szDirectory;
}
else
{
lpDirectory = ((LPCMINVOKECOMMANDINFOEX)pici)->lpDirectoryW;
}
pidl = ILCreateFromPath(lpDirectory);
#else
pidl = ILCreateFromPath(pici->lpDirectory);
#endif
}
else
{
pidl = NULL;
}
RealFindFiles(pidl, NULL);
ILFree(pidl);
break;
case FSIDM_FINDCOMPUTER:
SHFindComputer(NULL, NULL);
break;
}
return NOERROR;
}
STDMETHODIMP CShellFindExt::GetCommandString(
UINT_PTR idCmd,
UINT wFlags,
UINT * pwReserved,
LPSTR pszName,
UINT cchMax)
{
TraceMsg(TF_DOCFIND, "CSFE::GetCommandString idCmd = %d", idCmd);
if (wFlags & GCS_HELPTEXTA)
{
UINT cch;
if ((wFlags & GCS_HELPTEXTW) == GCS_HELPTEXTW)
cch = LoadStringW(HINST_THISDLL,
(UINT)(idCmd + IDS_MH_FSIDM_FIRST),
(LPWSTR)pszName, cchMax);
else
cch = LoadStringA(HINST_THISDLL,
(UINT)(idCmd + IDS_MH_FSIDM_FIRST),
pszName, cchMax);
if (cch)
return NOERROR;
else
return E_OUTOFMEMORY;
}
else
return E_NOTIMPL;
}
STDAPI CShellFindExt_CreateInstance(LPUNKNOWN, REFIID , void **);
HRESULT CALLBACK CShellFindExt_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
LPSHELLFINDEXT psfe;
HRESULT hres;
psfe = new CShellFindExt;
if (psfe)
{
hres = psfe->QueryInterface(riid, ppv);
psfe->Release();
}
else
{
*ppv = NULL;
hres = E_OUTOFMEMORY;
}
return hres;
}