Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1608 lines
46 KiB

// AppSearch
//
// Tool for searching a user's hard drives and locating
// applications that can be patched
//
// Author: t-michkr (9 June 2000)
//
// AppSearch.cpp
// User-interface
// We make use of some Win2K/IE5 common controls
#include <windows.h>
#include <commctrl.h>
#include <comdef.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <shellapi.h>
#include <assert.h>
#include "main.h"
#include "searchdb.h"
#include "filebrowser.h"
#include "resource.h"
// Compare data, used for sorting listview items.
struct SCompareParam
{
HWND hwList;
int iSubItem;
};
// Size of some static controls
const int c_nStaticLineHeight = 2;
const int c_nResultFrameWidth = 2;
// Minimum height of the result window
const int c_nResultListHeight = 201;
// ID's of controls we create ourselves
const int c_iStaticLineID = 50;
const int c_iResultListID = 51;
const int c_iResultFrameID = 52;
const int c_iStatusBarID = 53;
const int c_iFindAnimID = 54;
// Positions of menu items (these will be needed to be changed
// if the menu is altered)
const int c_iViewMenuPos = 2;
const int c_iArrangeIconsPos = 5;
// List view column info
const int c_nListColumnWidth = 250;
const int c_nNumListColumns = 2;
const int c_iListColumnNameIDS[c_nNumListColumns] = {
IDS_LISTNAME, IDS_LISTPATH};
// Number of children that need to be adjusted on resize
const int c_nChildren = 6;
// ID's of children that need to be adjusted on resize
int g_aiChildrenIDs[c_nChildren] = {IDC_BROWSE, IDC_FINDNOW, IDC_STOP, IDC_CLEARALL,
IDC_DRIVELIST, c_iFindAnimID};
// Margins of those children, determined from dialog box in HandleInitDialog().
int g_aiChildrenMargins[c_nChildren];
// Minimum window width and window height, determined from dialog box
// in HandleInitDialog().
int g_nMinWindowWidth;
int g_nWindowHeight;
// Whether or not the user is browsing through menus, used for displaying
// menu help text.
BOOL g_fInMenu = FALSE;
// Instance of this app.
HINSTANCE g_hinst = 0;
// Original window proc for the list view control.
WNDPROC pfnLVOrgWndProc = 0;
// Subclassed window proc for list view, intercepts WM_PAINT messages
// and writes "No items in view" if empty.
LRESULT CALLBACK LVWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
// Dialog proc for main application dialog.
BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
// Display shimming information for specified exe.
void ShowShimInfo(HWND hwnd, TCHAR* szAppPath);
// Returns an allocated string with resource ID id, or 0 on failure.
TCHAR* LoadStringResource(UINT id);
// Print an error box with message in string resource uiMsg.
void Error(HWND hwnd, UINT uiMsg);
// Compare entries in the listview, used for sorting.
int CALLBACK CompareListEntries(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
// Start searching the AppCompat database.
void StartSearch(HWND hwnd);
// Terminate the current search, does nothing if no search is active.
void StopSearch(HWND hwnd);
// Remove all results shown
void ClearResults(HWND hwnd);
// Update the status bar with the new message.
void UpdateStatus(HWND hwnd, PTSTR szMsg);
// Add a new app to the list
void AddApp(SMatchedExe* pme, HWND hwList);
// Message Handlers.
BOOL HandleInitDialog(HWND hwnd);
void HandleBrowse(HWND hwnd);
void HandleCommand(HWND hwnd, int iCtrlID, HWND hwChild);
void HandleEnterMenuLoop(HWND hwnd);
void HandleExitMenuLoop(HWND hwnd);
void HandleGetMinMaxInfo(HWND hwnd, LPMINMAXINFO pmmi);
void HandleMenuSelect(HWND hwnd, HMENU hMenu, UINT uiMenuID, UINT uiFlags);
void HandleNotify(HWND hwnd, int iCtrlID, void* pvArg);
void HandleSearchAddApp(HWND hwnd);
void HandleSearchUpdate(HWND hwnd, PTSTR szMsg);
void HandleSize(HWND hwnd, int iWidth, int iHeight);
void HandleSizing(HWND hwnd, int iEdge, LPRECT pRect);
// Program entry point.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int)
{
// Save our global instance handle.
g_hinst = hInstance;
// Make sure the common controls DLL is loaded.
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(icc);
// We use the list view, status bar, tree control, animate,
// tooltip, and comboboxex classes
icc.dwICC = ICC_USEREX_CLASSES | ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES
| ICC_TREEVIEW_CLASSES | ICC_ANIMATE_CLASS | ICC_TAB_CLASSES ;
if(InitCommonControlsEx(&icc) == FALSE)
return 0;
// Run the actual dialog for the application
return DialogBox(g_hinst, MAKEINTRESOURCE(IDD_MAINDIALOG),
0, DialogProc);
}
// Dialog proc for main dialog.
BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch(uiMsg)
{
// Do basic initialization, return FALSE means do not continue
// creating the window.
case WM_INITDIALOG:
return HandleInitDialog(hwndDlg);
break;
// Control sizing, to maintain a minimum dialog size.
case WM_SIZE:
HandleSize(hwndDlg, LOWORD(lParam), HIWORD(lParam));
break;
// Control sizing, to maintain a minimum dialog box size.
case WM_SIZING:
HandleSizing(hwndDlg, wParam, reinterpret_cast<LPRECT>(lParam));
break;
// Control maximizing, to maintain dialog size.
case WM_GETMINMAXINFO:
HandleGetMinMaxInfo(hwndDlg, reinterpret_cast<LPMINMAXINFO>(lParam));
break;
// Handle child control messages
case WM_COMMAND:
HandleCommand(hwndDlg, LOWORD(wParam), reinterpret_cast<HWND>(lParam));
break;
// Handle notifications from child controls
case WM_NOTIFY:
HandleNotify(hwndDlg, static_cast<int>(wParam),
reinterpret_cast<void*>(lParam));
break;
// Print help messages when user goes over a menu item.
case WM_MENUSELECT:
HandleMenuSelect(hwndDlg, reinterpret_cast<HMENU>(lParam),
LOWORD(wParam), HIWORD(wParam));
break;
// Note when user starts browsing through a menu.
case WM_ENTERMENULOOP:
HandleEnterMenuLoop(hwndDlg);
break;
// Note when user exits a menu.
case WM_EXITMENULOOP:
HandleExitMenuLoop(hwndDlg);
break;
// Search thread just found another app.
case WM_SEARCHDB_ADDAPP:
HandleSearchAddApp(hwndDlg);
break;
// Search thread is looking through a new directory
case WM_SEARCHDB_UPDATE:
HandleSearchUpdate(hwndDlg, reinterpret_cast<PTSTR>(lParam));
break;
// Search thread is complete.
case WM_SEARCHDB_DONE:
// Cleanup any leftover apps (just in case this message
// is received before WM_SEARCHDB_ADDAPP)
HandleSearchAddApp(hwndDlg);
// Terminate search
StopSearch(hwndDlg);
break;
// User wants to close this dialog
case WM_CLOSE:
// Terminate search
StopSearch(hwndDlg);
EndDialog(hwndDlg, TRUE);
break;
default:
return FALSE;
}
return TRUE;
}
// Do basic dialog box initialization.
BOOL HandleInitDialog(HWND hwnd)
{
// Change the application icon
HICON hIcon;
hIcon = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_APP));
if(!hIcon)
return FALSE;
// For SetClassLongPtr(), a return value of zero may not be an
// error, if the previous value was not set. Setting the
// last error to 0 and checking for that value indicates
// success.
// BUGBUG
// In Whistler, SetClassLongPtr returns an error, but still
// sets the correct icon. No error is returned for Win2K.
SetClassLongPtr(hwnd, GCLP_HICON, reinterpret_cast<LONG_PTR>(hIcon));
SetClassLongPtr(hwnd, GCLP_HICONSM, reinterpret_cast<LONG_PTR>(hIcon));
// Get minimum dimensions of dialog
RECT rcWindow;
if(!GetWindowRect(hwnd, &rcWindow))
return FALSE;
g_nMinWindowWidth = rcWindow.right - rcWindow.left;
g_nWindowHeight = rcWindow.bottom - rcWindow.top;
// Add a static line across the top (We can't put this in the resource template)
RECT rectCl;
if(!GetClientRect(hwnd, &rectCl))
return FALSE;
HWND hwStaticLine = CreateWindow(TEXT("static"),TEXT(""),
SS_ETCHEDHORZ | WS_CHILD | WS_VISIBLE, rectCl.top, rectCl.left,
rectCl.right - rectCl.left, c_nStaticLineHeight, hwnd,
reinterpret_cast<HMENU>(c_iStaticLineID), g_hinst, 0);
if(!hwStaticLine)
return FALSE;
// Fill the combobox.
HWND hwComboBox = GetDlgItem(hwnd, IDC_DRIVELIST);
if(!hwComboBox)
return FALSE;
SHFILEINFO sfi;
HIMAGELIST himagelist = reinterpret_cast<HIMAGELIST>(SHGetFileInfo(TEXT("C:\\"), 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX | SHGFI_SMALLICON));
LRESULT lr = SendMessage(hwComboBox, CBEM_SETIMAGELIST, 0,
reinterpret_cast<LPARAM>(himagelist));
assert(lr == NULL);
COMBOBOXEXITEM cbitem;
ZeroMemory(&cbitem, sizeof(cbitem));
cbitem.mask = CBEIF_TEXT | CBEIF_INDENT | CBEIF_IMAGE
| CBEIF_SELECTEDIMAGE;
cbitem.iItem = -1;
cbitem.pszText = LoadStringResource(IDS_ALLDRIVES);
if(!cbitem.pszText)
return FALSE;
cbitem.cchTextMax = lstrlen(cbitem.pszText) + 1;
cbitem.iIndent = 0;
LPITEMIDLIST pidl;
SHGetFolderLocation(0, CSIDL_DRIVES, 0, 0, &pidl);
SHGetFileInfo(reinterpret_cast<PTSTR>(pidl), 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX | SHGFI_PIDL);
cbitem.iImage = sfi.iIcon;
cbitem.iSelectedImage = sfi.iIcon;
if(SendMessage(hwComboBox, CBEM_INSERTITEM, 0,
reinterpret_cast<LPARAM>(&cbitem)))
{
delete cbitem.pszText;
return FALSE;
}
delete cbitem.pszText;
LPMALLOC pMalloc;
SHGetMalloc(&pMalloc);
pMalloc->Free(pidl);
pMalloc->Release();
TCHAR* szDrives = 0;
DWORD dwLen = GetLogicalDriveStrings(0, 0);
if(dwLen != 0)
{
szDrives = new TCHAR[dwLen+1];
if(!szDrives)
{
Error(hwnd, IDS_NOMEMSTOPPROG);
return FALSE;
}
}
else
return FALSE;
if(!GetLogicalDriveStrings(dwLen, szDrives))
{
delete szDrives;
return FALSE;
}
TCHAR* szCurrDrive = szDrives;
while(*szCurrDrive)
{
if(GetDriveType(szCurrDrive)==DRIVE_FIXED)
{
SHGetFileInfo(szCurrDrive, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX);
cbitem.pszText = szCurrDrive;
cbitem.cchTextMax = lstrlen(cbitem.pszText) + 1;
cbitem.iIndent = 1;
cbitem.iImage = sfi.iIcon;
cbitem.iSelectedImage = sfi.iIcon;
SendMessage(hwComboBox, CBEM_INSERTITEM, 0,
reinterpret_cast<LPARAM>(&cbitem));
}
szCurrDrive += lstrlen(szCurrDrive)+1;
}
delete szDrives;
if(SendMessage(hwComboBox, CB_SETCURSEL, 0, 0)== -1)
return FALSE;
// Add the animation control.
HWND hwChild;
RECT rcChild;
POINT pt;
HWND hwAnim = Animate_Create(hwnd, c_iFindAnimID,
WS_CHILD | ACS_CENTER | ACS_TRANSPARENT, g_hinst);
hwChild = GetDlgItem(hwnd, IDC_ANIMSPACEHOLDER);
GetWindowRect(hwChild, &rcChild);
DestroyWindow(hwChild);
pt.x = rcChild.left;
pt.y = rcChild.top;
ScreenToClient(hwnd, &pt);
SetWindowPos(hwAnim, 0, pt.x, pt.y, rcChild.right-rcChild.left,
rcChild.bottom - rcChild.top, SWP_NOZORDER);
Animate_Open(hwAnim, MAKEINTRESOURCE(IDR_FINDAVI));
ShowWindow(hwAnim, SW_SHOW);
// Get margins of all child window controls
pt.x = 0;
pt.y = 0;
for(int i = 0; i < c_nChildren; i++)
{
hwChild = GetDlgItem(hwnd, g_aiChildrenIDs[i]);
if(!hwChild)
return FALSE;
if(!GetWindowRect(hwChild, &rcChild))
return FALSE;
pt.x = rcChild.right;
if(!ScreenToClient(hwnd, &pt))
return FALSE;
g_aiChildrenMargins[i] = rectCl.right - pt.x;
}
// Setup default checked state
HMENU hMenu = GetMenu(hwnd);
if(!hMenu)
return FALSE;
CheckMenuRadioItem(hMenu, ID_VIEW_LARGEICONS,
ID_VIEW_ASDETAILS, ID_VIEW_ASDETAILS, MF_BYCOMMAND);
if(InitSearchDB() == FALSE)
return FALSE;
return TRUE;
}
// Display a browse dialog box, so that the user can select
// a specific path to search in.
void HandleBrowse(HWND hwnd)
{
// Show the browse dialog box, and get user response.
TCHAR* szPath = BrowseForFolder(hwnd, 0,
BF_SELECTDIRECTORIES | BF_HARDDRIVES);
// If they didn't select cancel . . .
if(szPath)
{
HWND hwDirSelBox = GetDlgItem(hwnd, IDC_DRIVELIST);
if(!hwDirSelBox)
{
DestroyWindow(hwnd);
return;
}
// For some reason, SHGetFileInfo doesn't work
// properly if the drive isn't terminated with '\'
if(szPath[lstrlen(szPath)-1] == TEXT(':'))
lstrcat(szPath, TEXT("\\"));
// Insert this item into the edit control of the combo box.
SHFILEINFO sfi;
COMBOBOXEXITEM cbim;
cbim.mask = CBEIF_IMAGE | CBEIF_TEXT | CBEIF_SELECTEDIMAGE;
cbim.iItem = -1;
cbim.pszText = szPath;
cbim.cchTextMax = lstrlen(szPath);
SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX);
cbim.iImage = sfi.iIcon;
cbim.iSelectedImage = sfi.iIcon;
if(!SendMessage(hwDirSelBox, CBEM_SETITEM, 0,
reinterpret_cast<LPARAM>(&cbim)))
{
DestroyWindow(hwnd);
return;
}
// For some reason, the icon in the edit box isn't updated
// unless the box is selected, loses focus, and then regains
// focus, so do that here.
if(!SetFocus(hwDirSelBox))
{
DestroyWindow(hwnd);
return;
}
if(!SetFocus(GetDlgItem(hwnd, IDC_BROWSE)))
{
DestroyWindow(hwnd);
return;
}
if(!SetFocus(hwDirSelBox))
{
DestroyWindow(hwnd);
return;
}
}
}
// Handle a command from a child input control.
void HandleCommand(HWND hwnd, int iCtrlID, HWND)
{
HMENU hMenu;
HWND hwList;
DWORD dwStyle, dwExStyle;
SHELLEXECUTEINFO shExecInfo;
SCompareParam cp;
switch(iCtrlID)
{
// MENU COMMANDS
case ID_APPSELECT_SHOWSHIMINFO:
case ID_FILE_SHOWSHIMINFO:
// Get list view control
hwList = GetDlgItem(hwnd, c_iResultListID);
if(hwList)
{
int nCount = ListView_GetItemCount(hwList);
int i;
// Loop through all items finding a selected one.
for(i = 0; i < nCount; i++)
{
if(ListView_GetItemState(hwList, i, LVIS_SELECTED)
& LVIS_SELECTED)
break;
}
// None found, we're done.
if(i == nCount)
break;
TCHAR szBuffer[c_nMaxStringLength];
ListView_GetItemText(hwList, i, 1,
szBuffer, c_nMaxStringLength);
ShowShimInfo(hwnd, szBuffer);
}
break;
// Use selected properties
case ID_APPSELECT_PROPERTIES:
case ID_FILE_PROPERTIES:
// Get list view control
hwList = GetDlgItem(hwnd, c_iResultListID);
if(hwList)
{
int nCount = ListView_GetItemCount(hwList);
int i;
// Loop through all items finding a selected one.
for(i = 0; i < nCount; i++)
{
if(ListView_GetItemState(hwList, i, LVIS_SELECTED)
& LVIS_SELECTED)
break;
}
// None found, we're done.
if(i == nCount)
break;
TCHAR szBuffer[c_nMaxStringLength];
ListView_GetItemText(hwList, i, 1,
szBuffer, c_nMaxStringLength);
ZeroMemory(&shExecInfo, sizeof(shExecInfo));
shExecInfo.cbSize = sizeof(shExecInfo);
shExecInfo.lpFile = szBuffer;
shExecInfo.lpVerb = TEXT("properties");
shExecInfo.fMask = SEE_MASK_INVOKEIDLIST;
ShellExecuteEx(&shExecInfo);
}
break;
case ID_FILE_EXIT:
EndDialog(hwnd, TRUE);
break;
case ID_EDIT_SELECTALL:
hwList = GetDlgItem(hwnd, c_iResultListID);
if(hwList)
{
int nCount = ListView_GetItemCount(hwList);
for(int i = 0; i < nCount; i++)
ListView_SetItemState(hwList, i, LVIS_SELECTED, LVIS_SELECTED);
}
break;
case ID_EDIT_INVERTSELECTION:
hwList = GetDlgItem(hwnd, c_iResultListID);
if(hwList)
{
int nCount = ListView_GetItemCount(hwList);
for(int i = 0; i < nCount; i++)
ListView_SetItemState(hwList, i,
LVIS_SELECTED ^ ListView_GetItemState(hwList, i, LVIS_SELECTED),
LVIS_SELECTED);
}
break;
case ID_VIEW_LARGEICONS:
case ID_VIEW_SMALLICONS:
case ID_VIEW_ASLIST:
case ID_VIEW_ASDETAILS:
hMenu = GetMenu(hwnd);
CheckMenuRadioItem(hMenu, ID_VIEW_LARGEICONS,
ID_VIEW_ASDETAILS, iCtrlID, MF_BYCOMMAND);
dwStyle = WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS;
dwExStyle = LVS_EX_LABELTIP;
switch(iCtrlID)
{
case ID_VIEW_LARGEICONS:
dwStyle |= LVS_ICON;
break;
case ID_VIEW_SMALLICONS:
dwStyle |= LVS_SMALLICON;
break;
case ID_VIEW_ASLIST:
dwStyle |= LVS_LIST;
break;
case ID_VIEW_ASDETAILS:
dwStyle |= LVS_REPORT;
dwExStyle |= LVS_EX_FULLROWSELECT;
break;
}
hwList = GetDlgItem(hwnd, c_iResultListID);
if(hwList)
{
SetWindowLongPtr(hwList, GWL_STYLE, dwStyle);
ListView_SetExtendedListViewStyleEx(hwList, 0, dwExStyle);
}
break;
case ID_VIEW_ARRANGEICONS_BYNAME:
hwList = GetDlgItem(hwnd, c_iResultListID);
cp.hwList = hwList;
cp.iSubItem = 0;
if(hwList)
ListView_SortItemsEx(hwList, CompareListEntries, &cp);
break;
case ID_VIEW_ARRANGEICONS_BYPATH:
hwList = GetDlgItem(hwnd, c_iResultListID);
cp.hwList = hwList;
cp.iSubItem = 1;
if(hwList)
ListView_SortItemsEx(hwList, CompareListEntries, &cp);
break;
case ID_VIEW_CHOOSECOLUMNS:
break;
case ID_VIEW_REFRESH:
StartSearch(hwnd);
break;
// PUSH BUTTON COMMANDS
case IDC_BROWSE:
HandleBrowse(hwnd);
break;
case IDC_FINDNOW:
StartSearch(hwnd);
break;
case IDC_STOP:
StopSearch(hwnd);
break;
case IDC_CLEARALL:
ClearResults(hwnd);
break;
}
}
void HandleSize(HWND hwnd, int /*nWidth*/, int /*nHeight*/)
{
RECT rectCl;
GetClientRect(hwnd, &rectCl);
HWND hwStatic = GetDlgItem(hwnd, c_iStaticLineID);
if(!hwStatic)
{
DestroyWindow(hwnd);
return;
}
MoveWindow(hwStatic,rectCl.left, rectCl.top, rectCl.right-rectCl.left,
c_nStaticLineHeight, TRUE);
// Adjust list box and status bar at the bottom of the window
HWND hwListBox, hwStatusBar, hwListFrame;
hwListFrame = GetDlgItem(hwnd, c_iResultFrameID);
hwListBox = GetDlgItem(hwnd, c_iResultListID);
hwStatusBar= GetDlgItem(hwnd, c_iStatusBarID);
assert(hwListBox == 0 ? !hwStatusBar && !hwListFrame : true);
if(hwListBox && hwStatusBar && hwListFrame)
{
RECT rectWnd;
GetWindowRect(hwnd, &rectWnd);
POINT ptListTop;
ptListTop.x = 0;
ptListTop.y = rectWnd.top + g_nWindowHeight;
ScreenToClient(hwnd, &ptListTop);
RECT rectSB;
GetClientRect(hwStatusBar, &rectSB);
POINT ptStatusTop;
ptStatusTop.x = 0;
ptStatusTop.y = rectWnd.bottom - (rectSB.bottom - rectSB.top);
ScreenToClient(hwnd, &ptStatusTop);
MoveWindow(hwListFrame, rectCl.left, ptListTop.y, rectCl.right - rectCl.left,
ptStatusTop.y - ptListTop.y, TRUE);
MoveWindow(hwListBox, rectCl.left + c_nResultFrameWidth,
ptListTop.y + c_nResultFrameWidth,
rectCl.right - rectCl.left - 2 * c_nResultFrameWidth,
ptStatusTop.y - ptListTop.y - 2 * c_nResultFrameWidth,
TRUE);
MoveWindow(hwStatusBar, rectCl.left, ptStatusTop.y,
rectCl.right - rectCl.left, rectSB.bottom - rectSB.top, TRUE);
}
// Adjust all controls
RECT rcChild;
HWND hwChild;
POINT ptTop;
int iWidth, iHeight;
for(int i = 0; i < c_nChildren; i++)
{
hwChild = GetDlgItem(hwnd, g_aiChildrenIDs[i]);
if(!hwChild)
{
DestroyWindow(hwnd);
return;
}
GetClientRect(hwChild, &rcChild);
iWidth = rcChild.right - rcChild.left;
iHeight = rcChild.bottom - rcChild.top;
ptTop.x = rcChild.left;
ptTop.y = rcChild.top;
ClientToScreen(hwChild, &ptTop);
ScreenToClient(hwnd, &ptTop);
if(g_aiChildrenIDs[i] == IDC_DRIVELIST)
iWidth = rectCl.right - g_aiChildrenMargins[i] - ptTop.x;
if(g_aiChildrenIDs[i] == IDC_DRIVELIST)
MoveWindow(hwChild, ptTop.x, ptTop.y, iWidth, iHeight, TRUE);
else
MoveWindow(hwChild, rectCl.right - g_aiChildrenMargins[i] - iWidth,
ptTop.y, iWidth, iHeight, TRUE);
}
for(i = 0; i < c_nChildren; i++)
InvalidateRect(GetDlgItem(hwnd, g_aiChildrenIDs[i]), 0, TRUE);
}
void HandleSizing(HWND hwnd, int iEdge, LPRECT pRect)
{
if((pRect->right - pRect->left) < g_nMinWindowWidth)
{
if(iEdge == WMSZ_BOTTOMLEFT || iEdge == WMSZ_LEFT
|| iEdge == WMSZ_TOPLEFT)
{
pRect->left = pRect->right - g_nMinWindowWidth;
}
else if(iEdge == WMSZ_BOTTOMRIGHT || iEdge == WMSZ_RIGHT
|| iEdge == WMSZ_TOPRIGHT)
{
pRect->right = pRect->left + g_nMinWindowWidth;
}
// Should never get here
else
{
OutputDebugString(TEXT("Weird sizing stuff in HandleSizing\n"));
}
}
if(GetDlgItem(hwnd, c_iResultListID))
{
HWND hwStatusBar = GetDlgItem(hwnd, c_iStatusBarID);
RECT rectStatus;
GetClientRect(hwStatusBar, &rectStatus);
if((pRect->bottom - pRect->top) < (g_nWindowHeight +
c_nResultListHeight))
{
if(iEdge == WMSZ_BOTTOM || iEdge == WMSZ_BOTTOMLEFT ||
iEdge == WMSZ_BOTTOMRIGHT)
{
pRect->bottom = pRect->top + (g_nWindowHeight +
c_nResultListHeight);
}
else if((iEdge == WMSZ_TOP) || (iEdge == WMSZ_TOPLEFT)
|| (iEdge == WMSZ_TOPRIGHT))
{
pRect->top = pRect->bottom - (g_nWindowHeight +
c_nResultListHeight);
}
// Should never get here
else
{
OutputDebugString(TEXT("Weird sizing stuff in HandleSizing\n"));
}
}
}
else
{
if((pRect->bottom - pRect->top) != g_nWindowHeight)
{
if(iEdge == WMSZ_BOTTOM || iEdge == WMSZ_BOTTOMLEFT ||
iEdge == WMSZ_BOTTOMRIGHT)
{
pRect->bottom = pRect->top + g_nWindowHeight;
}
else if((iEdge == WMSZ_TOP) || (iEdge == WMSZ_TOPLEFT)
|| (iEdge == WMSZ_TOPRIGHT))
{
pRect->top = pRect->bottom - g_nWindowHeight;
}
// Should never get here
else
{
OutputDebugString(TEXT("Weird sizing stuff in HandleSizing\n"));
}
}
}
}
void HandleGetMinMaxInfo(HWND hwnd, LPMINMAXINFO pmmi)
{
// Maximize normally if we have the result list box
if(GetDlgItem(hwnd, c_iResultListID))
return;
pmmi->ptMaxSize.y = g_nWindowHeight;
}
void HandleNotify(HWND hwnd, int iCtrlID, void* pvArg)
{
LPNMHDR pHdr = reinterpret_cast<LPNMHDR>(pvArg);
LPNMLISTVIEW pnmlvItem = reinterpret_cast<LPNMLISTVIEW>(pvArg);
HWND hwList;
HMENU hMenu;
switch(iCtrlID)
{
case c_iResultListID:
hwList = GetDlgItem(hwnd, c_iResultListID);
switch(pHdr->code)
{
case NM_RCLICK:
// Check if the click is on any row
if(pnmlvItem->iItem != -1)
{
// Create a context sensitive menu.
HMENU hmContext = LoadMenu(g_hinst,
MAKEINTRESOURCE(IDM_APPSELECT));
hmContext = GetSubMenu(hmContext, 0);
ClientToScreen(GetDlgItem(hwnd, c_iResultListID),
&pnmlvItem->ptAction);
TrackPopupMenuEx(hmContext, 0,
pnmlvItem->ptAction.x,
pnmlvItem->ptAction.y, hwnd, 0);
}
else
{
// FIXME
// Create the shortcut menu
}
break;
case LVN_ITEMCHANGED:
hwList = GetDlgItem(hwnd, c_iResultListID);
if(hwList)
{
int nCount = ListView_GetItemCount(hwList);
int i;
for(i = 0; i < nCount; i++)
{
if(ListView_GetItemState(hwList, i, LVIS_SELECTED)
& LVIS_SELECTED)
break;
}
hMenu = GetMenu(hwnd);
if(i == nCount)
{
EnableMenuItem(hMenu, ID_FILE_SHOWSHIMINFO,
MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, ID_FILE_PROPERTIES,
MF_BYCOMMAND | MF_GRAYED);
}
else
{
EnableMenuItem(hMenu, ID_FILE_SHOWSHIMINFO,
MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_FILE_PROPERTIES,
MF_BYCOMMAND | MF_ENABLED);
}
}
break;
}
default:
break;
}
}
void HandleMenuSelect(HWND hwnd, HMENU hMenu, UINT uiMenuID, UINT uiFlags)
{
HWND hwStatusBar = GetDlgItem(hwnd,c_iStatusBarID);
if(!hwStatusBar)
return;
TCHAR* szText = 0;
if(uiFlags & MF_POPUP)
{
if(hMenu != GetMenu(hwnd))
{
// Not a top-level menu
// Go through all lower-level submenus
// Right now only submenu is Arrange Icons
szText = LoadStringResource(IDS_VIEW_ARRANGEICONS);
}
}
else
{
switch(uiMenuID)
{
case ID_FILE_SHOWSHIMINFO:
szText = LoadStringResource(IDS_FILE_SHOWSHIM);
break;
case ID_FILE_DOWNLOADPATCH:
szText = LoadStringResource(IDS_FILE_DOWNLOADPATCH);
break;
case ID_FILE_PROPERTIES:
szText = LoadStringResource(IDS_FILE_PROPERTIES);
break;
case ID_FILE_SAVESEARCH:
szText = LoadStringResource(IDS_FILE_SAVESEARCH);
break;
case ID_FILE_EXIT:
szText = LoadStringResource(IDS_FILE_EXIT);
break;
case ID_EDIT_SELECTALL:
szText = LoadStringResource(IDS_EDIT_SELECTALL);
break;
case ID_EDIT_INVERTSELECTION:
szText = LoadStringResource(IDS_EDIT_INVERTSELECTION);
break;
case ID_VIEW_LARGEICONS:
szText = LoadStringResource(IDS_VIEW_LARGEICONS);
break;
case ID_VIEW_SMALLICONS:
szText = LoadStringResource(IDS_VIEW_SMALLICONS);
break;
case ID_VIEW_ASLIST:
szText = LoadStringResource(IDS_VIEW_ASLIST);
break;
case ID_VIEW_ASDETAILS:
szText = LoadStringResource(IDS_VIEW_ASDETAILS);
break;
case ID_VIEW_ARRANGEICONS_BYNAME:
szText = LoadStringResource(IDS_VIEW_ARRANGEICONS_BYNAME);
break;
case ID_VIEW_ARRANGEICONS_BYPATH:
szText = LoadStringResource(IDS_VIEW_ARRANGEICONS_BYPATH);
break;
case ID_VIEW_CHOOSECOLUMNS:
szText = LoadStringResource(IDS_VIEW_CHOOSECOLUMNS);
break;
case ID_VIEW_REFRESH:
szText = LoadStringResource(IDS_VIEW_REFRESH);
break;
case ID_HELP_HELPTOPICS:
szText = LoadStringResource(IDS_HELP_HELPTOPICS);
break;
case ID_HELP_WHATSTHIS:
szText = LoadStringResource(IDS_HELP_WHATSTHIS);
break;
}
}
if(szText)
{
SetWindowText(hwStatusBar, szText);
delete szText;
}
else
SetWindowText(hwStatusBar, TEXT(""));
}
void HandleEnterMenuLoop(HWND hwnd)
{
UpdateStatus(hwnd, TEXT(""));
g_fInMenu = TRUE;
}
void HandleExitMenuLoop(HWND hwnd)
{
UpdateStatus(hwnd, LoadStringResource(IDS_SEARCHDONE));
g_fInMenu = FALSE;
}
void HandleSearchAddApp(HWND hwnd)
{
HWND hwList = GetDlgItem(hwnd, c_iResultListID);
if(!hwList)
return;
SMatchedExe* pme;
while( (pme = GetMatchedExe()) != 0)
AddApp(pme, hwList);
delete pme;
}
void HandleSearchUpdate(HWND hwnd, PTSTR szMsg)
{
if(g_fInMenu == FALSE)
UpdateStatus(hwnd, szMsg);
delete szMsg;
}
int CALLBACK CompareListEntries(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
SCompareParam* pCompParam = reinterpret_cast<SCompareParam*>(lParamSort);
TCHAR szBuffer1[c_nMaxStringLength];
TCHAR szBuffer2[c_nMaxStringLength];
ListView_GetItemText(pCompParam->hwList, lParam1, pCompParam->iSubItem,
szBuffer1, c_nMaxStringLength);
ListView_GetItemText(pCompParam->hwList, lParam2, pCompParam->iSubItem,
szBuffer2, c_nMaxStringLength);
return lstrcmp(szBuffer1, szBuffer2);
}
void UpdateStatus(HWND hwnd, PTSTR szMsg)
{
HWND hwStatus = GetDlgItem(hwnd, c_iStatusBarID);
if(!hwStatus)
return;
SetWindowText(hwStatus, szMsg);
}
void AddApp(SMatchedExe* pme, HWND hwList)
{
int iItem, iImage;
HICON hIcon;
HIMAGELIST himl = ListView_GetImageList(hwList, LVSIL_NORMAL);
if(!himl)
{
himl = ImageList_Create(GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON), ILC_COLOR | ILC_MASK, 0, 0);
if(!himl)
{
DestroyWindow(GetParent(hwList));
return;
}
hIcon = LoadIcon(0, MAKEINTRESOURCE(IDI_APPLICATION));
ImageList_AddIcon(himl, hIcon);
ListView_SetImageList(hwList, himl, LVSIL_NORMAL);
}
HIMAGELIST himlSm = ListView_GetImageList(hwList, LVSIL_SMALL);
if(!himlSm)
{
himlSm = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_MASK, 0, 0);
if(!himlSm)
{
DestroyWindow(GetParent(hwList));
return;
}
hIcon = LoadIcon(0, MAKEINTRESOURCE(IDI_APPLICATION));
ImageList_AddIcon(himlSm, hIcon);
ListView_SetImageList(hwList, himlSm, LVSIL_SMALL);
}
hIcon = ExtractIcon(g_hinst, pme->szPath, 0);
if(!hIcon)
{
iImage = 0;
}
else
{
iImage = ImageList_AddIcon(himl, hIcon);
if(iImage == -1)
iImage = 0;
int iImageSm = ImageList_AddIcon(himlSm, hIcon);
assert(iImage == iImageSm);
DestroyIcon(hIcon);
}
iItem = ListView_GetItemCount(hwList);
// Add app to the list view.
LVITEM lvItem;
lvItem.mask = LVIF_IMAGE | LVIF_TEXT;
lvItem.iItem = iItem;
lvItem.iSubItem = 0;
lvItem.pszText = pme->szAppName;
lvItem.cchTextMax = lstrlen(pme->szAppName);
lvItem.iImage = iImage;
ListView_InsertItem(hwList, &lvItem);
lvItem.mask = LVIF_TEXT;
lvItem.iItem = iItem;
lvItem.iSubItem = 1;
lvItem.pszText = pme->szPath;
lvItem.cchTextMax = lstrlen(pme->szPath);
ListView_SetItem(hwList, &lvItem);
// If no items were in view prior, refresh view
//(otherwise "There are no items . . ." message will stay in listview.
if(iItem == 0)
RedrawWindow(hwList, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW);
}
// Start searching the database
void StartSearch(HWND hwnd)
{
HWND hwList, hwStatus, hwResultFrame;
hwList = GetDlgItem(hwnd, c_iResultListID);
hwStatus = GetDlgItem(hwnd, c_iStatusBarID);
hwResultFrame = GetDlgItem(hwnd, c_iResultFrameID);
// Both should be NULL or both non-NULL
assert( (hwList == 0) ? (hwStatus == 0 && hwResultFrame == 0) : true);
if(!hwList)
{
// Expand the window to encompass the new lsitview.
RECT rectWnd;
GetWindowRect(hwnd, &rectWnd);
MoveWindow(hwnd, rectWnd.left, rectWnd.top,
rectWnd.right - rectWnd.left,
c_nResultListHeight + g_nWindowHeight, TRUE);
// See if use has checked show small icons, large icons, etc.,
// and set appropriate listview style.
MENUITEMINFO mii;
HMENU hMenu = GetMenu(hwnd);
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
DWORD dwStyle = WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS;
DWORD dwExStyle = LVS_EX_LABELTIP;
GetMenuItemInfo(hMenu, ID_VIEW_LARGEICONS, FALSE, &mii);
if(mii.fState & MFS_CHECKED)
dwStyle |= LVS_ICON;
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
GetMenuItemInfo(hMenu, ID_VIEW_SMALLICONS, FALSE, &mii);
if(mii.fState & MFS_CHECKED)
dwStyle |= LVS_SMALLICON;
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
GetMenuItemInfo(hMenu, ID_VIEW_ASLIST, FALSE, &mii);
if(mii.fState & MFS_CHECKED)
dwStyle |= LVS_LIST;
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
GetMenuItemInfo(hMenu, ID_VIEW_ASDETAILS, FALSE, &mii);
if(mii.fState & MFS_CHECKED)
{
dwStyle |= LVS_REPORT;
dwExStyle |= LVS_EX_FULLROWSELECT;
}
// Create the status bar.
TCHAR* szCaption = LoadStringResource(IDS_SEARCHING);
if(!szCaption)
{
DestroyWindow(hwnd);
return;
}
hwStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE,
szCaption, hwnd, c_iStatusBarID);
if(!hwStatus)
{
DestroyWindow(hwnd);
return;
}
delete szCaption;
RECT rectCl;
RECT rectSB;
GetClientRect(hwnd, &rectCl);
GetClientRect(hwnd, &rectSB);
// Round about way of getting top coordinate of
// status bar to a client coordinate in main window
POINT ptStatusTop;
ptStatusTop.x = 0;
ptStatusTop.y = rectSB.top;
ClientToScreen(hwStatus, &ptStatusTop);
ScreenToClient(hwnd, &ptStatusTop);
// Bottom of window prior to resizing
POINT ptListTop;
ptListTop.x = 0;
ptListTop.y = rectWnd.bottom;
ScreenToClient(hwnd, &ptListTop);
// Create static frame around listview.
hwResultFrame = CreateWindowEx(0, TEXT("static"), TEXT(""),
WS_VISIBLE | WS_CHILD | SS_BLACKRECT | SS_SUNKEN,
rectCl.left, ptListTop.y, rectCl.right-rectCl.left,
ptStatusTop.y - ptListTop.y,
hwnd, reinterpret_cast<HMENU>(c_iResultFrameID), g_hinst, 0);
if(!hwResultFrame)
{
DestroyWindow(hwnd);
return;
}
// Create the listview window.
hwList = CreateWindowEx(0, WC_LISTVIEW, TEXT(""),
dwStyle, rectCl.left + c_nResultFrameWidth,
ptListTop.y + c_nResultFrameWidth,
rectCl.right - rectCl.left - 2 * c_nResultFrameWidth,
ptStatusTop.y - ptListTop.y - c_nResultFrameWidth,
hwnd,reinterpret_cast<HMENU>(c_iResultListID), g_hinst, 0);
if(!hwList)
{
DestroyWindow(hwnd);
return;
}
// Subclass the window
pfnLVOrgWndProc =
reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwList, GWL_WNDPROC));
SetWindowLongPtr(hwList, GWL_WNDPROC,
reinterpret_cast<UINT_PTR>(LVWndProc));
ListView_SetExtendedListViewStyleEx(hwList, 0, dwExStyle);
// Insert listview columns.
for(int i = 0; i < c_nNumListColumns; i++)
{
LVCOLUMN lvCol;
lvCol.mask = LVCF_FMT | LVCF_TEXT | LVCF_ORDER | LVCF_WIDTH |
LVCF_SUBITEM;
lvCol.fmt = LVCFMT_LEFT;
lvCol.pszText = LoadStringResource(c_iListColumnNameIDS[i]);
lvCol.cchTextMax = lstrlen(lvCol.pszText);
lvCol.iSubItem = i;
lvCol.iOrder = i;
lvCol.cx = c_nListColumnWidth;
ListView_InsertColumn(hwList, i, &lvCol);
delete lvCol.pszText;
}
// "Remaximize" if it is already maximized
if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_MAXIMIZE)
{
HWND hwDesktop = GetDesktopWindow();
RECT rcDesktop;
GetClientRect(hwDesktop, &rcDesktop);
MoveWindow(hwnd, rcDesktop.left, rcDesktop.top,
rcDesktop.right - rcDesktop.left, rcDesktop.bottom - rcDesktop.top,
TRUE);
// Set correct restore position
WINDOWPLACEMENT wndpl;
GetWindowPlacement(hwnd, &wndpl);
wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top +
g_nWindowHeight + c_nResultListHeight;
SetWindowPlacement(hwnd, &wndpl);
}
}
// If it exists, just reset the content
else
{
TCHAR* szMsg;
TCHAR* szCaption;
szMsg = LoadStringResource(IDS_CLRALLMSG);
szCaption = LoadStringResource(IDS_CLRALLCAPTION);
if(!szMsg || !szCaption)
{
DestroyWindow(hwnd);
return;
}
if(MessageBox(hwnd, szMsg, szCaption, MB_ICONINFORMATION |
MB_OKCANCEL) == IDOK)
ListView_DeleteAllItems(GetDlgItem(hwnd, c_iResultListID));
else
return;
}
// Enable the view options related to the result list
HMENU hMenu;
hMenu = GetMenu(hwnd);
HMENU hmView = GetSubMenu(hMenu, c_iViewMenuPos);
EnableMenuItem(hmView, c_iArrangeIconsPos, MF_BYPOSITION | MF_ENABLED);
EnableMenuItem(hMenu, ID_VIEW_ARRANGEICONS_BYPATH, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_VIEW_CHOOSECOLUMNS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_VIEW_REFRESH, MF_BYCOMMAND | MF_ENABLED);
EnableWindow(GetDlgItem(hwnd, IDC_STOP), TRUE);
EnableWindow(GetDlgItem(hwnd, IDC_CLEARALL), TRUE);
// Find out what to search for
HWND hwComboBox = GetDlgItem(hwnd, IDC_DRIVELIST);
// If zero is selected, search for everything.
if(SendMessage(hwComboBox, CB_GETCURSEL, 0, 0)==0)
SearchDB(0, hwnd);
else
{
TCHAR szBuffer[MAX_PATH+1];
COMBOBOXEXITEM cbim;
cbim.mask = CBEIF_TEXT;
cbim.iItem = -1;
cbim.pszText = szBuffer;
cbim.cchTextMax = MAX_PATH;
SendMessage(hwComboBox, CBEM_GETITEM, 0,
reinterpret_cast<LPARAM>(&cbim));
if(szBuffer[lstrlen(szBuffer)-1] != TEXT('\\'))
lstrcat(szBuffer, TEXT("\\"));
SearchDB(szBuffer, hwnd);
}
// Start animating the icon.
HWND hwAnim = GetDlgItem(hwnd, c_iFindAnimID);
Animate_Play(hwAnim, 0, -1, -1);
}
// Terminate the search.
void StopSearch(HWND hwnd)
{
// Stop searching the database.
StopSearchDB();
// Can't click "STOP" or anymore.
EnableWindow(GetDlgItem(hwnd, IDC_STOP), FALSE);
// Update with a search complete if not in menu.
if(g_fInMenu == FALSE)
UpdateStatus(hwnd, LoadStringResource(IDS_SEARCHDONE));
// Stop animating the icon.
HWND hwAnim = GetDlgItem(hwnd, c_iFindAnimID);
Animate_Stop(hwAnim);
}
// Remove result window.
void ClearResults(HWND hwnd)
{
TCHAR* szMsg;
TCHAR* szCaption;
szMsg = LoadStringResource(IDS_CLRALLMSG);
szCaption = LoadStringResource(IDS_CLRALLCAPTION);
if(!szMsg || !szCaption)
{
DestroyWindow(hwnd);
return;
}
if(MessageBox(hwnd, szMsg, szCaption, MB_ICONINFORMATION |
MB_OKCANCEL) == IDCANCEL)
return;
// Stop the search
StopSearch(hwnd);
// Get rid of all result-related windows.
HWND hwCtrl;
if( (hwCtrl = GetDlgItem(hwnd, c_iResultListID)) != 0)
DestroyWindow(hwCtrl);
if( (hwCtrl = GetDlgItem(hwnd, c_iStatusBarID)) != 0)
DestroyWindow(hwCtrl);
if( (hwCtrl = GetDlgItem(hwnd, c_iResultFrameID)) != 0)
DestroyWindow(hwCtrl);
// Restore original window size.
RECT rect;
GetWindowRect(hwnd, &rect);
MoveWindow(hwnd, rect.left, rect.top, rect.right - rect.left,
g_nWindowHeight, TRUE);
// Do cleanup if we were maximized
if(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_MAXIMIZE)
{
// Set correct restore position
WINDOWPLACEMENT wndpl;
GetWindowPlacement(hwnd, &wndpl);
wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top +
g_nWindowHeight;
SetWindowPlacement(hwnd, &wndpl);
}
// Disable the view options related to the result list
HMENU hMenu;
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, ID_FILE_DOWNLOADPATCH, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, ID_FILE_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
HMENU hmView = GetSubMenu(hMenu, c_iViewMenuPos);
EnableMenuItem(hmView, c_iArrangeIconsPos, MF_BYPOSITION | MF_GRAYED);
EnableMenuItem(hMenu, ID_VIEW_ARRANGEICONS_BYPATH, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, ID_VIEW_CHOOSECOLUMNS, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, ID_VIEW_REFRESH, MF_BYCOMMAND | MF_GRAYED);
EnableWindow(GetDlgItem(hwnd, IDC_CLEARALL), FALSE);
}
// Subclass for listview, customize painting.
LRESULT CALLBACK LVWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
// Only bother if it's a PAINT message and no items are in listbox.
if((uiMsg == WM_PAINT) && (ListView_GetItemCount(hwnd) == 0))
{
// Get rectangle for text.
RECT rc;
GetWindowRect(hwnd, &rc);
POINT pt;
pt.x = rc.left;
pt.y = rc.top;
ScreenToClient(hwnd, &pt);
rc.left = pt.x;
rc.top = pt.y;
pt.x = rc.right;
pt.y = rc.bottom;
ScreenToClient(hwnd, &pt);
rc.right = pt.x;
rc.bottom = pt.y;
HWND hwHeader = ListView_GetHeader(hwnd);
if(hwHeader)
{
RECT rcHeader;
Header_GetItemRect(hwHeader, 0, &rcHeader);
rc.top += rcHeader.bottom;
}
rc.top += 10;
// Do the default painting of the listview paint.
InvalidateRect(hwnd, &rc, TRUE);
CallWindowProc(pfnLVOrgWndProc, hwnd, uiMsg, wParam, lParam);
HDC hdc = GetDC(hwnd);
SaveDC(hdc);
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
HFONT hFont = static_cast<HFONT>(GetStockObject(ANSI_VAR_FONT));
SelectObject(hdc, hFont);
// Get message to print.
TCHAR* szMsg = LoadStringResource(IDS_EMPTYLIST);
DrawText(hdc, szMsg, -1, &rc, DT_CENTER | DT_WORDBREAK
| DT_NOPREFIX | DT_NOCLIP);
RestoreDC(hdc, -1);
ReleaseDC(hwnd, hdc);
delete szMsg;
return 0;
} else if(uiMsg == WM_NOTIFY)
{
HWND hwHeader = ListView_GetHeader(hwnd);
if(hwHeader)
{
if(static_cast<int>(wParam) == GetDlgCtrlID(hwHeader))
{
LPNMHEADER pHdr = reinterpret_cast<LPNMHEADER>(lParam);
if(pHdr->hdr.code == HDN_ITEMCHANGED)
{
RECT rc;
GetClientRect(hwHeader, &rc);
POINT pt;
pt.y = rc.bottom;
ClientToScreen(hwHeader, &pt);
ScreenToClient(hwnd, &pt);
GetClientRect(hwnd, &rc);
rc.top = pt.y;
InvalidateRect(hwnd, &rc, TRUE);
}
}
}
return CallWindowProc(pfnLVOrgWndProc, hwnd, uiMsg, wParam, lParam);
}
else
return CallWindowProc(pfnLVOrgWndProc, hwnd, uiMsg, wParam, lParam);
}
// Allocate a new string and copy a string resource into it.
TCHAR* LoadStringResource(UINT uID)
{
TCHAR szBuffer[c_nMaxStringLength];
TCHAR* szRet = 0;
if(LoadString(g_hinst, uID, szBuffer, c_nMaxStringLength))
{
szRet = new TCHAR[lstrlen(szBuffer)+1];
if(szRet)
lstrcpy(szRet, szBuffer);
}
return szRet;
}
// Print an error message box in hwnd, with string resource with id as
// message.
void Error(HWND hwnd, UINT id)
{
PTSTR szMsg = LoadStringResource(id);
PTSTR szCaption = LoadStringResource(IDS_ERROR);
if(szMsg && szCaption)
MessageBox(hwnd, szMsg, szCaption, MB_OK | MB_ICONERROR);
if(szMsg)
delete szMsg;
if(szCaption)
delete szCaption;
}