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.
5455 lines
172 KiB
5455 lines
172 KiB
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
#include <regstr.h>
|
|
#include <varutil.h>
|
|
#include "ids.h"
|
|
#include "findhlp.h"
|
|
#include "pidl.h"
|
|
#include "shitemid.h"
|
|
#include "defview.h"
|
|
#include "fstreex.h"
|
|
#include "views.h"
|
|
#include "cowsite.h"
|
|
#include "exdisp.h"
|
|
#include "shguidp.h"
|
|
#include "prop.h" // COLUMN_INFO
|
|
#include <limits.h>
|
|
#include "stgutil.h"
|
|
#include "netview.h"
|
|
#include "basefvcb.h"
|
|
#include "findfilter.h"
|
|
#include "defvphst.h"
|
|
#include "perhist.h"
|
|
#include "adoint.h"
|
|
#include "dspsprt.h"
|
|
#include "defcm.h"
|
|
#include "enumidlist.h"
|
|
#include "contextmenu.h"
|
|
|
|
// findband.cpp
|
|
STDAPI GetCIStatus(BOOL *pbRunning, BOOL *pbIndexed, BOOL *pbPermission);
|
|
STDAPI CatalogUptodate(LPCWSTR pszCatalog, LPCWSTR pszMachine);
|
|
|
|
class CFindFolder;
|
|
|
|
class CFindLVRange : public ILVRange
|
|
{
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// ILVRange
|
|
STDMETHODIMP IncludeRange(LONG iBegin, LONG iEnd);
|
|
STDMETHODIMP ExcludeRange(LONG iBegin, LONG iEnd);
|
|
STDMETHODIMP InvertRange(LONG iBegin, LONG iEnd);
|
|
STDMETHODIMP InsertItem(LONG iItem);
|
|
STDMETHODIMP RemoveItem(LONG iItem);
|
|
|
|
STDMETHODIMP Clear();
|
|
STDMETHODIMP IsSelected(LONG iItem);
|
|
STDMETHODIMP IsEmpty();
|
|
STDMETHODIMP NextSelected(LONG iItem, LONG *piItem);
|
|
STDMETHODIMP NextUnSelected(LONG iItem, LONG *piItem);
|
|
STDMETHODIMP CountIncluded(LONG *pcIncluded);
|
|
|
|
// Helperfunctions...
|
|
void SetOwner(CFindFolder *pff, DWORD dwMask)
|
|
{
|
|
// don't AddRef -- we're a member variable of the object punk points to
|
|
_pff = pff;
|
|
_dwMask = dwMask;
|
|
_cIncluded = 0;
|
|
}
|
|
void IncrementIncludedCount() {_cIncluded++;}
|
|
void DecrementIncludedCount() {_cIncluded--;}
|
|
protected:
|
|
CFindFolder *_pff;
|
|
DWORD _dwMask; // The mask we use to know which "selection" bit we are tracking...
|
|
LONG _cIncluded; // count included... (selected)
|
|
};
|
|
|
|
class CFindFolder : public IFindFolder,
|
|
public IShellFolder2,
|
|
public IShellIcon,
|
|
public IShellIconOverlay,
|
|
public IPersistFolder2
|
|
{
|
|
public:
|
|
CFindFolder(IFindFilter *pff);
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IShellFolder
|
|
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
|
|
STDMETHODIMP EnumObjects(THIS_ HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
|
|
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
|
|
STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
|
{ return BindToObject(pidl, pbc, riid, ppv); }
|
|
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
STDMETHODIMP CreateViewObject(HWND hwnd, REFIID riid, void **ppv);
|
|
STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut);
|
|
STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv);
|
|
STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
|
|
STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST *ppidlOut);
|
|
|
|
// IShellFolder2
|
|
STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
|
|
STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum);
|
|
STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
|
|
STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState);
|
|
STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
|
|
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
|
|
STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid);
|
|
|
|
// IFindFolder
|
|
STDMETHODIMP GetFindFilter(IFindFilter **pfilter);
|
|
STDMETHODIMP AddPidl(int i, LPCITEMIDLIST pidl, DWORD dwItemID, FIND_ITEM **ppItem);
|
|
STDMETHODIMP GetItem(int iItem, FIND_ITEM **ppItem);
|
|
STDMETHODIMP DeleteItem(int iItem);
|
|
STDMETHODIMP GetItemCount(INT *pcItems);
|
|
STDMETHODIMP ValidateItems(IUnknown *punkView, int iItemFirst, int cItems, BOOL bSearchComplete);
|
|
STDMETHODIMP GetFolderListItemCount(INT *pcCount);
|
|
STDMETHODIMP GetFolderListItem(int iItem, FIND_FOLDER_ITEM **ppItem);
|
|
STDMETHODIMP GetFolder(int iFolder, REFIID riid, void **ppv);
|
|
STDMETHODIMP_(UINT) GetFolderIndex(LPCITEMIDLIST pidl);
|
|
STDMETHODIMP SetItemsChangedSinceSort();
|
|
STDMETHODIMP ClearItemList();
|
|
STDMETHODIMP ClearFolderList();
|
|
STDMETHODIMP AddFolder(LPITEMIDLIST pidl, BOOL fCheckForDup, int * piFolder);
|
|
STDMETHODIMP SetAsyncEnum(IFindEnum *pfenum);
|
|
STDMETHODIMP GetAsyncEnum(IFindEnum **ppfenum);
|
|
STDMETHODIMP CacheAllAsyncItems();
|
|
STDMETHODIMP_(BOOL) AllAsyncItemsCached();
|
|
STDMETHODIMP SetAsyncCount(DBCOUNTITEM cCount);
|
|
STDMETHODIMP ClearSaveStateList();
|
|
STDMETHODIMP GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState);
|
|
STDMETHODIMP MapToSearchIDList(LPCITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl);
|
|
STDMETHODIMP GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent);
|
|
STDMETHODIMP SetControllerNotifyObject(IFindControllerNotify *pfcn);
|
|
STDMETHODIMP GetControllerNotifyObject(IFindControllerNotify **ppfcn);
|
|
STDMETHODIMP RememberSelectedItems();
|
|
STDMETHODIMP SaveFolderList(IStream *pstm);
|
|
STDMETHODIMP RestoreFolderList(IStream *pstm);
|
|
STDMETHODIMP SaveItemList(IStream *pstm);
|
|
STDMETHODIMP RestoreItemList(IStream *pstm, int *pcItems);
|
|
STDMETHODIMP RestoreSearchFromSaveFile(LPCITEMIDLIST pidlSaveFile, IShellFolderView *psfv);
|
|
|
|
STDMETHODIMP_(BOOL) HandleUpdateDir(LPCITEMIDLIST pidl, BOOL fCheckSubDirs);
|
|
STDMETHODIMP_(void) HandleRMDir(IShellFolderView *psfv, LPCITEMIDLIST pidl);
|
|
STDMETHODIMP_(void) UpdateOrMaybeAddPidl(IShellFolderView *psfv, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlOld);
|
|
STDMETHODIMP_(void) Save(IFindFilter* pfilter, HWND hwnd, DFBSAVEINFO * pSaveInfo, IShellView* psv, IUnknown * pObject);
|
|
STDMETHODIMP OpenContainingFolder(IUnknown *punkSite);
|
|
STDMETHODIMP AddDataToIDList(LPCITEMIDLIST pidl, int iFolder, LPCITEMIDLIST pidlFolder, UINT uFlags, UINT uRow, DWORD dwItemID, ULONG ulRank, LPITEMIDLIST *ppidl);
|
|
|
|
// IShellIcon
|
|
STDMETHODIMP GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex);
|
|
|
|
// IShellIconOverlay
|
|
STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex);
|
|
STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex);
|
|
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID *pClassID);
|
|
|
|
// IPersistFolder
|
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
|
|
|
|
// IPersistFolder2
|
|
STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl);
|
|
|
|
friend class CFindFolderViewCB;
|
|
friend class CFindLVRange;
|
|
|
|
HRESULT Init();
|
|
|
|
private:
|
|
~CFindFolder();
|
|
HRESULT _CompareFolderIndexes(int iFolder1, int iFolder2);
|
|
void _AddFIND_ITEMToSaveStateList(FIND_ITEM *pesfi);
|
|
LPITEMIDLIST _GetFullPidlForItem(LPCITEMIDLIST pidl);
|
|
HRESULT _UpdateItemList();
|
|
|
|
static int CALLBACK _SortForDataObj(void *p1, void *p2, LPARAM lparam);
|
|
static ULONG _Rank(LPCITEMIDLIST pidl);
|
|
static DWORD _ItemID(LPCITEMIDLIST pidl);
|
|
static PCHIDDENDOCFINDDATA _HiddenData(LPCITEMIDLIST pidl);
|
|
FIND_FOLDER_ITEM *_FolderListItem(int iFolder);
|
|
FIND_FOLDER_ITEM *_FolderListItem(LPCITEMIDLIST pidl);
|
|
static BOOL _MapColIndex(UINT *piColumn);
|
|
HRESULT _PrepareHIDA(UINT cidl, LPCITEMIDLIST *apidl, HDPA *phdpa);
|
|
|
|
HRESULT _GetDetailsFolder();
|
|
HRESULT _QueryItemShellFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf);
|
|
HRESULT _QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv);
|
|
HRESULT _Folder(FIND_FOLDER_ITEM *pffli, REFIID riid, void **ppv);
|
|
HRESULT _FolderFromItem(LPCITEMIDLIST pidl, REFIID riid, void **ppv);
|
|
|
|
HRESULT _GetFolderName(LPCITEMIDLIST pidl, DWORD gdnFlags, LPTSTR psz, UINT cch);
|
|
int _CompareByCachedSCID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
int _CompareNames(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwFlags);
|
|
HRESULT _GetItemDisplayName(LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR wzName, UINT cch);
|
|
HRESULT _GetFolderIDList(int iFolder, LPITEMIDLIST *ppidlParent);
|
|
|
|
LONG _cRef;
|
|
|
|
CFindLVRange _dflvrSel; // manage selection and cut range information...
|
|
CFindLVRange _dflvrCut;
|
|
|
|
HDPA _hdpaItems; // all of the items in the results
|
|
HDPA _hdpaPidf; // folders that the items came from
|
|
IFindFilter *_pfilter;
|
|
BOOL _fItemsChangedSinceSort;// Has the list changed since the last sort?
|
|
BOOL _fAllAsyncItemsCached; // Have we already cached all of the items?
|
|
BOOL _fSearchComplete;
|
|
BOOL _fInRefresh; // true if received prerefresh callback but postrefresh
|
|
|
|
LPITEMIDLIST _pidl;
|
|
IFindEnum *_pDFEnumAsync; // we have an async one, will need to call back for PIDLS and the like
|
|
DBCOUNTITEM _cAsyncItems; // Count of async items
|
|
int _iGetIDList; // index of the last IDlist we retrieved in callback.
|
|
HDSA _hdsaSaveStateForIDs; // Async - Remember which items are selected when we sort
|
|
int _cSaveStateSelected; // Number of items in selection list which are selected
|
|
IFindControllerNotify *_pfcn; // Sometimes need to let the "Controller" object know about things
|
|
CRITICAL_SECTION _csSearch;
|
|
BOOL _fcsSearch;
|
|
int _iCompareFolderCache1, _iCompareFolderCache2, _iCompareFolderCacheResult;
|
|
IShellFolder2 *_psfDetails;
|
|
|
|
SHCOLUMNID _scidCached; // Cached SCID for sorting the columns
|
|
UINT _uiColumnCached; // The index to the cached column for _scidCached
|
|
|
|
#if DEBUG
|
|
DWORD _GUIThreadID; // Items can only be added to _hdpaItems on the UI thread.
|
|
#endif
|
|
};
|
|
|
|
class CFindFolderViewCB : public CBaseShellFolderViewCB
|
|
{
|
|
public:
|
|
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
HRESULT SetShellView(IShellView *psv);
|
|
|
|
CFindFolderViewCB(CFindFolder* pff);
|
|
|
|
// IServiceProvider override
|
|
STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv);
|
|
|
|
// Web View Tasks
|
|
static HRESULT _OnOpenContainingFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
|
|
private:
|
|
~CFindFolderViewCB();
|
|
|
|
HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP);
|
|
HRESULT OnReArrange(DWORD pv, LPARAM lp);
|
|
HRESULT OnGETWORKINGDIR(DWORD pv, UINT wP, LPTSTR lP);
|
|
HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP);
|
|
HRESULT OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream**lP);
|
|
HRESULT OnGETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST *ppidl);
|
|
HRESULT OnGetItemIconIndex(DWORD pv, WPARAM iItem, int *piIcon);
|
|
HRESULT OnSetItemIconOverlay(DWORD pv, WPARAM iItem, int dwOverlayState);
|
|
HRESULT OnGetItemIconOverlay(DWORD pv, WPARAM iItem, int * pdwOverlayState);
|
|
HRESULT OnSETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST pidl);
|
|
HRESULT OnGetIndexForItemIDList(DWORD pv, int * piItem, LPITEMIDLIST pidl);
|
|
HRESULT OnDeleteItem(DWORD pv, LPCITEMIDLIST pidl);
|
|
HRESULT OnODFindItem(DWORD pv, int * piItem, NM_FINDITEM* pnmfi);
|
|
HRESULT OnODCacheHint(DWORD pv, NMLVCACHEHINT* pnmlvc);
|
|
HRESULT OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP);
|
|
HRESULT OnGetEmptyText(DWORD pv, UINT cchTextMax, LPTSTR pszText);
|
|
HRESULT OnSetEmptyText(DWORD pv, UINT res, LPCTSTR pszText);
|
|
HRESULT OnHwndMain(DWORD pv, HWND hwndMain);
|
|
HRESULT OnIsOwnerData(DWORD pv, DWORD *pdwFlags);
|
|
HRESULT OnSetISFV(DWORD pv, IShellFolderView* pisfv);
|
|
HRESULT OnWindowCreated(DWORD pv, HWND hwnd);
|
|
HRESULT OnWindowDestroy(DWORD pv, HWND wP);
|
|
HRESULT OnGetODRangeObject(DWORD pv, WPARAM wWhich, ILVRange **pplvr);
|
|
HRESULT OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP);
|
|
HRESULT OnGetIPersistHistory(DWORD pv, IPersistHistory **ppph);
|
|
HRESULT OnRefresh(DWORD pv, BOOL fPreRefresh);
|
|
HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *shtd);
|
|
HRESULT OnSortListData(DWORD pv, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
|
|
HRESULT _ProfferService(BOOL bProffer);
|
|
HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
|
|
HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
|
|
HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
|
|
HRESULT OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme);
|
|
|
|
CFindFolder* _pff;
|
|
UINT _iColSort; // Which column are we sorting by
|
|
BOOL _fIgnoreSelChange; // Sort in process
|
|
UINT _iFocused; // Which item has the focus?
|
|
UINT _cSelected; // Count of items selected
|
|
|
|
IProfferService* _pps; // ProfferService site.
|
|
DWORD _dwServiceCookie; // ProfferService cookie.
|
|
|
|
TCHAR _szEmptyText[128]; // empty results list text.
|
|
|
|
friend class CFindLVRange;
|
|
};
|
|
|
|
// Class to save and restore find state on the travel log
|
|
class CFindPersistHistory : public CDefViewPersistHistory
|
|
{
|
|
public:
|
|
CFindPersistHistory();
|
|
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID *pClassID);
|
|
|
|
// IPersistHistory
|
|
STDMETHODIMP LoadHistory(IStream *pStream, IBindCtx *pbc);
|
|
STDMETHODIMP SaveHistory(IStream *pStream);
|
|
|
|
protected:
|
|
IFindFolder* _GetDocFindFolder();
|
|
};
|
|
|
|
|
|
// {a5df1ea0-5702-11d1-83fa-00a0c90dc849}
|
|
const IID IID_IFindFolder = {0xa5df1ea0, 0x5702, 0x11d1, {0x83, 0xfa, 0x00, 0xa0, 0xc9, 0x0d, 0xc8, 0x49}};
|
|
|
|
// {5B8DCBF0-B096-11d1-9217-00403393B8F0}
|
|
const IID IID_IFindControllerNotify = {0x5b8dcbf0, 0xb096, 0x11d1, {0x92, 0x17, 0x0, 0x40, 0x33, 0x93, 0xb8, 0xf0}};
|
|
|
|
// 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))
|
|
|
|
// 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
|
|
{
|
|
ULARGE_INTEGER oUnicodeCriteria; // Offset of unicode find criteria.
|
|
DWORD NTsignature; // Signature of NT-generated find file.
|
|
} DFC_UNICODE_DESC;
|
|
|
|
|
|
enum
|
|
{
|
|
IDFCOL_NAME = 0, // default col from guy we are delegating to
|
|
IDFCOL_PATH,
|
|
IDFCOL_RANK,
|
|
};
|
|
|
|
const COLUMN_INFO c_find_cols[] =
|
|
{
|
|
DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL),
|
|
DEFINE_COL_STR_ENTRY(SCID_DIRECTORY, 30, IDS_PATH_COL),
|
|
DEFINE_COL_STR_MENU_ENTRY(SCID_RANK, 10, IDS_RANK_COL),
|
|
};
|
|
|
|
class CFindMenuBase : public IContextMenuCB , public CObjectWithSite
|
|
{
|
|
public:
|
|
CFindMenuBase();
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IContextMenuCB
|
|
STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) PURE;
|
|
|
|
protected:
|
|
virtual ~CFindMenuBase();
|
|
|
|
private:
|
|
LONG _cRef;
|
|
};
|
|
|
|
CFindMenuBase::CFindMenuBase() : _cRef(1)
|
|
{
|
|
}
|
|
|
|
CFindMenuBase::~CFindMenuBase()
|
|
{
|
|
}
|
|
|
|
STDMETHODIMP CFindMenuBase::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CFindMenuBase, IContextMenuCB), // IID_IContextMenuCB
|
|
QITABENT(CFindMenuBase, IObjectWithSite), // IID_IObjectWithSite
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
ULONG CFindMenuBase::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CFindMenuBase::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
|
|
class CFindMenuCB : public CFindMenuBase
|
|
{
|
|
public:
|
|
// IContextMenuCB
|
|
STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
};
|
|
|
|
STDMETHODIMP CFindMenuCB::CallBack(IShellFolder *psf, HWND hwnd,
|
|
IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case DFM_MERGECONTEXTMENU:
|
|
if (!(wParam & CMF_VERBSONLY))
|
|
{
|
|
LPQCMINFO pqcm = (LPQCMINFO)lParam;
|
|
if (!pdtobj)
|
|
{
|
|
UINT idStart = pqcm->idCmdFirst;
|
|
UINT idBGMain = 0, idBGPopup = 0;
|
|
IFindFolder *pff;
|
|
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff))))
|
|
{
|
|
IFindFilter *pfilter;
|
|
if (SUCCEEDED(pff->GetFindFilter(&pfilter)))
|
|
{
|
|
pfilter->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup);
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, idBGMain, idBGPopup, pqcm);
|
|
|
|
DeleteMenu(pqcm->hmenu, idStart+SFVIDM_EDIT_PASTE, MF_BYCOMMAND);
|
|
DeleteMenu(pqcm->hmenu, idStart+SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND);
|
|
DeleteMenu(pqcm->hmenu, idStart+SFVIDM_MISC_REFRESH, MF_BYCOMMAND);
|
|
|
|
IFindControllerNotify *pdcn;
|
|
if (S_OK == pff->GetControllerNotifyObject(&pdcn))
|
|
{
|
|
pdcn->Release();
|
|
}
|
|
else
|
|
{
|
|
DeleteMenu(pqcm->hmenu, idStart+FSIDM_SAVESEARCH, MF_BYCOMMAND);
|
|
}
|
|
|
|
pfilter->Release();
|
|
}
|
|
pff->Release();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
{
|
|
// Check if this is from item context menu
|
|
if (pdtobj)
|
|
{
|
|
switch(wParam)
|
|
{
|
|
case DFM_CMD_LINK:
|
|
hr = SHCreateLinks(hwnd, NULL, pdtobj, SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL);
|
|
break;
|
|
|
|
case DFM_CMD_DELETE:
|
|
// convert to DFM_INVOKCOMMANDEX to get flags bits
|
|
hr = DeleteFilesInDataObject(hwnd, 0, pdtobj, 0);
|
|
break;
|
|
|
|
case DFM_CMD_PROPERTIES:
|
|
// We need to pass an empty IDlist to combine with.
|
|
hr = SHLaunchPropSheet(CFSFolder_PropertiesThread, pdtobj,
|
|
(LPCTSTR)lParam, NULL, (void *)&c_idlDesktop);
|
|
break;
|
|
|
|
default:
|
|
// Note: Fixing the working of Translator Key is not worth fixing. Hence Punted.
|
|
// 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.
|
|
// 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
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case FSIDM_SAVESEARCH:
|
|
{
|
|
IFindFolder *pff;
|
|
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff))))
|
|
{
|
|
IFindControllerNotify *pdcn;
|
|
if (S_OK == pff->GetControllerNotifyObject(&pdcn))
|
|
{
|
|
pdcn->SaveSearch();
|
|
pdcn->Release();
|
|
}
|
|
pff->Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = S_FALSE; // one of view menu items, use the default code.
|
|
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:
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
class CFindFolderContextMenuItemCB : public CFindMenuBase
|
|
{
|
|
public:
|
|
// IContextMenuCB
|
|
STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
private:
|
|
CFindFolderContextMenuItemCB(IFindFolder* pff);
|
|
~CFindFolderContextMenuItemCB();
|
|
friend HRESULT CFindItem_Create(HWND hwnd, IFindFolder *pff, IContextMenu **ppcm);
|
|
|
|
STDMETHODIMP _GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode);
|
|
|
|
IFindFolder *_pff;
|
|
};
|
|
|
|
CFindFolderContextMenuItemCB::CFindFolderContextMenuItemCB(IFindFolder* pff) : _pff(pff)
|
|
{
|
|
_pff->AddRef();
|
|
}
|
|
|
|
CFindFolderContextMenuItemCB::~CFindFolderContextMenuItemCB()
|
|
{
|
|
_pff->Release();
|
|
}
|
|
|
|
STDMETHODIMP CFindFolderContextMenuItemCB::_GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode)
|
|
{
|
|
HRESULT hr;
|
|
if (idCmd == FSIDM_OPENCONTAININGFOLDER)
|
|
{
|
|
if (bUnicode)
|
|
hr = StringCchCopyW((LPWSTR)pszName, cchMax, L"OpenContainingFolder");
|
|
else
|
|
hr = StringCchCopyA((LPSTR)pszName, cchMax, "OpenContainingFolder");
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOTIMPL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolderContextMenuItemCB::CallBack(IShellFolder *psf, HWND hwnd,
|
|
IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
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:
|
|
_pff->OpenContainingFolder(_punkSite);
|
|
break;
|
|
default:
|
|
hr = E_FAIL; // not our command
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
case DFM_GETHELPTEXTW:
|
|
// probably need to implement these...
|
|
|
|
case DFM_GETVERBA:
|
|
case DFM_GETVERBW:
|
|
hr = _GetVerb((UINT_PTR)(LOWORD(wParam)), (LPSTR)lParam, (UINT)(HIWORD(wParam)), uMsg == DFM_GETVERBW);
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI CFindItem_Create(HWND hwnd, IFindFolder* pff, IContextMenu **ppcm)
|
|
{
|
|
*ppcm = NULL;
|
|
|
|
HRESULT hr;
|
|
// We want a quick IContextMenu implementation -- an empty defcm looks easiest
|
|
IContextMenuCB* pcmcb = new CFindFolderContextMenuItemCB(pff);
|
|
if (pcmcb)
|
|
{
|
|
hr = CDefFolderMenu_CreateEx(NULL, hwnd, 0, NULL, NULL, pcmcb, NULL, NULL, ppcm);
|
|
pcmcb->Release();
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::AddFolder(LPITEMIDLIST pidl, BOOL fCheckForDup, int *piFolder)
|
|
{
|
|
*piFolder = -1;
|
|
|
|
if (fCheckForDup)
|
|
{
|
|
EnterCriticalSection(&_csSearch);
|
|
for (int i = DPA_GetPtrCount(_hdpaPidf) - 1; i >= 0; i--)
|
|
{
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
|
|
if (pffli && ILIsEqual(&pffli->idl, pidl))
|
|
{
|
|
LeaveCriticalSection(&_csSearch);
|
|
*piFolder = i;
|
|
return S_OK;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_csSearch);
|
|
}
|
|
|
|
int cb = ILGetSize(pidl);
|
|
FIND_FOLDER_ITEM *pffli;;
|
|
HRESULT hr = SHLocalAlloc(sizeof(*pffli) - sizeof(pffli->idl) + cb, &pffli);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// pddfli->psf = NULL;
|
|
// pffli->fUpdateDir = FALSE;
|
|
memcpy(&pffli->idl, pidl, cb);
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
// Now add this item to our DPA...
|
|
*piFolder = DPA_AppendPtr(_hdpaPidf, pffli);
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
if (-1 != *piFolder)
|
|
{
|
|
// 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, _ILNext(pidl)); // skip the My Net Places entry
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LocalFree((HLOCAL)pffli);
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwState; // State of the item;
|
|
DWORD dwItemID; // Only used for Async support...
|
|
} FIND_ITEM_SAVE_STATE;
|
|
|
|
|
|
void CFindFolder::_AddFIND_ITEMToSaveStateList(FIND_ITEM *pesfi)
|
|
{
|
|
FIND_ITEM_SAVE_STATE essi;
|
|
essi.dwState = pesfi->dwState & CDFITEM_STATE_MASK;
|
|
essi.dwItemID = _ItemID(&pesfi->idl);
|
|
|
|
DSA_AppendItem(_hdsaSaveStateForIDs, (void *)&essi);
|
|
if (essi.dwState & LVIS_SELECTED)
|
|
_cSaveStateSelected++;
|
|
}
|
|
|
|
|
|
HRESULT CFindFolder::RememberSelectedItems()
|
|
{
|
|
EnterCriticalSection(&_csSearch);
|
|
// Currently has list of pidls...
|
|
for (int i = DPA_GetPtrCount(_hdpaItems); i-- > 0;)
|
|
{
|
|
// Pidl at start of structure...
|
|
FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_hdpaItems, i);
|
|
if (pesfi)
|
|
{
|
|
if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED))
|
|
_AddFIND_ITEMToSaveStateList(pesfi);
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_csSearch);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::ClearItemList()
|
|
{
|
|
// Clear out any async enumerators we may have
|
|
SetAsyncEnum(NULL);
|
|
_cAsyncItems = 0; // clear out our count of items...
|
|
_pfilter->ReleaseQuery();
|
|
|
|
// Also tell the filter to release everything...
|
|
EnterCriticalSection(&_csSearch);
|
|
if (_hdpaItems)
|
|
{
|
|
// Currently has list of pidls...
|
|
for (int i = DPA_GetPtrCount(_hdpaItems) - 1; i >= 0; i--)
|
|
{
|
|
// Pidl at start of structure...
|
|
FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_hdpaItems, i);
|
|
if (pesfi)
|
|
LocalFree((HLOCAL)pesfi);
|
|
}
|
|
|
|
_fSearchComplete = FALSE;
|
|
DPA_DeleteAllPtrs(_hdpaItems);
|
|
}
|
|
LeaveCriticalSection(&_csSearch);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::ClearFolderList()
|
|
{
|
|
EnterCriticalSection(&_csSearch);
|
|
if (_hdpaPidf)
|
|
{
|
|
for (int i = DPA_GetPtrCount(_hdpaPidf) - 1; i >= 0; i--)
|
|
{
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
|
|
if (pffli)
|
|
{
|
|
// Release the IShellFolder if we have one
|
|
if (pffli->psf)
|
|
pffli->psf->Release();
|
|
|
|
// And delete the item
|
|
LocalFree((HLOCAL)pffli);
|
|
}
|
|
}
|
|
DPA_DeleteAllPtrs(_hdpaPidf);
|
|
}
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
CFindFolder::CFindFolder(IFindFilter *pff) : _cRef(1), _iGetIDList(-1), _pfilter(pff), _iCompareFolderCache1(-1), _uiColumnCached(-1)
|
|
{
|
|
ASSERT(_pidl == NULL);
|
|
|
|
_pfilter->AddRef();
|
|
|
|
// initialize our LV selection objects...
|
|
_dflvrSel.SetOwner(this, LVIS_SELECTED);
|
|
_dflvrCut.SetOwner(this, LVIS_CUT);
|
|
|
|
#if DEBUG
|
|
_GUIThreadID = GetCurrentThreadId();
|
|
#endif
|
|
}
|
|
|
|
CFindFolder::~CFindFolder()
|
|
{
|
|
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);
|
|
|
|
_pfilter->Release();
|
|
|
|
if (_psfDetails)
|
|
_psfDetails->Release();
|
|
|
|
if (_fcsSearch)
|
|
{
|
|
DeleteCriticalSection(&_csSearch);
|
|
}
|
|
}
|
|
|
|
HRESULT CFindFolder::Init()
|
|
{
|
|
if (InitializeCriticalSectionAndSpinCount(&_csSearch, 0))
|
|
{
|
|
_fcsSearch = TRUE;
|
|
}
|
|
|
|
// Create the heap for the folder lists.
|
|
_hdpaPidf = DPA_CreateEx(64, GetProcessHeap());
|
|
|
|
// Create the DPA and DSA for the item list.
|
|
_hdpaItems = DPA_CreateEx(64, GetProcessHeap());
|
|
_hdsaSaveStateForIDs = DSA_Create(sizeof(FIND_ITEM_SAVE_STATE), 16);
|
|
|
|
return _fcsSearch && _hdsaSaveStateForIDs && _hdpaItems && _hdpaPidf ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::AddDataToIDList(LPCITEMIDLIST pidl, int iFolder, LPCITEMIDLIST pidlFolder, UINT uFlags, UINT uRow, DWORD dwItemID, ULONG ulRank, LPITEMIDLIST *ppidl)
|
|
{
|
|
HRESULT hr;
|
|
LPITEMIDLIST pidlToFree;
|
|
if (pidlFolder)
|
|
{
|
|
pidlToFree = NULL;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = _GetFolderIDList(iFolder, &pidlToFree);
|
|
pidlFolder = pidlToFree;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HIDDENDOCFINDDATA *phfd;
|
|
int cb = ILGetSize(pidlFolder);
|
|
int cbTotal = sizeof(*phfd) - sizeof(phfd->idlParent) + cb;
|
|
hr = SHLocalAlloc(cbTotal, &phfd);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
phfd->hid.cb = (WORD)cbTotal;
|
|
phfd->hid.wVersion = 0;
|
|
phfd->hid.id = IDLHID_DOCFINDDATA;
|
|
phfd->iFolder = (WORD)iFolder; // index to the folder DPA
|
|
phfd->wFlags = (WORD)uFlags;
|
|
phfd->uRow = uRow; // Which row in the CI;
|
|
phfd->dwItemID = dwItemID; // Only used for Async support...
|
|
phfd->ulRank = ulRank; // The rank returned by CI...
|
|
memcpy(&phfd->idlParent, pidlFolder, cb);
|
|
|
|
hr = ILCloneWithHiddenID(pidl, &phfd->hid, ppidl);
|
|
LocalFree(phfd);
|
|
}
|
|
ILFree(pidlToFree);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CreateFindWithFilter(IFindFilter *pff, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
CFindFolder *pfindfldr = new CFindFolder(pff);
|
|
if (pfindfldr)
|
|
{
|
|
hr = pfindfldr->Init();
|
|
if (SUCCEEDED(hr))
|
|
hr = pfindfldr->QueryInterface(riid, ppv);
|
|
pfindfldr->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI CDocFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
IFindFilter *pff;
|
|
HRESULT hr = CreateNameSpaceFindFilter(&pff);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateFindWithFilter(pff, riid, ppv);
|
|
pff->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI CComputerFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
IFindFilter *pff;
|
|
HRESULT hr = CreateDefaultComputerFindFilter(&pff);
|
|
if (pff)
|
|
{
|
|
hr = CreateFindWithFilter(pff, riid, ppv);
|
|
pff->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::MapToSearchIDList(LPCITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl)
|
|
{
|
|
*ppidl = NULL;
|
|
|
|
LPITEMIDLIST pidlParent;
|
|
LPCITEMIDLIST pidlChild;
|
|
if (SUCCEEDED(SplitIDList(pidl, &pidlParent, &pidlChild)))
|
|
{
|
|
EnterCriticalSection(&_csSearch);
|
|
|
|
// loop through our DPA list and see if we can find a matach
|
|
for (int i = 0; i < DPA_GetPtrCount(_hdpaPidf); i++)
|
|
{
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
|
|
if (pffli && ILIsEqual(pidlParent, &pffli->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 pidlToFree = NULL; // might need to cleanup in one case
|
|
|
|
// If this is not a FS folder, just clone it.
|
|
IShellFolder *psf;
|
|
if (fMapToReal && SUCCEEDED(_Folder(pffli, IID_PPV_ARG(IShellFolder, &psf))))
|
|
{
|
|
if (SUCCEEDED(SHGetRealIDL(psf, pidlChild, &pidlToFree)))
|
|
{
|
|
pidlChild = pidlToFree; // use this below...
|
|
}
|
|
psf->Release();
|
|
}
|
|
|
|
// create the doc find version of the pidl witht the
|
|
// extra hidden items embedded
|
|
AddDataToIDList(pidlChild, i, pidlParent, DFDF_NONE, 0, 0, 0, ppidl);
|
|
ILFree(pidlToFree); // may be NULL
|
|
|
|
break; // done with this loop
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
ILFree(pidlParent);
|
|
}
|
|
|
|
return *ppidl ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
// 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 CFindFolder::_UpdateItemList()
|
|
{
|
|
USHORT cb = 0;
|
|
int cItems;
|
|
if (SUCCEEDED(GetItemCount(&cItems)))
|
|
{
|
|
for (int i = 0; i < cItems; i++)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
if (DB_S_ENDOFROWSET == GetItem(i, &pesfi))
|
|
break;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// IFindFolder
|
|
HRESULT CFindFolder::SaveFolderList(IStream *pstm)
|
|
{
|
|
// We First pull all the items from RowSet (in Asynch case)
|
|
_UpdateItemList();
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
|
|
// Now loop through our DPA list and see if we can find a matach
|
|
for (int i = 0; i < DPA_GetPtrCount(_hdpaPidf); i++)
|
|
{
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
|
|
if (EVAL(pffli))
|
|
ILSaveToStream(pstm, &pffli->idl);
|
|
else
|
|
break;
|
|
}
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
// Now out a zero size item..
|
|
USHORT cb = 0;
|
|
pstm->Write(&cb, sizeof(cb), NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// IFindFolder, Restore results out to file.
|
|
HRESULT CFindFolder::RestoreFolderList(IStream *pstm)
|
|
{
|
|
// loop through and all all of the folders to our list...
|
|
LPITEMIDLIST pidl = NULL;
|
|
HRESULT hr;
|
|
|
|
for (;;)
|
|
{
|
|
hr = ILLoadFromStream(pstm, &pidl); // frees [in,out] pidl for us
|
|
|
|
if (pidl == NULL)
|
|
break; // end of the list
|
|
else
|
|
{
|
|
int i;
|
|
AddFolder(pidl, FALSE, &i);
|
|
}
|
|
}
|
|
|
|
ILFree(pidl); // don't forget to free last pidl
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::SaveItemList(IStream *pstm)
|
|
{
|
|
// We First serialize all of our PIDLS for each item in our list
|
|
int cItems;
|
|
if (SUCCEEDED(GetItemCount(&cItems)))
|
|
{
|
|
// And Save the items that are in the list
|
|
for (int i = 0; i < cItems; i++)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
HRESULT hr = GetItem(i, &pesfi);
|
|
|
|
if (hr == DB_S_ENDOFROWSET)
|
|
break;
|
|
if (SUCCEEDED(hr) && pesfi)
|
|
ILSaveToStream(pstm, &pesfi->idl);
|
|
}
|
|
}
|
|
|
|
USHORT cb = 0;
|
|
pstm->Write(&cb, sizeof(cb), NULL); // a Trailing NULL size to say end of pidl list...
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolder::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
|
|
FIND_ITEM *pesfi;
|
|
for (;;)
|
|
{
|
|
if (FAILED(ILLoadFromStream(pstm, &pidl)) || (pidl == NULL))
|
|
break;
|
|
|
|
if (FAILED(AddPidl(cItems, pidl, (UINT)-1, &pesfi)) || !pesfi)
|
|
break;
|
|
cItems++;
|
|
}
|
|
|
|
ILFree(pidl); // Free the last one read in
|
|
|
|
*pcItems = cItems;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolder::_GetFolderIDList(int iFolder, LPITEMIDLIST *ppidlParent)
|
|
{
|
|
*ppidlParent = NULL;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
EnterCriticalSection(&_csSearch);
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(iFolder);
|
|
if (pffli)
|
|
hr = SHILClone(&pffli->idl, ppidlParent);
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent)
|
|
{
|
|
return _GetFolderIDList(GetFolderIndex(pidl), ppidlParent);
|
|
}
|
|
|
|
HRESULT CFindFolder::SetControllerNotifyObject(IFindControllerNotify *pfcn)
|
|
{
|
|
IUnknown_Set((IUnknown **)&_pfcn, pfcn);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolder::GetControllerNotifyObject(IFindControllerNotify **ppfcn)
|
|
{
|
|
*ppfcn = _pfcn;
|
|
if (_pfcn)
|
|
_pfcn->AddRef();
|
|
return _pfcn ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CFindFolder::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CFindFolder::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CFindFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwzDisplayName,
|
|
ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::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)
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
|
{
|
|
IShellFolder *psf;
|
|
HRESULT hr = _QueryItemShellFolder(pidl, &psf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psf->BindToObject(pidl, pbc, riid, ppv);
|
|
psf->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Little helper function for bellow
|
|
HRESULT CFindFolder::_CompareFolderIndexes(int iFolder1, int iFolder2)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
|
|
FIND_FOLDER_ITEM *pffli1 = _FolderListItem(iFolder1);
|
|
FIND_FOLDER_ITEM *pffli2 = _FolderListItem(iFolder2);
|
|
|
|
if (pffli1 && pffli2)
|
|
{
|
|
// Check our 1-level deep cache. Since its is common for there to be multiple
|
|
// items in the same folder, during a sort operation, we often compare the
|
|
// same two folders repeatedly.
|
|
if ((_iCompareFolderCache1 != iFolder1) || (_iCompareFolderCache2 != iFolder2))
|
|
{
|
|
TCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
|
|
|
|
SHGetPathFromIDList(&pffli1->idl, szPath1);
|
|
SHGetPathFromIDList(&pffli2->idl, szPath2);
|
|
_iCompareFolderCacheResult = lstrcmpi(szPath1, szPath2);
|
|
_iCompareFolderCache1 = iFolder1;
|
|
_iCompareFolderCache2 = iFolder2;
|
|
}
|
|
hr = ResultFromShort(_iCompareFolderCacheResult);
|
|
}
|
|
|
|
LeaveCriticalSection(&_csSearch);
|
|
return hr;
|
|
}
|
|
|
|
PCHIDDENDOCFINDDATA CFindFolder::_HiddenData(LPCITEMIDLIST pidl)
|
|
{
|
|
return (PCHIDDENDOCFINDDATA)ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
|
|
}
|
|
|
|
|
|
UINT CFindFolder::GetFolderIndex(LPCITEMIDLIST pidl)
|
|
{
|
|
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA)ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
|
|
return phdfd ? phdfd->iFolder : -1;
|
|
}
|
|
|
|
FIND_FOLDER_ITEM *CFindFolder::_FolderListItem(int iFolder)
|
|
{
|
|
return (FIND_FOLDER_ITEM *)DPA_GetPtr(_hdpaPidf, iFolder);
|
|
}
|
|
|
|
FIND_FOLDER_ITEM *CFindFolder::_FolderListItem(LPCITEMIDLIST pidl)
|
|
{
|
|
return _FolderListItem(GetFolderIndex(pidl));
|
|
}
|
|
|
|
ULONG CFindFolder::_Rank(LPCITEMIDLIST pidl)
|
|
{
|
|
PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl);
|
|
// Could be mixed if so put ones without rank at the end...
|
|
return phdfd && (phdfd->wFlags & DFDF_EXTRADATA) ? phdfd->ulRank : 0;
|
|
}
|
|
|
|
DWORD CFindFolder::_ItemID(LPCITEMIDLIST pidl)
|
|
{
|
|
PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl);
|
|
return phdfd && (phdfd->wFlags & DFDF_EXTRADATA) ? phdfd->dwItemID : -1;
|
|
}
|
|
|
|
HRESULT CFindFolder::_GetItemDisplayName(LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR wzName, UINT cch)
|
|
{
|
|
// Get the IShellFolder:
|
|
IShellFolder *psf;
|
|
HRESULT hr = _QueryItemShellFolder(pidl, &psf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Get the display name:
|
|
hr = DisplayNameOf(psf, pidl, dwFlags, wzName, cch);
|
|
psf->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Given the 2 pidls, we extract the display name using DisplayNameOf and then
|
|
// if all goes well, we compare the two.
|
|
int CFindFolder::_CompareNames(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwFlags)
|
|
{
|
|
int iRetVal = 0;
|
|
WCHAR szName1[MAX_PATH], szName2[MAX_PATH];
|
|
|
|
// Get the name for 1
|
|
HRESULT hr = _GetItemDisplayName(pidl1, dwFlags, szName1, ARRAYSIZE(szName1));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Get the name for 2
|
|
hr = _GetItemDisplayName(pidl2, dwFlags, szName2, ARRAYSIZE(szName2));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Compare and set value
|
|
iRetVal = StrCmpLogicalW(szName1, szName2);
|
|
}
|
|
}
|
|
|
|
return iRetVal;
|
|
}
|
|
|
|
int CFindFolder::_CompareByCachedSCID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
int iRetVal = 0;
|
|
|
|
// If sort on name, we will skip this and use the code below.
|
|
if (!IsEqualSCID(_scidCached, SCID_NAME))
|
|
{
|
|
iRetVal = CompareBySCID(this, &_scidCached, pidl1, pidl2);
|
|
}
|
|
|
|
// If they are still the same, sort them alphabetically by the name:
|
|
// When we want to sort by name (either becuase we are sorting the
|
|
// name column, or because 2 items are identical in other regards) we
|
|
// want to display name vs the GetDetailsOf name for 2 reasons:
|
|
// 1. Some folders like IE's History don't support GetDetailsEx.
|
|
// 2. Recycle bin returns the file name, not the displayable name;
|
|
// so we would end up with "DC###..." instead of "New Folder".
|
|
if (iRetVal == 0)
|
|
{
|
|
iRetVal = _CompareNames(pidl1, pidl2, SHGDN_INFOLDER | SHGDN_NORMAL);
|
|
if (iRetVal == 0) // the display names are the same, could they be in different folders?
|
|
{
|
|
iRetVal = _CompareNames(pidl1, pidl2, SHGDN_FORPARSING);
|
|
}
|
|
}
|
|
|
|
return iRetVal;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
ASSERT(pidl1 == ILFindLastID(pidl1));
|
|
|
|
UINT iInputColumn = ((DWORD)lParam & SHCIDS_COLUMNMASK);
|
|
UINT iMappedColumn = iInputColumn;
|
|
|
|
if (_MapColIndex(&iMappedColumn))
|
|
{
|
|
if (IDFCOL_PATH == iMappedColumn)
|
|
{
|
|
UINT iFolder1 = GetFolderIndex(pidl1);
|
|
UINT iFolder2 = GetFolderIndex(pidl2);
|
|
|
|
if (iFolder1 != iFolder2)
|
|
return _CompareFolderIndexes(iFolder1, iFolder2);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(iMappedColumn == IDFCOL_RANK);
|
|
|
|
ULONG ulRank1 = _Rank(pidl1);
|
|
ULONG ulRank2 = _Rank(pidl2);
|
|
if (ulRank1 < ulRank2)
|
|
return ResultFromShort(-1);
|
|
if (ulRank1 > ulRank2)
|
|
return ResultFromShort(1);
|
|
}
|
|
}
|
|
|
|
// Check the SCID cache and update it if necessary.
|
|
if (_uiColumnCached != iInputColumn)
|
|
{
|
|
hr = MapColumnToSCID(iInputColumn, &_scidCached);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_uiColumnCached = iInputColumn;
|
|
}
|
|
}
|
|
|
|
// Check if one is a folder and not the other. put folders before files.
|
|
int iRes = CompareFolderness(this, pidl1, pidl2);
|
|
if (iRes == 0)
|
|
{
|
|
iRes = _CompareByCachedSCID(pidl1, pidl2);
|
|
}
|
|
|
|
return ResultFromShort(iRes);
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
if (IsEqualIID(riid, IID_IShellView))
|
|
{
|
|
IShellFolderViewCB* psfvcb = new CFindFolderViewCB(this);
|
|
if (psfvcb)
|
|
{
|
|
SFV_CREATE sSFV = {0};
|
|
sSFV.cbSize = sizeof(sSFV);
|
|
sSFV.pshf = this;
|
|
sSFV.psfvcb = psfvcb;
|
|
|
|
hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
|
|
|
|
psfvcb->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else if (IsEqualIID(riid, IID_IContextMenu))
|
|
{
|
|
IContextMenuCB *pcmcb = new CFindMenuCB();
|
|
if (pcmcb)
|
|
{
|
|
hr = CDefFolderMenu_CreateEx(NULL, hwnd,
|
|
0, NULL, this, pcmcb, NULL, NULL, (IContextMenu * *)ppv);
|
|
pcmcb->Release();
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
|
|
{
|
|
HRESULT hr;
|
|
if (cidl == 0)
|
|
{
|
|
// defview asks to see if any items can be renamed this way, lame
|
|
*prgfInOut = SFGAO_CANRENAME;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(*apidl == ILFindLastID(*apidl))
|
|
IShellFolder *psf;
|
|
hr = _QueryItemShellFolder(apidl[0], &psf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psf->GetAttributesOf(cidl, apidl, prgfInOut);
|
|
psf->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// To be called back from within CDefFolderMenuE - Currently only used
|
|
//
|
|
|
|
// Some helper functions
|
|
STDMETHODIMP CFindFolder::SetItemsChangedSinceSort()
|
|
{
|
|
_fItemsChangedSinceSort = TRUE;
|
|
_iCompareFolderCache1 = -1; // an invalid folder index value
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetItemCount(INT *pcItems)
|
|
{
|
|
ASSERT(pcItems);
|
|
DBCOUNTITEM cItems = 0;
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
if (_hdpaItems)
|
|
cItems = DPA_GetPtrCount(_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 CFindFolder::GetItem(int iItem, FIND_ITEM **ppItem)
|
|
{
|
|
HRESULT hr = E_FAIL; // just to init, use anything
|
|
FIND_ITEM *pesfi;
|
|
IFindEnum *pidfenum;
|
|
|
|
GetAsyncEnum(&pidfenum);
|
|
|
|
DWORD dwItemID = (UINT)-1;
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
int i = DPA_GetPtrCount(_hdpaItems);
|
|
pesfi = (FIND_ITEM *) DPA_GetPtr(_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 = _HiddenData(&pesfi->idl);
|
|
|
|
// 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)
|
|
{
|
|
// Overload, pass NULL to ADDPIDL to tell system to free that item
|
|
if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED))
|
|
_AddFIND_ITEMToSaveStateList(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;
|
|
|
|
hr = pidfenum->GetItemIDList(SANE_ITEMCOUNT(iItem), &pidlT);
|
|
if (SUCCEEDED(hr) && hr != 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 (hr != DB_S_ENDOFROWSET)
|
|
hr = pesfi ? S_OK : E_FAIL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::DeleteItem(int iItem)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (!_fInRefresh)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
|
|
hr = 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 = _HiddenData(&pesfi->idl);
|
|
|
|
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);
|
|
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// evil window crawling code to get the listview from defview
|
|
|
|
HWND ListviewFromView(HWND hwnd)
|
|
{
|
|
HWND hwndLV;
|
|
|
|
do
|
|
{
|
|
hwndLV = FindWindowEx(hwnd, NULL, WC_LISTVIEW, NULL);
|
|
}
|
|
while ((hwndLV == NULL) && (hwnd = GetWindow(hwnd, GW_CHILD)));
|
|
|
|
return hwndLV;
|
|
}
|
|
|
|
HWND ListviewFromViewUnk(IUnknown *punkView)
|
|
{
|
|
HWND hwnd;
|
|
if (SUCCEEDED(IUnknown_GetWindow(punkView, &hwnd)))
|
|
{
|
|
hwnd = ListviewFromView(hwnd);
|
|
}
|
|
return hwnd;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::ValidateItems(IUnknown *punkView, int iItem, int cItems, BOOL bSearchComplete)
|
|
{
|
|
IFindEnum *pidfenum;
|
|
if (S_OK != GetAsyncEnum(&pidfenum) || _fAllAsyncItemsCached)
|
|
return S_OK; // nothing to validate.
|
|
|
|
DWORD dwItemID = (UINT)-1;
|
|
|
|
int cItemsInList;
|
|
GetItemCount(&cItemsInList);
|
|
|
|
// force reload of rows
|
|
pidfenum->Reset();
|
|
|
|
HWND hwndLV = ListviewFromViewUnk(punkView);
|
|
|
|
int iLVFirst = ListView_GetTopIndex(hwndLV);
|
|
int 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);
|
|
FIND_ITEM *pesfi = (FIND_ITEM *) DPA_GetPtr(_hdpaItems, iItem);
|
|
LeaveCriticalSection(&_csSearch);
|
|
if (!pesfi) // Assume that if we have not gotten this one we are in the clear...
|
|
break;
|
|
|
|
PCHIDDENDOCFINDDATA phdfd = _HiddenData(&pesfi->idl);
|
|
|
|
if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA))
|
|
{
|
|
pidfenum->GetItemID(iItem, &dwItemID);
|
|
|
|
if (dwItemID != _ItemID(&pesfi->idl))
|
|
{
|
|
FIND_ITEM *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;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::AddPidl(int i, LPCITEMIDLIST pidl, DWORD dwItemID, FIND_ITEM **ppcdfi)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
ASSERT(GetCurrentThreadId() == _GUIThreadID);
|
|
|
|
if (NULL == pidl)
|
|
{
|
|
EnterCriticalSection(&_csSearch);
|
|
FIND_ITEM* pesfi = (FIND_ITEM*)DPA_GetPtr(_hdpaItems, i);
|
|
if (pesfi)
|
|
{
|
|
LocalFree((HLOCAL)pesfi);
|
|
DPA_SetPtr(_hdpaItems, i, NULL);
|
|
}
|
|
LeaveCriticalSection(&_csSearch);
|
|
if (ppcdfi)
|
|
*ppcdfi = NULL;
|
|
}
|
|
else
|
|
{
|
|
int cb = ILGetSize(pidl);
|
|
FIND_ITEM *pesfi;
|
|
hr = SHLocalAlloc(sizeof(*pesfi) - sizeof(pesfi->idl) + cb, &pesfi);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// pesfi->dwMask = 0;
|
|
// pesfi->dwState = 0;
|
|
pesfi->iIcon = -1;
|
|
memcpy(&pesfi->idl, pidl, cb);
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
BOOL bRet = DPA_SetPtr(_hdpaItems, i, (void *)pesfi);
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
if (bRet)
|
|
{
|
|
if (ppcdfi)
|
|
*ppcdfi = pesfi;
|
|
}
|
|
else
|
|
{
|
|
LocalFree((HLOCAL)pesfi);
|
|
pesfi = NULL;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::SetAsyncEnum(IFindEnum *pdfEnumAsync)
|
|
{
|
|
if (_pDFEnumAsync)
|
|
_pDFEnumAsync->Release();
|
|
|
|
_pDFEnumAsync = pdfEnumAsync;
|
|
if (pdfEnumAsync)
|
|
pdfEnumAsync->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::CacheAllAsyncItems()
|
|
{
|
|
if (_fAllAsyncItemsCached)
|
|
return S_OK; // Allready done it...
|
|
|
|
IFindEnum *pidfenum;
|
|
if (S_OK != GetAsyncEnum(&pidfenum))
|
|
return S_FALSE; // nothing to do...
|
|
|
|
// Probably the easiest thing to do is to simply walk through all of the items...
|
|
int maxItems = SANE_ITEMCOUNT(_cAsyncItems);
|
|
for (int i = 0; i < maxItems; i++)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
GetItem(i, &pesfi);
|
|
}
|
|
|
|
_fAllAsyncItemsCached = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL CFindFolder::AllAsyncItemsCached()
|
|
{
|
|
return _fAllAsyncItemsCached;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetAsyncEnum(IFindEnum **ppdfEnumAsync)
|
|
{
|
|
*ppdfEnumAsync = _pDFEnumAsync; // unreferecned!
|
|
return *ppdfEnumAsync ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::SetAsyncCount(DBCOUNTITEM cCount)
|
|
{
|
|
_cAsyncItems = cCount;
|
|
_fAllAsyncItemsCached = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::ClearSaveStateList()
|
|
{
|
|
DSA_DeleteAllItems(_hdsaSaveStateForIDs);
|
|
_cSaveStateSelected = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState)
|
|
{
|
|
for (int i = DSA_GetItemCount(_hdsaSaveStateForIDs); i-- > 0;)
|
|
{
|
|
// Pidl at start of structure...
|
|
FIND_ITEM_SAVE_STATE *pessi = (FIND_ITEM_SAVE_STATE*)DSA_GetItemPtr(_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(_hdsaSaveStateForIDs, i);
|
|
return S_OK;
|
|
}
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetFolderListItemCount(INT *pcItemCount)
|
|
{
|
|
*pcItemCount = 0;
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
if (_hdpaPidf)
|
|
*pcItemCount = DPA_GetPtrCount(_hdpaPidf);
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetFolderListItem(int iItem, FIND_FOLDER_ITEM **ppdffi)
|
|
{
|
|
EnterCriticalSection(&_csSearch);
|
|
*ppdffi = (FIND_FOLDER_ITEM *)DPA_GetPtr(_hdpaPidf, iItem);
|
|
LeaveCriticalSection(&_csSearch);
|
|
return *ppdffi ? S_OK : E_FAIL;
|
|
}
|
|
|
|
class CFindMenuWrap : public CContextMenuForwarder
|
|
{
|
|
public:
|
|
// IContextMenu overrides
|
|
STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
|
|
|
|
protected:
|
|
CFindMenuWrap(IDataObject* pdo, IContextMenu* pcmArray);
|
|
~CFindMenuWrap();
|
|
friend HRESULT DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2, REFIID riid, void** ppv);
|
|
|
|
private:
|
|
IDataObject * _pdtobj;
|
|
};
|
|
|
|
CFindMenuWrap::CFindMenuWrap(IDataObject* pdo, IContextMenu* pcmArray) : CContextMenuForwarder(pcmArray)
|
|
{
|
|
_pdtobj = pdo;
|
|
_pdtobj->AddRef();
|
|
}
|
|
|
|
CFindMenuWrap::~CFindMenuWrap()
|
|
{
|
|
_pdtobj->Release();
|
|
}
|
|
|
|
STDMETHODIMP CFindMenuWrap::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
|
|
{
|
|
BOOL fIsLink = FALSE;
|
|
|
|
// "link" has to create a link on the desktop, not in the folder (since we're not a real folder...)
|
|
if (IS_INTRESOURCE(lpici->lpVerb))
|
|
{
|
|
WCHAR szCommandString[64];
|
|
if (SUCCEEDED(ContextMenu_GetCommandStringVerb(_pcm, LOWORD((UINT_PTR)lpici->lpVerb), szCommandString, ARRAYSIZE(szCommandString))))
|
|
{
|
|
fIsLink = !StrCmpIW(szCommandString, L"link");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fIsLink = !StrCmpIA(lpici->lpVerb, "link");
|
|
}
|
|
|
|
if (fIsLink)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
return CContextMenuForwarder::InvokeCommand(lpici);
|
|
}
|
|
|
|
HRESULT DFWrapIContextMenu(HWND hwnd, IShellFolder *psf, LPCITEMIDLIST pidl,
|
|
IContextMenu* pcmExtra, void **ppvInOut)
|
|
{
|
|
IContextMenu *pcmWrap = NULL;
|
|
IContextMenu *pcmFree = (IContextMenu*)*ppvInOut;
|
|
|
|
IDataObject* pdo;
|
|
HRESULT hr = psf->GetUIObjectOf(hwnd, 1, &pidl, IID_X_PPV_ARG(IDataObject, NULL, &pdo));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = DFWrapIContextMenus(pdo, pcmFree, pcmExtra, IID_PPV_ARG(IContextMenu, &pcmWrap));
|
|
pdo->Release();
|
|
}
|
|
|
|
pcmFree->Release();
|
|
*ppvInOut = pcmWrap;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2, REFIID riid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
IContextMenu * pcmArray;
|
|
IContextMenu* rgpcm[2] = {pcm2, pcm1};
|
|
HRESULT hr = Create_ContextMenuOnContextMenuArray(rgpcm, ARRAYSIZE(rgpcm), IID_PPV_ARG(IContextMenu, &pcmArray));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CFindMenuWrap * p = new CFindMenuWrap(pdo, pcmArray);
|
|
if (p)
|
|
{
|
|
hr = p->QueryInterface(riid, ppv);
|
|
p->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
pcmArray->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CFindFolder::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CFindFolder, IShellFolder2), //IID_ISHELLFolder2
|
|
QITABENTMULTI(CFindFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
|
|
QITABENT(CFindFolder, IFindFolder), //IID_IFindFolder
|
|
QITABENT(CFindFolder, IShellIcon), //IID_IShellIcon
|
|
QITABENT(CFindFolder, IPersistFolder2), //IID_IPersistFolder2
|
|
QITABENTMULTI(CFindFolder, IPersistFolder, IPersistFolder2), //IID_IPersistFolder
|
|
QITABENTMULTI(CFindFolder, IPersist, IPersistFolder2), //IID_IPersist
|
|
QITABENT(CFindFolder, IShellIconOverlay), //IID_IShellIconOverlay
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
// IPersistFolder2 implementation
|
|
STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_DocFindFolder;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::Initialize(LPCITEMIDLIST pidl)
|
|
{
|
|
if (_pidl)
|
|
ILFree(_pidl);
|
|
|
|
return SHILClone(pidl, &_pidl);
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::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 CFindFolder::_SortForDataObj(void *p1, void *p2, 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)p1);
|
|
LPITEMIDLIST pidl2 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)p2);
|
|
CFindFolder *pff = (CFindFolder *)lparam;
|
|
|
|
return pff->GetFolderIndex(pidl2) - pff->GetFolderIndex(pidl1);
|
|
}
|
|
|
|
LPITEMIDLIST CFindFolder::_GetFullPidlForItem(LPCITEMIDLIST pidl)
|
|
{
|
|
LPITEMIDLIST pidlRet = NULL;
|
|
LPITEMIDLIST pidlParent;
|
|
if (S_OK == GetParentsPIDL(pidl, &pidlParent))
|
|
{
|
|
pidlRet = ILCombine(pidlParent, pidl);
|
|
ILFree(pidlParent);
|
|
}
|
|
return pidlRet;
|
|
}
|
|
|
|
// we generate a non flat HIDA. this is so clients that
|
|
// use this HIDA will bind to the folder that the results came
|
|
// from instead of this folder that has runtime state that won't
|
|
// be present if we rebind
|
|
|
|
HRESULT CFindFolder::_PrepareHIDA(UINT cidl, LPCITEMIDLIST * apidl, HDPA *phdpa)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
*phdpa = DPA_Create(0);
|
|
if (*phdpa)
|
|
{
|
|
if (DPA_Grow(*phdpa, cidl))
|
|
{
|
|
for (UINT i = 0; i < cidl; i++)
|
|
{
|
|
LPITEMIDLIST pidl = _GetFullPidlForItem(apidl[i]);
|
|
if (pidl)
|
|
DPA_InsertPtr(*phdpa, 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(*phdpa, _SortForDataObj, (LPARAM)this);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
|
|
REFIID riid, UINT * prgfInOut, void **ppv)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
*ppv = NULL;
|
|
|
|
// if just one item we can deletate to real folder
|
|
if (cidl == 1)
|
|
{
|
|
// Note we may have been passed in a complex item so find the last
|
|
ASSERT(ILIsEmpty(_ILNext(*apidl))); // should be a single level PIDL!
|
|
|
|
IShellFolder *psf;
|
|
hr = _QueryItemShellFolder(apidl[0], &psf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
|
|
|
|
// 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(hr))
|
|
{
|
|
if (IsEqualIID(riid, IID_IContextMenu))
|
|
{
|
|
// we also let the net/file guy add in a context menu if they want to
|
|
IContextMenu* pcmExtra = NULL;
|
|
_pfilter->GetItemContextMenu(hwnd, SAFECAST(this, IFindFolder*), &pcmExtra);
|
|
|
|
hr = DFWrapIContextMenu(hwnd, psf, apidl[0], pcmExtra, ppv);
|
|
|
|
ATOMICRELEASE(pcmExtra);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IQueryInfo)) // && SHGetAttributes(psf, apidl[0], SFGAO_FILESYSTEM))
|
|
{
|
|
WrapInfotip(SAFECAST(this, IShellFolder2 *), apidl[0], &SCID_DIRECTORY, (IUnknown *)*ppv);
|
|
}
|
|
}
|
|
psf->Release();
|
|
}
|
|
}
|
|
else if (cidl > 1)
|
|
{
|
|
if (IsEqualIID(riid, IID_IContextMenu))
|
|
{
|
|
// Try to create a menu object that we process ourself
|
|
// Yes, do context menu.
|
|
HKEY ahkeys[MAX_ASSOC_KEYS] = {0};
|
|
DWORD ckeys = 0;
|
|
|
|
LPITEMIDLIST pidlFull = _GetFullPidlForItem(apidl[0]);
|
|
if (pidlFull)
|
|
{
|
|
// Get the hkeyProgID and hkeyBaseProgID from the first item.
|
|
ckeys = SHGetAssocKeysForIDList(pidlFull, ahkeys, ARRAYSIZE(ahkeys));
|
|
ILFree(pidlFull);
|
|
}
|
|
|
|
IContextMenuCB *pcmcb = new CFindMenuCB();
|
|
if (pcmcb)
|
|
{
|
|
hr = CDefFolderMenu_Create2Ex(NULL, hwnd,
|
|
cidl, apidl, this, pcmcb,
|
|
ckeys, ahkeys,
|
|
(IContextMenu **)ppv);
|
|
pcmcb->Release();
|
|
}
|
|
|
|
SHRegCloseKeys(ahkeys, ckeys);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IDataObject))
|
|
{
|
|
HDPA hdpa;
|
|
hr = _PrepareHIDA(cidl, apidl, &hdpa);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHCreateFileDataObject(&c_idlDesktop, cidl, (LPCITEMIDLIST*)DPA_GetPtrPtr(hdpa),
|
|
NULL, (IDataObject **)ppv);
|
|
DPA_FreeIDArray(hdpa);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwRes, LPSTRRET pStrRet)
|
|
{
|
|
IShellFolder *psf;
|
|
HRESULT hr = _QueryItemShellFolder(pidl, &psf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if ((dwRes & SHGDN_INFOLDER) && (dwRes & SHGDN_FORPARSING) && !(dwRes & SHGDN_FORADDRESSBAR))
|
|
{
|
|
// The thumbnail cache uses this as a hit test... in search view we can have files with the same name.
|
|
dwRes &= ~SHGDN_INFOLDER;
|
|
}
|
|
hr = psf->GetDisplayNameOf(pidl, dwRes, pStrRet);
|
|
psf->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
|
|
DWORD dwRes, LPITEMIDLIST *ppidlOut)
|
|
{
|
|
if (ppidlOut)
|
|
*ppidlOut = NULL;
|
|
|
|
IShellFolder *psf;
|
|
HRESULT hr = _QueryItemShellFolder(pidl, &psf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidlRenamed;
|
|
hr = psf->SetNameOf(hwnd, pidl, PathFindFileName(pszName), dwRes, ppidlOut ? &pidlRenamed : NULL);
|
|
if (SUCCEEDED(hr) && ppidlOut)
|
|
{
|
|
hr = AddDataToIDList(pidlRenamed, GetFolderIndex(pidl), NULL, DFDF_NONE, 0, 0, 0, ppidlOut);
|
|
ILFree(pidlRenamed);
|
|
}
|
|
psf->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetDefaultSearchGUID(GUID *pGuid)
|
|
{
|
|
return _pfilter->GetDefaultSearchGUID(SAFECAST(this, IShellFolder2*), pGuid);
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
|
|
{
|
|
return _pfilter->EnumSearches(SAFECAST(this, IShellFolder2*), ppenum);
|
|
}
|
|
|
|
HRESULT CFindFolder::_Folder(FIND_FOLDER_ITEM *pffli, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pffli->psf)
|
|
hr = S_OK;
|
|
else
|
|
hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, &pffli->idl, &pffli->psf));
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = pffli->psf->QueryInterface(riid, ppv);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::GetFolder(int iFolder, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(iFolder);
|
|
if (pffli)
|
|
hr = _Folder(pffli, riid, ppv);
|
|
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::_FolderFromItem(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl);
|
|
if (phdfd)
|
|
{
|
|
hr = SHBindToObject(NULL, riid, &phdfd->idlParent, ppv);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::_QueryItemShellFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf)
|
|
{
|
|
*ppsf = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(pidl);
|
|
if (pffli)
|
|
{
|
|
if (pffli->psf)
|
|
hr = S_OK;
|
|
else
|
|
hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, &pffli->idl, &pffli->psf));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppsf = pffli->psf;
|
|
(*ppsf)->AddRef();
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
hr = _FolderFromItem(pidl, IID_PPV_ARG(IShellFolder, ppsf));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CFindFolder::_QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
EnterCriticalSection(&_csSearch);
|
|
|
|
FIND_FOLDER_ITEM *pffli = _FolderListItem(pidl);
|
|
if (pffli)
|
|
hr = _Folder(pffli, riid, ppv);
|
|
|
|
LeaveCriticalSection(&_csSearch);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
hr = _FolderFromItem(pidl, riid, ppv);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::_GetDetailsFolder()
|
|
{
|
|
HRESULT hr;
|
|
if (_psfDetails)
|
|
hr = S_OK; // in cache
|
|
else
|
|
{
|
|
IFindFilter *pfilter;
|
|
hr = GetFindFilter(&pfilter);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pfilter->GetColumnsFolder(&_psfDetails);
|
|
pfilter->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
|
|
{
|
|
HRESULT hr = _GetDetailsFolder();
|
|
if (SUCCEEDED(hr))
|
|
hr = _psfDetails->GetDefaultColumn(dwRes, pSort, pDisplay);
|
|
return hr;
|
|
}
|
|
|
|
BOOL CFindFolder::_MapColIndex(UINT *piColumn)
|
|
{
|
|
switch (*piColumn)
|
|
{
|
|
case IDFCOL_NAME: // 0
|
|
return FALSE;
|
|
|
|
case IDFCOL_PATH: // 1
|
|
case IDFCOL_RANK: // 2
|
|
return TRUE;
|
|
|
|
default: // >= 3
|
|
*piColumn -= IDFCOL_RANK;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetDefaultColumnState(UINT iColumn, DWORD *pdwState)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (_MapColIndex(&iColumn))
|
|
{
|
|
*pdwState = c_find_cols[iColumn].csFlags;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = _GetDetailsFolder();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _psfDetails->GetDefaultColumnState(iColumn, pdwState);
|
|
*pdwState &= ~SHCOLSTATE_SLOW; // virtual lv and defview
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::_GetFolderName(LPCITEMIDLIST pidl, DWORD gdnFlags, LPTSTR psz, UINT cch)
|
|
{
|
|
LPITEMIDLIST pidlFolder;
|
|
HRESULT hr = GetParentsPIDL(pidl, &pidlFolder);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHGetNameAndFlags(pidlFolder, gdnFlags, psz, cch, NULL);
|
|
ILFree(pidlFolder);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
|
|
{
|
|
HRESULT hr;
|
|
if (IsEqualSCID(*pscid, SCID_RANK))
|
|
{
|
|
hr = InitVariantFromUINT(pv, _Rank(pidl));
|
|
}
|
|
else
|
|
{
|
|
IShellFolder2 *psf;
|
|
hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellFolder2, &psf));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psf->GetDetailsEx(pidl, pscid, pv);
|
|
psf->Release();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (IsEqualSCID(*pscid, SCID_DIRECTORY))
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
hr = _GetFolderName(pidl, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szTemp, ARRAYSIZE(szTemp));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InitVariantFromStr(pv, szTemp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Figure out what the correct column index is to match the scid we are given
|
|
// where the returned index is relative to the folder passed in.
|
|
int MapSCIDToColumnForFolder(IShellFolder2 *psf, SHCOLUMNID scidIn)
|
|
{
|
|
SHCOLUMNID scidNew;
|
|
for (UINT i = 0; SUCCEEDED(psf->MapColumnToSCID(i, &scidNew)); i++)
|
|
{
|
|
if (IsEqualSCID(scidNew, scidIn))
|
|
{
|
|
return i; // found
|
|
}
|
|
}
|
|
return -1; // not found
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
|
|
{
|
|
HRESULT hr;
|
|
if (_MapColIndex(&iColumn))
|
|
{
|
|
if (pidl)
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
szTemp[0] = 0;
|
|
if (IDFCOL_PATH == iColumn)
|
|
{
|
|
_GetFolderName(pidl, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szTemp, ARRAYSIZE(szTemp));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(IDFCOL_RANK == iColumn);
|
|
ULONG uRank = _Rank(pidl);
|
|
if (uRank)
|
|
AddCommas(uRank, szTemp, ARRAYSIZE(szTemp));
|
|
}
|
|
hr = StringToStrRet(szTemp, &pdi->str);
|
|
}
|
|
else
|
|
{
|
|
hr = GetDetailsOfInfo(c_find_cols, ARRAYSIZE(c_find_cols), iColumn, pdi);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pidl)
|
|
{
|
|
IShellFolder2 *psf;
|
|
hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellFolder2, &psf));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// We cannot simply ask for GetDetailsOf because some folders map different
|
|
// column numbers to differnt values.
|
|
// Translate the column index to the SHCOLUMNID relative to this folder.
|
|
SHCOLUMNID colId;
|
|
hr = _GetDetailsFolder();
|
|
if (SUCCEEDED(hr))
|
|
hr = _psfDetails->MapColumnToSCID(iColumn, &colId);
|
|
|
|
// Use the SCID to get the correct column index...
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Get the column index for the SCID with respect to the other folder
|
|
int newIndex = MapSCIDToColumnForFolder(psf, colId);
|
|
if (newIndex != -1)
|
|
{
|
|
// Found the correct column index, so use it to get the data
|
|
hr = psf->GetDetailsOf(pidl, newIndex, pdi);
|
|
}
|
|
else
|
|
{
|
|
// Failed to find the correct column index.
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
psf->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = _GetDetailsFolder();
|
|
if (SUCCEEDED(hr))
|
|
hr = _psfDetails->GetDetailsOf(NULL, iColumn, pdi);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
|
|
{
|
|
HRESULT hr;
|
|
if (_MapColIndex(&iColumn))
|
|
{
|
|
hr = MapColumnToSCIDImpl(c_find_cols, ARRAYSIZE(c_find_cols), iColumn, pscid);
|
|
}
|
|
else
|
|
{
|
|
hr = _GetDetailsFolder();
|
|
if (SUCCEEDED(hr))
|
|
hr = _psfDetails->MapColumnToSCID(iColumn, pscid);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetFindFilter(IFindFilter **ppfilter)
|
|
{
|
|
return _pfilter->QueryInterface(IID_PPV_ARG(IFindFilter, ppfilter));
|
|
}
|
|
|
|
// IShellIcon::GetIconOf
|
|
STDMETHODIMP CFindFolder::GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex)
|
|
{
|
|
IShellIcon * psiItem;
|
|
HRESULT hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellIcon, &psiItem));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psiItem->GetIconOf(pidl, flags, piIndex);
|
|
psiItem->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// IShellIconOverlay
|
|
STDMETHODIMP CFindFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex)
|
|
{
|
|
IShellIconOverlay * psioItem;
|
|
HRESULT hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellIconOverlay, &psioItem));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psioItem->GetOverlayIndex(pidl, pIndex);
|
|
psioItem->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolder::RestoreSearchFromSaveFile(LPCITEMIDLIST pidlSaveFile, IShellFolderView *psfv)
|
|
{
|
|
// See if we can restore most of the search from here...
|
|
IStream *pstm;
|
|
HRESULT hr = StgBindToObject(pidlSaveFile, STGM_READ | STGM_SHARE_DENY_WRITE, IID_PPV_ARG(IStream, &pstm));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ULONG cbRead;
|
|
DFHEADER dfh;
|
|
|
|
// 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 (SUCCEEDED(pstm->Read(&dfh, sizeof(dfh), &cbRead)) &&
|
|
(sizeof(dfh) == cbRead) && (DOCFIND_SIG == dfh.wSig))
|
|
{
|
|
DFC_UNICODE_DESC desc;
|
|
LARGE_INTEGER dlibMove = {0, 0};
|
|
WORD fCharType = 0;
|
|
|
|
// Check the stream's signature to see if it was generated by Win95 or NT.
|
|
dlibMove.QuadPart = -(LONGLONG)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);
|
|
_pfilter->RestoreCriteria(pstm, dfh.cCriteria, fCharType);
|
|
|
|
// 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);
|
|
int cItems = 0;
|
|
RestoreItemList(pstm, &cItems);
|
|
if (cItems > 0)
|
|
psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
|
|
}
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
pstm->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// a form of this code is duplicated in browseui searchext.cpp
|
|
//
|
|
BOOL RealFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile)
|
|
{
|
|
// First create the top level browser...
|
|
IWebBrowser2 *pwb2;
|
|
HRESULT hr = CoCreateInstance(CLSID_ShellBrowserWindow, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pwb2));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT varClsid;
|
|
hr = InitBSTRVariantFromGUID(&varClsid, CLSID_FileSearchBand);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT varEmpty = {0};
|
|
|
|
// show a search bar
|
|
hr = pwb2->ShowBrowserBar(&varClsid, &varEmpty, &varEmpty);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Grab the band's IUnknown from browser property.
|
|
VARIANT varFsb;
|
|
hr = pwb2->GetProperty(varClsid.bstrVal, &varFsb);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// 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(QueryInterfaceVariant(varFsb, IID_PPV_ARG(IFileSearchBand, &pfsb))))
|
|
{
|
|
BSTR bstrSearch;
|
|
hr = BSTRFromCLSID(SRCID_SFileSearch, &bstrSearch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT varQueryFile = {0}, varScope = {0};
|
|
|
|
// 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);
|
|
|
|
VariantClear(&varScope);
|
|
VariantClear(&varQueryFile);
|
|
|
|
SysFreeString(bstrSearch);
|
|
}
|
|
pfsb->Release();
|
|
}
|
|
VariantClear(&varFsb);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = pwb2->put_Visible(TRUE);
|
|
}
|
|
VariantClear(&varClsid); // frees bstrFileSearchBand too
|
|
}
|
|
pwb2->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolder::OpenContainingFolder(IUnknown *punkSite)
|
|
{
|
|
IFolderView *pfv;
|
|
HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IEnumIDList *penum;
|
|
hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IEnumIDList, &penum));
|
|
if (S_OK == hr)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
ULONG c;
|
|
while (S_OK == penum->Next(1, &pidl, &c))
|
|
{
|
|
// Now get the parent of it.
|
|
LPITEMIDLIST pidlParent;
|
|
if (SUCCEEDED(GetParentsPIDL(pidl, &pidlParent)))
|
|
{
|
|
SHOpenFolderAndSelectItems(pidlParent, 1, (LPCITEMIDLIST *)&pidl, 0);
|
|
ILFree(pidlParent);
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
penum->Release();
|
|
}
|
|
pfv->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Save away the current search to a file on the desktop.
|
|
// For now the name will be automatically generated.
|
|
//
|
|
void CFindFolder::Save(IFindFilter* pfilter, HWND hwnd, DFBSAVEINFO * pSaveInfo, IShellView* psv, IUnknown *pObject)
|
|
{
|
|
TCHAR szFilePath[MAX_PATH];
|
|
IStream * pstm;
|
|
DFHEADER dfh;
|
|
TCHAR szTemp[MAX_PATH];
|
|
SHORT cb;
|
|
LARGE_INTEGER dlibMove = {0, 0};
|
|
ULARGE_INTEGER libCurPos;
|
|
FOLDERSETTINGS fs;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// 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 (pfilter->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
|
|
{
|
|
// 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
|
|
|
|
LPTSTR pszTitle;
|
|
BOOL fNameOk = FALSE;
|
|
|
|
pfilter->GenerateTitle(&pszTitle, TRUE);
|
|
if (pszTitle)
|
|
{
|
|
// Now add on the extension.
|
|
UINT cchRemaining = MAX_PATH - lstrlen(szFilePath);
|
|
cchRemaining -= 1; // "\" between path and filename
|
|
cchRemaining -= 4; // ".fnd" = 4 characters
|
|
cchRemaining -= 3; // "###" for unique-ify-ing the name
|
|
cchRemaining -= 1; // (no idea)
|
|
|
|
hr = StringCchCopy(szTemp, cchRemaining, pszTitle);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchCat(szTemp, ARRAYSIZE(szTemp), TEXT(".fnd"));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fNameOk = TRUE;
|
|
}
|
|
}
|
|
|
|
LocalFree(pszTitle); // And free the title string.
|
|
}
|
|
|
|
if (!fNameOk)
|
|
{
|
|
szTemp[0] = 0;
|
|
}
|
|
|
|
// Now loop through and replace all of the invalid characters with _'s
|
|
// we special case a few of the characters...
|
|
for (LPTSTR 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('_');
|
|
}
|
|
}
|
|
}
|
|
|
|
TCHAR szShortName[12];
|
|
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];
|
|
OPENFILENAME ofn = { 0 };
|
|
|
|
LoadString(g_hinst, IDS_FINDFILESFILTER, szFilter, ARRAYSIZE(szFilter));
|
|
LoadString(g_hinst, IDS_FINDSAVERESULTSTITLE, szTitle, ARRAYSIZE(szTitle));
|
|
|
|
//Strip out the # and make them Nulls for SaveAs Dialog
|
|
LPTSTR psz = szFilter;
|
|
while (*psz)
|
|
{
|
|
if (*psz == TEXT('#'))
|
|
*psz = 0;
|
|
psz++;
|
|
}
|
|
|
|
StringCchCopy(szFilename, ARRAYSIZE(szFilename), PathFindFileName(szFilePath));
|
|
PathRemoveFileSpec(szFilePath);
|
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwnd;
|
|
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;
|
|
ofn.lpTemplateName = NULL;
|
|
ofn.lpfnHook= NULL;
|
|
ofn.lCustData = NULL;
|
|
|
|
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);
|
|
SHParseDisplayName(szFilename, NULL, &pSaveInfo->pidlSaveFile, 0, NULL);
|
|
|
|
// 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);
|
|
hr = pfilter->SaveCriteria(pstm, DFC_FMT_ANSI);
|
|
if (SUCCEEDED(hr))
|
|
dfh.cCriteria = GetScode(hr);
|
|
|
|
// 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;
|
|
|
|
// 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...
|
|
//
|
|
// 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.
|
|
hr = pfilter->SaveCriteria(pstm, DFC_FMT_UNICODE);
|
|
|
|
// Append the unicode criteria descriptor to the end of the file.
|
|
pstm->Write(&desc, sizeof(desc), NULL);
|
|
//
|
|
// 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(&dfh, sizeof(dfh), NULL);
|
|
pstm->Release();
|
|
|
|
SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL);
|
|
SHChangeNotify(SHCNE_FREESPACE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL);
|
|
}
|
|
|
|
// Broke out from class to share with old and new code
|
|
BOOL CFindFolder::HandleUpdateDir(LPCITEMIDLIST 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...
|
|
BOOL fCurrentItemsMayBeImpacted = FALSE;
|
|
FIND_FOLDER_ITEM *pffli;
|
|
INT cPidf;
|
|
|
|
// First see which directories are effected...
|
|
GetFolderListItemCount(&cPidf);
|
|
for (int iPidf = 0; iPidf < cPidf; iPidf++)
|
|
{
|
|
if (SUCCEEDED(GetFolderListItem(iPidf, &pffli))
|
|
&& !pffli->fUpdateDir) // We may have already impacted these...
|
|
{
|
|
pffli->fUpdateDir = ILIsParent(pidl, &pffli->idl, FALSE);
|
|
fCurrentItemsMayBeImpacted |= pffli->fUpdateDir;
|
|
}
|
|
}
|
|
|
|
if (fCurrentItemsMayBeImpacted)
|
|
{
|
|
// Now we need to walk through the whole list and remove any entries
|
|
// that are no longer there...
|
|
//
|
|
int iItem;
|
|
if (SUCCEEDED(GetItemCount(&iItem)))
|
|
{
|
|
for (--iItem; iItem >= 0; iItem--)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
if (SUCCEEDED(GetItem(iItem, &pesfi)) && pesfi)
|
|
{
|
|
UINT iFolder = GetFolderIndex(&pesfi->idl);
|
|
|
|
// See if item may be impacted...
|
|
if (SUCCEEDED(GetFolderListItem(iFolder, &pffli)) && pffli->fUpdateDir)
|
|
pesfi->dwState |= CDFITEM_STATE_MAYBEDELETE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return fCurrentItemsMayBeImpacted;
|
|
}
|
|
|
|
void CFindFolder::UpdateOrMaybeAddPidl(IShellFolderView *psfv, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlOld)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// First see if we should try to do an update...
|
|
if (pidlOld)
|
|
{
|
|
LPITEMIDLIST pidlT;
|
|
if (S_OK == MapToSearchIDList(pidl, TRUE, &pidlT))
|
|
{
|
|
SetItemsChangedSinceSort();
|
|
UINT iItem;
|
|
// cast needed for bad interface def
|
|
hr = psfv->UpdateObject((LPITEMIDLIST)pidlOld, (LPITEMIDLIST)pidlT, &iItem);
|
|
|
|
ILFree(pidlT); // In either case simply blow away our generated pidl...
|
|
if (SUCCEEDED(hr))
|
|
return;
|
|
}
|
|
}
|
|
|
|
IShellFolder *psf;
|
|
LPCITEMIDLIST pidlChild;
|
|
if (SUCCEEDED(SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
|
|
{
|
|
BOOL fMatch = FALSE;
|
|
// See if this item matches the filter...
|
|
IFindFilter *pfilter;
|
|
if (SUCCEEDED(GetFindFilter(&pfilter)))
|
|
{
|
|
fMatch = pfilter->MatchFilter(psf, pidlChild) != 0;
|
|
pfilter->Release();
|
|
}
|
|
|
|
psf->Release();
|
|
|
|
if (fMatch)
|
|
{
|
|
LPITEMIDLIST pidlT;
|
|
if (S_OK != MapToSearchIDList(pidl, TRUE, &pidlT))
|
|
{
|
|
fMatch = FALSE;
|
|
|
|
// The folder has not been added before now...
|
|
TCHAR szPath[MAX_PATH];
|
|
SHGetPathFromIDList(pidl, szPath);
|
|
if (!IsFileInBitBucket(szPath))
|
|
{
|
|
PathRemoveFileSpec(szPath);
|
|
|
|
LPITEMIDLIST pidlFolder;
|
|
if (SUCCEEDED(SHParseDisplayName(szPath, NULL, &pidlFolder, 0, NULL)))
|
|
{
|
|
int iFolder;
|
|
hr = AddFolder(pidlFolder, TRUE, &iFolder);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fMatch = (S_OK == MapToSearchIDList(pidl, TRUE, &pidlT));
|
|
}
|
|
ILFree(pidlFolder);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fMatch)
|
|
{
|
|
// There are times we get notified twice. To handle this
|
|
// see if the item is already in our list. If so punt...
|
|
|
|
SetItemsChangedSinceSort();
|
|
|
|
UINT iItem;
|
|
if (FAILED(psfv->UpdateObject(pidlT, pidlT, &iItem)))
|
|
{
|
|
// item not in the view yet... so we need to add it
|
|
|
|
if (SUCCEEDED(GetItemCount((INT *)&iItem)))
|
|
{
|
|
// Normal case would be here to add the object
|
|
// We need to add this to our dpa and dsa...
|
|
FIND_ITEM *pesfi;
|
|
AddPidl(iItem, pidlT, (UINT)-1, &pesfi);
|
|
if (pesfi)
|
|
psfv->SetObjectCount(++iItem, SFVSOC_NOSCROLL);
|
|
}
|
|
}
|
|
ILFree(pidlT);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(NULL == pidlT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CFindFolder::HandleRMDir(IShellFolderView *psfv, LPCITEMIDLIST pidl)
|
|
{
|
|
BOOL fCurrentItemsMayBeImpacted = FALSE;
|
|
FIND_FOLDER_ITEM *pffli;
|
|
INT cItems;
|
|
FIND_ITEM *pesfi;
|
|
|
|
// First see which directories are effected...
|
|
GetFolderListItemCount(&cItems);
|
|
for (int iItem = 0; iItem < cItems; iItem++)
|
|
{
|
|
if (SUCCEEDED(GetFolderListItem(iItem, &pffli)))
|
|
{
|
|
pffli->fDeleteDir = ILIsParent(pidl, &pffli->idl, FALSE);
|
|
fCurrentItemsMayBeImpacted |= pffli->fDeleteDir;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
INT cItem;
|
|
GetFolderListItemCount(&cItem);
|
|
TraceMsg(TF_WARNING, "NULL pffli 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(GetItemCount(&iItem)))
|
|
{
|
|
for (--iItem; iItem >= 0; iItem--)
|
|
{
|
|
if (FAILED(GetItem(iItem, &pesfi)) || pesfi == NULL)
|
|
continue;
|
|
|
|
// See if item may be impacted...
|
|
UINT iFolder = GetFolderIndex(&pesfi->idl);
|
|
if (SUCCEEDED(GetFolderListItem(iFolder, &pffli))
|
|
&& pffli->fDeleteDir)
|
|
{
|
|
psfv->RemoveObject(&pesfi->idl, (UINT*)&cItems);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// export used for Start.Search-> cascade menu
|
|
|
|
STDAPI_(IContextMenu *) SHFind_InitMenuPopup(HMENU hmenu, HWND hwnd, UINT idCmdFirst, UINT idCmdLast)
|
|
{
|
|
IContextMenu * pcm = NULL;
|
|
HKEY hkFind = SHGetShellKey(SHELLKEY_HKLM_EXPLORER, TEXT("FindExtensions"), FALSE);
|
|
if (hkFind)
|
|
{
|
|
if (SUCCEEDED(CDefFolderMenu_CreateHKeyMenu(hwnd, 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;
|
|
}
|
|
|
|
|
|
void _SetObjectCount(IShellView *psv, int cItems, DWORD dwFlags)
|
|
{
|
|
IShellFolderView *psfv;
|
|
if (SUCCEEDED(psv->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
|
|
{
|
|
psfv->SetObjectCount(cItems, dwFlags);
|
|
psfv->Release();
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
PFNLVCOMPARE pfnCompare;
|
|
LPARAM lParamSort;
|
|
} FIND_SORT_INFO;
|
|
|
|
int CALLBACK _FindCompareItems(void *p1, void *p2, LPARAM lParam)
|
|
{
|
|
FIND_SORT_INFO *pfsi = (FIND_SORT_INFO*)lParam;
|
|
return pfsi->pfnCompare(PtrToInt(p1), PtrToInt(p2), pfsi->lParamSort);
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnSortListData(DWORD pv, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
|
|
{
|
|
EnterCriticalSection(&_pff->_csSearch);
|
|
|
|
// First mark the focused item in the list so we can find it later...
|
|
FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, _iFocused); // indirect
|
|
if (pesfi)
|
|
pesfi->dwState |= LVIS_FOCUSED;
|
|
|
|
int cItems = DPA_GetPtrCount(_pff->_hdpaItems);
|
|
HDPA hdpaForSorting = NULL;
|
|
if (cItems)
|
|
{
|
|
hdpaForSorting = DPA_Create(cItems);
|
|
}
|
|
|
|
if (hdpaForSorting)
|
|
{
|
|
for (int i = 0; i< cItems; i++)
|
|
{
|
|
DPA_SetPtr(hdpaForSorting, i, IntToPtr(i));
|
|
}
|
|
// sort out items
|
|
FIND_SORT_INFO fsi;
|
|
fsi.pfnCompare = pfnCompare;
|
|
fsi.lParamSort = lParamSort;
|
|
|
|
DPA_Sort(hdpaForSorting, _FindCompareItems, (LPARAM)&fsi);
|
|
for (i = 0; i < cItems; i++)
|
|
{
|
|
int iIndex = PtrToInt(DPA_FastGetPtr(hdpaForSorting, i));
|
|
|
|
// Move the items from _hdpaItems to hdpaForSorting in sorted order
|
|
DPA_SetPtr(hdpaForSorting, i, DPA_FastGetPtr(_pff->_hdpaItems, iIndex));
|
|
}
|
|
// Now switch the two HDPA to get the sorted list in the member variable
|
|
DPA_Destroy(_pff->_hdpaItems);
|
|
_pff->_hdpaItems = hdpaForSorting;
|
|
}
|
|
|
|
// Now find the focused item and scroll it into place...
|
|
IShellView *psv;
|
|
if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
|
|
{
|
|
int iFocused = -1;
|
|
|
|
// Tell the view we need to reshuffle....
|
|
// Gross, this one defaults to invalidate all which for this one is fine...
|
|
_SetObjectCount(psv, cItems, SFVSOC_INVALIDATE_ALL); // Invalidate all
|
|
|
|
for (int iEnd = cItems - 1; iEnd >= 0; iEnd--)
|
|
{
|
|
pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iEnd); // indirect
|
|
if (pesfi && pesfi->dwState & LVIS_FOCUSED)
|
|
iFocused = iEnd;
|
|
}
|
|
// Now handle the focused item...
|
|
if (iFocused != -1)
|
|
{
|
|
_pff->_iGetIDList = iFocused; // remember the last one we retrieved...
|
|
pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iFocused); // indirect
|
|
if (pesfi)
|
|
{
|
|
// flags depend on first one and also if selected?
|
|
psv->SelectItem(&pesfi->idl, SVSI_FOCUSED | SVSI_ENSUREVISIBLE | SVSI_SELECT);
|
|
pesfi->dwState &= ~LVIS_FOCUSED; // don't keep it around to get lost later...
|
|
}
|
|
}
|
|
|
|
_iFocused = iFocused;
|
|
_fIgnoreSelChange = FALSE;
|
|
psv->Release();
|
|
}
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnMergeMenu(DWORD pv, QCMINFO*lP)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - DF_FSNCallBack DVN_MERGEMENU"));
|
|
|
|
UINT idCmdFirst = lP->idCmdFirst;
|
|
|
|
UINT idBGMain = 0, idBGPopup = 0;
|
|
_pff->_pfilter->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup);
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, idBGPopup, lP);
|
|
|
|
// Lets remove some menu items that are not useful to us.
|
|
HMENU hmenu = lP->hmenu;
|
|
DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTE, MF_BYCOMMAND);
|
|
DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND);
|
|
// DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTESPECIAL, MF_BYCOMMAND);
|
|
|
|
// This is sortof bogus but if after the merge one of the
|
|
// menus has no items in it, remove the menu.
|
|
|
|
for (int i = GetMenuItemCount(hmenu) - 1; i >= 0; i--)
|
|
{
|
|
HMENU hmenuSub = GetSubMenu(hmenu, i);
|
|
|
|
if (hmenuSub && (GetMenuItemCount(hmenuSub) == 0))
|
|
{
|
|
DeleteMenu(hmenu, i, MF_BYPOSITION);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGETWORKINGDIR(DWORD pv, UINT wP, LPTSTR lP)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IShellFolderView *psfv;
|
|
if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
|
|
{
|
|
LPCITEMIDLIST *ppidls; // pointer to a list of pidls.
|
|
UINT cpidls = 0; // Count of pidls that were returned.
|
|
|
|
psfv->GetSelectedObjects(&ppidls, &cpidls);
|
|
|
|
if (cpidls > 0)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
if (SUCCEEDED(_pff->GetParentsPIDL(ppidls[0], &pidl)))
|
|
{
|
|
SHGetPathFromIDList(pidl, lP);
|
|
ILFree(pidl);
|
|
}
|
|
LocalFree((void *)ppidls); // const -> non const
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
psfv->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream **ppstm)
|
|
{
|
|
return _pff->_pfilter->GetColSaveStream(wP, ppstm);
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST *ppidl)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
|
|
if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
*ppidl = &pesfi->idl; // return alias!
|
|
return S_OK;
|
|
}
|
|
|
|
*ppidl = NULL;
|
|
return E_FAIL;
|
|
}
|
|
|
|
// in defviewx.c
|
|
STDAPI SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage);
|
|
|
|
HRESULT CFindFolderViewCB::OnGetItemIconIndex(DWORD pv, WPARAM iItem, int *piIcon)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
|
|
*piIcon = -1;
|
|
|
|
if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
if (pesfi->iIcon == -1)
|
|
{
|
|
IShellFolder* psf = (IShellFolder*)_pff;
|
|
SHGetIconFromPIDL(psf, NULL, &pesfi->idl, 0, &pesfi->iIcon);
|
|
}
|
|
|
|
*piIcon = pesfi->iIcon;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
HRESULT CFindFolderViewCB::OnSetItemIconOverlay(DWORD pv, WPARAM iItem, int iOverlayIndex)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
FIND_ITEM *pesfi;
|
|
if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
pesfi->dwMask |= ESFITEM_ICONOVERLAYSET;
|
|
pesfi->dwState |= INDEXTOOVERLAYMASK(iOverlayIndex) & LVIS_OVERLAYMASK;
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetItemIconOverlay(DWORD pv, WPARAM iItem, int * piOverlayIndex)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
*piOverlayIndex = SFV_ICONOVERLAY_DEFAULT;
|
|
FIND_ITEM *pesfi;
|
|
if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
if (pesfi->dwMask & ESFITEM_ICONOVERLAYSET)
|
|
{
|
|
*piOverlayIndex = OVERLAYMASKTO1BASEDINDEX(pesfi->dwState & LVIS_OVERLAYMASK);
|
|
}
|
|
else
|
|
*piOverlayIndex = SFV_ICONOVERLAY_UNSET;
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CFindFolderViewCB::OnSETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST pidl)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
|
|
_pff->_iGetIDList = (int) iItem; // remember the last one we retrieved...
|
|
|
|
if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
FIND_ITEM *pesfiNew;
|
|
|
|
if (SUCCEEDED(_pff->AddPidl((int) iItem, pidl, 0, &pesfiNew) && pesfiNew))
|
|
{
|
|
pesfiNew->dwState = pesfi->dwState;
|
|
LocalFree((HLOCAL)pesfi); // Free the old one...
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
BOOL DF_ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
BOOL bRet = (pidl1 == pidl2);
|
|
|
|
if (!bRet)
|
|
{
|
|
PCHIDDENDOCFINDDATA phdfd1 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl1, IDLHID_DOCFINDDATA);
|
|
PCHIDDENDOCFINDDATA phdfd2 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl2, IDLHID_DOCFINDDATA);
|
|
|
|
if (phdfd1 && phdfd2)
|
|
bRet = (phdfd1->iFolder == phdfd2->iFolder) && ILIsEqual(pidl1, pidl2);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetIndexForItemIDList(DWORD pv, int * piItem, LPITEMIDLIST pidl)
|
|
{
|
|
int cItems;
|
|
|
|
// Try to short circuit searching for pidls...
|
|
if (SUCCEEDED(_pff->GetItemCount(&cItems)) && _pff->_iGetIDList < cItems)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
|
|
if (SUCCEEDED(_pff->GetItem(_pff->_iGetIDList, &pesfi)) && pesfi)
|
|
{
|
|
if (DF_ILIsEqual(&pesfi->idl, pidl))
|
|
{
|
|
// Yep it was ours so return the index quickly..
|
|
*piItem = _pff->_iGetIDList;
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise let it search the old fashion way...
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnDeleteItem(DWORD pv, LPCITEMIDLIST pidl)
|
|
{
|
|
// We simply need to remove this item from our list. The
|
|
// underlying listview will decrement the count on their end...
|
|
FIND_ITEM *pesfi;
|
|
int iItem;
|
|
int cItems;
|
|
BOOL bFound;
|
|
|
|
if (!pidl)
|
|
{
|
|
_pff->SetAsyncEnum(NULL);
|
|
return S_OK; // special case telling us all items deleted...
|
|
}
|
|
|
|
bFound = FALSE;
|
|
|
|
if (SUCCEEDED(_pff->GetItem(_pff->_iGetIDList, &pesfi))
|
|
&& pesfi
|
|
&& (DF_ILIsEqual(&pesfi->idl, pidl)))
|
|
{
|
|
iItem = _pff->_iGetIDList;
|
|
bFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (SUCCEEDED(_pff->GetItemCount(&cItems)))
|
|
{
|
|
for (iItem = 0; iItem < cItems; iItem++)
|
|
{
|
|
if (SUCCEEDED(_pff->GetItem(iItem, &pesfi)) && pesfi && (DF_ILIsEqual(&pesfi->idl, pidl)))
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
_pff->DeleteItem(iItem);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnODFindItem(DWORD pv, int * piItem, NM_FINDITEM* pnmfi)
|
|
{
|
|
// We have to do the subsearch ourself to find the correct item...
|
|
// As the listview has no information saved in it...
|
|
|
|
int iItem = pnmfi->iStart;
|
|
int cItem;
|
|
UINT flags = pnmfi->lvfi.flags;
|
|
|
|
if (FAILED(_pff->GetItemCount(&cItem)))
|
|
return E_FAIL;
|
|
|
|
if ((flags & LVFI_STRING) == 0)
|
|
return E_FAIL; // Not sure what type of search this is...
|
|
|
|
int cbString = lstrlen(pnmfi->lvfi.psz);
|
|
|
|
for (int j = cItem; j-- != 0;)
|
|
{
|
|
if (iItem >= cItem)
|
|
{
|
|
if (flags & LVFI_WRAP)
|
|
iItem = 0;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Now we need to get the Display name for this item...
|
|
FIND_ITEM *pesfi;
|
|
TCHAR szPath[MAX_PATH];
|
|
IShellFolder* psf = (IShellFolder*)_pff;
|
|
|
|
if (SUCCEEDED(_pff->GetItem(iItem, &pesfi)) && pesfi &&
|
|
SUCCEEDED(DisplayNameOf(psf, &pesfi->idl, NULL, szPath, ARRAYSIZE(szPath))))
|
|
{
|
|
if (flags & (LVFI_PARTIAL|LVFI_SUBSTRING))
|
|
{
|
|
if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
|
|
pnmfi->lvfi.psz, cbString, szPath, cbString) == 2)
|
|
{
|
|
*piItem = iItem;
|
|
return S_OK;
|
|
}
|
|
}
|
|
else if (lstrcmpi(pnmfi->lvfi.psz, szPath) == 0)
|
|
{
|
|
*piItem = iItem;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
++iItem;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA *lP)
|
|
{
|
|
// Try to remember which item is focused...
|
|
if (lP->uNewState & LVIS_FOCUSED)
|
|
_iFocused = wPh;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnSetEmptyText(DWORD pv, UINT res, LPCTSTR pszText)
|
|
{
|
|
if (pszText && 0 == lstrcmp(_szEmptyText, pszText))
|
|
return S_OK;
|
|
|
|
StringCchCopy(_szEmptyText, ARRAYSIZE(_szEmptyText), pszText ? pszText : TEXT("")); // ok to truncate
|
|
|
|
HWND hwndLV = ListviewFromViewUnk(_punkSite);
|
|
if (hwndLV)
|
|
SendMessage(hwndLV, LVM_RESETEMPTYTEXT, 0, 0);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetEmptyText(DWORD pv, UINT cchTextMax, LPTSTR pszText)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (_szEmptyText[0])
|
|
{
|
|
hr = StringCchCopy(pszText, cchTextMax, _szEmptyText);
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_FINDVIEWEMPTYINIT, pszText, cchTextMax);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnReArrange(DWORD pv, LPARAM lparam)
|
|
{
|
|
UINT nCol = (UINT)lparam;
|
|
|
|
// See if there is any controller object registered that may want to take over this...
|
|
// if we are in a mixed query and we have already fetched the async items, simply sort
|
|
// the dpa's...
|
|
IFindEnum *pidfenum;
|
|
if (S_OK == _pff->GetAsyncEnum(&pidfenum))
|
|
{
|
|
if (!((pidfenum->FQueryIsAsync() == DF_QUERYISMIXED) && _pff->_fAllAsyncItemsCached))
|
|
{
|
|
if (_pff->_pfcn)
|
|
{
|
|
// if they return S_FALSE it implies that they handled it and they do not
|
|
// want the default processing to happen...
|
|
if (_pff->_pfcn->DoSortOnColumn(nCol, _iColSort == nCol) == S_FALSE)
|
|
{
|
|
_iColSort = nCol;
|
|
return S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we are running in the ROWSET way, we may want to have the ROWSET do the work...
|
|
// pass one we spawn off a new search with the right column sorted
|
|
if (_iColSort != nCol)
|
|
{
|
|
_iColSort = nCol;
|
|
}
|
|
|
|
// Warning the call above may release our AsyncEnum and generate a new one so
|
|
// Don't rely on it's existence here...
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// we must pull in all the results from ci
|
|
if (pidfenum->FQueryIsAsync() && !_pff->_fAllAsyncItemsCached)
|
|
_pff->CacheAllAsyncItems();
|
|
|
|
#ifdef DEBUG
|
|
#define MAX_LISTVIEWITEMS (100000000 & ~0xFFFF)
|
|
#define SANE_ITEMCOUNT(c) ((int)min(c, MAX_LISTVIEWITEMS))
|
|
if (pidfenum->FQueryIsAsync())
|
|
{
|
|
ASSERT(DPA_GetPtrCount(_pff->_hdpaItems) >= SANE_ITEMCOUNT(_pff->_cAsyncItems));
|
|
for (int i = 0; i < SANE_ITEMCOUNT(_pff->_cAsyncItems); i++)
|
|
{
|
|
FIND_ITEM *pesfi = (FIND_ITEM *)DPA_GetPtr(_pff->_hdpaItems, i);
|
|
|
|
ASSERT(pesfi);
|
|
if (!pesfi)
|
|
{
|
|
ASSERT(SUCCEEDED(_pff->GetItem(i, &pesfi)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Use the common sort.
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnWindowCreated(DWORD pv, HWND hwnd)
|
|
{
|
|
_ProfferService(TRUE); // register our service w/ top level container
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::_ProfferService(BOOL bProffer)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (bProffer)
|
|
{
|
|
// shouldn't be redundantly registering our service
|
|
ASSERT(NULL == _pps);
|
|
ASSERT(-1 == _dwServiceCookie);
|
|
|
|
IProfferService* pps;
|
|
hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IProfferService, &pps));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pps->ProfferService(SID_DocFindFolder, this, &_dwServiceCookie);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pps->AddRef();
|
|
_pps = pps;
|
|
}
|
|
pps->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (NULL == _pps)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = _pps->RevokeService(_dwServiceCookie);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ATOMICRELEASE(_pps);
|
|
_dwServiceCookie = -1;
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnWindowDestroy(DWORD pv, HWND wP)
|
|
{
|
|
_ProfferService(FALSE); // unregister our service w/ top level container
|
|
|
|
if (_pff->_pfcn)
|
|
_pff->_pfcn->StopSearch();
|
|
|
|
// The search may have a circular set of pointers. So call the
|
|
// delete items and folders here to remove these back references...
|
|
_pff->ClearItemList();
|
|
_pff->ClearFolderList();
|
|
|
|
IFindControllerNotify *pfcn;
|
|
if (_pff->GetControllerNotifyObject(&pfcn) == S_OK)
|
|
{
|
|
pfcn->ViewDestroyed();
|
|
pfcn->Release();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnIsOwnerData(DWORD pv, DWORD *pdwFlags)
|
|
{
|
|
*pdwFlags |= FWF_OWNERDATA; // we want virtual defview support
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetODRangeObject(DWORD pv, WPARAM wWhich, ILVRange **plvr)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
switch (wWhich)
|
|
{
|
|
case LVSR_SELECTION:
|
|
hr = _pff->_dflvrSel.QueryInterface(IID_PPV_ARG(ILVRange, plvr));
|
|
break;
|
|
case LVSR_CUT:
|
|
hr = _pff->_dflvrCut.QueryInterface(IID_PPV_ARG(ILVRange, plvr));
|
|
break;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnODCacheHint(DWORD pv, NMLVCACHEHINT* pnmlvc)
|
|
{
|
|
// The listview is giving us a hint of the items it is about to do something in a range
|
|
// so make sure we have pidls for each of the items in the range...
|
|
int iTo;
|
|
|
|
_pff->GetItemCount(&iTo);
|
|
if (iTo >= pnmlvc->iTo)
|
|
iTo = pnmlvc->iTo;
|
|
else
|
|
iTo--;
|
|
|
|
for (int i = pnmlvc->iFrom; i <= iTo; i++)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
if (FAILED(_pff->GetItem(i, &pesfi)))
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP)
|
|
{
|
|
*lP = FVM_DETAILS; // match the advanced mode of SC (+ Win2K parity)
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
|
|
{
|
|
ZeroMemory(pData, sizeof(*pData));
|
|
pData->dwLayout = SFVMWVL_DETAILS | SFVMWVL_FILES;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::_OnOpenContainingFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
CFindFolderViewCB* pThis = (CFindFolderViewCB*)(void*)pv;
|
|
return pThis->_pff->OpenContainingFolder(pThis->_punkSite);
|
|
}
|
|
|
|
const WVTASKITEM c_FindTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_SEARCH, IDS_HEADER_FIND_TT);
|
|
const WVTASKITEM c_FindTaskList[] =
|
|
{
|
|
WVTI_ENTRY_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_OPENCONTAININGFOLDER, IDS_TASK_OPENCONTAININGFOLDER, 0, IDS_TASK_OPENCONTAININGFOLDER_TT, IDI_TASK_OPENCONTAININGFOLDER, NULL, CFindFolderViewCB::_OnOpenContainingFolder),
|
|
};
|
|
|
|
HRESULT CFindFolderViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
|
|
{
|
|
ZeroMemory(pData, sizeof(*pData));
|
|
|
|
Create_IUIElement(&c_FindTaskHeader, &(pData->pSpecialTaskHeader));
|
|
|
|
LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_NETWORK) };
|
|
CreateIEnumIDListOnCSIDLs(_pidl, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &pData->penumOtherPlaces);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
|
|
{
|
|
ZeroMemory(pTasks, sizeof(*pTasks));
|
|
|
|
Create_IEnumUICommand((IUnknown*)(void*)this, c_FindTaskList, ARRAYSIZE(c_FindTaskList), &pTasks->penumSpecialTasks);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme)
|
|
{
|
|
ZeroMemory(pTheme, sizeof(*pTheme));
|
|
|
|
pTheme->pszThemeID = L"search";
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetIPersistHistory(DWORD pv, IPersistHistory **ppph)
|
|
{
|
|
// If they call us with ppph == NULL they simply want to know if we support
|
|
// the history so return S_OK;
|
|
if (ppph == NULL)
|
|
return S_OK;
|
|
|
|
// get the persist history from us and we hold folder and view objects
|
|
*ppph = NULL;
|
|
|
|
CFindPersistHistory *pdfph = new CFindPersistHistory();
|
|
if (!pdfph)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = pdfph->QueryInterface(IID_PPV_ARG(IPersistHistory, ppph));
|
|
pdfph->Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnRefresh(DWORD pv, BOOL fPreRefresh)
|
|
{
|
|
EnterCriticalSection(&_pff->_csSearch);
|
|
|
|
_pff->_fInRefresh = BOOLIFY(fPreRefresh);
|
|
// If we have old results tell defview the new count now...
|
|
if (!fPreRefresh && _pff->_hdpaItems)
|
|
{
|
|
IShellFolderView *psfv;
|
|
UINT cItems = DPA_GetPtrCount(_pff->_hdpaItems);
|
|
if (cItems && _punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
|
|
{
|
|
psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
|
|
psfv->Release();
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CFindFolderViewCB::OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *phtd)
|
|
{
|
|
if (IsOS(OS_ANYSERVER))
|
|
{
|
|
StringCchCopy(phtd->wszHelpFile, ARRAYSIZE(phtd->wszHelpFile), L"find.chm"); // ok to truncate
|
|
}
|
|
else
|
|
{
|
|
StringCchCopy(phtd->wszHelpTopic, ARRAYSIZE(phtd->wszHelpTopic), L"hcp://services/subsite?node=Unmapped/Search"); // ok to truncate
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
|
|
HANDLE_MSG(0, SFVM_GETWORKINGDIR, OnGETWORKINGDIR);
|
|
HANDLE_MSG(0, SFVM_GETCOLSAVESTREAM, OnGETCOLSAVESTREAM);
|
|
HANDLE_MSG(0, SFVM_GETITEMIDLIST, OnGETITEMIDLIST);
|
|
HANDLE_MSG(0, SFVM_SETITEMIDLIST, OnSETITEMIDLIST);
|
|
HANDLE_MSG(0, SFVM_SELCHANGE, OnSelChange);
|
|
HANDLE_MSG(0, SFVM_INDEXOFITEMIDLIST, OnGetIndexForItemIDList);
|
|
HANDLE_MSG(0, SFVM_DELETEITEM, OnDeleteItem);
|
|
HANDLE_MSG(0, SFVM_ODFINDITEM, OnODFindItem);
|
|
HANDLE_MSG(0, SFVM_ARRANGE, OnReArrange);
|
|
HANDLE_MSG(0, SFVM_GETEMPTYTEXT, OnGetEmptyText);
|
|
HANDLE_MSG(0, SFVM_SETEMPTYTEXT, OnSetEmptyText);
|
|
HANDLE_MSG(0, SFVM_GETITEMICONINDEX, OnGetItemIconIndex);
|
|
HANDLE_MSG(0, SFVM_SETICONOVERLAY, OnSetItemIconOverlay);
|
|
HANDLE_MSG(0, SFVM_GETICONOVERLAY, OnGetItemIconOverlay);
|
|
HANDLE_MSG(0, SFVM_FOLDERSETTINGSFLAGS, OnIsOwnerData);
|
|
HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
|
|
HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWindowDestroy);
|
|
HANDLE_MSG(0, SFVM_GETODRANGEOBJECT, OnGetODRangeObject);
|
|
HANDLE_MSG(0, SFVM_ODCACHEHINT, OnODCacheHint);
|
|
HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWTHEME, OnGetWebViewTheme);
|
|
HANDLE_MSG(0, SFVM_GETIPERSISTHISTORY, OnGetIPersistHistory);
|
|
HANDLE_MSG(0, SFVM_REFRESH, OnRefresh);
|
|
HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
|
|
HANDLE_MSG(0, SFVM_SORTLISTDATA, OnSortListData);
|
|
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
CFindFolderViewCB::CFindFolderViewCB(CFindFolder* pff) :
|
|
CBaseShellFolderViewCB(pff->_pidl, 0), _pff(pff), _fIgnoreSelChange(FALSE),
|
|
_iColSort((UINT)-1), _iFocused((UINT)-1), _cSelected(0), _pps(NULL), _dwServiceCookie(-1)
|
|
{
|
|
_pff->AddRef();
|
|
}
|
|
|
|
CFindFolderViewCB::~CFindFolderViewCB()
|
|
{
|
|
_pff->Release();
|
|
ASSERT(NULL == _pps);
|
|
ASSERT(_dwServiceCookie == -1);
|
|
}
|
|
|
|
// give the find command code access to defview via this QS that we proffered
|
|
|
|
HRESULT CFindFolderViewCB::QueryService(REFGUID guidService, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
*ppv = NULL;
|
|
if (guidService == SID_DocFindFolder)
|
|
{
|
|
hr = IUnknown_QueryService(_punkSite, SID_DefView, riid, ppv);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
CFindPersistHistory::CFindPersistHistory()
|
|
{
|
|
}
|
|
|
|
STDAPI CFindPersistHistory_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
CFindPersistHistory *pdfph = new CFindPersistHistory();
|
|
if (pdfph)
|
|
{
|
|
hr = pdfph->QueryInterface(riid, ppv);
|
|
pdfph->Release();
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Functions to support persisting the document into the history stream...
|
|
STDMETHODIMP CFindPersistHistory::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_DocFindPersistHistory;
|
|
return S_OK;
|
|
}
|
|
|
|
IFindFolder *CFindPersistHistory::_GetDocFindFolder()
|
|
{
|
|
IFindFolder *pdff = NULL;
|
|
|
|
// the _punksite is to the defview so we can simply QI for frame...
|
|
IFolderView *pfv;
|
|
if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
|
|
{
|
|
pfv->GetFolder(IID_PPV_ARG(IFindFolder, &pdff));
|
|
pfv->Release();
|
|
}
|
|
|
|
return pdff;
|
|
}
|
|
|
|
STDMETHODIMP CFindPersistHistory::LoadHistory(IStream *pstm, IBindCtx *pbc)
|
|
{
|
|
int cItems = 0;
|
|
IFindFolder *pdff = _GetDocFindFolder();
|
|
if (pdff)
|
|
{
|
|
pdff->RestoreFolderList(pstm);
|
|
pdff->RestoreItemList(pstm, &cItems);
|
|
pdff->Release();
|
|
}
|
|
|
|
IShellFolderView *psfv;
|
|
if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
|
|
{
|
|
psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
|
|
psfv->Release();
|
|
}
|
|
|
|
// call our base class to allow it to restore it's stuff as well.
|
|
return CDefViewPersistHistory::LoadHistory(pstm, pbc);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CFindPersistHistory::SaveHistory(IStream *pstm)
|
|
{
|
|
IFindFolder *pdff = _GetDocFindFolder();
|
|
if (pdff)
|
|
{
|
|
pdff->SaveFolderList(pstm);
|
|
pdff->SaveItemList(pstm);
|
|
pdff->Release();
|
|
}
|
|
// Let base class save out as well
|
|
return CDefViewPersistHistory::SaveHistory(pstm);
|
|
}
|
|
|
|
// use to manage the selection states for an owner data listview...
|
|
|
|
STDMETHODIMP_(ULONG) CFindLVRange::AddRef()
|
|
{
|
|
return _pff->AddRef();
|
|
}
|
|
STDMETHODIMP_(ULONG) CFindLVRange::Release()
|
|
{
|
|
return _pff->Release();
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CFindLVRange, ILVRange), // IID_ILVRange
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
// ILVRange methods
|
|
STDMETHODIMP CFindLVRange::IncludeRange(LONG iBegin, LONG iEnd)
|
|
{
|
|
// Including the range must load the elements as we need the object ptr...
|
|
FIND_ITEM *pesfi;
|
|
int iTotal;
|
|
|
|
_pff->GetItemCount(&iTotal);
|
|
if (iEnd > iTotal)
|
|
iEnd = iTotal-1;
|
|
|
|
for (long i = iBegin; i <= iEnd;i++)
|
|
{
|
|
if (SUCCEEDED(_pff->GetItem(i, &pesfi)) && pesfi)
|
|
{
|
|
if ((pesfi->dwState & _dwMask) == 0)
|
|
{
|
|
_cIncluded++;
|
|
pesfi->dwState |= _dwMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::ExcludeRange(LONG iBegin, LONG iEnd)
|
|
{
|
|
// Excluding the range is OK to not load the elements as this would be to deslect all...
|
|
|
|
EnterCriticalSection(&_pff->_csSearch);
|
|
if (iEnd >= DPA_GetPtrCount(_pff->_hdpaItems))
|
|
iEnd = DPA_GetPtrCount(_pff->_hdpaItems) - 1;
|
|
|
|
for (long i = iBegin; i <= iEnd; i++)
|
|
{
|
|
FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_pff->_hdpaItems, i);
|
|
if (pesfi)
|
|
{
|
|
if (pesfi->dwState & _dwMask)
|
|
{
|
|
_cIncluded--;
|
|
pesfi->dwState &= ~_dwMask;
|
|
}
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::InvertRange(LONG iBegin, LONG iEnd)
|
|
{
|
|
// Including the range must load the elements as we need the object ptr...
|
|
int iTotal;
|
|
|
|
_pff->GetItemCount(&iTotal);
|
|
if (iEnd > iTotal)
|
|
iEnd = iTotal-1;
|
|
|
|
for (long i = iBegin; i <= iEnd;i++)
|
|
{
|
|
FIND_ITEM *pesfi;
|
|
if (SUCCEEDED(_pff->GetItem(i, &pesfi)) && pesfi)
|
|
{
|
|
if ((pesfi->dwState & _dwMask) == 0)
|
|
{
|
|
_cIncluded++;
|
|
pesfi->dwState |= _dwMask;
|
|
}
|
|
else
|
|
{
|
|
_cIncluded--;
|
|
pesfi->dwState &= ~_dwMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::InsertItem(LONG iItem)
|
|
{
|
|
// We already maintain the list anyway...
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::RemoveItem(LONG iItem)
|
|
{
|
|
// We maintain the list so don't do anything...
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::Clear()
|
|
{
|
|
// If there are things selected, need to unselect them now...
|
|
if (_cIncluded)
|
|
ExcludeRange(0, LONG_MAX);
|
|
|
|
_cIncluded = 0;
|
|
_pff->ClearSaveStateList();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::IsSelected(LONG iItem)
|
|
{
|
|
// Don't force the items to be generated if they were not before...
|
|
HRESULT hr = S_FALSE;
|
|
|
|
EnterCriticalSection(&_pff->_csSearch);
|
|
FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem);
|
|
if (pesfi)
|
|
hr = pesfi->dwState & _dwMask ? S_OK : S_FALSE;
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
|
|
// Assume not selected if we don't have the item yet...
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::IsEmpty()
|
|
{
|
|
return _cIncluded ? S_FALSE : S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::NextSelected(LONG iItem, LONG *piItem)
|
|
{
|
|
EnterCriticalSection(&_pff->_csSearch);
|
|
LONG cItems = DPA_GetPtrCount(_pff->_hdpaItems);
|
|
|
|
while (iItem < cItems)
|
|
{
|
|
FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem);
|
|
if (pesfi && (pesfi->dwState & _dwMask))
|
|
{
|
|
*piItem = iItem;
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
return S_OK;
|
|
}
|
|
iItem++;
|
|
}
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
*piItem = -1;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::NextUnSelected(LONG iItem, LONG *piItem)
|
|
{
|
|
EnterCriticalSection(&_pff->_csSearch);
|
|
LONG cItems = DPA_GetPtrCount(_pff->_hdpaItems);
|
|
|
|
while (iItem < cItems)
|
|
{
|
|
FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem);
|
|
if (!pesfi || ((pesfi->dwState & _dwMask) == 0))
|
|
{
|
|
*piItem = iItem;
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
return S_OK;
|
|
}
|
|
iItem++;
|
|
}
|
|
LeaveCriticalSection(&_pff->_csSearch);
|
|
*piItem = -1;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CFindLVRange::CountIncluded(LONG *pcIncluded)
|
|
{
|
|
*pcIncluded = _cIncluded;
|
|
|
|
// Sortof Gross, but if looking at selection then also include the list of items
|
|
// that are selected in our save list...
|
|
if (_dwMask & LVIS_SELECTED)
|
|
*pcIncluded += _pff->_cSaveStateSelected;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Define OleDBEnum translation structure...
|
|
typedef struct _dfodbet // DFET for short
|
|
{
|
|
struct _dfodbet *pdfetNext;
|
|
LPWSTR pwszFrom;
|
|
int cbFrom;
|
|
LPWSTR pwszTo;
|
|
} DFODBET;
|
|
|
|
|
|
class CContentIndexEnum : public IFindEnum, public IShellService
|
|
{
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IFindEnum
|
|
STDMETHODIMP Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState);
|
|
STDMETHODIMP Skip(int celt);
|
|
STDMETHODIMP Reset();
|
|
STDMETHODIMP StopSearch();
|
|
STDMETHODIMP_(BOOL) FQueryIsAsync();
|
|
STDMETHODIMP GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone);
|
|
STDMETHODIMP GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl);
|
|
STDMETHODIMP GetItemID(UINT iItem, DWORD *puWorkID);
|
|
STDMETHODIMP SortOnColumn(UINT iCOl, BOOL fAscending);
|
|
|
|
// IShellService
|
|
STDMETHODIMP SetOwner(IUnknown* punkOwner);
|
|
|
|
CContentIndexEnum(IFindFilter *pfilter, IFindFolder *pff, DWORD grfFlags,
|
|
int iColSort, LPTSTR pszProgressText, IRowsetWatchNotify *prwn);
|
|
|
|
HRESULT DoQuery(LPWSTR *apwszPaths, UINT *pcPaths);
|
|
|
|
private:
|
|
~CContentIndexEnum();
|
|
|
|
HRESULT _BuildAndSetCommandTree(int iCol, BOOL fReverse);
|
|
HRESULT _SetCmdProp(ICommand *pCommand);
|
|
HRESULT _MapColumns(IUnknown *punk, DBORDINAL cCols, DBBINDING *pBindings, const DBID * pDbCols, HACCESSOR &hAccessor);
|
|
void _ReleaseAccessor();
|
|
HRESULT _CacheRowSet(UINT iItem);
|
|
HRESULT _TranslateFolder(LPCWSTR pszParent, LPWSTR pszResult, UINT cchSize, BOOL *pfTranslated);
|
|
void _ClearFolderState();
|
|
|
|
LONG _cRef;
|
|
IFindFilter *_pfilter;
|
|
IRowsetWatchNotify *_prwn;
|
|
IFindFolder *_pff;
|
|
int _iColSort;
|
|
DWORD _grfFlags;
|
|
DWORD _grfWarnings;
|
|
LPTSTR _pszProgressText;
|
|
|
|
TCHAR _szCurrentDir[MAX_PATH];
|
|
IShellFolder *_psfCurrentDir;
|
|
LPITEMIDLIST _pidlFolder;
|
|
int _iFolder;
|
|
|
|
HRESULT _hrCurrent;
|
|
ICommand *_pCommand;
|
|
IRowsetLocate *_pRowset;
|
|
IRowsetAsynch *_pRowsetAsync;
|
|
HACCESSOR _hAccessor;
|
|
HACCESSOR _hAccessorWorkID;
|
|
HROW _ahrow[100]; // Cache 100 hrows out for now
|
|
UINT _ihrowFirst; // The index of which row is cached out first
|
|
DBCOUNTITEM _cRows; // number of hrows in _ahrow
|
|
DFODBET *_pdfetFirst; // Name translation list.
|
|
};
|
|
|
|
STDAPI CreateOleDBEnum(IFindFilter *pfilter, IShellFolder *psf,
|
|
LPWSTR *apwszPaths, UINT *pcPaths, DWORD grfFlags, int iColSort,
|
|
LPTSTR pszProgressText, IRowsetWatchNotify *prwn, IFindEnum **ppdfenum)
|
|
{
|
|
*ppdfenum = NULL;
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
IFindFolder *pff;
|
|
psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff));
|
|
|
|
CContentIndexEnum* pdfenum = new CContentIndexEnum(pfilter, pff, grfFlags, iColSort, pszProgressText, prwn);
|
|
|
|
if (pdfenum)
|
|
{
|
|
hr = pdfenum->DoQuery(apwszPaths, pcPaths);
|
|
if (hr == S_OK) // We only continue to use this if query returne S_OK...
|
|
*ppdfenum = (IFindEnum*)pdfenum;
|
|
else
|
|
{
|
|
pdfenum->Release(); // release the memory we allocated
|
|
}
|
|
}
|
|
|
|
if (pff)
|
|
pff->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
const DBID c_aDbCols[] =
|
|
{
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_NAME}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_PATH}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_ATTRIBUTES}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_SIZE}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_WRITETIME}},
|
|
{{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR) PROPID_QUERY_RANK}},
|
|
};
|
|
|
|
const DBID c_aDbWorkIDCols[] =
|
|
{
|
|
{{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR)PROPID_QUERY_WORKID}}
|
|
};
|
|
|
|
const LPCWSTR c_awszColSortNames[] = {
|
|
L"FileName[a],Path[a]",
|
|
L"Path[a],FileName[a]",
|
|
L"Size[a]",
|
|
NULL,
|
|
L"Write[a]",
|
|
L"Rank[d]"
|
|
};
|
|
|
|
const ULONG c_cDbCols = ARRAYSIZE(c_aDbCols);
|
|
const DBID c_dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0};
|
|
const GUID c_guidQueryExt = DBPROPSET_QUERYEXT;
|
|
const GUID c_guidRowsetProps = {0xc8b522be,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
|
|
|
|
CContentIndexEnum::CContentIndexEnum(IFindFilter *pfilter, IFindFolder *pff,
|
|
DWORD grfFlags, int iColSort, LPTSTR pszProgressText, IRowsetWatchNotify *prwn) :
|
|
_cRef(1), _ihrowFirst((UINT)-1), _pfilter(pfilter),
|
|
_pff(pff), _prwn(prwn), _grfFlags(grfFlags),
|
|
_grfWarnings(DFW_DEFAULT), _iColSort(iColSort), _pszProgressText(pszProgressText)
|
|
{
|
|
_szCurrentDir[0] = 0;
|
|
|
|
ASSERT(_pRowset == 0);
|
|
ASSERT(_pRowsetAsync == 0);
|
|
ASSERT(_pCommand == 0);
|
|
ASSERT(_hAccessor == 0);
|
|
ASSERT(_hAccessorWorkID ==0);
|
|
ASSERT(_cRows == 0);
|
|
|
|
if (_pfilter)
|
|
{
|
|
_pfilter->AddRef();
|
|
_pfilter->GetWarningFlags(&_grfWarnings);
|
|
}
|
|
|
|
if (_pff)
|
|
_pff->AddRef();
|
|
|
|
if (_prwn)
|
|
_prwn->AddRef();
|
|
}
|
|
|
|
void CContentIndexEnum::_ClearFolderState()
|
|
{
|
|
ATOMICRELEASE(_psfCurrentDir);
|
|
ILFree(_pidlFolder);
|
|
_pidlFolder = NULL;
|
|
_iFolder = -1;
|
|
_szCurrentDir[0] = 0;
|
|
}
|
|
|
|
CContentIndexEnum::~CContentIndexEnum()
|
|
{
|
|
ATOMICRELEASE(_pfilter);
|
|
ATOMICRELEASE(_pff);
|
|
ATOMICRELEASE(_prwn);
|
|
|
|
_ClearFolderState();
|
|
|
|
if (_pRowset)
|
|
{
|
|
ATOMICRELEASE(_pRowsetAsync);
|
|
|
|
// Release any cached rows.
|
|
_CacheRowSet((UINT)-1);
|
|
|
|
if (_hAccessor || _hAccessorWorkID)
|
|
_ReleaseAccessor();
|
|
|
|
_pRowset->Release();
|
|
}
|
|
|
|
ATOMICRELEASE(_pCommand);
|
|
|
|
// Release any name translations we may have allocated.
|
|
DFODBET *pdfet = _pdfetFirst;
|
|
while (pdfet)
|
|
{
|
|
DFODBET *pdfetT = pdfet;
|
|
pdfet = pdfet->pdfetNext; // First setup to look at the next item before we free stuff...
|
|
LocalFree((HLOCAL)pdfetT->pwszFrom);
|
|
LocalFree((HLOCAL)pdfetT->pwszTo);
|
|
LocalFree((HLOCAL)pdfetT);
|
|
}
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENTMULTI(CContentIndexEnum, IUnknown, IFindEnum), // IID_IUNKNOWN
|
|
QITABENT(CContentIndexEnum, IShellService), // IID_IShellService
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
ULONG CContentIndexEnum::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CContentIndexEnum::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState)
|
|
{
|
|
return E_PENDING; // as good a return as any to say that we are async...
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::Skip(int celt)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::Reset()
|
|
{
|
|
// overload Reset to mean dump the rowset cache!!!
|
|
_CacheRowSet(-1);
|
|
// still return failiure
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::StopSearch()
|
|
{
|
|
// Lets see if we can find one that works...
|
|
HRESULT hr = _pCommand->Cancel();
|
|
if (FAILED(hr))
|
|
hr = _pRowsetAsync->Stop();
|
|
if (FAILED(hr))
|
|
{
|
|
IDBAsynchStatus *pdbas;
|
|
if (SUCCEEDED(_pRowset->QueryInterface(IID_PPV_ARG(IDBAsynchStatus, &pdbas))))
|
|
{
|
|
hr = pdbas->Abort(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN);
|
|
pdbas->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
BOOL CContentIndexEnum::FQueryIsAsync()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone)
|
|
{
|
|
if (!_pRowsetAsync)
|
|
return E_FAIL;
|
|
|
|
BOOL fMore;
|
|
DBCOUNTITEM dwDen, dwNum;
|
|
HRESULT hr = _pRowsetAsync->RatioFinished(&dwDen, &dwNum, pdwTotalAsync, &fMore);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pfQueryDone = dwDen == dwNum;
|
|
*pnPercentComplete = dwDen ? (int)((dwNum * 100) / dwDen) : 100;
|
|
}
|
|
else
|
|
*pfQueryDone = TRUE; // in case that is all they are looking at...
|
|
return hr;
|
|
}
|
|
|
|
// modify pszPath until you can parse it, return result in *ppidl
|
|
|
|
HRESULT _StripToParseableName(LPTSTR pszPath, LPITEMIDLIST *ppidl)
|
|
{
|
|
*ppidl = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
PathRemoveBackslash(pszPath);
|
|
while (PathRemoveFileSpec(pszPath) && FAILED(hr))
|
|
{
|
|
hr = SHParseDisplayName(pszPath, NULL, ppidl, 0, NULL);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// we could not get pidl for this item for some reason. we have to put
|
|
// it in the list of bad items so that we can tell ci not to give it to
|
|
// us the next time we do search
|
|
void _ExcludeFromFutureSearch(LPCTSTR pszParent)
|
|
{
|
|
HKEY hkey;
|
|
HRESULT hr;
|
|
|
|
TCHAR szParent[MAX_PATH];
|
|
hr = StringCchCopy(szParent, ARRAYSIZE(szParent), pszParent);
|
|
if (FAILED(hr))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ERROR_SUCCESS == RegCreateKeyExW(HKEY_CURRENT_USER, CI_SPECIAL_FOLDERS, 0, L"", 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hkey, NULL))
|
|
{
|
|
LPITEMIDLIST pidlT;
|
|
if (SUCCEEDED(_StripToParseableName(szParent, &pidlT)))
|
|
{
|
|
ILFree(pidlT);
|
|
|
|
DWORD dwInsert = 0; // init to zero in case query info bellow fails
|
|
int iEnd;
|
|
TCHAR sz[MAX_PATH], szName[12];
|
|
RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwInsert, NULL, NULL, NULL, NULL);
|
|
// start from the end as there is a high chance we added this at the end
|
|
for (int i = dwInsert - 1; i >= 0; i--)
|
|
{
|
|
StringCchPrintf(szName, ARRAYSIZE(szName), L"%d", i); // ok to truncate
|
|
if (ERROR_SUCCESS == SHRegGetString(hkey, NULL, szName, sz, ARRAYSIZE(sz)))
|
|
{
|
|
LPTSTR pszTemp = StrStrI(sz + 1, szParent); // +1 to pass " that's at the beginning of the string
|
|
if (pszTemp && pszTemp == sz + 1)
|
|
{
|
|
dwInsert = i; // overwrite this value
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
iEnd = lstrlen(sz);
|
|
if (EVAL(iEnd > 1))
|
|
{
|
|
int iBackslash = iEnd - 3;
|
|
ASSERT(sz[iBackslash] == L'\\');
|
|
sz[iBackslash] = L'\0';
|
|
pszTemp = StrStrI(szParent, sz + 1);
|
|
sz[iBackslash] = L'\\';
|
|
if (pszTemp && pszTemp == szParent)
|
|
{
|
|
dwInsert = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwInsert != -1)
|
|
{
|
|
hr = StringCchPrintf(szName, ARRAYSIZE(szName), L"%d", dwInsert);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (PathAppend(szParent, TEXT("*")))
|
|
{
|
|
PathQuoteSpaces(szParent);
|
|
RegSetValueEx(hkey, szName, 0, REG_SZ, (BYTE *)szParent, (lstrlen(szParent) + 1) * sizeof(szParent[0]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
// If it is a UNC it might be one we need to translate, to handle the case that
|
|
// content index does not support redirected drives.
|
|
|
|
HRESULT CContentIndexEnum::_TranslateFolder(LPCTSTR pszParent, LPTSTR pszResult, UINT cchSize, BOOL *pfTranslated)
|
|
{
|
|
BOOL fTranslated = FALSE;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (PathIsUNC(pszParent))
|
|
{
|
|
for (DFODBET *pdfet = _pdfetFirst; pdfet; pdfet = pdfet->pdfetNext)
|
|
{
|
|
if ((StrCmpNIW(pszParent, pdfet->pwszFrom, pdfet->cbFrom) == 0)
|
|
&& (pszParent[pdfet->cbFrom] == L'\\'))
|
|
{
|
|
// Ok we have a translation to use.
|
|
hr = StringCchCopy(pszResult, cchSize, pdfet->pwszTo);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// need + 1 here or we'll get something like "w:\\winnt" bogus path, that is.
|
|
hr = StringCchCat(pszResult, cchSize, &pszParent[pdfet->cbFrom + 1]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fTranslated = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fTranslated)
|
|
{
|
|
// default to the same
|
|
hr = StringCchCopy(pszResult, cchSize, pszParent);
|
|
}
|
|
|
|
*pfTranslated = fTranslated;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl)
|
|
{
|
|
*ppidl = NULL;
|
|
|
|
HRESULT hr = _CacheRowSet(iItem);
|
|
if (S_OK != hr)
|
|
{
|
|
return E_FAIL; // we could not get the item someone asked for, so error...
|
|
}
|
|
|
|
PROPVARIANT* data[c_cDbCols];
|
|
|
|
hr = _pRowset->GetData(_ahrow[iItem - _ihrowFirst], _hAccessor, &data);
|
|
if (hr != S_OK)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// data[0].pwszVal is the file name
|
|
// data[1].pwszVal is the full path (including file name)
|
|
// data[2].ulVal is the attribute
|
|
// data[3].ulVal is the size in byte
|
|
// data[4].filetime is the last write time in UTC
|
|
// data[5].ulVal is the rank of the item...
|
|
|
|
WIN32_FIND_DATA fd = {0};
|
|
|
|
fd.dwFileAttributes = data[2]->ulVal;
|
|
fd.nFileSizeLow = data[3]->ulVal;
|
|
fd.ftLastWriteTime = data[4]->filetime;
|
|
|
|
ASSERT(ShowSuperHidden() || !IsSuperHidden(fd.dwFileAttributes)); // query should exclude these
|
|
|
|
hr = StringCchCopy(fd.cFileName, ARRAYSIZE(fd.cFileName), data[0]->pwszVal);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
WCHAR szParent[MAX_PATH];
|
|
|
|
hr = StringCchCopy(szParent, ARRAYSIZE(szParent), data[1]->pwszVal); // full path
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
PathRemoveFileSpec(szParent); // strip to parent folder path
|
|
|
|
WCHAR szTranslatedParent[MAX_PATH];
|
|
BOOL fTranslated;
|
|
|
|
hr = _TranslateFolder(szParent, szTranslatedParent, ARRAYSIZE(szTranslatedParent), &fTranslated);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Working with a new folder?
|
|
//
|
|
if (lstrcmp(szParent, _szCurrentDir) != 0)
|
|
{
|
|
_ClearFolderState(); // our previous "current folder" state is now invalid
|
|
|
|
hr = SHParseDisplayName(szTranslatedParent, NULL, &_pidlFolder, 0, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _pff->AddFolder(_pidlFolder, TRUE, &_iFolder);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _pff->GetFolder(_iFolder, IID_PPV_ARG(IShellFolder, &_psfCurrentDir));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// on succesful init of this folder save the cache key
|
|
hr = StringCchCopy(_szCurrentDir, ARRAYSIZE(_szCurrentDir), szParent);
|
|
}
|
|
}
|
|
}
|
|
else if (hr != E_OUTOFMEMORY && !fTranslated)
|
|
{
|
|
_ExcludeFromFutureSearch(szParent);
|
|
}
|
|
_hrCurrent = hr; // save error state for next time around
|
|
|
|
if (FAILED(hr))
|
|
_ClearFolderState();
|
|
}
|
|
else
|
|
{
|
|
hr = _hrCurrent;
|
|
}
|
|
|
|
//
|
|
// Now that we have changed to the current folder, lets get the item out
|
|
// of the folder.
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// success implies the state of these variables
|
|
ASSERT((NULL != _psfCurrentDir) && (NULL != _pidlFolder) && (_iFolder > 0));
|
|
|
|
DWORD dwItemID;
|
|
GetItemID(iItem, &dwItemID);
|
|
|
|
LPITEMIDLIST pidl;
|
|
hr = SHSimpleIDListFromFindData2(_psfCurrentDir, &fd, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _pff->AddDataToIDList(pidl, _iFolder, _pidlFolder, DFDF_EXTRADATA, iItem, dwItemID, data[5]->ulVal, ppidl);
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// failure implies these should be clear
|
|
ASSERT((NULL == _psfCurrentDir) && (NULL == _pidlFolder));
|
|
|
|
LPITEMIDLIST pidlFull;
|
|
if (SUCCEEDED(_StripToParseableName(szTranslatedParent, &pidlFull)))
|
|
{
|
|
LPCITEMIDLIST pidlChild;
|
|
if (SUCCEEDED(SplitIDList(pidlFull, &_pidlFolder, &pidlChild)))
|
|
{
|
|
hr = _pff->AddFolder(_pidlFolder, TRUE, &_iFolder);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _pff->GetFolder(_iFolder, IID_PPV_ARG(IShellFolder, &_psfCurrentDir));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _pff->AddDataToIDList(pidlChild, _iFolder, _pidlFolder, DFDF_NONE, 0, 0, 0, ppidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// on succesful init of this folder save the cache key
|
|
hr = StringCchCopy(_szCurrentDir, ARRAYSIZE(_szCurrentDir), szTranslatedParent);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
PathRemoveFileSpec(_szCurrentDir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ILFree(pidlFull);
|
|
|
|
if (FAILED(hr))
|
|
_ClearFolderState();
|
|
}
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::GetItemID(UINT iItem, DWORD *puItemID)
|
|
{
|
|
*puItemID = (UINT)-1;
|
|
HRESULT hr = _CacheRowSet(iItem);
|
|
if (S_OK == hr)
|
|
{
|
|
PROPVARIANT* data[1];
|
|
hr = _pRowset->GetData(_ahrow[iItem - _ihrowFirst], _hAccessorWorkID, &data);
|
|
if (S_OK == hr)
|
|
{
|
|
// Only one data column so this is easy...
|
|
// The ULVal is the thing we are after...
|
|
*puItemID = data[0]->ulVal;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::SortOnColumn(UINT iCol, BOOL fAscending)
|
|
{
|
|
// Ok We need to generate the Sort String...
|
|
return _BuildAndSetCommandTree(iCol, fAscending);
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::SetOwner(IUnknown* punkOwner)
|
|
{
|
|
// Used to set the docfind folder and from that the filter.
|
|
ATOMICRELEASE(_pfilter);
|
|
ATOMICRELEASE(_pff);
|
|
|
|
if (punkOwner)
|
|
{
|
|
punkOwner->QueryInterface(IID_PPV_ARG(IFindFolder, &_pff));
|
|
if (_pff)
|
|
_pff->GetFindFilter(&_pfilter);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::_MapColumns(IUnknown *punk, DBORDINAL cCols,
|
|
DBBINDING *pBindings, const DBID *pDbCols,
|
|
HACCESSOR &hAccessor)
|
|
{
|
|
DBORDINAL aMappedColumnIDs[c_cDbCols];
|
|
|
|
IColumnsInfo *pColumnsInfo;
|
|
HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IColumnsInfo, &pColumnsInfo));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pColumnsInfo->MapColumnIDs(cCols, pDbCols, aMappedColumnIDs);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (ULONG i = 0; i < cCols; i++)
|
|
pBindings[i].iOrdinal = aMappedColumnIDs[i];
|
|
|
|
IAccessor *pIAccessor;
|
|
hr = punk->QueryInterface(IID_PPV_ARG(IAccessor, &pIAccessor));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hAccessor = 0;
|
|
hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cCols, pBindings, 0, &hAccessor, 0);
|
|
pIAccessor->Release();
|
|
}
|
|
}
|
|
pColumnsInfo->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CContentIndexEnum::_ReleaseAccessor()
|
|
{
|
|
IAccessor *pIAccessor;
|
|
HRESULT hr = _pRowset->QueryInterface(IID_PPV_ARG(IAccessor, &pIAccessor));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (_hAccessor)
|
|
pIAccessor->ReleaseAccessor(_hAccessor, 0);
|
|
if (_hAccessorWorkID)
|
|
pIAccessor->ReleaseAccessor(_hAccessorWorkID, 0);
|
|
|
|
pIAccessor->Release();
|
|
}
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::_CacheRowSet(UINT iItem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!_pRowset)
|
|
return E_FAIL;
|
|
|
|
if (!_cRows || !InRange(iItem, _ihrowFirst, _ihrowFirst+(UINT)_cRows-1) || (iItem == (UINT)-1))
|
|
{
|
|
// Release the last cached element we had.
|
|
if (_cRows != 0)
|
|
_pRowset->ReleaseRows(ARRAYSIZE(_ahrow), _ahrow, 0, 0, 0);
|
|
|
|
// See if we are simply releasing our cached data...
|
|
_cRows = 0;
|
|
_ihrowFirst = (UINT)-1;
|
|
if (iItem == (UINT)-1)
|
|
return S_OK;
|
|
|
|
// Ok try to read in the next on...
|
|
BYTE bBookMark = (BYTE) DBBMK_FIRST;
|
|
HROW *rghRows = (HROW *)_ahrow;
|
|
|
|
// change this to fetch 100 or so rows at the time -- huge perf improvment
|
|
hr = _pRowset->GetRowsAt(0, 0, sizeof(bBookMark), &bBookMark, iItem, ARRAYSIZE(_ahrow), &_cRows, &rghRows);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
_ihrowFirst = iItem;
|
|
|
|
if ((DB_S_ENDOFROWSET == hr) || (_cRows == 0))
|
|
{
|
|
if (_cRows == 0)
|
|
_ihrowFirst = -1;
|
|
else
|
|
hr = S_OK; // we got some items and caller expects S_OK so change DB_S_ENDOFROWSET to noerror
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CContentIndexEnum::_SetCmdProp(ICommand *pCommand)
|
|
{
|
|
#define MAX_PROPS 8
|
|
|
|
DBPROPSET aPropSet[MAX_PROPS];
|
|
DBPROP aProp[MAX_PROPS];
|
|
ULONG cProps = 0;
|
|
HRESULT hr;
|
|
|
|
// asynchronous query
|
|
|
|
aProp[cProps].dwPropertyID = DBPROP_IRowsetAsynch;
|
|
aProp[cProps].dwOptions = 0;
|
|
aProp[cProps].dwStatus = 0;
|
|
aProp[cProps].colid = c_dbcolNull;
|
|
aProp[cProps].vValue.vt = VT_BOOL;
|
|
aProp[cProps].vValue.boolVal = VARIANT_TRUE;
|
|
|
|
aPropSet[cProps].rgProperties = &aProp[cProps];
|
|
aPropSet[cProps].cProperties = 1;
|
|
aPropSet[cProps].guidPropertySet = c_guidRowsetProps;
|
|
|
|
cProps++;
|
|
|
|
// don't timeout queries
|
|
|
|
aProp[cProps].dwPropertyID = DBPROP_COMMANDTIMEOUT;
|
|
aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
|
|
aProp[cProps].dwStatus = 0;
|
|
aProp[cProps].colid = c_dbcolNull;
|
|
aProp[cProps].vValue.vt = VT_I4;
|
|
aProp[cProps].vValue.lVal = 0;
|
|
|
|
aPropSet[cProps].rgProperties = &aProp[cProps];
|
|
aPropSet[cProps].cProperties = 1;
|
|
aPropSet[cProps].guidPropertySet = c_guidRowsetProps;
|
|
|
|
cProps++;
|
|
|
|
// We can handle PROPVARIANTs
|
|
|
|
aProp[cProps].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
|
|
aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
|
|
aProp[cProps].dwStatus = 0;
|
|
aProp[cProps].colid = c_dbcolNull;
|
|
aProp[cProps].vValue.vt = VT_BOOL;
|
|
aProp[cProps].vValue.boolVal = VARIANT_TRUE;
|
|
|
|
aPropSet[cProps].rgProperties = &aProp[cProps];
|
|
aPropSet[cProps].cProperties = 1;
|
|
aPropSet[cProps].guidPropertySet = c_guidQueryExt;
|
|
|
|
cProps++;
|
|
|
|
ICommandProperties * pCmdProp = 0;
|
|
hr = pCommand->QueryInterface(IID_PPV_ARG(ICommandProperties, &pCmdProp));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pCmdProp->SetProperties(cProps, aPropSet);
|
|
pCmdProp->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// create the query command string
|
|
|
|
HRESULT CContentIndexEnum::_BuildAndSetCommandTree(int iCol, BOOL fReverse)
|
|
{
|
|
LPWSTR pwszRestrictions = NULL;
|
|
DWORD dwGQRFlags;
|
|
HRESULT hr = _pfilter->GenerateQueryRestrictions(&pwszRestrictions, &dwGQRFlags);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ULONG ulDialect;
|
|
hr = _pfilter->GetQueryLanguageDialect(&ulDialect);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// NOTE: hard coded to our current list of columns
|
|
WCHAR wszSort[80]; // use this to sort by different columns...
|
|
wszSort[0] = 0;
|
|
|
|
if ((iCol >= 0) && (iCol < ARRAYSIZE(c_awszColSortNames)) && c_awszColSortNames[iCol])
|
|
{
|
|
// Sort order is hardcoded for ascending.
|
|
hr = StringCchCopy(wszSort, ARRAYSIZE(wszSort), c_awszColSortNames[iCol]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchCat(wszSort, ARRAYSIZE(wszSort), L",Path[a],FileName[a]");
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
// don't sort on anything which doesn't let us
|
|
// do proper sort string construction
|
|
wszSort[0] = 0;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DBCOMMANDTREE *pTree = NULL;
|
|
hr = CITextToFullTreeEx(pwszRestrictions, ulDialect,
|
|
L"FileName,Path,Attrib,Size,Write,Rank,WorkID",
|
|
wszSort[0] ? wszSort : NULL, 0, &pTree, 0, 0, LOCALE_USER_DEFAULT);
|
|
if (FAILED(hr))
|
|
{
|
|
// Map this to one that I know about
|
|
// Note: We will only do this if we require CI else we will try to fallback to old search...
|
|
// Note we are running into problems where CI says we are contained in a Catalog even if
|
|
// CI process is not running... So try to avoid this if possible
|
|
if (dwGQRFlags & GQR_REQUIRES_CI)
|
|
hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_CONSTRAINT);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ICommandTree *pCmdTree;
|
|
hr = _pCommand->QueryInterface(IID_PPV_ARG(ICommandTree, &pCmdTree));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pCmdTree->SetCommandTree(&pTree, DBCOMMANDREUSE_NONE, FALSE);
|
|
pCmdTree->Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LocalFree((HLOCAL)pwszRestrictions);
|
|
return hr;
|
|
}
|
|
|
|
#define cbP (sizeof (PROPVARIANT *))
|
|
|
|
// [in, out] apwszPaths this is modified
|
|
// [in, out] pcPaths
|
|
|
|
HRESULT CContentIndexEnum::DoQuery(LPWSTR *apwszPaths, UINT *pcPaths)
|
|
{
|
|
UINT nPaths = *pcPaths;
|
|
WCHAR** aScopes = NULL;
|
|
WCHAR** aScopesOrig = NULL;
|
|
ULONG* aDepths = NULL;
|
|
WCHAR** aCatalogs = NULL;
|
|
WCHAR** aMachines = NULL;
|
|
WCHAR wszPath[MAX_PATH];
|
|
LPWSTR pwszPath = wszPath;
|
|
LPWSTR pszMachineAlloc = NULL, pszCatalogAlloc = NULL;
|
|
LPWSTR pwszMachine, pwszCatalog;
|
|
UINT i, iPath = 0;
|
|
DWORD dwQueryRestrictions;
|
|
|
|
// Initiailize all of our query values back to unused
|
|
_hAccessor = NULL;
|
|
_hAccessorWorkID = NULL;
|
|
_pRowset = NULL;
|
|
_pRowsetAsync = NULL;
|
|
_pCommand = NULL;
|
|
|
|
// Get array of search paths...
|
|
#define MAX_MACHINE_NAME_LEN 32
|
|
|
|
BOOL fIsCIRunning, fCiIndexed, fCiPermission;
|
|
GetCIStatus(&fIsCIRunning, &fCiIndexed, &fCiPermission);
|
|
|
|
// First pass see if we have anything that make use at all of CI if not lets simply bail and let
|
|
// old code walk the list...
|
|
HRESULT hr = _pfilter->GenerateQueryRestrictions(NULL, &dwQueryRestrictions);
|
|
if (FAILED(hr))
|
|
goto Abort;
|
|
|
|
if ((dwQueryRestrictions & GQR_MAKES_USE_OF_CI) == 0)
|
|
{
|
|
hr = S_FALSE;
|
|
goto Abort;
|
|
}
|
|
|
|
// allocate the arrays that we need to pass to CIMakeICommand and
|
|
// the buffers needed for the machine name and catalog name
|
|
aDepths = (ULONG*)LocalAlloc(LPTR, nPaths * sizeof(ULONG));
|
|
aScopes = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
|
|
aScopesOrig = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
|
|
aCatalogs = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
|
|
aMachines = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
|
|
pszMachineAlloc = pwszMachine = (LPWSTR)LocalAlloc(LPTR, nPaths * MAX_MACHINE_NAME_LEN * sizeof(WCHAR));
|
|
pszCatalogAlloc = pwszCatalog = (LPWSTR)LocalAlloc(LPTR, nPaths * MAX_PATH * sizeof(WCHAR));
|
|
|
|
if (!aDepths || !aScopes || !aScopesOrig || !aCatalogs ||
|
|
!aMachines || !pszMachineAlloc || !pszCatalogAlloc)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Abort;
|
|
}
|
|
|
|
// This following loop does two things,
|
|
// 1. Check if all the scopes are indexed, if any one scope is not,
|
|
// fail the call and we'll do the win32 find.
|
|
// 2. Prepare the arrays of parameters that we need to pass to
|
|
// CIMakeICommand().
|
|
//
|
|
// NOTE: Reinerf says this code looks busted for nPaths > 1. See bug 199254 for comments.
|
|
for (i = 0; i < nPaths; i++)
|
|
{
|
|
ULONG cchMachine = MAX_MACHINE_NAME_LEN;
|
|
ULONG cchCatalog = MAX_PATH;
|
|
WCHAR wszUNCPath[MAX_PATH];
|
|
BOOL fRemapped = FALSE;
|
|
|
|
// if CI is not running we can still do ci queries on a remote drive (if it is indexed)
|
|
// so we cannot just bail if ci is not running on user's machine
|
|
if (!fIsCIRunning && !PathIsRemote(apwszPaths[i]))
|
|
continue; // do grep on this one
|
|
|
|
hr = LocateCatalogsW(apwszPaths[i], 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog);
|
|
if (hr != S_OK)
|
|
{
|
|
// see if by chance this is a network redirected drive. If so we CI does not handle
|
|
// these. See if we can remap to UNC path to ask again...
|
|
if (!PathIsUNC(apwszPaths[i]))
|
|
{
|
|
DWORD nLength = ARRAYSIZE(wszUNCPath);
|
|
// this api takes TCHAR, but we only compile this part for WINNT...
|
|
DWORD dwType = SHWNetGetConnection(apwszPaths[i], wszUNCPath, &nLength);
|
|
if ((dwType == NO_ERROR) || (dwType == ERROR_CONNECTION_UNAVAIL))
|
|
{
|
|
fRemapped = TRUE;
|
|
LPWSTR pwsz = PathSkipRootW(apwszPaths[i]);
|
|
BOOL fOk = TRUE;
|
|
if (pwsz)
|
|
{
|
|
if (!PathAppendW(wszUNCPath, pwsz))
|
|
{
|
|
fOk = FALSE;
|
|
}
|
|
}
|
|
if (fOk)
|
|
{
|
|
cchMachine = MAX_MACHINE_NAME_LEN; // reset in params
|
|
cchCatalog = MAX_PATH;
|
|
|
|
hr = LocateCatalogsW(wszUNCPath, 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (hr != S_OK)
|
|
{
|
|
continue; // this one is not indexed.
|
|
}
|
|
|
|
if (S_FALSE == CatalogUptodate(pwszCatalog, pwszMachine))
|
|
{
|
|
// not up todate
|
|
if (dwQueryRestrictions & GQR_REQUIRES_CI)
|
|
{
|
|
// ci not up to date and we must use it..
|
|
// inform the user that results may not be complete
|
|
if (!(_grfWarnings & DFW_IGNORE_INDEXNOTCOMPLETE))
|
|
{
|
|
hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXNOTCOMPLETE);
|
|
goto Abort;
|
|
}
|
|
//else use ci although index is not complete
|
|
}
|
|
else
|
|
{
|
|
// ci is not upto date so just use grep for this drive so user can get
|
|
// complete results
|
|
pwszCatalog[0] = 0;
|
|
pwszMachine[0] = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
aDepths[iPath] = (_grfFlags & DFOO_INCLUDESUBDIRS) ? QUERY_DEEP : QUERY_SHALLOW;
|
|
aScopesOrig[iPath] = apwszPaths[i];
|
|
if (fRemapped)
|
|
{
|
|
aScopes[iPath] = StrDupW(wszUNCPath);
|
|
if (aScopes[iPath] == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Abort;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aScopes[iPath] = apwszPaths[i];
|
|
}
|
|
|
|
aCatalogs[iPath] = pwszCatalog;
|
|
aMachines[iPath] = pwszMachine;
|
|
|
|
pwszCatalog += MAX_PATH; // advance the catalog and machine name buffer
|
|
pwszMachine += MAX_MACHINE_NAME_LEN;
|
|
iPath++; // next item in this list
|
|
}
|
|
|
|
if (iPath == 0)
|
|
{
|
|
// no catalogs found; - We should check to see if by chance the user specified a query that
|
|
// is CI based if so error apapropriately...
|
|
hr = (dwQueryRestrictions & GQR_REQUIRES_CI) ? MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXSEARCH) : S_FALSE;
|
|
goto Abort;
|
|
}
|
|
|
|
// Get ICommand.
|
|
hr = CIMakeICommand(&_pCommand, iPath, aDepths, aScopes, aCatalogs, aMachines);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// create the query command string - Assume default sort...
|
|
hr = _BuildAndSetCommandTree(_iColSort, FALSE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if ((dwQueryRestrictions & GQR_REQUIRES_CI) && (nPaths != iPath))
|
|
{
|
|
// check warning flags to see if we should ignore and continue
|
|
if (0 == (_grfWarnings & DFW_IGNORE_CISCOPEMISMATCH))
|
|
{
|
|
hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_SCOPEMISMATCH);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Get IRowset.
|
|
_SetCmdProp(_pCommand);
|
|
hr = _pCommand->Execute(0, IID_IRowsetLocate, 0, 0, (IUnknown **)&_pRowset);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// we have the IRowset.
|
|
// Real work to get the Accessor
|
|
DBBINDING aPropMainCols[c_cDbCols] =
|
|
{
|
|
{ 0,cbP*0,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,cbP*1,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,cbP*2,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,cbP*3,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,cbP*4,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,cbP*5,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }
|
|
};
|
|
|
|
hr = _MapColumns(_pRowset, c_cDbCols, aPropMainCols, c_aDbCols, _hAccessor);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// OK lets also get the accessor for the WorkID...
|
|
hr = _MapColumns(_pRowset, ARRAYSIZE(c_aDbWorkIDCols), aPropMainCols, c_aDbWorkIDCols, _hAccessorWorkID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _pRowset->QueryInterface(IID_PPV_ARG(IRowsetAsynch, &_pRowsetAsync));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
goto Abort;
|
|
|
|
// If we got here than at least some of our paths are indexed
|
|
// we may need to compress the list down of the ones we did not handle...
|
|
*pcPaths = (nPaths - iPath); // Let caller know how many we did not process
|
|
|
|
// need to move all the ones we did not process to the start of the list...
|
|
// we always process this list here as we may need to allocate translation lists to be used to
|
|
// translate the some UNCS back to the mapped drive the user passed in.
|
|
|
|
UINT j = 0, iInsert = 0;
|
|
iPath--; // make it easy to detect
|
|
for (i = 0; i < nPaths; i++)
|
|
{
|
|
if (aScopesOrig[j] == apwszPaths[i])
|
|
{
|
|
if (aScopesOrig[j] != aScopes[j])
|
|
{
|
|
// There is a translation in place.
|
|
DFODBET *pdfet = (DFODBET*)LocalAlloc(LPTR, sizeof(*pdfet));
|
|
if (pdfet)
|
|
{
|
|
pdfet->pdfetNext = _pdfetFirst;
|
|
_pdfetFirst = pdfet;
|
|
pdfet->pwszFrom = aScopes[j];
|
|
pdfet->cbFrom = lstrlenW(pdfet->pwszFrom);
|
|
pdfet->pwszTo = aScopesOrig[j];
|
|
aScopes[j] = aScopesOrig[j]; // Make sure loop below does not delete pwszFrom
|
|
apwszPaths[i] = NULL; // Likewise for pswsTo...
|
|
}
|
|
|
|
}
|
|
if (apwszPaths[i])
|
|
{
|
|
LocalFree((HLOCAL)apwszPaths[i]);
|
|
apwszPaths[i] = NULL;
|
|
}
|
|
|
|
if (j < iPath)
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
apwszPaths[iInsert++] = apwszPaths[i]; // move to right place
|
|
}
|
|
}
|
|
iPath++; // setup to go through cleanupcode...
|
|
|
|
// Fall through to cleanup code...
|
|
|
|
Abort:
|
|
// Warning... Since a failure return from this function will
|
|
// release this class, most all of the allocated items up till the failure should
|
|
// be released... Also cleanup any paths we may have allocated...
|
|
for (i = 0; i < iPath; i++)
|
|
{
|
|
if (aScopesOrig[i] != aScopes[i])
|
|
LocalFree(aScopes[i]);
|
|
}
|
|
|
|
if (aDepths)
|
|
LocalFree(aDepths);
|
|
|
|
if (aScopes)
|
|
LocalFree(aScopes);
|
|
|
|
if (aScopesOrig)
|
|
LocalFree(aScopesOrig);
|
|
|
|
if (aCatalogs)
|
|
LocalFree(aCatalogs);
|
|
|
|
if (aMachines)
|
|
LocalFree(aMachines);
|
|
|
|
if (pszMachineAlloc)
|
|
LocalFree(pszMachineAlloc);
|
|
|
|
if (pszCatalogAlloc)
|
|
LocalFree(pszCatalogAlloc);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// This is the main external entry point to start a search. This will
|
|
// create a new thread to process the
|
|
STDAPI_(BOOL) SHFindComputer(LPCITEMIDLIST, LPCITEMIDLIST)
|
|
{
|
|
IContextMenu *pcm;
|
|
HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IContextMenu, &pcm));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CMINVOKECOMMANDINFO ici = {0};
|
|
|
|
ici.cbSize = sizeof(ici);
|
|
ici.lpParameters = "{996E1EB1-B524-11d1-9120-00A0C98BA67D}"; // Search Guid of Find Computers
|
|
ici.nShow = SW_NORMAL;
|
|
|
|
hr = pcm->InvokeCommand(&ici);
|
|
|
|
pcm->Release();
|
|
}
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
BOOL _IsComputerPidl(LPCITEMIDLIST pidl)
|
|
{
|
|
CLSID clsid;
|
|
if (SUCCEEDED(GetCLSIDFromIDList(pidl, &clsid)))
|
|
{
|
|
return (IsEqualCLSID(clsid, CLSID_NetworkPlaces)
|
|
|| IsEqualCLSID(clsid, CLSID_NetworkRoot)
|
|
|| IsEqualCLSID(clsid, CLSID_NetworkDomain));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// 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 && _IsComputerPidl(pidlFolder))
|
|
{
|
|
return SHFindComputer(pidlFolder, pidlSaveFile);
|
|
}
|
|
|
|
return RealFindFiles(pidlFolder, pidlSaveFile);
|
|
}
|