/*
 * 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);

    }
}


