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.
 
 
 
 
 
 

1354 lines
32 KiB

//////////////////////////////////////////////////////////////////////////////
//
// WFSearch.c - Windows File Manager Search code
//
// Version 3.10
//
// Copyright (c) 1992-1993, Microsoft Corp. All rights reserved
//
//
// Strategy for single multithreaded search:
//
// 3 ways to terminate search
// 1. Search completes normally
// 2. User hits cancel on search progress dlg (search dlg)
// 3. User closes mdi search window (search window) while searching
//
// bCancel indicates quit searching (cases 2,3)
// 2-> SEARCH_CANCEL
// 3-> SEARCH_MDICLOSE
//
// Case 1: W: SendMessage FS_SEARCHEND to hwndFrame
// M: hThread = NULL < FS_SEARCHEND >
// if search dlg up
// kill search dlg
// hSearchDlg = NULL
// M: invalidate hwndLB < SearchEnd >
// if SEARCH_ERROR
// message, quit
// if no matches
// message, quit
// show normal window
// W: Worker thread dies
//
// Case 2: M: bCancel = TRUE
// SEARCH_CANCEL
// W: handle like Case 1.
//
// Case 3: M: bCancel = TRUE
// SEARCH_MDICLOSE
// hwndSearch = NULL
// M: ClearSearchLB() < WM_DESTROY >
// M: Listbox cleared < ClearSearchLB >
// MDI search window destroyed
//
// << asynchronous >>
//
// W: SendMessage FS_SEARCHEND
//
// << Now synchronous >>
//
// M: hThread = NULL
// destroy search dlg
// M: ClearSearchLB() < SearchEnd >
// M: free memory < ClearSearchLB >
//
// All race conditions are eliminated by using SendMessages to
// the main thread or by "don't care" situations.
//
// Other race conditions:
//
// M: W:
// Search completes
// Hide/Cancel hit
// FS_SEARCHEND sent
//
// No problem, since Hide/Cancel normally force a search completion.
// Now it just happens sooner.
//
//
// M: W:
// MDI search close
// Search completes
//
// MDI Search close must complete before FS_SEARCHEND can process.
// (Since it is SendMessage'd by W:)
//
//
// Freeing associated memory. This is done by whoever leaves last.
// If the search window is closed first, the worker thread cleans up. If
// the search completes and then the mdi search window is closed, the
// memory is freed by the mdi search window.
//
// FILE HISTORY:
//
/////////////////////////////////////////////////////////////////////////////
#include "winfile.h"
#include "lfn.h"
#include <commctrl.h>
#include <vdmapi.h>
INT maxExt;
INT iDirsRead;
DWORD dwLastUpdateTime;
INT maxExtLast;
VOID UpdateIfDirty(HWND hWnd);
INT FillSearchLB(HWND hwndLB, LPWSTR szSearchFileSpec, BOOL bSubDirOnly);
INT SearchList(
HWND hwndLB,
LPWSTR szPath,
LPWSTR szFileSpec,
BOOL bRecurse,
LPXDTALINK* plpStart,
INT iFileCount,
BOOL bRoot);
VOID ClearSearchLB(BOOL bWorkerCall);
INT SearchDrive();
#define SEARCH_FILE_WIDTH_DEFAULT 50
/////////////////////////////////////////////////////////////////////
//
// Name: SearchList
//
// Synopsis: Recursive search
//
//
// Return: INT, # of files found
//
//
// Assumes:
//
// Effects:
//
//
// Notes:
//
/////////////////////////////////////////////////////////////////////
INT
SearchList(
HWND hwndLB,
LPWSTR szPath,
LPWSTR szFileSpec,
BOOL bRecurse,
LPXDTALINK* plpStart,
INT iFileCount,
BOOL bRoot)
{
INT iRetVal;
SIZE size;
BOOL bFound;
LPWSTR pszNewPath;
LPWSTR pszNextFile;
LFNDTA lfndta;
LPXDTA lpxdta;
HDC hdc;
HANDLE hOld;
DWORD dwTimeNow;
BOOL bLowercase;
WCHAR szTemp[MAXPATHLEN];
BOOL bLFN;
DWORD dwAttrs;
INT iBitmap;
//
// hack: setup ATTR_LOWERCASE if a letter'd (NON-unc) drive
// LATER: do GetVolumeInfo for UNC too!
//
bLowercase = (wTextAttribs & TA_LOWERCASEALL) ||
((wTextAttribs & TA_LOWERCASE) && !SearchInfo.bCasePreserved);
dwTimeNow = GetTickCount();
if (dwTimeNow > dwLastUpdateTime+1000) {
dwLastUpdateTime = dwTimeNow;
SearchInfo.iDirsRead = iDirsRead;
SearchInfo.iFileCount = iFileCount;
PostMessage(hwndFrame, FS_SEARCHUPDATE, iDirsRead, iFileCount);
}
iDirsRead++;
if (!*plpStart) {
*plpStart = MemNew();
if (!*plpStart) {
MemoryError:
SearchInfo.dwError = ERROR_NOT_ENOUGH_MEMORY;
SearchInfo.eStatus = SEARCH_ERROR;
return iFileCount;
}
//
// Never shows altname
//
MemLinkToHead(*plpStart)->dwAlternateFileNameExtent = 0;
SetWindowLong(GetParent(hwndLB), GWL_HDTA, (LONG)*plpStart);
SearchInfo.lpStart = *plpStart;
}
//
// allocate the buffer for this level
//
pszNewPath = (LPWSTR)LocalAlloc(LPTR, ByteCountOf(lstrlen(szPath) + MAXFILENAMELEN + 2));
if (!pszNewPath) {
goto MemoryError;
}
lstrcpy(pszNewPath, szPath);
AddBackslash(pszNewPath);
pszNextFile = pszNewPath + lstrlen(pszNewPath);
lstrcpy(pszNextFile, szFileSpec);
bFound = WFFindFirst(&lfndta, pszNewPath, ATTR_ALL);
hdc = GetDC(hwndLB);
hOld = SelectObject(hdc, hFont);
//
// Ignore file not found errors AND access denied errors
// AND PATH_NOT_FOUND when not in the root
//
if (!bFound && ERROR_FILE_NOT_FOUND != lfndta.err &&
(bRoot ||
ERROR_ACCESS_DENIED != lfndta.err &&
ERROR_PATH_NOT_FOUND != lfndta.err &&
ERROR_INVALID_NAME != lfndta.err)) {
SearchInfo.eStatus = SEARCH_ERROR;
SearchInfo.dwError = lfndta.err;
bRecurse = FALSE;
goto SearchCleanup;
}
while (bFound) {
//
// allow escape to exit
//
if (SearchInfo.bCancel) {
bRecurse = FALSE;
break;
}
//
// Make sure this is not a "." or ".." directory
//
if (!ISDOTDIR(lfndta.fd.cFileName)) {
lstrcpy(pszNextFile, lfndta.fd.cFileName);
// Warning: was OemToChar(pszNewPath, szMessage);
// but taken out since no translation necessary.
// Here on out _was_ using szMessage
// (Multithreaded=> szMessage usage BAD)
bLFN = IsLFN(lfndta.fd.cFileName);
if (bLowercase) {
lstrcpy(szTemp, pszNewPath);
CharLower(szTemp);
GetTextExtentPoint32(hdc, szTemp, lstrlen(szTemp), &size);
} else {
GetTextExtentPoint32(hdc, pszNewPath, lstrlen(pszNewPath), &size);
}
maxExt = max(size.cx,maxExt);
lpxdta = MemAdd(plpStart, lstrlen(pszNewPath), 0);
if (!lpxdta) {
bRecurse = FALSE; // simulate an abort
SearchInfo.dwError = ERROR_NOT_ENOUGH_MEMORY;
SearchInfo.eStatus = SEARCH_ERROR;
break;
}
dwAttrs = lpxdta->dwAttrs = lfndta.fd.dwFileAttributes;
lpxdta->ftLastWriteTime = lfndta.fd.ftLastWriteTime;
lpxdta->qFileSize.LowPart = lfndta.fd.nFileSizeLow;
lpxdta->qFileSize.HighPart = lfndta.fd.nFileSizeHigh;
lstrcpy(MemGetFileName(lpxdta), pszNewPath);
MemGetAlternateFileName(lpxdta)[0] = CHAR_NULL;
if (bLFN)
lpxdta->dwAttrs |= ATTR_LFN;
if (!SearchInfo.bCasePreserved)
lpxdta->dwAttrs |= ATTR_LOWERCASE;
if (dwAttrs & ATTR_DIR)
iBitmap = BM_IND_CLOSE;
else if (dwAttrs & (ATTR_HIDDEN | ATTR_SYSTEM))
iBitmap = BM_IND_RO;
else if (IsProgramFile(lfndta.fd.cFileName))
iBitmap = BM_IND_APP;
else if (IsDocument(lfndta.fd.cFileName))
iBitmap = BM_IND_DOC;
else
iBitmap = BM_IND_FIL;
lpxdta->byBitmap = iBitmap;
SendMessage(hwndFrame,
FS_SEARCHLINEINSERT,
(WPARAM)&iFileCount,
(LPARAM)lpxdta);
}
//
// Search for more files in the current directory
//
bFound = WFFindNext(&lfndta);
}
SearchCleanup:
WFFindClose(&lfndta);
if (hOld)
SelectObject(hdc, hOld);
ReleaseDC(hwndLB, hdc);
if (!bRecurse)
goto SearchEnd;
//
// Now see if there are any subdirectories here
//
lstrcpy(pszNextFile, szStarDotStar);
bFound = WFFindFirst(&lfndta, pszNewPath, ATTR_DIR | ATTR_HS);
while (bFound) {
//
// allow escape to exit
//
if (SearchInfo.bCancel) {
bRecurse = FALSE;
break;
}
//
// Make sure this is not a "." or ".." directory.
//
if (!ISDOTDIR(lfndta.fd.cFileName) &&
(lfndta.fd.dwFileAttributes & ATTR_DIR)) {
//
// Yes, search and add files in this directory
//
lstrcpy(pszNextFile, lfndta.fd.cFileName);
//
// Add all files in this subdirectory.
//
iRetVal = SearchList(hwndLB,
pszNewPath,
szFileSpec,
bRecurse,
plpStart,
iFileCount,
FALSE);
iFileCount = iRetVal;
if (SEARCH_ERROR == SearchInfo.eStatus) {
break;
}
}
bFound = WFFindNext(&lfndta);
}
WFFindClose(&lfndta);
SearchEnd:
//
// Save the number of files in the xdtahead structure.
//
MemLinkToHead(SearchInfo.lpStart)->dwEntries = iFileCount;
LocalFree((HANDLE)pszNewPath);
return iFileCount;
}
VOID
FixUpFileSpec(
LPWSTR szFileSpec)
{
WCHAR szTemp[MAXPATHLEN+1];
register LPWSTR p;
if (*szFileSpec == CHAR_DOT) {
lstrcpy(szTemp, SZ_STAR);
lstrcat(szTemp, szFileSpec);
lstrcpy(szFileSpec, szTemp);
}
//
// HACK: If there isn't a dot and the last char is a *, append ".*"
//
p = szFileSpec;
while ((*p) && (*p != CHAR_DOT))
++p;
if ((!*p) && (p != szFileSpec)) {
--p;
if (*p == CHAR_STAR)
lstrcat(p, SZ_DOTSTAR);
}
}
/*--------------------------------------------------------------------------*/
/* */
/* FillSearchLB() - */
/* */
/*--------------------------------------------------------------------------*/
/* This parses the given string for Drive, PathName, FileSpecs and
* calls SearchList() with proper parameters;
*
* hwndLB : List box where files are to be displayed;
* szSearchFileSpec : ANSI path to search
* bSubDirOnly : TRUE, if only subdirectories are to be searched;
*/
INT
FillSearchLB(HWND hwndLB, LPWSTR szSearchFileSpec, BOOL bRecurse)
{
INT iRet;
WCHAR szFileSpec[MAXPATHLEN+1];
WCHAR szPathName[MAXPATHLEN+1];
LPXDTALINK lpStart = NULL;
//
// Get the file specification part of the string.
//
lstrcpy(szFileSpec, szSearchFileSpec);
lstrcpy(szPathName, szSearchFileSpec);
StripPath(szFileSpec);
StripFilespec(szPathName);
FixUpFileSpec(szFileSpec);
maxExt = 0;
iDirsRead = 1;
dwLastUpdateTime = 0;
iRet = SearchList(hwndLB,
szPathName,
szFileSpec,
bRecurse,
&lpStart,
0,
TRUE);
//
// Only SetSel if none set already
//
if (LB_ERR == SendMessage(hwndLB, LB_GETCURSEL, 0, 0L))
SendMessage(hwndLB, LB_SETSEL, TRUE, 0L);
return(iRet);
}
VOID
GetSearchPath(HWND hWnd, LPWSTR pszPath)
{
LPWSTR p;
WCHAR szTemp[MAXPATHLEN+32];
// the search window doesn't have a current directory
GetWindowText(hWnd, szTemp, COUNTOF(szTemp));
// the window text looks like "Search Results: C:\FOO\BAR\*.*"
p = szTemp;
while (*p && *p != CHAR_COLON) // find the :
++p;
p += 2; // skip the ": "
lstrcpy(pszPath, p);
}
//--------------------------------------------------------------------------
//
// UpdateSearchStatus() -
//
//--------------------------------------------------------------------------
VOID
UpdateSearchStatus(HWND hwndLB, INT nCount)
{
SetStatusText(0, SST_FORMAT|SST_RESOURCE, (LPWSTR) MAKEINTRESOURCE(IDS_SEARCHMSG), nCount);
if (SearchInfo.hThread)
SetStatusText(1, SST_RESOURCE, (LPWSTR) MAKEINTRESOURCE(IDS_SEARCHING));
else
SetStatusText(1, 0, szNULL);
}
/////////////////////////////////////////////////////////////////////
//
// Name: SearchWndProc
//
// Synopsis:
//
// Return:
//
//
// Assumes:
//
// Effects:
//
//
// Notes:
//
/////////////////////////////////////////////////////////////////////
LONG
SearchWndProc(
register HWND hwnd,
UINT uMsg,
WPARAM wParam,
LONG lParam)
{
INT iSel;
HWND hwndLB;
WCHAR szPath[MAXPATHLEN];
WCHAR szTitle[128];
WCHAR szMessage[MAXMESSAGELEN];
hwndLB = GetDlgItem(hwnd, IDCW_LISTBOX);
switch (uMsg) {
case WM_COMPAREITEM:
#define lpcis ((LPCOMPAREITEMSTRUCT)lParam)
#define lpItem1 ((LPXDTA)(lpcis->itemData1))
#define lpItem2 ((LPXDTA)(lpcis->itemData2))
return lstrcmpi(MemGetFileName(lpItem1), MemGetFileName(lpItem2));
#undef lpcis
#undef lpItem1
#undef lpItem2
case WM_CLOSE:
//
// This should be ok since we can't pre-empt ourselves
//
if (SearchInfo.hThread) {
SearchInfo.bCancel = TRUE;
SearchInfo.eStatus = SEARCH_MDICLOSE;
}
hwndSearch = NULL;
SendMessage(hwndMDIClient, WM_MDIDESTROY, (WPARAM) hwnd, 0L);
return 0;
case FS_GETDRIVE:
//
// Returns the letter of the corresponding directory
//
// !! BUGBUG !!
//
// returns error on UNC
//
SendMessage(hwnd, FS_GETDIRECTORY, COUNTOF(szPath), (LPARAM)szPath);
return (LRESULT) CHAR_A + DRIVEID(szPath);
case FS_GETDIRECTORY:
GetSearchPath(hwnd, szPath);
StripFilespec(szPath); // remove the filespec
AddBackslash(szPath); // to be the same as DirWndProc
lstrcpy((LPWSTR)lParam, szPath);
break;
case FS_GETFILESPEC:
// the search window doesn't have a current directory
GetSearchPath(hwnd, szPath);
StripPath(szPath); // remove the path (leave the filespec)
lstrcpy((LPWSTR)lParam, szPath);
break;
case FS_SETSELECTION:
// wParam is the select(TRUE)/unselect(FALSE) param
// lParam is the filespec to match against
SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L);
DSSetSelection(hwndLB, wParam, (LPWSTR)lParam, TRUE);
SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hwndLB, NULL, TRUE);
break;
case FS_GETSELECTION:
return (LONG)DirGetSelection(NULL,
hwnd,
hwndLB,
wParam,
(BOOL *)lParam,
NULL);
break;
case WM_MDIACTIVATE:
//
// Invalidate the cache since we are switching windows
//
ExtSelItemsInvalidate();
//
// we are receiving the activation
//
if (GET_WM_MDIACTIVATE_FACTIVATE(hwnd, wParam, lParam)) {
EnableCheckTBButtons(hwnd);
//
// update status bar
// and inform SearchList to update the status bar
//
UpdateSearchStatus(hwndLB,(INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L));
SearchInfo.bUpdateStatus = TRUE;
//
// if we are dirty, ask if we should update
//
UpdateIfDirty(hwnd);
} else {
//
// Don't update status
//
SearchInfo.bUpdateStatus = FALSE;
}
break;
case WM_FSC:
//
// if the search window is not active or FSCs are disabled
// don't prompt now, wait till we get the end FSC or are
// activated (above in WM_ACTIVATE)
//
if (cDisableFSC ||
(hwnd != (HWND)SendMessage(hwndMDIClient, WM_MDIGETACTIVE, 0, 0L)) ||
(GetActiveWindow() != hwndFrame)) {
SetWindowLong(hwnd, GWL_FSCFLAG, TRUE);
break;
}
SetWindowLong(hwnd, GWL_FSCFLAG, FALSE);
SendMessage(hwnd, FS_CHANGEDISPLAY, CD_SEARCHUPDATE, 0L);
break;
case FS_CHANGEDISPLAY:
//
// For safety, clear the CD_DONTSTEAL flag, although
// it should never be set here (set by DrivesSetDrive)
//
wParam &= ~CD_DONTSTEAL;
if (wParam == CD_VIEW || wParam == CD_SEARCHFONT) {
uNewView = GetWindowLong(hwnd, GWL_VIEW);
//
// in case font changed, update maxExt
//
if (CD_SEARCHFONT == wParam) {
//
// Update dwEntries.. bogus since duplicated in iFileCount,
// but quick fix.
//
MemLinkToHead(SearchInfo.lpStart)->dwEntries =
(DWORD)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
maxExt = GetMaxExtent(hwndLB, SearchInfo.lpStart, FALSE);
}
FixTabsAndThings(hwndLB,
(WORD *)GetWindowLong(hwnd, GWL_TABARRAY),
maxExt + dxText,
0,
uNewView);
InvalidateRect(hwndLB, NULL, TRUE);
} else {
// Allow only 1 thread to be created!
if (SearchInfo.hThread)
return 0L;
if (wParam == CD_SEARCHUPDATE) {
LoadString(hAppInstance, IDS_SEARCHTITLE, szTitle, COUNTOF(szTitle));
LoadString(hAppInstance, IDS_SEARCHREFRESH, szMessage, COUNTOF(szMessage));
if (MessageBox(hwnd, szMessage, szTitle, MB_YESNO | MB_ICONQUESTION) != IDYES)
break;
}
//
// Now clear the listbox
//
ClearSearchLB(FALSE);
//
// is this a refresh?
//
if (!lParam) {
GetSearchPath(hwnd, szPath);
} else {
lstrcpy(szPath, (LPWSTR)lParam); // explicit re-search
}
LoadString(hAppInstance, IDS_SEARCHTITLE, szMessage, COUNTOF(szMessage));
lstrcat(szMessage, szPath);
SetWindowText(hwnd, szMessage);
//
// Init, just like SearchDlgProc
//
SearchInfo.iDirsRead = 0;
SearchInfo.iFileCount = 0;
SearchInfo.eStatus = SEARCH_NULL;
SearchInfo.bCancel = FALSE;
// Create our dialog! (modeless)
CreateDialog(hAppInstance, (LPWSTR) MAKEINTRESOURCE(SEARCHPROGDLG), hwndFrame, (DLGPROC) SearchProgDlgProc);
} // ELSE from wParam == CD_VIEW
break;
case WM_SETFOCUS:
SetFocus(hwndLB);
UpdateIfDirty(hwnd);
return SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
case WM_COMMAND:
//
// Was this a double-click?
//
switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
case LBN_DBLCLK:
SendMessage(hwndFrame,
WM_COMMAND,
GET_WM_COMMAND_MPS(IDM_OPEN, 0, 0));
break;
case LBN_SELCHANGE:
{
INT i;
ExtSelItemsInvalidate();
for (i = 0; i < iNumExtensions; i++) {
(extensions[i].ExtProc)(hwndFrame, FMEVENT_SELCHANGE, 0L);
}
break;
}
}
break;
case WM_DESTROY:
{
HANDLE hMem;
ClearSearchLB(FALSE);
SearchInfo.hwndLB = NULL;
if (hMem = (HANDLE)GetWindowLong(hwnd, GWL_TABARRAY))
LocalFree(hMem);
break;
}
case WM_CREATE:
{
// globals used:
// SearchInfo.szSearch path to start search at
// SearchInfo.bDontSearchSubs tells us not to do a recursive search
RECT rc;
WORD *pwTabs;
GetClientRect(hwnd, &rc);
hwndLB = CreateWindowEx(0L, szListbox, NULL,
WS_CHILD | WS_BORDER | LBS_SORT | LBS_NOTIFY |
LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL |
LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT |
WS_VSCROLL | WS_HSCROLL | WS_VISIBLE,
-1, -1, rc.right+2, rc.bottom+2,
hwnd, (HMENU)IDCW_LISTBOX,
hAppInstance, NULL);
if (!hwndLB)
return -1L;
if ((pwTabs = (WORD *)LocalAlloc(LPTR,sizeof(WORD) * MAX_TAB_COLUMNS)) == NULL)
return -1L;
hwndSearch = hwnd;
SetWindowLong(hwnd, GWL_TYPE, TYPE_SEARCH);
SetWindowLong(hwnd, GWL_VIEW, uNewView);
SetWindowLong(hwnd, GWL_SORT, IDD_NAME);
SetWindowLong(hwnd, GWL_ATTRIBS,ATTR_DEFAULT);
SetWindowLong(hwnd, GWL_FSCFLAG, FALSE);
SetWindowLong(hwnd, GWL_HDTA, 0L);
SetWindowLong(hwnd, GWL_TABARRAY, (LONG)pwTabs);
SetWindowLong(hwnd, GWL_LASTFOCUS, (LONG)hwndLB);
SetWindowLong(hwnd, GWL_LISTPARMS, (LONG)hwnd);
SetWindowLong(hwnd, GWL_IERROR, 0);
//
// Fill the listbox
//
SendMessage(hwndLB, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
SearchInfo.hwndLB = hwndLB;
//
// Create our dialog! (modeless)
//
CreateDialog(hAppInstance, (LPWSTR) MAKEINTRESOURCE(SEARCHPROGDLG), hwndFrame, (DLGPROC) SearchProgDlgProc);
break;
}
case WM_DRAGLOOP:
//
// WM_DRAGLOOP is sent to the source window as the object is moved.
//
// wParam: TRUE if the object is currently over a droppable sink
// lParam: LPDROPSTRUCT
//
//
// DRAGLOOP is used to turn the source bitmaps on/off as we drag.
//
DSDragLoop(hwndLB, wParam, (LPDROPSTRUCT)lParam);
break;
case WM_DRAGSELECT:
//
// WM_DRAGSELECT is sent to a sink whenever an new object is dragged
// inside of it.
//
// wParam: TRUE if the sink is being entered, FALSE if it's being
// exited.
// lParam: LPDROPSTRUCT
//
//
// DRAGSELECT is used to turn our selection rectangle on or off.
//
#define lpds ((LPDROPSTRUCT)lParam)
//
// Turn on/off status bar
//
SendMessage(hwndStatus,
SB_SETTEXT,
SBT_NOBORDERS|255,
(LPARAM)szNULL);
SendMessage(hwndStatus, SB_SIMPLE, (wParam ? 1 : 0), 0L);
UpdateWindow(hwndStatus);
iSelHilite = lpds->dwControlData;
DSRectItem(hwndLB, iSelHilite, (BOOL)wParam, TRUE);
break;
case WM_DRAGMOVE:
//
// WM_DRAGMOVE is sent to a sink as the object is being dragged
// within it.
//
// wParam: Unused
// lParam: LPDROPSTRUCT
//
//
// DRAGMOVE is used to move our selection rectangle among sub-items.
//
#define lpds ((LPDROPSTRUCT)lParam)
//
// Get the subitem we are over.
//
iSel = lpds->dwControlData;
//
// Is it a new one?
//
if (iSel == iSelHilite)
break;
//
// Yup, un-select the old item.
//
DSRectItem(hwndLB, iSelHilite, FALSE, TRUE);
//
// Select the new one.
iSelHilite = iSel;
DSRectItem(hwndLB, iSel, TRUE, TRUE);
break;
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT lpLBItem;
PWORD pwTabs;
DWORD dwView = GetWindowLong(hwnd, GWL_VIEW);
lpLBItem = (LPDRAWITEMSTRUCT)lParam;
iSel = lpLBItem->itemID;
if (iSel < 0)
break;
if (maxExt > maxExtLast) {
pwTabs = (WORD *)GetWindowLong(hwndSearch, GWL_TABARRAY);
//
// Need to update tabs
//
FixTabsAndThings(SearchInfo.hwndLB,
pwTabs,
maxExt+dxText,
0,
dwView);
maxExtLast = maxExt;
//
// If there is > 1 column (i.e., wider filenames shift over
// size/date/time/flags column), then we must invalidate the
// entire rect to realign everything.
//
// Note that we explicitly ignore VIEW_DOSNAMES, since this
// is not displayed in the search window.
//
if (dwView & ~VIEW_DOSNAMES)
InvalidateRect(SearchInfo.hwndLB, NULL, TRUE);
}
DrawItem(hwnd,
GetWindowLong(hwnd, GWL_VIEW),
(LPDRAWITEMSTRUCT)lParam,
TRUE);
break;
}
case WM_DROPOBJECT:
return DSDropObject(hwnd, hwndLB, (LPDROPSTRUCT)lParam, TRUE);
case WM_LBTRACKPOINT:
return DSTrackPoint(hwnd, hwndLB, wParam, lParam, TRUE);
case WM_MEASUREITEM:
#define pLBMItem ((LPMEASUREITEMSTRUCT)lParam)
pLBMItem->itemHeight = dyFileName;
break;
#undef pLBItem
case WM_QUERYDROPOBJECT:
//
// Ensure that we are dropping on the client area of the listbox.
//
#define lpds ((LPDROPSTRUCT)lParam)
//
// Ensure that we can accept the format.
//
switch (lpds->wFmt) {
case DOF_EXECUTABLE:
case DOF_DIRECTORY:
case DOF_DOCUMENT:
case DOF_MULTIPLE:
if (lpds->hwndSink == hwnd)
lpds->dwControlData = (DWORD)-1L;
return TRUE;
}
return FALSE;
#undef lpds
case WM_SIZE:
if (wParam != SIZEICONIC) {
MoveWindow(GetDlgItem(hwnd, IDCW_LISTBOX),
-1, -1,
LOWORD(lParam)+2,
HIWORD(lParam)+2,
TRUE);
}
/*** FALL THRU ***/
default:
return(DefMDIChildProc(hwnd, uMsg, wParam, lParam));
}
return(0L);
}
VOID
UpdateIfDirty(HWND hwnd)
{
if (GetWindowLong(hwnd, GWL_FSCFLAG)) {
//
// I am clean
//
SetWindowLong(hwnd, GWL_FSCFLAG, FALSE);
SendMessage(hwnd, FS_CHANGEDISPLAY, CD_SEARCHUPDATE, 0L);
}
}
VOID
LockSearchFile()
{
EnableMenuItem( GetMenu(hwndFrame), IDM_SEARCH, MF_BYCOMMAND | MF_GRAYED );
}
VOID
UnlockSearchFile()
{
EnableMenuItem( GetMenu(hwndFrame), IDM_SEARCH, MF_BYCOMMAND | MF_ENABLED );
}
LRESULT CALLBACK
SearchProgDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DWORD dwIgnore;
WCHAR szTemp[MAXPATHLEN];
INT i;
DRIVE drive;
switch (uMsg) {
case WM_INITDIALOG:
SearchInfo.hSearchDlg = hDlg;
//
// Initialize display
//
SendMessage(hwndFrame, FS_SEARCHUPDATE,
SearchInfo.iDirsRead, SearchInfo.iFileCount);
lstrcpy(szTemp,SearchInfo.szSearch);
StripPath(szTemp);
SetDlgItemText(hDlg, IDD_NAME, szTemp);
lstrcpy(szTemp,SearchInfo.szSearch);
StripFilespec(szTemp);
SetDlgItemPath(hDlg, IDD_PATH, szTemp);
// Speedy switch to search window (change drive)
// And also check if bCasePreserved.
if (CHAR_COLON == SearchInfo.szSearch[1]) {
drive = DRIVEID(SearchInfo.szSearch);
SearchInfo.bCasePreserved = IsCasePreservedDrive(drive);
for (i=0; i<cDrives; i++) {
if (drive == rgiDrive[i]) {
break;
}
}
// don't do this if drive doesn't exist
if (i != cDrives) {
SetWindowLong(hwndDriveBar, GWL_CURDRIVEIND, i);
SetWindowLong(hwndDriveBar, GWL_CURDRIVEFOCUS, i);
UpdateStatus(hwndSearch);
SelectToolbarDrive(i);
InvalidateRect(hwndDriveBar, NULL, TRUE);
UpdateWindow(hwndDriveBar);
}
} else {
//
// LATER: handle UNC case
//
// Should actually hit network to see if case preserving
//
SearchInfo.bCasePreserved = FALSE;
}
//
// Add thread here!
//
if (!SearchInfo.hThread) {
SearchInfo.hThread = CreateThread( NULL, // Security
0L, // Stack Size
(LPTHREAD_START_ROUTINE)SearchDrive,
NULL,
0L,
&dwIgnore );
}
return TRUE;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDCANCEL:
SearchInfo.bCancel = TRUE;
SearchInfo.eStatus = SEARCH_CANCEL;
return TRUE;
case IDD_HIDE:
DestroyWindow(SearchInfo.hSearchDlg);
SearchInfo.hSearchDlg = NULL;
return TRUE;
}
return TRUE;
default:
return 0;
}
return 0;
}
/////////////////////////////////////////////////////////////////////
//
// Name: SearchEnd
//
// Synopsis: Handles display window of search results/brings up hwndSearch
//
// IN VOID
//
// Return: VOID
//
//
// Assumes: At this point, either hwndSearch & hwndLB are valid,
// or eStatus == SEARCH_MDICLOSE.
//
// This is safe since eStatus is set to SEARCH_MDICLOSE before
// hwnd{Search,LB} are destroyed.
//
// Must be called by main thread.
//
// Effects:
//
//
// Notes: This call is atomic with respect to all SendMessages in
// the main UI thread. (This is called only by FS_SEARCHEND
// in FrameWndProc) We can safely ignore certain race conditions:
//
// hwndSearch
//
/////////////////////////////////////////////////////////////////////
VOID
SearchEnd(VOID)
{
HWND hwndMDIChild;
if (SEARCH_MDICLOSE == SearchInfo.eStatus) {
//
// Free up the data structure
// Actually called by main thread, but on behalf of worker thread.
//
ClearSearchLB(TRUE);
} else {
InvalidateRect(SearchInfo.hwndLB, NULL, TRUE);
}
if (SEARCH_ERROR == SearchInfo.eStatus) {
LoadString(hAppInstance, IDS_SEARCHTITLE, szTitle, COUNTOF(szTitle));
FormatError(TRUE, szMessage, COUNTOF(szMessage), SearchInfo.dwError);
MessageBox(hwndFrame, szMessage, szTitle, MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
if (0 == SearchInfo.iRet)
goto CloseWindow;
} else if (0 == SearchInfo.iRet && SEARCH_MDICLOSE != SearchInfo.eStatus) {
LoadString(hAppInstance, IDS_SEARCHTITLE, szTitle, COUNTOF(szTitle));
LoadString(hAppInstance, IDS_SEARCHNOMATCHES, szMessage, COUNTOF(szMessage));
MessageBox(hwndFrame, szMessage, szTitle, MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
CloseWindow:
ShowWindow(hwndSearch, SW_HIDE);
PostMessage(hwndSearch, WM_CLOSE, 0, 0L);
return;
}
if (SEARCH_MDICLOSE != SearchInfo.eStatus) {
hwndMDIChild = (HWND)SendMessage(hwndMDIClient, WM_MDIGETACTIVE, 0, 0L);
if (hwndMDIChild) {
ShowWindow(hwndSearch,
GetWindowLong(hwndMDIChild, GWL_STYLE) & WS_MAXIMIZE ?
SW_SHOWMAXIMIZED :
SW_SHOWNORMAL);
}
SendMessage(hwndMDIClient, WM_MDIACTIVATE, (WPARAM) hwndSearch, 0L);
UpdateSearchStatus(SearchInfo.hwndLB,
(INT)SendMessage(SearchInfo.hwndLB, LB_GETCOUNT, 0, 0L));
}
}
INT
SearchDrive()
{
maxExtLast = SEARCH_FILE_WIDTH_DEFAULT;
FixTabsAndThings(SearchInfo.hwndLB,
(WORD *)GetWindowLong(hwndSearch, GWL_TABARRAY),
maxExtLast,
0,
GetWindowLong(hwndSearch, GWL_VIEW));
SearchInfo.iRet = FillSearchLB(SearchInfo.hwndLB,
SearchInfo.szSearch,
!SearchInfo.bDontSearchSubs);
if (SearchInfo.hThread) {
CloseHandle(SearchInfo.hThread);
} else {
//
// Error case?
//
}
SendMessage(hwndFrame, FS_SEARCHEND,0,0L);
ExitThread(0L);
return 0;
}
/////////////////////////////////////////////////////////////////////
//
// Name: ClearSearchLB
//
// Synopsis: Clears out memory associated with search LB/kills LB.
//
// INC bWorkerCall -- T/F if called by worker thread
//
// Return: VOID
//
//
// Assumes: * hThread non-NULL if worker thread up
// (if hThread=NULL, memory already freed)
// * SearchInfo.pBrickSearch not changing
//
// If called by main thread, hwndLB is valid.
//
// Effects: SearchInfo.pBrickSearch freed
// hwndLB cleared, if it exists.
//
//
// Notes:
//
/////////////////////////////////////////////////////////////////////
VOID
ClearSearchLB(BOOL bWorkerCall)
{
//
// When to free up:
//
// 1. Worker thread is dead (called by main thread) OR
// 2. Search window is gone (called by worker)
//
if (!SearchInfo.hThread || bWorkerCall) {
MemDelete(SearchInfo.lpStart);
}
if (!bWorkerCall) {
ExtSelItemsInvalidate();
SendMessage(SearchInfo.hwndLB, LB_RESETCONTENT, 0, 0);
}
}