LISTS.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/*
* LISTS.C
*
* This file implements a generalized multi-collumn listbox with a standard
* frame window.
*/
#define UNICODE
#include <windows.h>
#include <windowsx.h>
#include <string.h>
#include <stdlib.h>
#include "ddespy.h"
#include "globals.h"
#include "lists.h"

int CompareItems(LPTSTR psz1, LPTSTR psz2, INT SortCol, INT cCols);
int CmpCols(LPTSTR psz1, LPTSTR psz2, INT SortCol);
void DrawLBItem(LPDRAWITEMSTRUCT lpdis);
long CALLBACK MCLBClientWndProc(HWND hwnd, UINT msg, WPARAM wParam, LONG lPAram);

UINT cyHeading;


#ifdef UNICODE

#define atoi atoiW


//*********************************************************************
//
// atoiW
//
// Unicode version of atoi.
//

INT atoiW (LPTSTR s) {
INT i = 0;

while (isdigit (*s)) {
i = i*10 + (BYTE)*s - TEXT('0');
s++;
}
return i;
}

#endif

HWND CreateMCLBFrame(
HWND hwndParent,
LPTSTR lpszTitle, // frame title string
UINT dwStyle, // frame styles
HICON hIcon,
HBRUSH hbrBkgnd, // background for heading.
LPTSTR lpszHeadings) // tab delimited list of headings. The number of
// headings indicate the number of collumns.
{
static BOOL fRegistered = FALSE;
MCLBCREATESTRUCT mclbcs;

if (!fRegistered) {
WNDCLASS wc;
HDC hdc;
TEXTMETRIC tm;

wc.style = WS_OVERLAPPED | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MCLBClientWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 4;
wc.hInstance = hInst;
wc.hIcon = hIcon;
wc.hCursor = NULL;
wc.hbrBackground = hbrBkgnd;
wc.lpszMenuName = NULL;
wc.lpszClassName = (LPCTSTR) RefString(IDS_LISTCLASS);
RegisterClass(&wc);

hdc = GetDC(GetDesktopWindow());
GetTextMetrics(hdc, &tm);
cyHeading = tm.tmHeight;
ReleaseDC(GetDesktopWindow(), hdc);

fRegistered = TRUE;
}
mclbcs.lpszHeadings = lpszHeadings;

return(CreateWindow((LPCTSTR) RefString(IDS_LISTCLASS),
(LPCTSTR) lpszTitle, dwStyle,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
hwndParent, NULL, hInst, (LPVOID)&mclbcs));
}


LONG CALLBACK MCLBClientWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
MCLBSTRUCT *pmclb;
RECT rc;
INT i;

if (msg == WM_CREATE) {
LPTSTR psz;
MCLBCREATESTRUCT FAR *pcs;

pcs = (MCLBCREATESTRUCT FAR *)((LPCREATESTRUCT)lParam)->lpCreateParams;
pmclb = (MCLBSTRUCT *)LocalAlloc(LPTR, sizeof(MCLBSTRUCT));
psz = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR)
* (lstrlen(pcs->lpszHeadings) + 1));
lstrcpy((LPTSTR)psz, pcs->lpszHeadings);
pmclb->pszHeadings = psz;
pmclb->cCols = 1;
while (*psz) {
if (*psz == '\t') {
pmclb->cCols++;
}
psz++;
}
pmclb->SortCol = 0;
SetWindowLong(hwnd, 0, (UINT)pmclb);
GetClientRect(hwnd, &rc);
pmclb->hwndLB = CreateWindow((LPCTSTR) RefString(IDS_LBOX),
(LPCTSTR) szNULL,
MYLBSTYLE | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU)pmclb->cCols, hInst, NULL);
return(pmclb->hwndLB ? 0 : -1);
}

pmclb = (MCLBSTRUCT *)GetWindowLong(hwnd, 0);

switch (msg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
DRAWITEMSTRUCT dis;

BeginPaint(hwnd, &ps);
SetBkMode(ps.hdc, TRANSPARENT);
dis.hwndItem = hwnd;
dis.hDC = ps.hdc;
GetClientRect(hwnd, &dis.rcItem);
dis.rcItem.bottom = dis.rcItem.top + cyHeading;
dis.CtlType = ODT_BUTTON; // hack to avoid erasure
dis.CtlID = pmclb->cCols;
dis.itemID = 0;
dis.itemAction = ODA_DRAWENTIRE;
dis.itemData = (UINT)(LPTSTR)pmclb->pszHeadings;
dis.itemState = 0;
DrawLBItem(&dis);
EndPaint(hwnd, &ps);
}
break;

case WM_SIZE:
MoveWindow(pmclb->hwndLB, 0, cyHeading, LOWORD(lParam),
HIWORD(lParam) - cyHeading, TRUE);
break;

case WM_LBUTTONDOWN:
{
HWND hwndLB;
INT i;

// determine which collumn the mouse landed and sort on that collumn.

SendMessage(hwnd, WM_SETREDRAW, 0, 0);
GetClientRect(hwnd, &rc);
InflateRect(&rc, -1, -1);
pmclb->SortCol = LOWORD(lParam) * pmclb->cCols / (rc.right - rc.left);
hwndLB = CreateWindow((LPCTSTR) RefString(IDS_LBOX),
(LPCTSTR) szNULL, MYLBSTYLE, 1, cyHeading + 1,
rc.right - rc.left, rc.bottom - rc.top - cyHeading,
hwnd, (HMENU)pmclb->cCols, hInst, NULL);
for (i = (INT)SendMessage(pmclb->hwndLB, LB_GETCOUNT, 0, 0); i;
i--) {
SendMessage(hwndLB, LB_ADDSTRING, 0,
SendMessage(pmclb->hwndLB, LB_GETITEMDATA, i - 1, 0));
SendMessage(pmclb->hwndLB, LB_SETITEMDATA, i - 1, 0);
}
ShowWindow(hwndLB, SW_SHOW);
ShowWindow(pmclb->hwndLB, SW_HIDE);
DestroyWindow(pmclb->hwndLB);
pmclb->hwndLB = hwndLB;
SendMessage(hwnd, WM_SETREDRAW, 1, 0);
InvalidateRect(hwnd, NULL, FALSE);
}
break;

case WM_DELETEITEM:

if ((UINT)((LPDELETEITEMSTRUCT)lParam)->itemData)
LocalFree(LocalHandle((PVOID)((LPDELETEITEMSTRUCT)lParam)->itemData));
break;

case WM_MEASUREITEM:
((LPMEASUREITEMSTRUCT)lParam)->itemHeight = cyHeading;
break;

case WM_DRAWITEM:
GetClientRect(hwnd, &rc);
// This fudge makes the collumns line up with the heading.
((LPDRAWITEMSTRUCT)lParam)->rcItem.right = rc.right;
DrawLBItem((LPDRAWITEMSTRUCT)lParam);
return(DefWindowProc(hwnd, msg, wParam, lParam));
break;

case WM_COMPAREITEM:
return(CompareItems((LPTSTR)((LPCOMPAREITEMSTRUCT)lParam)->itemData1,
(LPTSTR)((LPCOMPAREITEMSTRUCT)lParam)->itemData2,
pmclb->SortCol,
pmclb->cCols));
break;

case WM_DESTROY:
LocalFree(LocalHandle((PVOID)pmclb->pszHeadings));
LocalFree(LocalHandle((PVOID)pmclb));
break;

case WM_CLOSE:
for (i = 0; i < IT_COUNT && (hwndTrack[i] != hwnd); i++) {
;
}
pro.fTrack[i] = FALSE;
hwndTrack[i] = NULL;
SetFilters();
DestroyWindow(hwnd);
break;

default:
return(DefWindowProc(hwnd, msg, wParam, lParam));
}
}




/*
* Make this return FALSE if addition not needed.
*
* if pszSearch != NULL, searches for pszSearch - collumns may contain
* wild strings - TEXT("*")
* If found, the string is removed from the LB.
* Adds pszReplace to LB.
*/
VOID AddMCLBText(LPTSTR pszSearch, LPTSTR pszReplace, HWND hwndLBFrame)
{
MCLBSTRUCT *pmclb;
INT lit;
LPTSTR psz;

pmclb = (MCLBSTRUCT *)GetWindowLong(hwndLBFrame, 0);

SendMessage(pmclb->hwndLB, WM_SETREDRAW, 0, 0);
if (pszSearch != NULL) {
lit = (INT)SendMessage(pmclb->hwndLB, LB_FINDSTRING, (WPARAM)-1, (LONG)(LPTSTR)pszSearch);
if (lit >= 0) {
SendMessage(pmclb->hwndLB, LB_DELETESTRING, lit, 0);
}
}
psz = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * (lstrlen(pszReplace) + 1));
lstrcpy(psz, pszReplace);
SendMessage(pmclb->hwndLB, WM_SETREDRAW, 1, 0);
SendMessage(pmclb->hwndLB, LB_ADDSTRING, 0, (LONG)(LPTSTR)psz);
}


/*
* This function assumes that the text in cCol is an ASCII number. 0 is
* returned if it is not found.
*/
INT GetMCLBColValue(LPTSTR pszSearch, HWND hwndLBFrame, INT cCol)
{
MCLBSTRUCT *pmclb;
LPTSTR psz;
INT lit;

pmclb = (MCLBSTRUCT *)GetWindowLong(hwndLBFrame, 0);

lit = (INT)SendMessage(pmclb->hwndLB, LB_FINDSTRING, (WPARAM)-1,
(LPARAM)(LPTSTR)pszSearch);
if (lit < 0) {
return(0);
}
psz = (LPTSTR)SendMessage(pmclb->hwndLB, LB_GETITEMDATA, lit, 0);
while (--cCol && (psz = wcschr(psz, '\t') + 1)) {
;
}
if (psz) {
return(atoi(psz));
} else {
return(0);
}
}



/*
* Returns fFoundAndRemoved
*/
BOOL DeleteMCLBText(LPTSTR pszSearch, HWND hwndLBFrame)
{
MCLBSTRUCT *pmclb;
INT lit;

pmclb = (MCLBSTRUCT *)GetWindowLong(hwndLBFrame, 0);
lit = (INT)SendMessage(pmclb->hwndLB, LB_FINDSTRING, (WPARAM)-1,
(LONG)(LPTSTR)pszSearch);
if (lit >= 0) {
SendMessage(pmclb->hwndLB, LB_DELETESTRING, lit, 0);
return(TRUE);
}
return(FALSE);
}


/*
* Returns >0 if item1 comes first, <0 if item2 comes first, 0 if ==.
*/
INT CompareItems(LPTSTR psz1, LPTSTR psz2, INT SortCol, INT cCols)
{
INT i, Col;

i = CmpCols(psz1, psz2, SortCol);
if (i != 0) {
return(i);
}
for (Col = 0; Col < cCols; Col++) {
if (Col == SortCol) {
continue;
}
i = CmpCols(psz1, psz2, Col);
if (i != 0) {
return(i);
}
}
return(0);
}


INT CmpCols(LPTSTR psz1, LPTSTR psz2, INT SortCol)
{
LPTSTR psz, pszT1, pszT2;
INT iRet;

while (SortCol--) {
psz = wcschr(psz1, '\t');
if (psz != NULL) {
psz1 = psz + 1;
} else {
psz1 = psz1 + lstrlen(psz1);
}
psz = wcschr(psz2, '\t');
if (psz != NULL) {
psz2 = psz + 1;
} else {
psz2 = psz2 + lstrlen(psz2);
}
}
pszT1 = wcschr(psz1, '\t');
pszT2 = wcschr(psz2, '\t');

if (pszT1) {
*pszT1 = '\0';
}
if (pszT2) {
*pszT2 = '\0';
}

if (!lstrcmp((LPCTSTR)RefString(IDS_WILD), psz1)
|| !lstrcmp((LPCTSTR) RefString(IDS_WILD), psz2)) {
iRet = 0;
} else {
iRet = lstrcmp(psz1, psz2);
}

if (pszT1) {
*pszT1 = '\t';
}
if (pszT2) {
*pszT2 = '\t';
}

return(iRet);
}



VOID DrawLBItem(LPDRAWITEMSTRUCT lpdis)
{
RECT rcDraw;
INT cxSection;
LPTSTR psz, pszEnd;

if (!lpdis->itemData)
return;
if ((lpdis->itemAction & ODA_DRAWENTIRE) ||
((lpdis->itemAction & ODA_SELECT) &&
(lpdis->itemState & ODS_SELECTED))) {
rcDraw = lpdis->rcItem;
if (lpdis->CtlType != ODT_BUTTON) { // hack to avoid erasure
HBRUSH hbr;

hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
FillRect(lpdis->hDC, &lpdis->rcItem, hbr);
DeleteObject(hbr);
}
cxSection = (rcDraw.right - rcDraw.left) / lpdis->CtlID;
psz = (LPTSTR)(UINT)lpdis->itemData;
rcDraw.right = rcDraw.left + cxSection;
while (pszEnd = wcschr(psz, '\t')) {
*pszEnd = '\0';
DrawText(lpdis->hDC, psz, -1, &rcDraw, DT_LEFT);
OffsetRect(&rcDraw, cxSection, 0);
*pszEnd = '\t';
psz = pszEnd + 1;
}
DrawText(lpdis->hDC, psz, -1, &rcDraw, DT_LEFT);

if (lpdis->itemState & ODS_SELECTED)
InvertRect(lpdis->hDC, &lpdis->rcItem);

if (lpdis->itemState & ODS_FOCUS)
DrawFocusRect(lpdis->hDC, &lpdis->rcItem);

} else if (lpdis->itemAction & ODA_SELECT) {

InvertRect(lpdis->hDC, &lpdis->rcItem);

} else if (lpdis->itemAction & ODA_FOCUS) {

DrawFocusRect(lpdis->hDC, &lpdis->rcItem);

}
}