Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4133 lines
137 KiB

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1993
//
// File: docfind2.c
//
// Description: This file should contains most of the document find code
// that is specific to the default search filters.
//
//
// History:
// 12-29-93 KurtE Created.
//
//---------------------------------------------------------------------------
#include "shellprv.h"
#pragma hdrstop
#include "fstreex.h"
//===========================================================================
// Define the Default data filter data structures
//===========================================================================
// Use the code from property sheet to create the dialogs
HWND WINAPI CreatePage(PSP *hpage, HWND hwndParent);
//
// Define the internal structure of our default filter
typedef struct _CDFFilter // fff
{
IDocFindFileFilter dfff;
UINT cRef;
HWND hwndTabs;
HANDLE hMRUSpecs;
// Here are the paths that we are to search on...
// Data associated with the Look in field
IShellFolder * psfMyComputer;
int iMyComputer;
// Data associated with the file name.
BOOL fNameChanged; // The name changed earlier
LPTSTR pszFileSpec; // the one we do compares with
LPITEMIDLIST pidlStart; // Starting location ID list.
TCHAR szPath[MAX_PATH]; // Location of where to start search from
TCHAR szUserInputFileSpec[MAX_PATH]; // File pattern.
TCHAR szText[MAXSTRLEN]; // Limit text to max editable size
#ifdef UNICODE
CHAR szTextA[MAXSTRLEN];
#endif
BOOL fTopLevelOnly; // Search on top level only?
BOOL fShowAllObjects; // Should we show all files?
BOOL fExcludeExts; // Wild card in extension...
BOOL fFilterChanged; // Something in the filter changed.
BOOL fWeRestoredSomeCriteria; // We need to initilize the pages...
// Fields associated with the file type
BOOL fTypeChanged; // Type changed;
int iType; // Index of the type.
int iTypeLast; // Save away last type...
PHASHITEM phiType; // Save away hash item
TCHAR szTypeName[80]; // The display name for type
TCHAR szTypeFilePatterns[MAX_PATH]; // The file patterns associated with type
LPGREPINFO lpgi; // Grep information.
int iSizeType; // What type of size 0 - none, 1 > 2 <
DWORD dwSize; // Size comparison
WORD wDateType; // 0 - none, 1 days before, 2 months before...
WORD wDateValue; // (Num of months or days)
WORD dateModifiedBefore;
WORD dateModifiedAfter;
BOOL fFoldersOnly; // Are we searching for folders?
} CDFFilter, FAR* LPDFFILTER;
// Define common page data for each of our pages
typedef struct { // dfpsp
PSP hpsp;
HANDLE hThreadInit;
HWND hwndDlg;
LPDFFILTER pdff;
DWORD dwState;
} DOCFINDPROPSHEETPAGE, * LPDOCFINDPROPSHEETPAGE;
// BUGBUG I don't get it... why the manual calculation of the structure size?
#define DOCFINDPSHTSIZE (SIZEOF(PROPSHEETPAGE)+SIZEOF(HANDLE)+SIZEOF(HWND)+SIZEOF(LPDFFILTER)+SIZEOF(DWORD))
#define DFPAGE_INIT 0x0001 /* This page has been initialized */
#define DFPAGE_CHANGE 0x0002 /* The user has modified the page */
//===========================================================================
// Prototypes of some of the internal functions.
//===========================================================================
BOOL CALLBACK DocFind_DFNameLocDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DocFind_DFDetailsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DocFind_DFDateDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
//===========================================================================
// Define some other module global data
//===========================================================================
#pragma data_seg(".text", "CODE")
const DFPAGELIST s_dfplDefault[] =
{
{DLG_DFNAMELOC, DocFind_DFNameLocDlgProc},
{DLG_DFDATE, DocFind_DFDateDlgProc},
{DLG_DFDETAILS, DocFind_DFDetailsDlgProc}
};
const DWORD aNameHelpIDs[] = {
IDD_STATIC, IDH_FINDFILENAME_NAME,
IDD_FILESPEC, IDH_FINDFILENAME_NAME,
IDD_PATH, IDH_FINDFILENAME_LOOKIN,
IDD_BROWSE, IDH_FINDFILENAME_BROWSE,
IDD_TOPLEVELONLY, IDH_FINDFILENAME_TOPLEVEL,
0, 0
};
const DWORD aCriteriaHelpIDs[] = {
IDD_STATIC, IDH_FINDFILECRIT_OFTYPE,
IDD_TYPECOMBO, IDH_FINDFILECRIT_OFTYPE,
IDD_CONTAINS, IDH_FINDFILECRIT_CONTTEXT,
IDD_SIZECOMP, IDH_FINDFILECRIT_SIZEIS,
IDD_SIZEVALUE, IDH_FINDFILECRIT_K,
IDD_SIZEUPDOWN, IDH_FINDFILECRIT_K,
IDD_SIZELBL, IDH_FINDFILECRIT_K,
0, 0
};
const DWORD aDateHelpIDs[] = {
IDD_MDATE_FROM, IDH_FINDFILEDATE_FROM,
IDD_MDATE_AND, IDH_FINDFILEDATE_TO,
IDD_MDATE_TO, IDH_FINDFILEDATE_TO,
IDD_MDATE_ALL, IDH_FINDFILEDATE_ALLFILES,
IDD_MDATE_PARTIAL, IDH_FINDFILEDATE_CREATEORMOD,
IDD_MDATE_DAYS, IDH_FINDFILEDATE_DAYS,
IDD_MDATE_DAYLBL, IDH_FINDFILEDATE_DAYS,
IDD_MDATE_MONTHS, IDH_FINDFILEDATE_MONTHS,
IDD_MDATE_MONTHLBL, IDH_FINDFILEDATE_MONTHS,
IDD_MDATE_BETWEEN, IDH_FINDFILEDATE_RANGE,
IDD_MDATE_NUMDAYS, IDH_FINDFILEDATE_DAYS,
IDD_MDATE_DAYSUPDOWN, IDH_FINDFILEDATE_DAYS,
IDD_MDATE_NUMMONTHS, IDH_FINDFILEDATE_MONTHS,
IDD_MDATE_MONTHSUPDOWN, IDH_FINDFILEDATE_MONTHS,
IDD_MDATE_FROM, IDH_FINDFILEDATE_FROM,
IDD_MDATE_TO, IDH_FINDFILEDATE_TO,
0, 0
};
#pragma data_seg()
//==========================================================================
//
// Create the default filter for our find code... They should be completly
// self contained...
//
//===========================================================================
// CDFFilter : member prototype
//===========================================================================
HRESULT STDMETHODCALLTYPE CDFFilter_QueryInterface(LPDOCFINDFILEFILTER pdfff, REFIID riid, LPVOID FAR* ppvObj);
ULONG STDMETHODCALLTYPE CDFFilter_AddRef(LPDOCFINDFILEFILTER pdfff);
ULONG STDMETHODCALLTYPE CDFFilter_Release(LPDOCFINDFILEFILTER pdfff);
STDMETHODIMP CDFFilter_GetIconsAndMenu (LPDOCFINDFILEFILTER pdfff,
HWND hwndDlg, HICON *phiconSmall, HICON *phiconLarge, HMENU *phmenu);
STDMETHODIMP CDFFilter_GetStatusMessageIndex (LPDOCFINDFILEFILTER pdfff,
UINT uContext, UINT *puMsgIndex);
STDMETHODIMP CDFFilter_GetFolderMergeMenuIndex (LPDOCFINDFILEFILTER pdfff,
UINT *puMergeMenu);
STDMETHODIMP CDFFilter_AddPages(LPDOCFINDFILEFILTER pdfff, HWND hwndTabs,
LPITEMIDLIST pidlStart);
STDMETHODIMP CDFFilter_FFilterChanged(LPDOCFINDFILEFILTER pdfff);
STDMETHODIMP CDFFilter_GenerateTitle(LPDOCFINDFILEFILTER pdfff, LPTSTR *ppszTitle, BOOL fFileName);
STDMETHODIMP CDFFilter_ClearSearchCriteria(LPDOCFINDFILEFILTER pdfff);
STDMETHODIMP CDFFilter_PrepareToEnumObjects(LPDOCFINDFILEFILTER pdfff, DWORD *pdwFlags);
STDMETHODIMP CDFFilter_EnableChanges(LPDOCFINDFILEFILTER pdfff, BOOL fEnable);
STDMETHODIMP CDFFilter_CreateDetails(LPDOCFINDFILEFILTER pdfff,
HWND hwndDlg, HDPA hdpaPidf, LPVOID FAR* ppvOut);
STDMETHODIMP CDFFilter_EnumObjects (LPDOCFINDFILEFILTER pdfff, LPSHELLFOLDER psf,
DWORD grfFlags, LPTSTR pszProgressText, IDFEnum **ppdfenum) PURE;
STDMETHODIMP CDFFilter_FDoesItemMatchFilter(LPDOCFINDFILEFILTER pdfff,
LPTSTR pszFolder, WIN32_FIND_DATA * pfinddata, LPSHELLFOLDER psf,
LPITEMIDLIST pidl);
STDMETHODIMP CDFFilter_SaveCriteria(LPDOCFINDFILEFILTER pdfff, IStream * pstm, WORD fCharType);
STDMETHODIMP CDFFilter_RestoreCriteria(LPDOCFINDFILEFILTER pdfff,
IStream * pstm, int cCriteria, WORD fCharType);
STDMETHODIMP CDFFilter_DeclareFSNotifyInterest(LPDOCFINDFILEFILTER pdfff, HWND hwndDlg, UINT uMsg );
#pragma data_seg(DATASEG_READONLY)
IDocFindFileFilterVtbl c_DFFilterVtbl =
{
CDFFilter_QueryInterface,
CDFFilter_AddRef,
CDFFilter_Release,
CDFFilter_GetIconsAndMenu,
CDFFilter_GetStatusMessageIndex,
CDFFilter_GetFolderMergeMenuIndex,
CDFFilter_AddPages,
CDFFilter_FFilterChanged,
CDFFilter_GenerateTitle,
CDFFilter_PrepareToEnumObjects,
CDFFilter_ClearSearchCriteria,
CDFFilter_EnableChanges,
CDFFilter_CreateDetails,
CDFFilter_EnumObjects,
CDFFilter_FDoesItemMatchFilter,
CDFFilter_SaveCriteria,
CDFFilter_RestoreCriteria,
CDFFilter_DeclareFSNotifyInterest
};
#pragma data_seg()
//==========================================================================
// Creation function to create default find filter...
//==========================================================================
IDocFindFileFilter * CreateDefaultDocFindFilter()
{
LPDFFILTER pfff = (void*)LocalAlloc(LPTR, SIZEOF(CDFFilter));
if (pfff == NULL)
return(NULL);
pfff->dfff.lpVtbl = &c_DFFilterVtbl;
pfff->cRef = 1;
pfff->wDateType = IDD_MDATE_ALL;
// We should now simply return the filter
return &pfff->dfff;
}
//==========================================================================
// Query interface for the docfind filter interface...
//==========================================================================
HRESULT STDMETHODCALLTYPE CDFFilter_QueryInterface(LPDOCFINDFILEFILTER pdfff, REFIID riid, LPVOID FAR* ppvObj)
{
return (E_NOTIMPL);
}
//==========================================================================
// IDocFindFileFilter::AddRef
//==========================================================================
ULONG STDMETHODCALLTYPE CDFFilter_AddRef(LPDOCFINDFILEFILTER pdfff)
{
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
this->cRef++;
return(this->cRef);
}
//==========================================================================
// IDocFindFileFilter::Release
//==========================================================================
ULONG STDMETHODCALLTYPE CDFFilter_Release(LPDOCFINDFILEFILTER pdfff)
{
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
this->cRef--;
if (this->cRef>0)
{
return(this->cRef);
}
// Destroy the MRU Lists...
if (this->hMRUSpecs)
FreeMRUList(this->hMRUSpecs);
if (this->lpgi)
{
FreeGrepBufs(this->lpgi);
this->lpgi = NULL;
}
// Release our usage of My computer
if (this->psfMyComputer)
{
this->psfMyComputer->lpVtbl->Release(this->psfMyComputer);
}
if (this->pidlStart)
ILFree(this->pidlStart);
if (this->pszFileSpec)
LocalFree( this->pszFileSpec );
LocalFree((HLOCAL)this);
return(0);
}
//==========================================================================
// Function to let the find know which icons to display and the top level menu
//==========================================================================
STDMETHODIMP CDFFilter_GetIconsAndMenu (LPDOCFINDFILEFILTER pdfff,
HWND hwndDlg, HICON *phiconSmall, HICON *phiconLarge, HMENU *phmenu)
{
*phiconSmall = LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_DOCFIND),
IMAGE_ICON, g_cxSmIcon, g_cySmIcon, LR_DEFAULTCOLOR);
*phiconLarge = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(IDI_DOCFIND));
// BUGBUG:: Still menu to process!
*phmenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(MENU_FINDDLG));
return (S_OK);
}
//==========================================================================
// Function to get the string resource index number that is proper for the
// current type of search.
//==========================================================================
STDMETHODIMP CDFFilter_GetStatusMessageIndex (LPDOCFINDFILEFILTER pdfff,
UINT uContext, UINT *puMsgIndex)
{
// Currently context is not used
*puMsgIndex = IDS_FILESFOUND;
return (S_OK);
}
//==========================================================================
// Function to let find know which menu to load to merge for the folder
//==========================================================================
STDMETHODIMP CDFFilter_GetFolderMergeMenuIndex (LPDOCFINDFILEFILTER pdfff,
UINT *puMergeMenu)
{
*puMergeMenu = POPUP_DOCFIND_POPUPMERGE;
return (S_OK);
}
//==========================================================================
// Helper function for add page to the IDocFindFileFilter::AddPages
//==========================================================================
HRESULT DocFind_AddPages(LPDOCFINDFILEFILTER pdfff, HWND hwndTabs,
const DFPAGELIST *pdfpl, int cdfpl)
{
int i;
TCHAR szTemp[128+50];
RECT rc;
int dxMax = 0;
int dyMax = 0;
TC_DFITEMEXTRA tie;
LPDOCFINDPROPSHEETPAGE pdfpsp;
HWND hwndDlg;
tie.tci.mask = TCIF_TEXT | TCIF_PARAM;
tie.hwndPage = NULL;
tie.tci.pszText = szTemp;
TabCtrl_SetItemExtra(hwndTabs, CB_DFITEMEXTRA);
hwndDlg = GetParent(hwndTabs);
// First go through and create all of the dialog pages.
//
for (i=0; i < cdfpl; i++)
{
pdfpsp = Alloc(SIZEOF(DOCFINDPROPSHEETPAGE));
if (pdfpsp == NULL)
break;
pdfpsp->hpsp.psp.dwSize = DOCFINDPSHTSIZE;
pdfpsp->hpsp.psp.dwFlags = PSP_DEFAULT | PSP_SHPAGE;
pdfpsp->hpsp.psp.hInstance = HINST_THISDLL;
pdfpsp->hpsp.psp.lParam = 0;
pdfpsp->hpsp.psp.pszTemplate = MAKEINTRESOURCE(pdfpl[i].id);
pdfpsp->hpsp.psp.pfnDlgProc = pdfpl[i].pfn;
pdfpsp->pdff = (struct _CDFFilter *)pdfff;
pdfpsp->hThreadInit = NULL;
tie.hwndPage = CreatePage(&pdfpsp->hpsp, hwndDlg);
if (tie.hwndPage != NULL)
{
GetWindowText(tie.hwndPage, szTemp, ARRAYSIZE(szTemp));
GetWindowRect(tie.hwndPage, &rc);
if ((rc.bottom - rc.top) > dyMax)
dyMax = rc.bottom - rc.top;
if ((rc.right - rc.left) > dxMax)
dxMax = rc.right - rc.left;
TabCtrl_InsertItem(hwndTabs, 1000, &tie.tci);
}
}
// We now need to resize everything to fit with the dialog templates
rc.left = rc.top = 0;
rc.right = dxMax;
rc.bottom = dyMax;
TabCtrl_AdjustRect(hwndTabs, TRUE, &rc);
// Size the page now
SetWindowPos(hwndTabs, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
// Now set the first page as active. We should be able to do this by
// simply posting a WM_COMMAND to the main dialog
SendNotify(hwndDlg, hwndTabs, TCN_SELCHANGE, NULL);
return (S_OK);
}
void WaitForPageInitToComplete(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
if (pdfpsp && pdfpsp->hThreadInit)
{
WaitForSendMessageThread(pdfpsp->hThreadInit, INFINITE);
CloseHandle(pdfpsp->hThreadInit);
pdfpsp->hThreadInit = NULL;
}
}
//==========================================================================
// IDocFindFileFilter::AddPages
//==========================================================================
STDMETHODIMP CDFFilter_AddPages(LPDOCFINDFILEFILTER pdfff, HWND hwndTabs,
LPITEMIDLIST pidlStart)
{
HRESULT hres;
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
// save away a pointer to the filter
this->hwndTabs = hwndTabs;
// if a pidlStart is passed in convert it to a path string
if (pidlStart)
{
this->pidlStart = ILClone(pidlStart);
SHGetPathFromIDList(pidlStart, this->szPath);
}
hres = DocFind_AddPages(pdfff, hwndTabs, s_dfplDefault, ARRAYSIZE(s_dfplDefault));
if (SUCCEEDED(hres))
{
// We should walk through all of the pages and have them init and the like
// we don't need to do this for the first page as it should be done automatically...
int cPages;
TC_DFITEMEXTRA tie;
HWND hwndMainDlg;
hwndMainDlg = GetParent(this->hwndTabs);
for (cPages = TabCtrl_GetItemCount(this->hwndTabs) -1; cPages >= 1; cPages--)
{
tie.tci.mask = TCIF_PARAM;
TabCtrl_GetItem(this->hwndTabs, cPages, &tie.tci);
SendNotify(tie.hwndPage, hwndMainDlg, PSN_SETACTIVE, NULL);
SendNotify(tie.hwndPage, hwndMainDlg, PSN_KILLACTIVE, NULL);
}
}
return hres;
}
//==========================================================================
// IDocFindFileFilter::FFilterChanged - Returns S_OK if nothing changed.
//==========================================================================
STDMETHODIMP CDFFilter_FFilterChanged(LPDOCFINDFILEFILTER pdfff)
{
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
BOOL fFilterChanged = this->fFilterChanged;
this->fFilterChanged = FALSE;
return((fFilterChanged? S_FALSE : S_OK));
}
//==========================================================================
// IDocFindFileFilter::GenerateTitle - Generates the title given the current
// search criteria.
//==========================================================================
STDMETHODIMP CDFFilter_GenerateTitle(LPDOCFINDFILEFILTER pdfff,
LPTSTR *ppszTitle, BOOL fFileName)
{
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
LPTSTR pszMsg;
BOOL fFilePattern;
int iRes;
TCHAR szFindName[80]; // German should not exceed this find: ->???
//
// Lets generate a title for the search. The title will depend on
// the file patern(s), the type field and the containing text field
//
fFilePattern = (this->szUserInputFileSpec[0] != TEXT('\0')) &&
(lstrcmp(this->szUserInputFileSpec, c_szStarDotStar) != 0);
// First see if there is a type field
if (this->iType > 0)
{
// We have a type field no check for content...
if (this->szText[0] != TEXT('\0'))
{
// There is text!
// Should now use type but...
// else see if the name field is not NULL and not *.*
if (fFilePattern)
iRes = IDS_FIND_TITLE_TYPE_NAME_TEXT;
else
iRes = IDS_FIND_TITLE_TYPE_TEXT;
}
else
{
// No type or text, see if file pattern
// Containing not found, first search for type then named
if (fFilePattern)
iRes = IDS_FIND_TITLE_TYPE_NAME;
else
iRes = IDS_FIND_TITLE_TYPE;
}
}
else
{
// No Type field ...
// first see if there is text to be searched for!
if (this->szText[0] != TEXT('\0'))
{
// There is text!
// Should now use type but...
// else see if the name field is not NULL and not *.*
if (fFilePattern)
iRes = IDS_FIND_TITLE_NAME_TEXT;
else
iRes = IDS_FIND_TITLE_TEXT;
}
else
{
// No type or text, see if file pattern
// Containing not found, first search for type then named
if (fFilePattern)
iRes = IDS_FIND_TITLE_NAME;
else
iRes = IDS_FIND_TITLE_ALL;
}
}
// We put : in for first spot for title bar. For name creation
// we remove it which will put the number at the end...
if (!fFileName)
LoadString(HINST_THISDLL, IDS_FIND_TITLE_FIND,
szFindName, ARRAYSIZE(szFindName));
pszMsg = ShellConstructMessageString(HINST_THISDLL,
MAKEINTRESOURCE(iRes),
fFileName? szNULL : szFindName,
this->szTypeName, this->szUserInputFileSpec, this->szText);
*ppszTitle = pszMsg; // Return the pointer to the caller
return (S_OK);
}
//==========================================================================
// IDocFindFileFilter::ClearSearchCriteria
//==========================================================================
STDMETHODIMP CDFFilter_ClearSearchCriteria(LPDOCFINDFILEFILTER pdfff)
{
int cPages;
HWND hwndMainDlg;
TC_DFITEMEXTRA tie;
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
hwndMainDlg = GetParent(this->hwndTabs);
for (cPages = TabCtrl_GetItemCount(this->hwndTabs) -1; cPages >= 0; cPages--)
{
tie.tci.mask = TCIF_PARAM;
TabCtrl_GetItem(this->hwndTabs, cPages, &tie.tci);
SendNotify(tie.hwndPage, hwndMainDlg, PSN_RESET, NULL);
}
// Also clear out a few other fields...
this->szUserInputFileSpec[0] = TEXT('\0');
this->iType = 0;
this->szText[0] = TEXT('\0');
return (S_OK);
}
//==========================================================================
// DocFind_SetupWildCardingOnFileSpec - returns TRUE if wildards are in
// extension.
//==========================================================================
BOOL DocFind_SetupWildCardingOnFileSpec(LPTSTR pszSpecIn,
LPTSTR * pszSpecOut)
{
BOOL fExcludeExts = FALSE;
LPTSTR pszIn = pszSpecIn;
LPTSTR pszOut;
LPTSTR pszStar;
BOOL fQuote;
// allocate a buffer that should be able to hold the resultant
// string. When all is said and done we'll re-allocate to the
// correct size.
*pszSpecOut = LocalAlloc( LPTR, 3*MAX_PATH*SIZEOF(TCHAR) );
if (*pszSpecOut==NULL)
return FALSE;
pszOut = *pszSpecOut;
while (*pszIn != TEXT('\0'))
{
LPTSTR pszT;
int ich;
TCHAR c;
// Strip in leading spaces out of there
while (*pszIn == TEXT(' '))
pszIn++;
if (*pszIn == TEXT('\0'))
break;
if (pszOut != *pszSpecOut)
*pszOut++ = TEXT(';');
if (FALSE != (fQuote = (*pszIn == TEXT('"'))))
{
// The user asked for something litteral.
pszT = pszIn = CharNext(pszIn);
while (*pszT && (*pszT != TEXT('"')))
pszT = CharNext(pszT);
}
else
{
pszT = pszIn + (ich = StrCSpn(pszIn, TEXT(",; ")));
}
c = *pszT; // Save away the seperator character that was found
*pszT = TEXT('\0'); //
// Put in a couple of tests for * and *.*
if ((lstrcmp(pszIn, c_szStar) == 0) ||
(lstrcmp(pszIn, c_szStarDotStar) == 0))
{
// Complete wild card so set a null criteria
*pszT = c; // Restore char;
pszOut = *pszSpecOut; // Set to start of string
break;
}
if (fQuote)
{
lstrcpy(pszOut, pszIn);
pszOut += lstrlen(pszIn);
}
else if ((pszStar = StrRChr(pszIn, NULL, TEXT('*'))) == NULL)
{
// Now check for an extension.
if ((pszStar = StrRChr(pszIn, NULL, TEXT('.'))) == NULL)
{
// Total wild no extension. we simply convert this to *foo*
// No wild cards so make it *Foo*
*pszOut++ = TEXT('*');
lstrcpy(pszOut, pszIn);
pszOut += ich;
*pszOut++ = TEXT('*');
fExcludeExts = TRUE;
}
else
{
// They have an extension! and no wild cards.
// For now I assume exact match. But this could be
// converted to something like *foo*.ext
lstrcpy(pszOut, pszIn);
pszOut += ich;
}
}
else
{
// Includes wild cards
lstrcpy(pszOut, pszIn);
pszOut += ich;
// if the * is at the end we assume fWild Ext. not
// 100% correct but close enough
if (*(pszStar+1) == TEXT('\0'))
fExcludeExts = TRUE;
else if (StrChr(pszIn, TEXT('.')) == NULL)
{
// No extension was specified so assume all
// extensions.
*pszOut++ = TEXT('.');
*pszOut++ = TEXT('*'); // Add on .* to the name
fExcludeExts = TRUE;
}
}
*pszT = c; // Restore char;
if (c == TEXT('\0'))
break;
// Skip beyond quotes
if (*pszT == TEXT('"'))
pszT++;
pszIn = pszT + 1; // setup for the next item
}
// Make sure we null terminate our output string;
if (pszOut == *pszSpecOut)
fExcludeExts = TRUE;
*pszOut++ = TEXT('\0');
// re-alloc the buffer down to the actual size of the string...
*pszSpecOut = LocalReAlloc( *pszSpecOut,
(pszOut-*pszSpecOut) * SIZEOF(TCHAR),
LMEM_MOVEABLE
);
return(fExcludeExts);
}
//==========================================================================
// IDocFindFileFilter::PrepareToEnumObjects
//==========================================================================
STDMETHODIMP CDFFilter_PrepareToEnumObjects(LPDOCFINDFILEFILTER pdfff, DWORD *pdwFlags)
{
int cPages;
TC_DFITEMEXTRA tie;
LPTSTR pszT;
SHELLSTATE ss;
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
if (this->hwndTabs)
{
HWND hwndMainDlg;
hwndMainDlg = GetParent(this->hwndTabs);
for (cPages = TabCtrl_GetItemCount(this->hwndTabs) -1; cPages >= 0; cPages--)
{
tie.tci.mask = TCIF_PARAM;
TabCtrl_GetItem(this->hwndTabs, cPages, &tie.tci);
SendNotify(tie.hwndPage, hwndMainDlg, PSN_APPLY, NULL);
}
}
// Update the flags and buffer strings
if (this->fTopLevelOnly)
*pdwFlags &= ~DFOO_INCLUDESUBDIRS;
else
*pdwFlags |= DFOO_INCLUDESUBDIRS;
// Also get the shell state variables to see if we should show extensions and the like
SHGetSetSettings(&ss, SSF_SHOWEXTENSIONS|SSF_SHOWALLOBJECTS, FALSE);
if (ss.fShowExtensions)
*pdwFlags |= DFOO_SHOWEXTENSIONS;
else
*pdwFlags &= ~DFOO_SHOWEXTENSIONS;
this->fShowAllObjects = ss.fShowAllObjects;
if (ss.fShowAllObjects)
*pdwFlags |= DFOO_SHOWALLOBJECTS;
else
*pdwFlags &= ~DFOO_SHOWALLOBJECTS;
// Now lets generate the file patern we will ask the system to look for
// for now we will simply copy the file spec in...
// Here is where we try to put some smarts into the file patterns stuff
// It will go something like:
// look between each ; or , and see if there are any wild cards. If not
// do something like *patern*.
// Also if there is no search pattern or if it is * or *.*, set the
// filter to NULL as to speed it up.
//
this->fExcludeExts = DocFind_SetupWildCardingOnFileSpec(this->szUserInputFileSpec,
&this->pszFileSpec);
// If we are in show all object mode then we turn off the exclude stuff...
if (*pdwFlags & DFOO_SHOWALLOBJECTS)
this->fExcludeExts = FALSE;
//
// Also if there is a search string associated with this search
// criteria, we need to initialize the search to allow greping on
// it.
// First check to see if we have an old one to release...
if (this->lpgi)
{
FreeGrepBufs(this->lpgi);
this->lpgi = NULL;
}
if (this->szText[0] != TEXT('\0'))
{
#ifdef UNICODE
LPSTR lpszText;
UINT cchLength;
cchLength = lstrlen(this->szText)+1;
lpszText = this->szTextA;
WideCharToMultiByte(CP_ACP, 0, this->szText, cchLength, this->szTextA, cchLength, NULL, NULL);
// Must double NULL terminate lpszText. InitGrepInfo requires
// this format!
lpszText[cchLength] = '\0'; // Do not wrap with TEXT(); should be ANSI
#ifdef DOCFIND_RESUPPORT
this->lpgi = InitGrepInfo(lpszText, (LPSTR)szNULL,
*pdwFlags & (FFLT_REGULAR | FFLT_CASESEN));
#else
this->lpgi = InitGrepInfo(lpszText, (LPSTR)szNULL,
*pdwFlags & (FFLT_CASESEN));
#endif
#else
#ifdef DOCFIND_RESUPPORT
this->lpgi = InitGrepInfo(this->szText, (LPTSTR)szNULL,
*pdwFlags & (FFLT_REGULAR | FFLT_CASESEN));
#else
this->lpgi = InitGrepInfo(this->szText, (LPTSTR)szNULL,
*pdwFlags & (FFLT_CASESEN));
#endif
#endif
}
return (S_OK);
}
//==========================================================================
// IDocFindFileFilter::EnableChanges
//==========================================================================
STDMETHODIMP CDFFilter_EnableChanges(LPDOCFINDFILEFILTER pdfff, BOOL fEnable)
{
int cPages;
HWND hwndMainDlg;
TC_DFITEMEXTRA tie;
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
hwndMainDlg = GetParent(this->hwndTabs);
for (cPages = TabCtrl_GetItemCount(this->hwndTabs) -1; cPages >= 0; cPages--)
{
tie.tci.mask = TCIF_PARAM;
TabCtrl_GetItem(this->hwndTabs, cPages, &tie.tci);
SendMessage(tie.hwndPage, DFM_ENABLECHANGES, (WPARAM)fEnable, 0);
}
return (S_OK);
}
//==========================================================================
// IDocFindFileFilter::FDoesItemMatchFilter
//==========================================================================
STDMETHODIMP CDFFilter_FDoesItemMatchFilter(LPDOCFINDFILEFILTER pdfff,
LPTSTR pszFolder, WIN32_FIND_DATA * pfinddata,
LPSHELLFOLDER psf, LPITEMIDLIST pidl)
{
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
SCODE sc = MAKE_SCODE(0, 0, 1);
WORD wFileDate, wFileTime;
FILETIME ftLocal;
// Note: We do not use the IDList in this one...
// This function does filtering of the file information for
// things that are not part of the standard file filter
// First things we dont show hidden files
// If show all is set then we should include hidden files also...
if (!this->fShowAllObjects &&
(pfinddata->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
return (0); // does not match
// Process the case where we are looking for folders only
if (this->fFoldersOnly &&
((pfinddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0))
return (0); // does not match
switch (this->iSizeType)
{
case 1: // >
if (!(pfinddata->nFileSizeLow > this->dwSize))
return (0); // does not match
break;
case 2: // <
if (!(pfinddata->nFileSizeLow < this->dwSize))
return (0); // does not match
break;
}
// See if we should compare dates...
FileTimeToLocalFileTime(&pfinddata->ftLastWriteTime, &ftLocal);
FileTimeToDosDateTime(&ftLocal, &wFileDate, &wFileTime);
if (this->dateModifiedBefore != 0)
{
if (!(wFileDate <= this->dateModifiedBefore))
return (0); // does not match
}
if (this->dateModifiedAfter != 0)
{
if (!(wFileDate >= this->dateModifiedAfter))
return (0); // does not match
}
// Match file specificaitions.
if (this->pszFileSpec && this->pszFileSpec[0])
{
if (!PathMatchSpec(pfinddata->cFileName, this->pszFileSpec))
return (0); // does not match
}
if (this->fExcludeExts &&
_SHFindExcludeExt(pfinddata->cFileName)>=0)
return (0); // does not match
if (this->szTypeFilePatterns[0])
{
if (!PathMatchSpec(pfinddata->cFileName, this->szTypeFilePatterns))
return (0); // does not match
}
//
// See if we need to do a grep of the file
if (this->lpgi)
{
HANDLE hfil;
BOOL fMatch = FALSE;
TCHAR szPath[MAX_PATH];
// Don't grep files with the system bit set.
// This was added explicitly to not search things like the
// swap file and the like.
if (pfinddata->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
return (0); // does not match
lstrcpy(szPath, pszFolder);
PathAppend(szPath, pfinddata->cFileName);
hfil = CreateFile(szPath,
GENERIC_READ ,
FILE_SHARE_READ ,
0, OPEN_EXISTING, 0, 0);
if (hfil != INVALID_HANDLE_VALUE)
{
// Go get around wimpy APIS that set the access date...
FILETIME ftLastAccess;
if (GetFileTime(hfil, NULL, &ftLastAccess, NULL))
SetFileTime(hfil, NULL, &ftLastAccess, NULL);
fMatch = FileFindGrep(this->lpgi, hfil, FIND_FILE, NULL) != 0;
CloseHandle(hfil);
}
if (!fMatch)
return (0); // does not match
}
return (sc); // return TRUE to imply yes!
}
//==========================================================================
// Helper function for save criteria that will output the string and
// and id to the specified file. it will also test for NULL and the like
//==========================================================================
int Docfind_SaveCriteriaItem(IStream * pstm, WORD wNum,
LPTSTR psz, WORD fCharType)
{
if ((psz == NULL) || (*psz == TEXT('\0')))
return 0;
else
{
LPVOID pszText = (LPVOID)psz; // Ptr to output text. Defaults to source.
#ifdef WINNT
//
// These are required to support ANSI-unicode conversions.
//
LPSTR pszAnsi = NULL; // For unicode-to-ansi conversion.
LPWSTR pszWide = NULL; // For ansi-to-unicode conversion.
#endif
DFCRITERIA dfc;
dfc.wNum = wNum;
dfc.cbText = (WORD) ((lstrlen(psz) + 1) * SIZEOF(TCHAR));
#ifdef WINNT
#ifdef UNICODE
//
// Source string is Unicode but caller wants to save as ANSI.
//
if (DFC_FMT_ANSI == fCharType)
{
// Convert to ansi and write ansi.
dfc.cbText = (WORD) WideCharToMultiByte(CP_ACP, 0L, psz, -1, pszAnsi, 0, NULL, NULL);
if ((pszAnsi = (LPSTR)LocalAlloc(LMEM_FIXED, dfc.cbText)) != NULL)
{
WideCharToMultiByte(CP_ACP, 0L, psz, -1, pszAnsi, dfc.cbText / sizeof(pszAnsi[0]), NULL, NULL);
pszText = (LPVOID)pszAnsi;
}
}
#else
//
// Source string is ANSI but caller wants to save as Unicode.
//
if (DFC_FMT_UNICODE == fCharType)
{
// Convert to unicode and write unicode.
dfc.cbText = MultiByteToWideChar(CP_ACP, 0L, psz, -1, pszWide, 0);
if ((pszWide = (LPWSTR)LocalAlloc(LMEM_FIXED, dfc.cbText)) != NULL)
{
MultiByteToWideChar(CP_ACP, 0L, psz, -1, pszWide, dfc.cbText / sizeof(pszWide[0]));
pszText = (LPVOID)pszWide;
}
}
#endif // UNICODE
#endif // WINNT
pstm->lpVtbl->Write(pstm, (LPTSTR)&dfc, SIZEOF(dfc), NULL); // Output index
pstm->lpVtbl->Write(pstm, pszText, dfc.cbText, NULL); // output string + NULL
#ifdef WINNT
//
// Free up conversion buffers if any were created.
//
if (NULL != pszAnsi)
LocalFree(pszAnsi);
if (NULL != pszWide)
LocalFree(pszWide);
#endif
}
return(1);
}
//==========================================================================
// IDocFindFileFilter::SaveCriteria
//==========================================================================
STDMETHODIMP CDFFilter_SaveCriteria(LPDOCFINDFILEFILTER pdfff, IStream * pstm, WORD fCharType)
{
const TCHAR c_szPercentD[] = TEXT("%d");
//
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
int cCriteria;
TCHAR szTemp[40]; // some random size
LPITEMIDLIST pidlMyComputer;
// The caller should have already validated the stuff and updated
// everything for the current filter information.
// we need to walk through and check each of the items to see if we
// have a criteria to save away. this includes:
// (Name, Path, Type, Contents, size, modification dates)
cCriteria = Docfind_SaveCriteriaItem(pstm, IDD_FILESPEC, this->szUserInputFileSpec, fCharType);
pidlMyComputer = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE);
if (this->pidlStart && ILIsEqual(this->pidlStart, pidlMyComputer))
{
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_PATH,
TEXT("::"), fCharType);
}
else
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_PATH,
this->szPath, fCharType);
ILFree(pidlMyComputer);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_TYPECOMBO,
this->szTypeName, fCharType);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_CONTAINS,
this->szText, fCharType);
// Also save away the state of the top level only
wsprintf(szTemp, c_szPercentD, this->fTopLevelOnly);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_TOPLEVELONLY, szTemp, fCharType);
// The Size field is little more fun!
if (this->iSizeType != 0)
{
wsprintf(szTemp, TEXT("%d %ld"), this->iSizeType, this->dwSize);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_SIZECOMP, szTemp, fCharType);
}
// Likewise for the dates, should be fun as we need to save it depending on
// how the date was specified
switch (this->wDateType)
{
case IDD_MDATE_ALL:
// nothing to store
break;
case IDD_MDATE_DAYS:
wsprintf(szTemp, c_szPercentD, this->wDateValue);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_MDATE_NUMDAYS, szTemp, fCharType);
break;
case IDD_MDATE_MONTHS:
wsprintf(szTemp, c_szPercentD, this->wDateValue);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_MDATE_NUMMONTHS, szTemp, fCharType);
break;
case IDD_MDATE_BETWEEN:
if (this->dateModifiedAfter != 0)
{
wsprintf(szTemp, c_szPercentD, this->dateModifiedAfter);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_MDATE_FROM, szTemp, fCharType);
}
if (this->dateModifiedBefore != 0)
{
wsprintf(szTemp, c_szPercentD, this->dateModifiedBefore);
cCriteria += Docfind_SaveCriteriaItem(pstm, IDD_MDATE_TO, szTemp, fCharType);
}
break;
}
return (MAKE_SCODE(0, 0, cCriteria));
}
//==========================================================================
// IDocFindFileFilter::RestoreCriteria
//==========================================================================
STDMETHODIMP CDFFilter_RestoreCriteria(LPDOCFINDFILEFILTER pdfff,
IStream * pstm, int cCriteria, WORD fCharType)
{
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
TCHAR szTemp[MAX_PATH]; // some random size
if (cCriteria > 0)
this->fWeRestoredSomeCriteria = TRUE;
while (cCriteria--)
{
// BUGBUG(DavePl) I'm assuming that if UNICODE chars are written in this
// stream, the cb is written accordingly. ie: what you put in the stream
// is your own business, but write the cb as a byte count, not char count
DFCRITERIA dfc;
int cb;
if (FAILED(pstm->lpVtbl->Read(pstm, &dfc, SIZEOF(dfc), &cb))
|| (cb != SIZEOF(dfc)) || (dfc.cbText > SIZEOF(szTemp)))
break;
#ifdef WINNT
#ifdef UNICODE
if (DFC_FMT_UNICODE == fCharType)
{
//
// Destination is Unicode and we're reading Unicode data from stream.
// No conversion required.
//
if (FAILED(pstm->lpVtbl->Read(pstm, szTemp, dfc.cbText, &cb))
|| (cb != dfc.cbText))
break;
}
else
{
char szAnsi[MAX_PATH];
//
// Destination is Unicode but we're reading ANSI data from stream.
// Read ansi. Convert to unicode.
//
if (FAILED(pstm->lpVtbl->Read(pstm, szAnsi, dfc.cbText, &cb))
|| (cb != dfc.cbText))
break;
MultiByteToWideChar(CP_ACP, 0L, szAnsi, -1, szTemp, ARRAYSIZE(szTemp));
}
#else
if (DFC_FMT_ANSI == fCharType)
{
//
// Destination is ANSI and we're reading ANSI data from stream.
// No conversion required.
//
if (FAILED(pstm->lpVtbl->Read(pstm, szTemp, dfc.cbText, &cb))
|| (cb != dfc.cbText))
break;
}
else
{
//
// Destination is ANSI but we're reading Unicode data from stream.
// Read unicode. Convert to ansi.
//
WCHAR szWide[MAX_PATH];
if (FAILED(pstm->lpVtbl->Read(pstm, szWide, dfc.cbText, &cb))
|| (cb != dfc.cbText))
break;
WideCharToMultiByte(CP_ACP, 0L, szWide, -1, szTemp, ARRAYSIZE(szTemp), NULL, NULL);
}
#endif // UNICODE
#else
if (FAILED(pstm->lpVtbl->Read(pstm, &szTemp, dfc.cbText, &cb))
|| (cb != dfc.cbText))
break;
#endif // WINNT
switch (dfc.wNum)
{
case IDD_FILESPEC:
lstrcpy(this->szUserInputFileSpec, szTemp);
break;
case IDD_PATH:
if (lstrcmp(szTemp, TEXT("::")) == 0)
{
this->pidlStart = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE);
szTemp[0] = TEXT('\0');
}
else if (StrChr(szTemp,TEXT(';')) == NULL)
{
// Simple pidl...
this->pidlStart = ILCreateFromPath(szTemp);
}
lstrcpy(this->szPath, szTemp);
break;
case IDD_TOPLEVELONLY:
this->fTopLevelOnly = StrToInt(szTemp);
break;
case IDD_TYPECOMBO:
lstrcpy(this->szTypeName, szTemp);
break;
case IDD_CONTAINS:
lstrcpy(this->szText, szTemp);
break;
case IDD_SIZECOMP:
// we need to extract off the two parts, the type and
// the value
this->iSizeType = szTemp[0] - TEXT('0');
this->dwSize = StrToInt(&szTemp[2]);
break;
case IDD_MDATE_NUMDAYS:
this->wDateType = IDD_MDATE_DAYS;
this->wDateValue = (WORD) StrToInt(szTemp);
break;
case IDD_MDATE_NUMMONTHS:
this->wDateType = IDD_MDATE_MONTHS;
this->wDateValue = (WORD) StrToInt(szTemp);
break;
case IDD_MDATE_FROM:
this->wDateType = IDD_MDATE_BETWEEN;
this->dateModifiedAfter = (WORD) StrToInt(szTemp);
break;
case IDD_MDATE_TO:
this->wDateType = IDD_MDATE_BETWEEN;
this->dateModifiedBefore = (WORD) StrToInt(szTemp);
break;
}
}
return (S_OK);
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Now starting the code for the name and location page
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//=========================================================================
void DocFind_SizeControl(HWND hwndDlg, int id, int cx, BOOL fCombo)
{
RECT rc;
RECT rcList;
HWND hwndCtl;
GetWindowRect(hwndCtl = GetDlgItem(hwndDlg, id), &rc);
MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rc, 2);
if (fCombo)
{
// These guys are comboboxes so work with them...
SendMessage(hwndCtl, CB_GETDROPPEDCONTROLRECT, 0,
(LPARAM)(RECT *)&rcList);
rc.bottom += (rcList.bottom - rcList.top);
}
SetWindowPos(hwndCtl, NULL, 0, 0, cx - rc.left,
rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
//==========================================================================
//
// Process the WM_SIZE of the details page
//
void DocFind_DFNameLocOnSize(HWND hwndDlg, UINT state, int cx, int cy)
{
RECT rc;
int cxMargin;
HWND hwndCtl;
if (state == SIZE_MINIMIZED)
return; // don't bother when we are minimized...
// Get the location of first static to calculate margin
GetWindowRect(GetDlgItem(hwndDlg, IDD_STATIC), &rc);
MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rc, 2);
cxMargin = rc.left;
cx -= cxMargin;
DocFind_SizeControl(hwndDlg, IDD_FILESPEC, cx, TRUE);
// Now move the browse button
GetWindowRect(hwndCtl = GetDlgItem(hwndDlg, IDD_BROWSE), &rc);
MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rc, 2);
SetWindowPos(hwndCtl, NULL, cx - (rc.right - rc.left), rc.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
// And size the path field
DocFind_SizeControl(hwndDlg, IDD_PATH,
cx - cxMargin - (rc.right - rc.left), TRUE);
}
/*----------------------------------------------------------------------------
/ build_drive_string implementation
/ ------------------
/ Purpose:
/ Convert from a bit stream ( 1 bit per drive ) to a comma seperated
/ list of drives.
/
/ Notes:
/
/ In:
/ uDrives = 1 bit per drive (bit 0 == A:, bit 25 == Z:) bit == 1 indicates drive
/ to be listed.
/ pszBiffer -> buffer to place text into
/ iBufferSize = size of the buffer.
/ pSepStr -> seperating string used to seperate drive names
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
static void build_drive_string( UINT uDrives, LPTSTR pBuffer, int iSize, LPTSTR pSepStr )
{
TCHAR szDrive[] = TEXT("A:");
int iMaxStrLen = lstrlen( szDrive ) + lstrlen( pSepStr );
Assert( pBuffer != NULL ); // sanitise the parameters
Assert( iSize > 0 );
*pBuffer = L'\0';
while ( uDrives && ( iSize > iMaxStrLen ) )
{
if ( uDrives & 1 )
{
lstrcat( pBuffer, szDrive );
if ( uDrives & ~1 )
lstrcat( pBuffer, pSepStr );
iSize -= iMaxStrLen;
}
szDrive[0]++;
uDrives >>= 1;
}
}
//==========================================================================
// Initialize the Name and loacation page
//==========================================================================
DWORD CALLBACK DocFind_RealDFNameLocInit(LPVOID lpThreadParameters)
{
LPDOCFINDPROPSHEETPAGE pdfpsp = lpThreadParameters;
LPDFFILTER pdff = pdfpsp->pdff;
// We process the message after the WM_CREATE or WM_INITDLG as to
// allow the dialog to come up quicker...
TCHAR szPath[MAX_PATH];
LPITEMIDLIST pidlWindows;
IShellFolder * psf;
HWND hwndCtl;
HRESULT hres;
LPITEMIDLIST pidlAbs;
LPITEMIDLIST pidlStart = pdff->pidlStart;
int ipidlStart = -1; // If pidl passed in and in our list already...
HANDLE hEnum;
UINT uFixedDrives = 0; // 1 bit per 'fixed' drive, start at 0.
int iMyComputer;
int iDrive;
GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
if (szPath[1] == TEXT(':'))
szPath[3] = TEXT('\0');
pidlWindows = SHSimpleIDListFromPath(szPath);
// We need to initialize the look in list with the list of
// directories that are under "My Computer"
hwndCtl = GetDlgItem(pdfpsp->hwndDlg, IDD_PATH);
if (pdff->psfMyComputer == NULL)
{
LPITEMIDLIST pidlMyComputer = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE);
LPSHELLFOLDER psfDesktop=Desktop_GetShellFolder(TRUE);
/* We need the index for my computer so that we can insert the all local storage
/ item in after it. */
iMyComputer = DocFind_LocCBAddPidl(hwndCtl, Desktop_GetShellFolder(TRUE), &s_idlEmpty,
pidlMyComputer, &pidlAbs, FALSE);
if (pidlStart && ILIsEqual(pidlAbs, pidlStart))
{
pidlStart = NULL; // So I wont keep looking
ipidlStart = 0; // First item in the list!
}
if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
pidlMyComputer, NULL, &IID_IShellFolder,
&pdff->psfMyComputer)))
{
// We now need to iterate over the children under this guy...
LPENUMIDLIST penum;
LPITEMIDLIST pidl;
psf = pdff->psfMyComputer;
hres = psf->lpVtbl->EnumObjects(psf, pdfpsp->hwndDlg, SHCONTF_NONFOLDERS, &penum);
if (SUCCEEDED(hres))
{
while (NULL != (pidl = DocFind_NextIDL(psf, penum)))
{
ULONG ulAttrs;
int i;
// We only want to add this object if it is a
// file system level object, like drives.
//
ulAttrs = SFGAO_FILESYSTEM;
psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &ulAttrs);
if (ulAttrs & SFGAO_FILESYSTEM)
{
i = DocFind_LocCBAddPidl(hwndCtl, psf, pidlMyComputer,
pidl, &pidlAbs, FALSE);
if (ILIsEqual(pidlAbs, pidlWindows))
pdff->iMyComputer = i;
if (pidlStart && ILIsEqual(pidlAbs, pidlStart))
{
pidlStart = NULL;
ipidlStart = i; // First item in the list!
}
/* Update the bit mask to reflect if this is a fixed drive, the flags do not define
/ this therefore we must get the drive number and the type of drive from that. UNC
/ drives will have a drive number of -1, and therefore can be skipped in our checks. */
SHGetPathFromIDList( pidlAbs, szPath );
if ( ( iDrive = PathGetDriveNumber( szPath ) ) >= 0 )
{
if ( ( RealDriveTypeFlags( iDrive, FALSE ) & DRIVE_TYPE ) == DRIVE_FIXED )
{
Assert( ( uFixedDrives & ( 1 << iDrive ) ) == 0 ); // must be an empty drive slot
uFixedDrives |= 1 << iDrive;
}
}
}
ILFree(pidl);
}
penum->lpVtbl->Release(penum);
}
}
ILFree(pidlMyComputer);
/* If we found some fixed drives then attempt to add a new entry to the list box. This is placed below
/ the 'My Computer' entry and maps to all the fixed drives. As this kind of path (referencing multiple roots)
/ cannot be expresed using a PIDL we set the PIDL to NULL, and store the bit array containing the drive
/ mappings in the structure.
/
/ Because of this we must be careful to catch the PIDL being NULL, and composing the correct search path
/ when we validate the page. */
if ( uFixedDrives != 0 )
{
LPDFCBITEM pdfcbi;
LPVOID pMessage;
TCHAR szDrives[ MAX_PATH ];
LPTSTR ppDrives[] = { szDrives, NULL };
TCHAR szBuffer[ 256 ];
int iItem;
build_drive_string( uFixedDrives, szDrives, ARRAYSIZE(szDrives), TEXT(",") );
LoadString( HINST_THISDLL, IDS_FINDSEARCH_ALLDRIVES, szBuffer, ARRAYSIZE( szBuffer ) );
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
szBuffer, // our template string to format from
0, 0, // id and locale (ignored)
(LPTSTR) &pMessage, 0, // buffer and min size fo allocated
(va_list*) ppDrives ); // argument strings
Assert( pMessage);
if ( pMessage )
{
pdfcbi = (LPDFCBITEM) LocalAlloc( LPTR, SIZEOF(DFCBITEM) );
if ( pdfcbi )
{
pdfcbi ->pidl = NULL;
pdfcbi ->iImage = II_DRIVEFIXED;
pdfcbi ->uFixedDrives = uFixedDrives;
iItem = SendMessage( hwndCtl, CB_INSERTSTRING, iMyComputer + 1, (LPARAM) pMessage );
Assert( iItem != CB_ERRSPACE );
if ( iItem != CB_ERRSPACE )
{
SendMessage( hwndCtl, CB_SETITEMDATA, iItem, (LPARAM) pdfcbi );
if ( ipidlStart > iMyComputer )
ipidlStart++;
if ( pdff->iMyComputer > iMyComputer )
pdff->iMyComputer++;
}
else
{
LocalFree( (HLOCAL) pdfcbi );
}
}
LocalFree( (HGLOBAL) pMessage );
}
}
// Also see if there are any UNC style connections that the
// user might be interested in seeing in this list
//
if (WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_DISK,
RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED, NULL, &hEnum) == WN_SUCCESS)
{
LPITEMIDLIST pidl;
DWORD dwCount=1;
union
{
NETRESOURCE nr; // Large stack usage but I
TCHAR buf[1024]; // Dont think it is thunk to 16 bits...
}nrb;
DWORD dwBufSize = SIZEOF(nrb);
while (WNetEnumResource(hEnum, &dwCount, &nrb.buf,
&dwBufSize) == WN_SUCCESS)
{
// We only want to add items if they do not have a local
// name. If they had a local name we would have already
// added them!
if ((nrb.nr.lpRemoteName != NULL) &&
((nrb.nr.lpLocalName == NULL) || (*nrb.nr.lpLocalName == TEXT('\0'))))
{
pidl = ILCreateFromPath(nrb.nr.lpRemoteName);
if (pidl)
{
DocFind_LocCBAddPidl(hwndCtl,
Desktop_GetShellFolder(TRUE), &s_idlEmpty,
pidl, NULL, FALSE);
}
}
}
WNetCloseEnum(hEnum);
}
}
if (pidlStart)
{
// We were passed in a pidl that is not already in our list so add it!
LPITEMIDLIST pidlParent = ILClone(pidlStart);
LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);
ILRemoveLastID(pidlParent);
if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
pidlParent, NULL, &IID_IShellFolder, &psf)))
{
SetWindowText(hwndCtl, pdff->szPath);
ipidlStart = DocFind_LocCBAddPidl(hwndCtl, psf, pidlParent,
(LPITEMIDLIST)ILFindLastID(pidlStart), NULL, TRUE);
psf->lpVtbl->Release(psf);
}
ILFree(pidlParent);
}
// If we have starting text for path set it as the text for it now...
if ((ipidlStart == -1) && (pdff->szPath[0] != TEXT('\0')))
{
SetWindowText(hwndCtl, pdff->szPath);
}
else
{
if (ipidlStart == -1)
ipidlStart = pdff->iMyComputer;
// Finally select the right one in the list.
SendMessage(hwndCtl, CB_SETCURSEL, ipidlStart, 0);
FORWARD_WM_COMMAND(pdfpsp->hwndDlg, IDD_PATH, hwndCtl,
CBN_SELCHANGE, PostMessage);
}
SendMessage(hwndCtl, CB_SETEDITSEL, 0, MAKELPARAM(0,-1));
ILFree(pidlWindows);
return(0);
}
void DocFind_DFNameLocInit(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
// We want to set the default search drive to the windows drive.
// I am going to be a bit slimmy, but...
//
CheckDlgButton(pdfpsp->hwndDlg, IDD_TOPLEVELONLY, !pdff->fTopLevelOnly);
if ((pdfpsp->dwState & DFPAGE_INIT) == 0)
{
DWORD idThread;
// Do most of the init code offline in a second thread...
pdfpsp->hThreadInit = CreateThread(NULL, 0,
DocFind_RealDFNameLocInit, (LPVOID)pdfpsp, 0, &idThread);
pdff->hMRUSpecs = DocFind_UpdateMRUItem(NULL, pdfpsp->hwndDlg, IDD_FILESPEC,
s_szDocSpecMRU, pdff->szUserInputFileSpec, szNULL);
// Update our state to let us know that we have already initialized...
pdfpsp->dwState |= DFPAGE_INIT;
}
if (pdff->fTypeChanged)
{
// We need to see if the file named field has an extension
// if it does, we will black it out as the user changed the
// type field.
if (StrChr(pdff->szUserInputFileSpec, TEXT('.')) != NULL)
SetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC, szNULL);
pdff->fTypeChanged = FALSE;
}
}
//==========================================================================
// Helper function to display a message box, set the focus to the the
// item with the problem, set the abort condition and return.
//==========================================================================
void DocFind_ReportItemValueError(HWND hwndDlg, int idCtl,
int iMsg, LPTSTR pszValue)
{
// We may pass through these pages at init time to make sure that
// everything is initialize properly when we restore a search...
if (!IsWindowVisible(hwndDlg))
return;
ShellMessageBox(HINST_THISDLL, hwndDlg,
MAKEINTRESOURCE(iMsg),
MAKEINTRESOURCE(IDS_FINDFILES), MB_OK|MB_ICONERROR, pszValue);
SetFocus(GetDlgItem(hwndDlg, idCtl));
SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE); // Tell it to abort
return;
}
//==========================================================================
//
// DocFind_UpdateMRUItem - Initializes and Updates an MRU list item
//
HANDLE DocFind_UpdateMRUItem(HANDLE hMRU, HWND hwndDlg, int iDlgItem,
LPCTSTR szSection, LPTSTR pszInitString, LPCTSTR pszAddIfEmpty)
{
HANDLE hCB;
int i, nMax;
TCHAR szItem[MAX_PATH];
BOOL fAllowEmptyItem = (pszAddIfEmpty == NULL) || (pszAddIfEmpty && (*pszAddIfEmpty == TEXT('\0')));
hCB = GetDlgItem(hwndDlg, iDlgItem);
if (hMRU == NULL) {
MRUINFO mi = {
SIZEOF(MRUINFO),
10,
0L,
HKEY_CURRENT_USER,
szSection,
NULL
};
hMRU = CreateMRUList(&mi);
}
if (hMRU == NULL)
return(NULL);
SendMessage(hCB, CB_RESETCONTENT, 0, 0L);
// Only Allow empty string if the AddifEmpty is set to empty string
if (((pszInitString != NULL) && (*pszInitString != TEXT('\0'))) || fAllowEmptyItem)
AddMRUString(hMRU, pszInitString);
if (((nMax = EnumMRUList(hMRU, -1, NULL, 0)) == 0) && (!fAllowEmptyItem))
{
AddMRUString(hMRU, pszAddIfEmpty);
nMax++;
}
for (i=0; i<nMax; ++i)
{
if ((EnumMRUList(hMRU, i, szItem, ARRAYSIZE(szItem)) > 0) ||
fAllowEmptyItem)
{
/* The command to run goes in the combobox.
*/
SendMessage(hCB, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)szItem);
if (szItem[0] == TEXT('\0'))
fAllowEmptyItem = FALSE; // only allow 1
}
}
SendMessage(hCB, CB_SETCURSEL, 0, 0L);
return(hMRU);
}
//==========================================================================
// Validate the page to make sure that the data is valid. If it is not
// we need to display a message to the user and also set the focus to
// the invalid field.
//==========================================================================
void DocFind_DFNameGetPathOrPidl(LPDOCFINDPROPSHEETPAGE pdfpsp,
LPTSTR pszPath, LPITEMIDLIST *ppidl)
{
int iSel;
LPDFCBITEM pdfcbi;
// if the pidl was previously set, clear it now.
if (*ppidl)
ILFree(*ppidl);
// If the user types in a fully qualified name in the filespec
// we will use the path part to override the look in field
GetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC, pszPath, MAX_PATH);
if (!PathIsRelative(pszPath))
{
// First Update the file pattern
if (PathIsRoot(pszPath) || PathIsDirectory(pszPath))
{
SetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC, c_szNULL);
}
else
{
SetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC,
PathFindFileName(pszPath));
PathRemoveFileSpec(pszPath); // remove the last part...
}
// Setup the text of the look in field
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH, CB_SETCURSEL, (WPARAM)-1, 0);
SetDlgItemText(pdfpsp->hwndDlg, IDD_PATH, pszPath);
}
iSel = (int)SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH, CB_GETCURSEL, 0, 0);
if (iSel != CB_ERR)
{
pdfcbi = (LPDFCBITEM)SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH,
CB_GETITEMDATA, iSel, 0);
/* If pidl == NULL then we special case and expand the fixed drive flags out to a path
/ containing the drives that we are going to search. */
if ( pdfcbi ->pidl )
{
SHGetPathFromIDList(pdfcbi->pidl, pszPath);
*ppidl = ILClone(pdfcbi->pidl);
}
else
{
Assert( pdfcbi ->uFixedDrives );
build_drive_string( pdfcbi ->uFixedDrives, pszPath, MAX_PATH, TEXT("\\;") );
*ppidl = NULL;
}
}
else
{
LPTSTR pszT;
GetDlgItemText(pdfpsp->hwndDlg, IDD_PATH, pszPath, MAX_PATH);
// See if we have an Exact match in the combobox...
// This handles the case the user types directly in a path...
iSel = (int)SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH,
CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pszPath);
if (iSel != CB_ERR)
{
pdfcbi = (LPDFCBITEM)SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH,
CB_GETITEMDATA, iSel, 0);
/* If pidl == NULL then we special case and expand the fixed drive flags out to a path
/ containing the drives that we are going to search. */
if ( pdfcbi ->pidl )
{
*ppidl = ILClone(pdfcbi->pidl);
}
else
{
Assert( pdfcbi ->uFixedDrives );
build_drive_string( pdfcbi ->uFixedDrives, pszPath, MAX_PATH, TEXT("\\;") );
*ppidl = NULL;
}
}
else
{
// If we only have one path
pszT = pszPath + StrCSpn(pszPath, TEXT(",;"));
if (*pszT == TEXT('\0'))
{
// Try to parse the display name into a Pidl
PathQualify(pszPath);
*ppidl = ILCreateFromPath(pszPath);
if (!*ppidl)
{
TCHAR szDisplayName[MAX_PATH];
int cch;
// See if the beginning of the string matches the
// start of an item in the list. If so we should
// Try to see if we can convert it to a valid
// pidl/string...
for (iSel=SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH,
CB_GETCOUNT, 0, 0); iSel >= 1; iSel--)
{
GetDlgItemText(pdfpsp->hwndDlg, IDD_PATH, pszPath, MAX_PATH);
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH, CB_GETLBTEXT,
(WPARAM)iSel, (LPARAM)szDisplayName);
cch = lstrlen(szDisplayName);
if ((CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
szDisplayName, cch, pszPath, cch) == 2) &&
(pszPath[cch] == TEXT('\\')))
{
LPDFCBITEM pdfcbi;
pdfcbi = (LPDFCBITEM)SendDlgItemMessage(pdfpsp->hwndDlg, IDD_PATH,
CB_GETITEMDATA, (WPARAM)iSel, 0);
// Don't blow up if we get a failure here
if (pdfcbi != (LPDFCBITEM)-1)
{
SHGetPathFromIDList(pdfcbi->pidl, szDisplayName);
PathAppend(szDisplayName, CharNext(pszPath+cch));
*ppidl = ILCreateFromPath(szDisplayName);
if (*ppidl)
{
lstrcpy(pszPath, szDisplayName);
break;
}
}
#ifdef DEBUG
else
{
DebugMsg(DM_TRACE, TEXT("DocFind_DFNameGetPathOrPidl - CBItem ptr = -1"));
}
#endif
}
}
}
}
else
{
// Multiple things let it run by itself...
*ppidl = NULL; // dont have a pidl to begin with...
}
}
}
}
//==========================================================================
// Validate the page to make sure that the data is valid. If it is not
// we need to display a message to the user and also set the focus to
// the invalid field.
//==========================================================================
void DocFind_DFNameLocValidatePage(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
HWND hwndDlg = pdfpsp->hwndDlg;
TCHAR szTemp[MAX_PATH];
LPTSTR pszT;
LPITEMIDLIST pidl = NULL;
// Make sure the look in field init code has completed..
WaitForPageInitToComplete(pdfpsp);
DocFind_DFNameGetPathOrPidl(pdfpsp, szTemp, &pidl);
if (pidl == NULL)
{
if (lstrlen(szTemp) == 0)
{
DocFind_ReportItemValueError(hwndDlg, IDD_PATH, IDS_FINDDATAREQUIRED, szTemp);
return;
}
// Try to make input like: "C:\;d:\" work. Only validate the first one.
pszT = szTemp + StrCSpn(szTemp, TEXT(",;"));
*pszT = TEXT('\0'); // Null out the point...
PathQualify(szTemp);
if (!PathIsDirectory(szTemp))
{
DocFind_ReportItemValueError(hwndDlg, IDD_PATH, IDS_FINDWRONGPATH, szTemp);
return;
}
}
else
{
BYTE bType;
// Don't allow the user to start search at junction point that
// is a file system directory as special code is probably involved
// to search these folders.
bType = SIL_GetType(ILFindLastID(pidl));
if (bType == (SHID_FS_DIRECTORY | SHID_JUNCTION) ||
bType == (SHID_FS_DIRUNICODE | SHID_JUNCTION) )
{
// Ok it is a junction, but are the objects under it File system?
HRESULT hres;
IShellFolder *psf;
LPITEMIDLIST pidlFile;
LONG Flags = 0;
hres = SHBindToIDListParent(pidl, &IID_IShellFolder, &psf, &pidlFile);
if (SUCCEEDED(hres))
{
Flags = (SFGAO_FILESYSANCESTOR|SFGAO_FOLDER);
psf->lpVtbl->GetAttributesOf(psf, 1, &pidlFile, &Flags);
psf->lpVtbl->Release(psf);
}
if (Flags & (SFGAO_FILESYSANCESTOR|SFGAO_FOLDER) !=
(SFGAO_FILESYSANCESTOR|SFGAO_FOLDER))
{
ILFree(pidl);
DocFind_ReportItemValueError(hwndDlg, IDD_PATH,
IDS_FINDNOTFINDABLE, szTemp);
return;
}
}
ILFree(pidl);
}
// Now Check the file specification.
GetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC, szTemp, ARRAYSIZE(szTemp));
if (lstrcmp(szTemp, pdff->szUserInputFileSpec) != 0)
{
// Make sure that the file spec does not contain invalid characters
pszT = szTemp + StrCSpn(szTemp, TEXT(":\\"));
if (*pszT != TEXT('\0'))
{
DocFind_ReportItemValueError(hwndDlg, IDD_FILESPEC, IDS_FINDINVALIDFILENAME,
szTemp);
return;
}
// The file pattern changed.
lstrcpy(pdff->szUserInputFileSpec, szTemp);
// See if we should invalidate the type field
if (StrChr(szTemp, TEXT('.')) != NULL)
{
pdff->fNameChanged = TRUE;
pdff->szTypeFilePatterns[0] = TEXT('\0'); // null it out...
}
}
}
//==========================================================================
//
// Apply any changes that happened in the name loc page to the filter
//
void DocFind_DFNameLocApply(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
// Get the path name and qualify it
DocFind_DFNameGetPathOrPidl(pdfpsp, pdff->szPath, &pdff->pidlStart);
// Also get the file specification.
if (pdff->fTypeChanged)
{
// We need to see if the file named field has an extension
// if it does, we will black it out as the user changed the
// type field.
if (StrChr(pdff->szUserInputFileSpec, TEXT('.')) != NULL)
SetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC, szNULL);
pdff->fTypeChanged = FALSE;
}
GetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC, pdff->szUserInputFileSpec,
ARRAYSIZE(pdff->szUserInputFileSpec));
DocFind_UpdateMRUItem(pdff->hMRUSpecs, pdfpsp->hwndDlg, IDD_FILESPEC,
s_szDocSpecMRU, pdff->szUserInputFileSpec, NULL);
pdff->fTopLevelOnly = !IsDlgButtonChecked(pdfpsp->hwndDlg, IDD_TOPLEVELONLY);
}
//==========================================================================
//
// DocFind_OnCommand - Process the WM_COMMAND messages
//
void DocFind_DFNameLocOnCommand(HWND hwndDlg, UINT id, HWND hwndCtl, UINT code)
{
LPDOCFINDPROPSHEETPAGE pdfpsp = (LPDOCFINDPROPSHEETPAGE)GetWindowLong(hwndDlg, DWL_USER);
switch (id) {
case IDD_BROWSE:
if (code == BN_CLICKED)
{
LPITEMIDLIST pidlCurrent;
TCHAR szDisplayName[MAX_PATH];
BROWSEINFO bi = {
hwndDlg,
NULL,
szDisplayName,
MAKEINTRESOURCE(IDS_FINDSEARCHTITLE),
BIF_RETURNONLYFSDIRS,
NULL
};
pidlCurrent = SHBrowseForFolder(&bi);
if (pidlCurrent && pidlCurrent->mkid.cb==0)
{
// The user chose the desktop folder. Try converting
// it over to the desktop folder directory...
ILFree(pidlCurrent);
pidlCurrent = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, FALSE);
}
// Now convert into a path name to update the drop down list
// with...
if (pidlCurrent)
{
int i;
HWND hwndCtl = GetDlgItem(hwndDlg, IDD_PATH);
// Make sure the look in field init code has completed..
WaitForPageInitToComplete(pdfpsp);
if ((i=DocFind_LocCBFindPidl(hwndCtl, pidlCurrent)) >= 0)
{
SendMessage(hwndCtl, CB_SETCURSEL, i, 0);
FORWARD_WM_COMMAND(hwndDlg, IDD_PATH, hwndCtl,
CBN_SELCHANGE, SendMessage);
ILFree(pidlCurrent);
}
else
{
// Lets try getting the full display name for these items...
TCHAR szTemp[MAX_PATH];
LPDFCBITEM pdfcbi;
if (SHGetPathFromIDList(pidlCurrent, szTemp))
lstrcpy(szDisplayName, szTemp);
// Add this item to our Drop Down list
pdfcbi = (LPDFCBITEM)LocalAlloc(LPTR, SIZEOF(DFCBITEM));
if (pdfcbi)
{
pdfcbi->pidl = pidlCurrent;
pdfcbi->iImage = bi.iImage;
i = SendMessage(hwndCtl, CB_ADDSTRING, 0,
(LPARAM)szDisplayName);
if (i != CB_ERRSPACE)
SendMessage(hwndCtl, CB_SETITEMDATA, i, (LPARAM)pdfcbi);
SendMessage(hwndCtl, CB_SETCURSEL, i, 0);
FORWARD_WM_COMMAND(hwndDlg, IDD_PATH, hwndCtl,
CBN_SELCHANGE, SendMessage);
}
}
}
}
break;
case IDD_PATH:
// Make sure the look in field init code has completed..
WaitForPageInitToComplete(pdfpsp);
if ((code == CBN_SELCHANGE) || (code == CBN_SELENDOK))
{
int iSel = (int)SendMessage(hwndCtl, CB_GETCURSEL, 0, 0);
if (iSel != CB_ERR)
{
HWND hwndFocus = GetFocus();
if ((hwndFocus == hwndCtl) || IsChild(hwndCtl, hwndFocus))
SendMessage(hwndCtl, CB_SETEDITSEL, 0, MAKELPARAM(0,-1));
}
}
break;
case IDD_FILESPEC:
if ((code == CBN_SELCHANGE) || (code == CBN_EDITCHANGE))
pdfpsp->pdff->fFilterChanged = TRUE;
break;
}
}
//==========================================================================
//
// This function is the dialog (or property sheet page) for the name and
// location page.
//
BOOL CALLBACK DocFind_DFNameLocDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPDOCFINDPROPSHEETPAGE pdfpsp = (LPDOCFINDPROPSHEETPAGE)GetWindowLong(hwndDlg, DWL_USER);
switch (msg) {
HANDLE_MSG(hwndDlg, WM_COMMAND, DocFind_DFNameLocOnCommand);
HANDLE_MSG(hwndDlg, WM_SIZE, DocFind_DFNameLocOnSize);
HANDLE_MSG(hwndDlg, WM_MEASUREITEM, DocFind_LocCBMeasureItem);
HANDLE_MSG(hwndDlg, WM_DRAWITEM, DocFind_LocCBDrawItem);
case WM_INITDIALOG:
SetWindowLong(hwndDlg, DWL_USER, lParam);
pdfpsp = (LPDOCFINDPROPSHEETPAGE)lParam;
pdfpsp->hwndDlg = hwndDlg;
break;
case WM_WININICHANGE:
case WM_SYSCOLORCHANGE:
RelayMessageToChildren(hwndDlg, msg, wParam, lParam);
break;
case WM_DESTROY:
// We should free all of the id lists associated with this object
{
int i;
LPDFCBITEM pdfcbi;
HWND hwndCtl = GetDlgItem(hwndDlg, IDD_PATH);
WaitForPageInitToComplete(pdfpsp);
for (i= (int)SendDlgItemMessage(hwndDlg, IDD_PATH, CB_GETCOUNT, 0, 0) - 1;
i >= 0 ; i--)
{
pdfcbi = (LPDFCBITEM)SendMessage(hwndCtl,
CB_GETITEMDATA, i, 0);
if (pdfcbi)
{
ILFree(pdfcbi->pidl);
LocalFree(pdfcbi);
SendMessage(hwndCtl, CB_SETITEMDATA, i, 0);
}
}
}
break;
case WM_NCDESTROY:
Free(pdfpsp);
SetWindowLong(hwndDlg, DWL_USER, 0);
return FALSE; // We MUST return FALSE to avoid mem-leak
case DFM_ENABLECHANGES:
EnableWindow(GetDlgItem(hwndDlg, IDD_FILESPEC), (BOOL)wParam);
EnableWindow(GetDlgItem(hwndDlg, IDD_PATH), (BOOL)wParam);
EnableWindow(GetDlgItem(hwndDlg, IDD_TOPLEVELONLY), (BOOL)wParam);
break;
case WM_HELP:
WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP,
(DWORD) (LPTSTR) aNameHelpIDs);
break;
case WM_CONTEXTMENU: // right mouse click
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
(DWORD) (LPTSTR) aNameHelpIDs);
break;
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code) {
case PSN_KILLACTIVE:
DocFind_DFNameLocValidatePage(pdfpsp);
break;
case PSN_SETACTIVE:
DocFind_DFNameLocInit(pdfpsp);
break;
case PSN_APPLY:
if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
DocFind_DFNameLocApply(pdfpsp);
break;
case PSN_RESET:
if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
{
// Null the filespec
SetDlgItemText(hwndDlg, IDD_FILESPEC, c_szNULL);
// Reset to default search from place...
SendDlgItemMessage(hwndDlg, IDD_PATH, CB_SETCURSEL,
pdfpsp->pdff->iMyComputer, 0);
FORWARD_WM_COMMAND(hwndDlg, IDD_PATH,
GetDlgItem(hwndDlg, IDD_PATH),
CBN_SELCHANGE, SendMessage);
CheckDlgButton(hwndDlg, IDD_TOPLEVELONLY, TRUE);
}
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Now starting the code for the details page
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//==========================================================================
//
// Process the WM_SIZE of the details page
//
void DocFind_DFDetailsOnSize(HWND hwndDlg, UINT state, int cx, int cy)
{
RECT rc;
if (state == SIZE_MINIMIZED)
return; // don't bother when we are minimized...
// Get the location of first static to calculate margin
GetWindowRect(GetDlgItem(hwndDlg, IDD_STATIC), &rc);
MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rc, 2);
cx -= rc.left;
DocFind_SizeControl(hwndDlg, IDD_TYPECOMBO, cx, TRUE);
DocFind_SizeControl(hwndDlg, IDD_CONTAINS, cx, FALSE);
}
//==========================================================================
//
// Initialize the Details page.
//
void DocFind_DFDetailsInit(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
HWND hwndCtl;
int i;
TCHAR szTemp[80];
if ((pdfpsp->dwState & DFPAGE_INIT) == 0)
{
// Initialize the list of types
hwndCtl = GetDlgItem(pdfpsp->hwndDlg, IDD_TYPECOMBO);
FillListWithClasses(hwndCtl, TRUE, GCD_MUSTHAVEEXTASSOC);
LoadString(HINST_THISDLL, IDS_FOLDERTYPENAME, szTemp, ARRAYSIZE(szTemp));
i = SendMessage(hwndCtl, CB_ADDSTRING, 0, (LONG)(LPTSTR)szTemp);
SendMessage(hwndCtl, CB_SETITEMDATA, i, (DWORD)AddHashItem(NULL, c_szFolderClass));
// See if the type name is non null, if so we should initialize it...
if (pdff->szTypeName[0] != TEXT('\0'))
{
i = SendMessage(hwndCtl, CB_FINDSTRING, (WPARAM)-1, (LPARAM)pdff->szTypeName);
if (i > 0)
pdff->iType = i + 1; // The All files and folders will be inserted at top...
}
// Turn off the Sort bit and add the all item at the start...
SetWindowLong (hwndCtl, GWL_STYLE,
GetWindowLong(hwndCtl, GWL_STYLE) & ~CBS_SORT);
LoadString(HINST_THISDLL, IDS_FINDALLFILETYPES, szTemp, ARRAYSIZE(szTemp));
SendMessage(hwndCtl, CB_INSERTSTRING, 0, (LONG)(LPTSTR)szTemp);
SendMessage(hwndCtl, CB_SETCURSEL, pdff->iType, 0);
// If the filter contains search text we need to initialize the
// string in the dialog
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_CONTAINS,
EM_SETLIMITTEXT, MAXSTRLEN-1, 0);
if (pdff->szText[0] != TEXT('0'))
SetDlgItemText(pdfpsp->hwndDlg, IDD_CONTAINS, pdff->szText);
// We need to initialize the size fields
SendMessage(hwndCtl=GetDlgItem(pdfpsp->hwndDlg, IDD_SIZECOMP),
CB_ADDSTRING, 0, (LONG)(LPTSTR)c_szSpace);
for (i=IDS_FINDGT; i<= IDS_FINDLT;i++)
{
LoadString(HINST_THISDLL, i, szTemp, ARRAYSIZE(szTemp));
SendMessage(hwndCtl, CB_ADDSTRING, 0, (LONG)(LPTSTR)szTemp);
}
// Remember to convert back to Kbytes...
SendMessage(hwndCtl, CB_SETCURSEL, pdff->iSizeType, 0);
if (pdff->iSizeType != 0)
SetDlgItemInt(pdfpsp->hwndDlg, IDD_SIZEVALUE, pdff->dwSize/1024, FALSE);
else
SetDlgItemText(pdfpsp->hwndDlg, IDD_SIZEVALUE, c_szNULL);
// Set up the up down as a buddy to our edit control
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_SIZEUPDOWN, UDM_SETBUDDY,
(int)GetDlgItem(pdfpsp->hwndDlg, IDD_SIZEVALUE), 0L);
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_SIZEUPDOWN, UDM_SETRANGE,
0, MAKELONG(32767, 0));
// Update our state to let us know that we have already initialized...
pdfpsp->dwState |= DFPAGE_INIT;
}
if (pdff->fNameChanged)
{
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_TYPECOMBO, CB_SETCURSEL, 0, 0L);
pdff->fNameChanged = FALSE;
}
}
//==========================================================================
//
// DocFind_HandleTypeChange - Handle when the user has selected an item
// in the type combobox, by updating the Named field to the appropriate
// filters.
//========================================================================
BOOL DocFind_GetTypeFilePatterns(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
HWND hwndDlg = pdfpsp->hwndDlg;
PHASHITEM phi;
TCHAR szClassName[CCH_KEYMAX];
TCHAR szExts[MAX_PATH];
TCHAR szExt[CCH_KEYMAX];
HKEY hk;
int i;
LONG lcb;
TCHAR szClassNameKey[CCH_KEYMAX];
// Get the class name by getting the assocated atom
phi = (PHASHITEM)SendDlgItemMessage(hwndDlg, IDD_TYPECOMBO, CB_GETITEMDATA, pdff->iType, 0L);
if ((phi == 0) && (phi != (PHASHITEM)LB_ERR))
{
pdff->szTypeFilePatterns[0] = TEXT('\0');
pdff->szTypeName[0] = TEXT('\0');
pdff->fFoldersOnly=FALSE; // Turn of folder only mode!
return TRUE; // Item does not have an atom...
}
szExts[0] = TEXT('\0');
// Also save away the display name for the class
SendDlgItemMessage(hwndDlg, IDD_TYPECOMBO, CB_GETLBTEXT, pdff->iType,
(LPARAM)(LPTSTR)pdff->szTypeName);
GetHashItemName(NULL, phi, szClassName, ARRAYSIZE(szClassName));
pdff->fFoldersOnly = (lstrcmpi(szClassName, c_szFolderClass) == 0);
// Now we need to find all extensions that map to this class
if (RegOpenKey(HKEY_CLASSES_ROOT, szNULL, &hk) == ERROR_SUCCESS)
{
for (i = 0; RegEnumKey(hk, i, szExt, ARRAYSIZE(szExt)) == ERROR_SUCCESS; i++)
{
// Skip things that aren't extensions
if (szExt[0] != TEXT('.'))
continue;
// get the class name
lcb = SIZEOF(szClassNameKey);
if (RegQueryValue(hk, szExt, szClassNameKey, &lcb) != ERROR_SUCCESS)
continue;
// Now see if the class key matches the one we are looking for?
if (lstrcmpi(szClassName, szClassNameKey) == 0)
{
if (szExts[0] != TEXT('\0'))
lstrcat(szExts, TEXT(";"));
lstrcat(szExts, TEXT("*"));
lstrcat(szExts, szExt);
}
}
RegCloseKey(hk);
}
// And now setup the name filter...
lstrcpy(pdff->szTypeFilePatterns, szExts);
return(pdff->fFoldersOnly || (szExt[0] != TEXT('\0')));
}
//==========================================================================
// Apply changes that happend in the details page
//==========================================================================
void DocFind_DFDetailsApply(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
HWND hwndDlg = pdfpsp->hwndDlg;
BOOL fValidNum;
GetDlgItemText(hwndDlg, IDD_CONTAINS, pdff->szText, ARRAYSIZE(pdff->szText) - 1);
pdff->szText[lstrlen(pdff->szText)+1] = TEXT('\0'); // double \0
// Now get the size constraints
pdff->iSizeType = SendDlgItemMessage(hwndDlg, IDD_SIZECOMP, CB_GETCURSEL, 0, 0);
pdff->dwSize = GetDlgItemInt(hwndDlg, IDD_SIZEVALUE, &fValidNum, FALSE) * 1024;
}
//==========================================================================
// Validate the Details page
//==========================================================================
void DocFind_DFDetailsValidatePage(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
HWND hwndDlg = pdfpsp->hwndDlg;
int iSizeType;
TCHAR szTemp[10];
int cb;
BOOL fValidNum;
iSizeType = SendDlgItemMessage(hwndDlg, IDD_SIZECOMP, CB_GETCURSEL, 0, 0);
cb = GetDlgItemText(hwndDlg, IDD_SIZEVALUE, szTemp, ARRAYSIZE(szTemp));
GetDlgItemInt(hwndDlg, IDD_SIZEVALUE, &fValidNum, FALSE);
if (((iSizeType != 0) || (cb > 0)) && !fValidNum)
{
DocFind_ReportItemValueError(hwndDlg, IDD_SIZEVALUE, IDS_FINDINVALIDNUMBER, NULL);
return;
}
// We will update out type information here, as this will be called
// before we switch pages and as such we can update the named field
// if appropriate before we display the other page.
pdff->iType = SendDlgItemMessage(hwndDlg, IDD_TYPECOMBO, CB_GETCURSEL, 0, 0);
if (pdff->iType != pdff->iTypeLast)
{
// The type changed.
DocFind_GetTypeFilePatterns(pdfpsp);
pdff->iTypeLast = pdff->iType;
pdff->fTypeChanged = TRUE;
}
}
//==========================================================================
//
// This function is the dialog (or property sheet page) for the other details
// such as type, size and contains...
//
BOOL CALLBACK DocFind_DFDetailsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPDOCFINDPROPSHEETPAGE pdfpsp = (LPDOCFINDPROPSHEETPAGE)GetWindowLong(hwndDlg, DWL_USER);
int i;
switch (msg) {
HANDLE_MSG(hwndDlg, WM_SIZE, DocFind_DFDetailsOnSize);
case WM_INITDIALOG:
SetWindowLong(hwndDlg, DWL_USER, lParam);
pdfpsp = (LPDOCFINDPROPSHEETPAGE)lParam;
pdfpsp->hwndDlg = hwndDlg;
break;
case WM_WININICHANGE:
case WM_SYSCOLORCHANGE:
RelayMessageToChildren(hwndDlg, msg, wParam, lParam);
break;
case WM_NCDESTROY:
Free(pdfpsp);
SetWindowLong(hwndDlg, DWL_USER, 0);
return FALSE; // We MUST return FALSE to avoid mem-leak
case DFM_ENABLECHANGES:
for (i=IDD_TYPECOMBO; i <= IDD_SIZEUPDOWN; i++)
{
EnableWindow(GetDlgItem(hwndDlg, i), (BOOL)wParam);
}
break;
case WM_DESTROY:
//
// Destroy the list of atoms (actually hash items) that were created
// as part of the types table
//
DeleteListAttoms(GetDlgItem(hwndDlg, IDD_TYPECOMBO), TRUE);
break;
case WM_HELP:
WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP,
(DWORD) (LPTSTR) aCriteriaHelpIDs);
break;
case WM_CONTEXTMENU: // right mouse click
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
(DWORD) (LPTSTR) aCriteriaHelpIDs);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDD_TYPECOMBO:
if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
pdfpsp->pdff->fFilterChanged = TRUE;
break;
case IDD_CONTAINS:
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
pdfpsp->pdff->fFilterChanged = TRUE;
break;
}
break;
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code) {
case PSN_KILLACTIVE:
DocFind_DFDetailsValidatePage(pdfpsp);
break;
case PSN_SETACTIVE:
DocFind_DFDetailsInit(pdfpsp);
break;
case PSN_APPLY:
if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
DocFind_DFDetailsApply(pdfpsp);
break;
case PSN_RESET:
if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
{
// Rest to all classes
SendDlgItemMessage(hwndDlg, IDD_TYPECOMBO, CB_SETCURSEL, 0, 0);
// Set containing text to null
SetDlgItemText(hwndDlg, IDD_CONTAINS, c_szNULL);
// Reset the Size compare fields
SendDlgItemMessage(hwndDlg, IDD_SIZECOMP, CB_SETCURSEL, 0, 0);
SetDlgItemText(hwndDlg, IDD_SIZEVALUE, c_szNULL);
}
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Now starting the code for the date page
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//==========================================================================
void DocFind_DFDateSetFilterType(HWND hwndDlg, int idCtl)
{
// Define a structure that defines which controls should be enabled/Disabled when
// the user selects one of the radio buttons
static struct
{
int iFirst;
int iLast;
} adsft[] = {{0,0}, {0,0}, {IDD_MDATE_NUMDAYS, IDD_MDATE_DAYSUPDOWN},
{IDD_MDATE_NUMMONTHS, IDD_MDATE_MONTHSUPDOWN},
{IDD_MDATE_FROM, IDD_MDATE_TO}};
int i;
// Set the first set of radio buttons
CheckRadioButton(hwndDlg, IDD_MDATE_ALL, IDD_MDATE_PARTIAL,
(idCtl == IDD_MDATE_ALL? IDD_MDATE_ALL : IDD_MDATE_PARTIAL));
if (idCtl == IDD_MDATE_PARTIAL)
{
// Find which button is checked
for (idCtl = IDD_MDATE_BETWEEN; idCtl >= IDD_MDATE_DAYS; idCtl--)
{
if (IsDlgButtonChecked(hwndDlg, idCtl))
break;
}
}
if (idCtl != IDD_MDATE_ALL)
CheckRadioButton(hwndDlg, IDD_MDATE_DAYS, IDD_MDATE_BETWEEN, idCtl);
// Now use the id as an index into the array of items and enable or disable windows
idCtl -= IDD_MDATE_ALL;
for (i=IDD_MDATE_NUMDAYS; i<= IDD_MDATE_TO;i++)
{
EnableWindow(GetDlgItem(hwndDlg, i), (i >= adsft[idCtl].iFirst) &&
(i <= adsft[idCtl].iLast));
}
}
WORD DocFind_GetTodaysDosDateMinusNDays(int nDays)
{
SYSTEMTIME st;
union
{
FILETIME ft;
LARGE_INTEGER li;
}ftli;
WORD FatTime;
WORD FatDate;
// Now we need to
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ftli.ft);
FileTimeToLocalFileTime(&ftli.ft, &ftli.ft);
// Now decrement the file time by the count of days * the number of
// 100NS time units per day. Assume that nDays is positive.
if (nDays > 0)
{
#define NANO_SECONDS_PER_DAY 864000000000
ftli.li.QuadPart = ftli.li.QuadPart - ((__int64)nDays * NANO_SECONDS_PER_DAY);
}
FileTimeToDosDateTime(&ftli.ft, &FatDate,&FatTime);
DebugMsg(DM_TRACE, TEXT("DocFind %d days = %x"), nDays, FatDate);
return(FatDate);
}
//==========================================================================
//
// Initialize the Date page.
//
void _DFDateInitDates(HWND hwndDlg, BOOL fDefaultRange)
{
WORD wDate;
TCHAR szTemp[20];
SetDlgItemText(hwndDlg, IDD_MDATE_NUMMONTHS, TEXT("1"));
SendDlgItemMessage(hwndDlg, IDD_MDATE_NUMMONTHS,
EM_SETLIMITTEXT, 3, 0);
// Need to initialize the range of days field for 90 days...
// pdff is a little more fun!
if (fDefaultRange)
{
// Default case!
wDate = DocFind_GetTodaysDosDateMinusNDays(0);
GetDateString(wDate, szTemp);
SetDlgItemText(hwndDlg, IDD_MDATE_TO, szTemp);
wDate = DocFind_GetTodaysDosDateMinusNDays(90);
GetDateString(wDate, szTemp);
SetDlgItemText(hwndDlg, IDD_MDATE_FROM, szTemp);
DocFind_DFDateSetFilterType(hwndDlg, IDD_MDATE_ALL);
}
}
void DocFind_DFDateInit(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
TCHAR szTemp[20];
//
// Lets setup the buddys
//
// Set up the up down as a buddy to our edit control
if (!(pdfpsp->dwState & DFPAGE_INIT))
{
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_MDATE_DAYSUPDOWN, UDM_SETBUDDY,
(int)GetDlgItem(pdfpsp->hwndDlg, IDD_MDATE_NUMDAYS), 0L);
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_MDATE_DAYSUPDOWN, UDM_SETRANGE,
0, MAKELONG(999, 0));
SetDlgItemText(pdfpsp->hwndDlg, IDD_MDATE_NUMDAYS, TEXT("1"));
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_MDATE_NUMDAYS,
EM_SETLIMITTEXT, 3, 0);
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_MDATE_MONTHSUPDOWN, UDM_SETBUDDY,
(int)GetDlgItem(pdfpsp->hwndDlg, IDD_MDATE_NUMMONTHS), 0L);
SendDlgItemMessage(pdfpsp->hwndDlg, IDD_MDATE_MONTHSUPDOWN, UDM_SETRANGE,
0, MAKELONG(999, 0));
// Now Initialize the dates...
_DFDateInitDates(pdfpsp->hwndDlg, pdff->wDateType !=IDD_MDATE_BETWEEN);
switch (pdff->wDateType)
{
case IDD_MDATE_DAYS:
SetDlgItemInt(pdfpsp->hwndDlg, IDD_MDATE_NUMDAYS, pdff->wDateValue, FALSE);
break;
case IDD_MDATE_MONTHS:
SetDlgItemInt(pdfpsp->hwndDlg, IDD_MDATE_NUMMONTHS, pdff->wDateValue, FALSE);
break;
case IDD_MDATE_BETWEEN:
// If the date fields have values, put numbers in them...
if (pdff->dateModifiedBefore != 0)
{
GetDateString(pdff->dateModifiedBefore, szTemp);
SetDlgItemText(pdfpsp->hwndDlg, IDD_MDATE_TO, szTemp);
}
if (pdff->dateModifiedAfter != 0)
{
GetDateString(pdff->dateModifiedAfter, szTemp);
SetDlgItemText(pdfpsp->hwndDlg, IDD_MDATE_FROM, szTemp);
}
}
// Initialize the radio buttons
DocFind_DFDateSetFilterType(pdfpsp->hwndDlg, pdff->wDateType);
// Update our state to let us know that we have already initialized...
pdfpsp->dwState |= DFPAGE_INIT;
}
}
//==========================================================================
//
// Apply changes that happend in the Date page
//
void DocFind_DFDateApply(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
HWND hwndDlg = pdfpsp->hwndDlg;
TCHAR szTemp[20]; // more than enough room to hold date string...
BOOL fValid;
// See what type of date modification that the user is interested in
pdff->dateModifiedBefore = 0;
if (IsDlgButtonChecked(pdfpsp->hwndDlg, IDD_MDATE_ALL))
{
pdff->wDateType = IDD_MDATE_ALL;
pdff->dateModifiedAfter = 0;
}
else if (IsDlgButtonChecked(pdfpsp->hwndDlg, IDD_MDATE_DAYS))
{
int nDays;
BOOL fValidNum;
// Get the days field
nDays = GetDlgItemInt(pdfpsp->hwndDlg, IDD_MDATE_NUMDAYS, &fValidNum, FALSE);
ASSERT(nDays < MAXUSHORT);
pdff->wDateValue = (WORD) nDays;
Assert (nDays >= 0);
pdff->dateModifiedAfter = DocFind_GetTodaysDosDateMinusNDays(nDays);
pdff->wDateType = IDD_MDATE_DAYS;
}
else if (IsDlgButtonChecked(pdfpsp->hwndDlg, IDD_MDATE_MONTHS))
{
// We need to see how many months the user has requested.
// and then do our best to convert this into a valid date.
int nMonths;
SYSTEMTIME st;
FILETIME ft;
WORD FatTime;
BOOL fValidNum;
// Get the days field
nMonths = GetDlgItemInt(pdfpsp->hwndDlg, IDD_MDATE_NUMMONTHS, &fValidNum, FALSE);
ASSERT(nMonths < MAXUSHORT);
pdff->wDateValue = (WORD) nMonths;
Assert (nMonths >= 0);
GetSystemTime(&st);
st.wYear -= (WORD) nMonths / 12;
nMonths = nMonths % 12;
if (nMonths < st.wMonth)
st.wMonth -= (WORD) nMonths;
else
{
st.wYear--;
st.wMonth = (WORD)(12 - (nMonths - st.wMonth));
}
// Now normalize back to a valid date.
while (!SystemTimeToFileTime(&st, &ft))
{
st.wDay--; // must not be valid date for month...
}
FileTimeToLocalFileTime(&ft, &ft);
FileTimeToDosDateTime(&ft, &pdff->dateModifiedAfter,&FatTime);
DebugMsg(DM_TRACE, TEXT("DocFind %d months = %x"), nMonths, pdff->dateModifiedAfter);
pdff->wDateType = IDD_MDATE_MONTHS;
}
else
{
if (GetDlgItemText(hwndDlg, IDD_MDATE_FROM, szTemp, ARRAYSIZE(szTemp)) > 0)
pdff->dateModifiedAfter = ParseDateString(szTemp, &fValid);
else
pdff->dateModifiedAfter = 0;
if (GetDlgItemText(hwndDlg, IDD_MDATE_TO, szTemp, ARRAYSIZE(szTemp)) > 0)
{
pdff->dateModifiedBefore = ParseDateString(szTemp, &fValid);
// For Ivan he would like the dates to be able to be stated either
// forward or backwords ie either: 3/1/94 to 3/25/94 or
// 3/25/94 to 3/1/94
if ((pdff->dateModifiedAfter != 0) &&
(pdff->dateModifiedBefore != 0) &&
(pdff->dateModifiedAfter > pdff->dateModifiedBefore))
{
WORD wTemp = pdff->dateModifiedBefore;
pdff->dateModifiedBefore = pdff->dateModifiedAfter;
pdff->dateModifiedAfter = wTemp;
}
}
else
pdff->dateModifiedBefore = 0;
pdff->wDateType = IDD_MDATE_BETWEEN;
}
}
//==========================================================================
// Validate the Date page
//==========================================================================
void DocFind_DFDateValidatePage(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
LPDFFILTER pdff = pdfpsp->pdff;
HWND hwndDlg = pdfpsp->hwndDlg;
TCHAR szTemp[20];
BOOL fValid;
int nVal;
// See what type of date modification that the user is interested in
pdff->dateModifiedBefore = 0;
if (IsDlgButtonChecked(pdfpsp->hwndDlg, IDD_MDATE_ALL))
{
return; // nothing to check
}
else if (IsDlgButtonChecked(pdfpsp->hwndDlg, IDD_MDATE_DAYS))
{
// Get the days field
nVal = GetDlgItemInt(pdfpsp->hwndDlg, IDD_MDATE_NUMDAYS,
&fValid, FALSE);
if (!fValid || (nVal <= 0))
{
DocFind_ReportItemValueError(hwndDlg, IDD_MDATE_NUMDAYS, IDS_FINDINVALIDNUMBER, NULL);
return;
}
}
else if (IsDlgButtonChecked(pdfpsp->hwndDlg, IDD_MDATE_MONTHS))
{
// We need to see how many months the user has requested.
// and then do our best to convert this into a valid date.
nVal = GetDlgItemInt(pdfpsp->hwndDlg, IDD_MDATE_NUMMONTHS,
&fValid, FALSE);
if (!fValid || (nVal <= 0))
{
DocFind_ReportItemValueError(hwndDlg, IDD_MDATE_NUMMONTHS, IDS_FINDINVALIDNUMBER, NULL);
return;
}
}
else
{
WORD wDateBefore = 0xffff;
WORD wDateAfter = 0x0000;
if (GetDlgItemText(hwndDlg, IDD_MDATE_FROM, szTemp, ARRAYSIZE(szTemp)) > 0)
{
wDateAfter = ParseDateString(szTemp, &fValid);
if (!fValid)
{
DocFind_ReportItemValueError(hwndDlg, IDD_MDATE_FROM, IDS_FINDINVALIDDATE, NULL);
return;
}
}
if (GetDlgItemText(hwndDlg, IDD_MDATE_TO, szTemp, ARRAYSIZE(szTemp)) > 0)
{
wDateBefore = ParseDateString(szTemp, &fValid);
if (!fValid)
{
DocFind_ReportItemValueError(hwndDlg, IDD_MDATE_TO, IDS_FINDINVALIDDATE, NULL);
return;
}
}
#ifdef FOR_IVAN
// Now make sure the After is before the Before
if (wDateAfter > wDateBefore)
{
DocFind_ReportItemValueError(hwndDlg, IDD_MDATE_TO, IDS_FINDINVALIDDATE, NULL);
return;
}
#endif
}
}
//==========================================================================
//
// This function is the dialog (or property sheet page) for the date page
//
BOOL CALLBACK DocFind_DFDateDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPDOCFINDPROPSHEETPAGE pdfpsp = (LPDOCFINDPROPSHEETPAGE)GetWindowLong(hwndDlg, DWL_USER);
int i;
switch (msg) {
case WM_INITDIALOG:
SetWindowLong(hwndDlg, DWL_USER, lParam);
pdfpsp = (LPDOCFINDPROPSHEETPAGE)lParam;
pdfpsp->hwndDlg = hwndDlg;
break;
case WM_WININICHANGE:
case WM_SYSCOLORCHANGE:
RelayMessageToChildren(hwndDlg, msg, wParam, lParam);
break;
case WM_NCDESTROY:
Free(pdfpsp);
SetWindowLong(hwndDlg, DWL_USER, 0);
return FALSE; // We MUST return FALSE to avoid mem-leak
case DFM_ENABLECHANGES:
if (wParam)
{
// Reenable, call our set filter type which will
// reenable the right windows.
for (i=IDD_MDATE_ALL; i <= IDD_MDATE_BETWEEN; i++)
{
// Reenable the buttons...
EnableWindow(GetDlgItem(hwndDlg, i), TRUE);
}
DocFind_DFDateSetFilterType(hwndDlg, pdfpsp->pdff->wDateType);
}
else
{
for (i=IDD_MDATE_ALL; i <= IDD_MDATE_TO; i++)
{
// Disable all of the windows.
EnableWindow(GetDlgItem(hwndDlg, i), FALSE);
}
}
break;
case WM_HELP:
WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP,
(DWORD) (LPTSTR) aDateHelpIDs);
break;
case WM_CONTEXTMENU: // right mouse click
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
(DWORD) (LPTSTR) aDateHelpIDs);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDD_MDATE_ALL:
case IDD_MDATE_PARTIAL:
case IDD_MDATE_DAYS:
case IDD_MDATE_MONTHS:
case IDD_MDATE_BETWEEN:
// This sets the new type of date matching we will be processing
DocFind_DFDateSetFilterType(hwndDlg, (GET_WM_COMMAND_ID(wParam, lParam)));
break;
}
break;
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code) {
case PSN_KILLACTIVE:
DocFind_DFDateValidatePage(pdfpsp);
break;
case PSN_SETACTIVE:
if ((pdfpsp->dwState & DFPAGE_INIT) == 0)
DocFind_DFDateInit(pdfpsp);
break;
case PSN_APPLY:
if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
DocFind_DFDateApply(pdfpsp);
break;
case PSN_RESET:
if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
{
// Set modification dates to NULL
_DFDateInitDates(hwndDlg, TRUE);
}
}
break;
default:
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Define the Details interface used for this search. This includes
// The column header definitions.
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//===========================================================================
// CDFDetails : member prototype - Docfind Folder implementation
//===========================================================================
ULONG STDMETHODCALLTYPE CDFDetails_Release(IShellDetails * psd);
STDMETHODIMP CDFDetails_GetDetailsOf(IShellDetails * psd,
LPCITEMIDLIST pidl, UINT iCol, LPSHELLDETAILS lpDetails);
STDMETHODIMP CDFDetails_ColumnClick(IShellDetails * psd, UINT iColumn);
//===========================================================================
// CDFDetails : Vtable
//===========================================================================
#pragma warning(error: 4090 4028 4047)
#pragma data_seg(DATASEG_READONLY)
extern const UINT s_auMapDFColToFSCol[];
enum
{
IDFCOL_NAME = 0,
IDFCOL_PATH,
IDFCOL_SIZE,
IDFCOL_TYPE,
IDFCOL_MODIFIED,
IDFCOL_MAX, // Make sure this is the last enum item
} ;
#pragma data_seg(DATASEG_READONLY)
const COL_DATA s_df_cols[] = {
{IDFCOL_NAME, IDS_NAME_COL, 20, LVCFMT_LEFT},
{IDFCOL_PATH, IDS_PATH_COL, 20, LVCFMT_LEFT},
{IDFCOL_SIZE, IDS_SIZE_COL, 8, LVCFMT_RIGHT},
{IDFCOL_TYPE, IDS_TYPE_COL, 20, LVCFMT_LEFT},
{IDFCOL_MODIFIED, IDS_MODIFIED_COL, 30, LVCFMT_LEFT},
};
IShellDetailsVtbl c_DFDetailVtbl =
{
SH32Unknown_QueryInterface,
SH32Unknown_AddRef,
SH32Unknown_Release,
CDFDetails_GetDetailsOf,
CDFDetails_ColumnClick,
};
#pragma data_seg()
#pragma warning(default: 4090 4028 4047)
typedef struct _CDFDetails
{
SH32Unknown SH32Unk;
// Pointer to docfind folder
HWND hwndDlg;
HDPA hdpaPidf;
} CDFDetails;
STDMETHODIMP CDFFilter_CreateDetails(LPDOCFINDFILEFILTER pdfff,
HWND hwndDlg, HDPA hdpaPidf, LPVOID FAR* ppvOut)
{
HRESULT hres = (E_OUTOFMEMORY);
CDFDetails *psd;
psd = (void*)LocalAlloc(LPTR, SIZEOF(CDFDetails));
if (!psd)
{
goto Error1;
}
psd->SH32Unk.unk.lpVtbl = (IUnknownVtbl *)&c_DFDetailVtbl;
psd->SH32Unk.cRef = 1;
psd->SH32Unk.riid = &IID_IShellDetails;
psd->hwndDlg = hwndDlg;
psd->hdpaPidf = hdpaPidf;
*ppvOut = psd;
return(NOERROR);
Error1:;
return(hres);
}
STDMETHODIMP CDFDetails_GetDetailsOf(IShellDetails * psd, LPCITEMIDLIST pidl,
UINT iColumn, LPSHELLDETAILS lpDetails)
{
CDFDetails * this = IToClass(CDFDetails, SH32Unk.unk, psd);
#ifdef UNICODE
TCHAR szTemp[MAX_PATH];
#endif
if (iColumn >= IDFCOL_MAX)
{
return((E_NOTIMPL));
}
lpDetails->str.uType = STRRET_CSTR;
lpDetails->str.cStr[0] = '\0';
if (!pidl)
{
#ifdef UNICODE
LoadString(HINST_THISDLL, s_df_cols[iColumn].ids,
szTemp, ARRAYSIZE(szTemp));
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
} else {
return E_OUTOFMEMORY;
}
#else
LoadString(HINST_THISDLL, s_df_cols[iColumn].ids,
lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
#endif
lpDetails->fmt = s_df_cols[iColumn].iFmt;
lpDetails->cxChar = s_df_cols[iColumn].cchCol;
return(NOERROR);
}
if (iColumn == IDFCOL_PATH)
{
// We need to now get to the idlist of the items folder.
LPDFFOLDERLISTITEM pdffli = DPA_FastGetPtr(this->hdpaPidf,
*DF_IFLDRPTR(pidl));
if (pdffli != NULL)
{
// This one is not part of the standard file system view...
#ifdef UNICODE
SHGetPathFromIDList(pdffli->pidl, szTemp);
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
} else {
return E_OUTOFMEMORY;
}
#else
SHGetPathFromIDList(pdffli->pidl, lpDetails->str.cStr);
#endif
return(NOERROR);
}
return((E_INVALIDARG));
}
else
{
// We need to now get to the idlist of the items folder.
LPDFFOLDERLISTITEM pdffli = DPA_FastGetPtr(this->hdpaPidf,
*DF_IFLDRPTR(pidl));
// Let the file system function do it for us...
return FS_GetDetailsOf(pdffli->pidl, pidl, s_auMapDFColToFSCol[iColumn], lpDetails);
}
}
STDMETHODIMP CDFDetails_ColumnClick(IShellDetails * psd, UINT iColumn)
{
CDFDetails * this = IToClass(CDFDetails, SH32Unk.unk, psd);
Assert(iColumn < IDFCOL_MAX);
ShellFolderView_ReArrange(this->hwndDlg, iColumn);
return NOERROR;
}
//===========================================================================
// CDFEnum: class definition
//===========================================================================
#define DIRBUF_CBGROW ((MAX_PATH+1)*SIZEOF(TCHAR))
typedef struct _CDFEnum // DFENUM (Doc find Container)
{
IDFEnum dfenum;
UINT cRef;
IShellFolder *psf; // Pointer to shell folder
// Stuff to use in the search
LPTSTR pszPath; // Passed in path from creater
// Handle cases where we search one level deep like at \\compname and the like
//
LPITEMIDLIST pidlStart; // Passed in pidl to begin from
IShellFolder *psfStart; // The starting folder for the search.
int cchStart; // How many characters are in the
IShellFolder *psfTopLevel; // Top level shellfolder
LPENUMIDLIST penumTopLevel; // Top level enum function.
DWORD grfFlags; // Flags that control things like recursion
WIN32_FIND_DATA finddata; // Win32 file data to use
// filter info...
LPTSTR pszProgressText; // Path Buffer pointer
IDocFindFileFilter * pdfff;// The file filter to use...
// enumeration state
BOOL fFirstPass; // Is this the first pass?
int ichPathFirst; // ich into path string of current path...
LPTSTR pszPathNext; // filter path enumeration state
int iFolder; // Which folder are we adding items for?
BOOL fAddedSubDirs;
BOOL fObjReturnedInDir; // Has an object been returned in this dir?
int depth; // directory level (relative to pszPath)
HANDLE hfind;
DIRBUF FAR* pdbStack; // Linked list of DIRBUFs to enum
DIRBUF FAR* pdbReuse;
} CDFEnum, FAR* LPDFENUM;
//===========================================================================
// CDFEnum : member prototype - Docfind Folder implementation
//===========================================================================
HRESULT STDMETHODCALLTYPE CDFEnum_QueryInterface(
IDFEnum * pdfenum, REFIID riid, LPVOID FAR* ppvObj);
ULONG STDMETHODCALLTYPE CDFEnum_AddRef(IDFEnum * pdfenum);
ULONG STDMETHODCALLTYPE CDFEnum_Release(IDFEnum * pdfenum);
STDMETHODIMP CDFEnum_Next(IDFEnum * pdfenum, LPITEMIDLIST *ppidl,
int *pcObjectSearched, int *pcFoldersSearched, volatile BOOL *pfContinue, int *pState, HWND hwnd);
STDMETHODIMP CDefDFEnum_Skip(IDFEnum * pdfenum, int celt);
STDMETHODIMP CDefDFEnum_Reset(IDFEnum * pdfenum);
//===========================================================================
// CDFEnum : Vtable
//===========================================================================
#pragma warning(error: 4090 4028 4047)
#pragma data_seg(DATASEG_READONLY)
IDFEnumVtbl c_DFFIterVtbl =
{
CDFEnum_QueryInterface,
CDFEnum_AddRef,
CDFEnum_Release,
CDFEnum_Next,
CDefDFEnum_Skip,
CDefDFEnum_Reset,
};
//==========================================================================
// CDFEnum - Helper Functions
//==========================================================================
DIRBUF FAR* DFDirBuf_Pop(LPDFENUM this)
{
DIRBUF FAR* pdb = this->pdbStack;
this->pdbStack = pdb->pdbNext;
this->depth--;
#ifdef FIND_TRACE
DebugMsg(DM_ERROR, TEXT("CDFEnum::Next: Pop (%d)%s"), this->depth, pdb->psz);
#endif
if (!this->pdbReuse)
{
this->pdbReuse = pdb;
}
else
{
Assert(pdb->psz);
LocalFree((HLOCAL)pdb->psz);
LocalFree((HLOCAL)pdb);
}
return this->pdbStack;
}
//==========================================================================
// CDFEnum::QueryInterface
//==========================================================================
HRESULT STDMETHODCALLTYPE CDFEnum_QueryInterface(IDFEnum * pdfenum, REFIID riid, LPVOID FAR* ppvObj)
{
return (E_NOTIMPL);
}
//==========================================================================
// IDFEnum::AddRef
//==========================================================================
ULONG STDMETHODCALLTYPE CDFEnum_AddRef(IDFEnum * pdfenum)
{
LPDFENUM this = IToClass(CDFEnum, dfenum, pdfenum);
this->cRef++;
return(this->cRef);
}
//==========================================================================
// CDFFilter_EnumObjects - Get The real recursive filtered enumerator...
//==========================================================================
HRESULT CDFFilter_EnumObjects( IDocFindFileFilter * pdfff,
LPSHELLFOLDER psf, DWORD grfFlags,
LPTSTR pszProgressText, IDFEnum **ppdfenum)
{
// We need to construct the iterator
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
LPDFENUM pdfenum = LocalAlloc(LPTR, SIZEOF(CDFEnum));
if (pdfenum == NULL)
{
DebugMsg(DM_WARNING, TEXT("Docfind E_OUTOFMEMORY: %s line %d"), __FILE__, __LINE__);
return (E_OUTOFMEMORY);
}
// Now initialize the data structures.
pdfenum->dfenum.lpVtbl = &c_DFFIterVtbl;
pdfenum->cRef = 1;
pdfenum->psf = psf;
pdfenum->pidlStart = this->pidlStart;
if (pdfenum->pidlStart)
{
// We need to handle the case that the starting point may be
// the desktop. In this case we can simply addref
LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);
if (pdfenum->pidlStart->mkid.cb == 0)
{
pdfenum->psfStart = psfDesktop;
psfDesktop->lpVtbl->AddRef(psfDesktop);
SHGetPathFromIDList(pdfenum->pidlStart, pszProgressText);
PathAddBackslash(pszProgressText); // Add slash to get
pdfenum->cchStart = lstrlen(pszProgressText);
}
else
{
if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
pdfenum->pidlStart, NULL, &IID_IShellFolder,
&pdfenum->psfStart)))
{
// OK convert to display name, such that we can find the lenght of the
// text that we can remove
SHGetPathFromIDList(pdfenum->pidlStart, pszProgressText);
PathAddBackslash(pszProgressText); // Add slash to get
pdfenum->cchStart = lstrlen(pszProgressText);
}
else
{
pdfenum->psfStart = NULL; // handle error case???
}
}
}
pdfenum->pszPath = this->szPath;
pdfenum->pszProgressText = pszProgressText;
pdfenum->grfFlags = grfFlags;
pdfenum->pdfff = pdfff;
pdfenum->hfind = INVALID_HANDLE_VALUE;
pdfenum->fFirstPass = TRUE;
// Save away the filter pointer
pdfff->lpVtbl->AddRef(pdfff);
// The rest of the fields should be zero/NULL
*ppdfenum = &pdfenum->dfenum; // Return the appropriate value;
return NOERROR;
}
//==========================================================================
// CDFEnum::Release
//==========================================================================
ULONG STDMETHODCALLTYPE CDFEnum_Release(IDFEnum * pdfenum)
{
LPDFENUM this = IToClass(CDFEnum, dfenum, pdfenum);
this->cRef--;
if (this->cRef>0)
{
return(this->cRef);
}
// If we still have an open File Handle close it now.
if (this->hfind != INVALID_HANDLE_VALUE)
{
FindClose(this->hfind);
this->hfind = INVALID_HANDLE_VALUE;
}
// Release any Directory buffers we may have queued up
while (this->pdbStack)
DFDirBuf_Pop(this);
if (this->pdbReuse)
{
Assert(this->pdbReuse->psz);
LocalFree((HLOCAL)this->pdbReuse->psz);
LocalFree((HLOCAL)this->pdbReuse);
}
if (this->pdfff)
this->pdfff->lpVtbl->Release(this->pdfff);
// Release top level enum and shell folder
if (this->psfStart)
this->psfStart->lpVtbl->Release(this->psfStart);
if (this->psfTopLevel)
this->psfTopLevel->lpVtbl->Release(this->psfTopLevel);
if (this->penumTopLevel)
this->penumTopLevel->lpVtbl->Release(this->penumTopLevel);
LocalFree((HLOCAL)this);
return(0);
}
//===========================================================================
// _CDFEnumSetupNextPath - This function handles the case when we have
// processed all of the subdirectories for a path and setups for the
// next one. This handles the cases where we may need to do name space
// enumeration for things like: MyComputer or servers
BOOL _CDFEnumSetupNextPath(CDFEnum * this)
{
ULONG ulAttrs;
LPITEMIDLIST pidl;
if (this->fFirstPass)
{
if (this->pidlStart)
{
LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);
LPCITEMIDLIST pidlLast;
IShellFolder *psf;
HRESULT hres;
ulAttrs = SFGAO_FILESYSTEM;
if (this->pidlStart->mkid.cb > 0)
{
// We need to get the shell folder for the parent of this one.
pidlLast = ILFindLastID(this->pidlStart);
pidl = ILClone(this->pidlStart);
ILRemoveLastID(pidl);
if (pidlLast != this->pidlStart)
{
hres = psfDesktop->lpVtbl->BindToObject(psfDesktop,
pidl, NULL, &IID_IShellFolder, &psf);
}
else
{
// might be a folder on the desktop...
psf = psfDesktop;
hres = NOERROR;
psf->lpVtbl->AddRef(psf);
}
if (FAILED(hres))
{
ILFree(pidl);
return(FALSE); // could not get parent.
}
psf->lpVtbl->GetAttributesOf(psf, 1, &pidlLast, &ulAttrs);
psf->lpVtbl->Release(psf);
ILFree(pidl); // release our use of
}
else
ulAttrs = 0; // This is my computer!
if ((ulAttrs & SFGAO_FILESYSTEM) == 0)
{
// We will be iterating over children.
if (FAILED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
this->pidlStart, NULL, &IID_IShellFolder,
&this->psfTopLevel)))
return(FALSE); // bail
if (FAILED(this->psfTopLevel->lpVtbl->EnumObjects(
this->psfTopLevel,
(HWND)NULL, // BUGBUG: hwndOwner?
SHCONTF_FOLDERS,
&this->penumTopLevel)))
{
this->psfTopLevel->lpVtbl->Release(this->psfTopLevel);
this->psfTopLevel = NULL; // dont release twice!
return(FALSE);
}
}
else
{
// Normal one so no problem.
SHGetPathFromIDList(this->pidlStart, this->pszPath);
this->pszPathNext = this->pszPath;
}
}
else
{
this->pszPathNext = this->pszPath;
}
this->fFirstPass = FALSE;
}
// See if we need to enumerate using the penum or simply look for the
// next sub-string
if (this->penumTopLevel)
{
// We need to get the next IDlist to search.
LPITEMIDLIST pidlAbs;
while (NULL != (pidl = DocFind_NextIDL(this->psfTopLevel, this->penumTopLevel)))
{
ulAttrs = SFGAO_FILESYSTEM;
if (SUCCEEDED(this->psfTopLevel->lpVtbl->GetAttributesOf(
this->psfTopLevel, 1, &pidl, &ulAttrs)))
{
if (ulAttrs & SFGAO_FILESYSTEM)
break; // found the next one to process
}
}
if (!pidl)
return(FALSE);
// Convert to PATH;
pidlAbs = ILCombine(this->pidlStart, pidl);
SHGetPathFromIDList(pidlAbs, this->pszPath);
this->pszPathNext = this->pszPath;
ILFree(pidlAbs);
ILFree(pidl);
}
// No more directory names: copy a path from
// the filter path and start again...
//
this->pszPathNext = (LPTSTR)NextPath(this->pszPathNext, this->pszProgressText, MAX_PATH);
return(this->pszPathNext != NULL);
}
//===========================================================================
// _CDFENUM_ShouldWePushFile - Helper function, that given the fact that
// a file is a directory, is this one we should search???
//
BOOL _CDFEnum_ShouldWePushFile(CDFEnum *this)
{
// Make sure that it is a directory and that we are doing a recursive
// search.
if ((this->grfFlags & DFOO_INCLUDESUBDIRS) == 0)
return(FALSE);
if ((this->finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
return(FALSE);
// If the directory is marked hidden and we are not doing show all
// files then also return FALSE
if ( ((this->grfFlags & DFOO_SHOWALLOBJECTS) == 0) &&
((this->finddata.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
return(FALSE);
// If it is a system directory we need to do more work
// BUGBUG - Make work for briefcase...
if (((this->finddata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0))
{
TCHAR szPath[MAX_PATH];
LPITEMIDLIST pidl;
HRESULT hres;
IShellFolder *psf;
LPITEMIDLIST pidlFile;
LONG Flags = 0;
lstrcpy(szPath, this->pszProgressText);
PathAppend(szPath, this->finddata.cFileName);
pidl = ILCreateFromPath(szPath);
if (!pidl)
return FALSE;
hres = SHBindToIDListParent(pidl, &IID_IShellFolder, &psf, &pidlFile);
if (SUCCEEDED(hres))
{
Flags = SFGAO_FILESYSANCESTOR|SFGAO_FOLDER;
if (FAILED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidlFile, &Flags)))
Flags = 0;
psf->lpVtbl->Release(psf);
}
ILFree(pidl);
if ((Flags & (SFGAO_FILESYSANCESTOR|SFGAO_FOLDER)) !=
(SFGAO_FILESYSANCESTOR|SFGAO_FOLDER))
return(FALSE);
}
// This is one to be pushed!
return(TRUE);
}
//===========================================================================
// CDFEnum::Next Recursive Iterator that is very special to the docfind.
// It will walk each directory, breath first, it will call the
// defined callback function to determine if it is an
// interesting file to us. It will also return additional
// information, such as: The number of folders and files
// searched, so we can give results back to the user. It
// will return control to the caller whenever:
// a) Finds a match.
// b) runs out of things to search.
// c) Starts searching in another directory
// d) when the callback says to...
//
//
//===========================================================================
STDMETHODIMP CDFEnum_Next(IDFEnum * pdfenum, LPITEMIDLIST *ppidl,
int *pcObjectSearched, int *pcFoldersSearched,
volatile BOOL *pfContinue, int *piState, HWND hwnd)
{
// If we aren't enumerating a directory, then get the next directory
// name from the dir list, and begin enumerating its contents...
//
CDFEnum * this = IToClass(CDFEnum, dfenum, pdfenum);
BOOL fContinue = TRUE;
HRESULT hres;
do
{
if (this->hfind != INVALID_HANDLE_VALUE)
{
if (!FindNextFile(this->hfind, &this->finddata))
{
// No more files: close the hfind...
//
FindClose(this->hfind);
this->hfind = INVALID_HANDLE_VALUE;
}
}
if (this->hfind == INVALID_HANDLE_VALUE)
{
// If this is our first time through, prime the enumeration
// by copying the first path from the filter path...
//
// First time through should fall out here.
this->fObjReturnedInDir = FALSE; // Nothing returned here yet
do
{
DIRBUF FAR* pdb;
int ichPathEnd;
if (!*pfContinue)
{
*piState = GNF_DONE;
return NOERROR;
}
// If at end of current DIRBUF, then pop it
// off, repeating until we find a DIRBUF that
// has directory names left to enumerate.
//
pdb = this->pdbStack;
while (pdb && pdb->psz[pdb->ichDirNext] == 0)
{
pdb = DFDirBuf_Pop(this);
}
// If we added some subdirectories while
// enumerating the current directory,
// then we are now descending another level...
//
if (this->fAddedSubDirs)
{
this->fAddedSubDirs = FALSE;
this->depth++;
}
// If there are still more directory names, append it
// to the path and start from there. Otherwise,
// get the next path from the filter path and start over.
//
if (pdb)
{
// Lop off the old directory, and add the current
// directory to it...
//
this->pszProgressText[pdb->ichPathEnd] = 0;
this->fAddedSubDirs = FALSE;
if (PathCombine(this->pszProgressText,
this->pszProgressText,
&pdb->psz[pdb->ichDirNext]) == NULL)
{
this->hfind = INVALID_HANDLE_VALUE;
pdb->psz[pdb->ichDirNext] = 0;
continue;
}
// Advance ichDirNext
//
pdb->ichDirNext += lstrlen(&pdb->psz[pdb->ichDirNext]) + 1;
}
else
{
if (!_CDFEnumSetupNextPath(this))
{
*piState = GNF_DONE;
return NOERROR;
}
PathRemoveBackslash(this->pszProgressText);
}
// append "\*.*" to end of path for FindFirstFile...
ichPathEnd = lstrlen(this->pszProgressText);
if (ichPathEnd >= (MAX_PATH - 5))
this->hfind = INVALID_HANDLE_VALUE;
else
{
PathAppend(this->pszProgressText, c_szStarDotStar);
// If we get here we will be going to a new directory...
#ifdef FIND_TRACE
DebugMsg(DM_ERROR, TEXT("CDFEnum::Next: Find First (%d)%s"), this->depth, this->pszProgressText);
#endif
(*pcFoldersSearched)++; // Increment the number of folders searched
this->hfind = FindFirstFileRetry(hwnd, this->pszProgressText, &this->finddata, NULL);
// Remove the "\*.*"...
this->pszProgressText[ichPathEnd] = 0;
}
// Loop until we find a directory that contains files...
}
while (this->hfind == INVALID_HANDLE_VALUE);
}
// We've got a WIN32_FIND_DATA to return.
//
// Always skip "." and ".."...
//
if ((this->finddata.cFileName[0] == TEXT('.')) &&
((this->finddata.cFileName[1] == TEXT('\0')) ||
((this->finddata.cFileName[1] == TEXT('.')) &&
(this->finddata.cFileName[2] == TEXT('\0')))))
continue; // Try the next entry
(*pcObjectSearched)++;
// If we are enumerating subdirectories, add directories
// to the DIRBUF... But dont add any items that have the
// system bit set as these are probably junction points.
//
if (_CDFEnum_ShouldWePushFile(this))
{
// in search.c this was: DirBuf_Add(this, this->finddata.cFileName);
DIRBUF FAR* pdbT;
int cb;
if (!this->fAddedSubDirs)
{
this->fAddedSubDirs = TRUE;
pdbT = this->pdbReuse;
if (pdbT)
{
this->pdbReuse = NULL;
pdbT->cb = 0; // Set size of data to 0 as to not retry first item
}
else
{
pdbT = (DIRBUF FAR*)LocalAlloc(LPTR, SIZEOF(DIRBUF));
if (pdbT)
{
pdbT->cbAlloc = DIRBUF_CBGROW;
pdbT->psz = LocalAlloc(LPTR, pdbT->cbAlloc);
if (!pdbT->psz)
{
LocalFree((HLOCAL)pdbT);
pdbT = NULL;
DebugMsg(DM_TRACE, TEXT("DocFind: LocalAlloc Failed"));
Assert(FALSE);
}
}
else
{
DebugMsg(DM_TRACE, TEXT("DocFind: LocalAlloc Failed"));
Assert(FALSE);
}
}
// Add to our stack...
//
if (pdbT)
{
pdbT->pdbNext = this->pdbStack;
this->pdbStack = pdbT;
pdbT->ichDirNext = 0;
pdbT->ichPathEnd = lstrlen(this->pszProgressText);
}
}
else
pdbT = this->pdbStack;
if (pdbT)
{
cb = (lstrlen(this->finddata.cFileName) + 1) * SIZEOF(TCHAR);
if (pdbT->cb + cb + SIZEOF(TCHAR) > pdbT->cbAlloc)
{
LPTSTR pszNew;
pdbT->cbAlloc += DIRBUF_CBGROW;
pszNew = LocalReAlloc((HLOCAL)pdbT->psz, pdbT->cbAlloc,
LMEM_MOVEABLE|LMEM_ZEROINIT);
if (!pszNew)
{
DebugMsg(DM_TRACE, TEXT("DocFind: Alloc Failed"));
return (E_OUTOFMEMORY);
}
pdbT->psz = pszNew;
}
lstrcpy(pdbT->psz + (pdbT->cb / SIZEOF(TCHAR)), this->finddata.cFileName);
// Add an extra zero terminator to mark end of list...
pdbT->psz[(pdbT->cb + cb) / SIZEOF(TCHAR)] = TEXT('\0');
pdbT->cb += cb;
}
#ifdef FIND_TRACE
DebugMsg(DM_ERROR, TEXT("CDFEnum::Next: Push (%d)%s"), this->depth, this->finddata.cFileName);
#endif
}
// Here is where we call of to our filter to see if we want the
// file, but for now we will assume that we do
//
fContinue = FALSE; // We can exit the loop;
if (this->pdfff->lpVtbl->FDoesItemMatchFilter(this->pdfff,
this->pszProgressText, &this->finddata, NULL, NULL) != 0)
*piState = GNF_MATCH;
else
*piState = GNF_NOMATCH;
// Generate the PIDL to return;
if (*piState == GNF_MATCH)
{
LPITEMIDLIST pidl;
LPITEMIDLIST pidlToAdd;
// See if we Need to Add a directory entry for this item
if (!this->fObjReturnedInDir)
{
this->fObjReturnedInDir = TRUE;
// Now Create A new File Cabinet Entry.
pidl = NULL; // Setup for failure case.
if (this->psfStart)
{
// We have a starting IDLIST and IShellFolder to use, to parse
// the name with. This is important to use in cases like searching
// over the network with UNC names
// First properly handle the root one...
if (this->cchStart >= lstrlen(this->pszProgressText))
{
// Root one simply clone the root
pidl = ILClone(this->pidlStart);
}
else
{
LPITEMIDLIST pidlPartial = NULL;
LPOLESTR pwsz = (void*)LocalAlloc(LPTR, MAX_PATH*SIZEOF(WCHAR));
if (pwsz)
{
ULONG pcchEaten;
// Remember to skip over the characters that are common with our
// psfStart
StrToOleStr(pwsz, this->pszProgressText + this->cchStart);
this->psfStart->lpVtbl->ParseDisplayName(this->psfStart,
NULL, NULL, pwsz, &pcchEaten, &pidlPartial, NULL);
LocalFree((HLOCAL)pwsz);
}
else
{
DebugMsg(DM_TRACE, TEXT("DocFind: LocalAlloc Failed"));
Assert(FALSE);
return (E_OUTOFMEMORY);
}
if (pidlPartial)
{
pidl = ILCombine(this->pidlStart, pidlPartial);
ILFree(pidlPartial);
}
}
}
// try to convert from full path...
if (pidl == NULL)
{
// the path may be relative so qualify it now.
PathQualify(this->pszProgressText);
hres = SHILCreateFromPath(this->pszProgressText, &pidl, NULL);
if (FAILED(hres))
{
DebugMsg(DM_TRACE, TEXT("DocFind: Create Pidl for Folder list Failed"));
Assert(FALSE);
return hres;
}
}
hres = CDFFolder_AddFolderToFolderList(
this->psf, pidl, NULL, &this->iFolder);
if (FAILED(hres))
{
DebugMsg(DM_TRACE, TEXT("DocFind: Add Folder To folder list Failed"));
Assert(FALSE);
return hres;
}
}
pidl = (LPITEMIDLIST)CFSFolder_FillIDFolder(&this->finddata,
this->pszProgressText, (LPARAM)NULL);
if (!pidl)
{
*piState = GNF_ERROR;
DebugMsg(DM_WARNING, TEXT("Docfind E_OUTOFMEMORY: %s line %d"), __FILE__, __LINE__);
return (E_OUTOFMEMORY);
}
// Now append our blank at the start
pidlToAdd = ILCombine((LPITEMIDLIST)pidl, (LPITEMIDLIST)&s_mkidBlank);
ILFree(pidl);
if (pidlToAdd)
{
pidlToAdd->mkid.cb += DF_APPENDSIZE;
// We must special case ones that are marked as
// a junction point as the GUID is marked at the end...
// This is a rather mondo hack, but what the hell...
if (SIL_GetType(pidlToAdd) & SHID_JUNCTION)
{
// This is a rather Mondo hack.
// Junction points have a GUID at the end
// We need to make room by moving the CLSID down
//
// BUGBUG (DavePl) does this really need to handle overlapping
// regions? If not, change it to less expensive CopyMemory
MoveMemory((LPBYTE)pidlToAdd+pidlToAdd->mkid.cb-SIZEOF(CLSID),
(LPBYTE)pidlToAdd+pidlToAdd->mkid.cb-SIZEOF(CLSID)-DF_APPENDSIZE,
SIZEOF(CLSID));
}
*(DF_SIGPTR(pidlToAdd)) = DF_TAGSIG;
ASSERT(this->iFolder < MAXSHORT);
*(DF_IFLDRPTR(pidlToAdd)) = (WORD) this->iFolder;
// Now add this to the view
*ppidl = pidlToAdd;
}
else
{
*piState = GNF_ERROR;
DebugMsg(DM_WARNING, TEXT("Docfind E_OUTOFMEMORY: %s line %d"), __FILE__, __LINE__);
return (E_OUTOFMEMORY);
}
}
} while (fContinue && *pfContinue);
return NOERROR;
}
/*----------------------------------------------------------------------------
/ CDFFilter_DeclareFSNotifyInterest implementation
/ ---------------------------------
/ Purpose:
/ Registering our interest in FS change notifications.
/
/ Notes:
/ -
/
/ In:
/ pdfff -> description of the find filter
/ hwndDlg = window handle of the find dialog
/ uMsg = message to be sent to window when informing of notify
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
STDMETHODIMP CDFFilter_DeclareFSNotifyInterest(LPDOCFINDFILEFILTER pdfff, HWND hwndDlg, UINT uMsg )
{
LPDFFILTER this = IToClass(CDFFilter, dfff, pdfff);
SHChangeNotifyEntry fsne;
TCHAR szPath[ MAX_PATH ];
LPTSTR pszPath;
fsne.fRecursive = TRUE;
/* If we have a IDL list then don't bother breaking the path up */
if ( this ->pidlStart )
{
fsne.pidl = this ->pidlStart;
SHChangeNotifyRegister(hwndDlg, SHCNRF_NewDelivery | SHCNRF_ShellLevel, SHCNE_DISKEVENTS, uMsg, 1, &fsne );
}
else
{
pszPath = this ->szPath;
/* While we still have elements in the path */
while ( NULL != ( pszPath = (LPTSTR) NextPath( pszPath, szPath, MAX_PATH ) ) )
{
PathAddBackslash( szPath );
/* Create a PIDL and declare that it is interested in events on that window */
fsne.pidl = ILCreateFromPath( szPath );
if ( NULL != fsne.pidl )
{
SHChangeNotifyRegister(hwndDlg, SHCNRF_NewDelivery | SHCNRF_ShellLevel, SHCNE_DISKEVENTS, uMsg, 1, &fsne );
ILFree((LPITEMIDLIST)fsne.pidl);
}
}
}
return NOERROR;
}