/*
 * Interface code to listbox widgets
 *
 * Copyright  Martin Ayotte, 1993
 *
 */

/*
#define DEBUG_LISTBOX
*/

static char Copyright[] = "Copyright Martin Ayotte, 1993";

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include "windows.h"
#include "user.h"
#include "heap.h"
#include "win.h"
#include "listbox.h"
#include "scroll.h"
#include "dirent.h"
#include <sys/stat.h>

#define GMEM_ZEROINIT 0x0040


LPHEADLIST ListBoxGetWindowAndStorage(HWND hwnd, WND **wndPtr);
LPHEADLIST ListBoxGetStorageHeader(HWND hwnd);
void StdDrawListBox(HWND hwnd);
void OwnerDrawListBox(HWND hwnd);
int ListBoxFindMouse(HWND hwnd, int X, int Y);
int CreateListBoxStruct(HWND hwnd);
int ListBoxAddString(HWND hwnd, LPSTR newstr);
int ListBoxInsertString(HWND hwnd, UINT uIndex, LPSTR newstr);
int ListBoxGetText(HWND hwnd, UINT uIndex, LPSTR OutStr);
int ListBoxDeleteString(HWND hwnd, UINT uIndex);
int ListBoxFindString(HWND hwnd, UINT nFirst, LPSTR MatchStr);
int ListBoxResetContent(HWND hwnd);
int ListBoxSetCurSel(HWND hwnd, WORD wIndex);
int ListBoxSetSel(HWND hwnd, WORD wIndex);
int ListBoxDirectory(HWND hwnd, UINT attrib, LPSTR filespec);
int ListBoxGetItemRect(HWND hwnd, WORD wIndex, LPRECT rect);
int ListBoxSetItemHeight(HWND hwnd, WORD wIndex, long height);
int ListBoxDefaultItem(HWND hwnd, WND *wndPtr, 
	LPHEADLIST lphl, LPLISTSTRUCT lpls);
int ListBoxFindNextMatch(HWND hwnd, WORD wChar);



void LISTBOX_CreateListBox(LPSTR className, LPSTR listboxLabel, HWND hwnd)
{
    WND *wndPtr    = WIN_FindWndPtr(hwnd);
    WND *parentPtr = WIN_FindWndPtr(wndPtr->hwndParent);
    DWORD style;
    char widgetName[15];

#ifdef DEBUG_LISTBOX
    printf("listbox: label = %s, x = %d, y = %d\n", listboxLabel,
	   wndPtr->rectClient.left, wndPtr->rectClient.top);
    printf("        width = %d, height = %d\n",
	   wndPtr->rectClient.right - wndPtr->rectClient.left,
	   wndPtr->rectClient.bottom - wndPtr->rectClient.top);
#endif

    if (!wndPtr)
	return;

    style = wndPtr->dwStyle & 0x0000FFFF;
/*
    if ((style & LBS_NOTIFY) == LBS_NOTIFY)
    if ((style & LBS_SORT) == LBS_SORT)
*/    
    sprintf(widgetName, "%s%d", className, wndPtr->wIDmenu);
    wndPtr->winWidget = XtVaCreateManagedWidget(widgetName,
				    compositeWidgetClass,
				    parentPtr->winWidget,
				    XtNx, wndPtr->rectClient.left,
				    XtNy, wndPtr->rectClient.top,
				    XtNwidth, wndPtr->rectClient.right -
				        wndPtr->rectClient.left,
				    XtNheight, wndPtr->rectClient.bottom -
				        wndPtr->rectClient.top,
				    NULL );
    GlobalUnlock(hwnd);
    GlobalUnlock(wndPtr->hwndParent);
}



/***********************************************************************
 *           WIDGETS_ListBoxWndProc
 */
LONG LISTBOX_ListBoxWndProc( HWND hwnd, WORD message,
			   WORD wParam, LONG lParam )
{    
    WND  *wndPtr;
    LPHEADLIST  lphl;
    WORD	wRet;
    RECT	rect;
    int		y;
    int		width, height;
    CREATESTRUCT *createStruct;
    static RECT rectsel;
    switch(message)
    {
    case WM_CREATE:
	CreateListBoxStruct(hwnd);
	lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
	createStruct = (CREATESTRUCT *)lParam;
     	if (HIWORD(createStruct->lpCreateParams) != 0)
	    lphl->hWndLogicParent = (HWND)HIWORD(createStruct->lpCreateParams);
	else
	    lphl->hWndLogicParent = GetParent(hwnd);
	width = wndPtr->rectClient.right - wndPtr->rectClient.left;
	height = wndPtr->rectClient.bottom - wndPtr->rectClient.top;
	lphl->hWndScroll = CreateWindow("SCROLLBAR", "",
		WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBS_VERT,
		width - 17, 0, 16, height, hwnd, 1, wndPtr->hInstance, NULL);
    	ShowWindow(lphl->hWndScroll, SW_HIDE);
	SetScrollRange(lphl->hWndScroll, WM_VSCROLL, 1, lphl->ItemsCount, TRUE);
	return 0;
    case WM_DESTROY:
        lphl = ListBoxGetStorageHeader(hwnd);
	ListBoxResetContent(hwnd);
	DestroyWindow(lphl->hWndScroll);
	free(lphl);
        printf("ListBox WM_DESTROY !\n");
	return 0;

    case WM_VSCROLL:
        lphl = ListBoxGetStorageHeader(hwnd);
        if (lphl == NULL) return 0;
        y = lphl->FirstVisible;
        switch(wParam) {
            case SB_LINEUP:
		if (lphl->FirstVisible > 1)
		    lphl->FirstVisible--;
		break;
            case SB_LINEDOWN:
		if (lphl->FirstVisible < lphl->ItemsCount)
		    lphl->FirstVisible++;
		break;
	    case SB_PAGEUP:
		if (lphl->FirstVisible > 1)  
		    lphl->FirstVisible -= lphl->ItemsVisible;
		break;
	    case SB_PAGEDOWN:
		if (lphl->FirstVisible < lphl->ItemsCount)  
		    lphl->FirstVisible += lphl->ItemsVisible;
		break;
	    case SB_THUMBTRACK:
	    	lphl->FirstVisible = LOWORD(lParam);
		break;
	    }
	if (lphl->FirstVisible < 1)    lphl->FirstVisible = 1;
	if (lphl->FirstVisible > lphl->ItemsCount)
	    lphl->FirstVisible = lphl->ItemsCount;
	if (y != lphl->FirstVisible) {
	    SetScrollPos(lphl->hWndScroll, WM_VSCROLL, lphl->FirstVisible, TRUE);
	    InvalidateRect(hwnd, NULL, TRUE);
	    UpdateWindow(hwnd);
            }
	return 0;
	
    case WM_LBUTTONDOWN:
        lphl = ListBoxGetStorageHeader(hwnd);
        if (lphl == NULL) return 0;
	lphl->PrevSelected = lphl->ItemSelected;
        wRet = ListBoxFindMouse(hwnd, LOWORD(lParam), HIWORD(lParam));
	ListBoxSetCurSel(hwnd, wRet);
	ListBoxGetItemRect(hwnd, wRet, &rectsel);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
	return 0;
    case WM_LBUTTONUP:
	lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
        if (lphl == NULL) return 0;
	if (lphl->PrevSelected != lphl->ItemSelected)
	    SendMessage(lphl->hWndLogicParent, WM_COMMAND, wndPtr->wIDmenu,
		        	MAKELONG(hwnd, LBN_SELCHANGE));
	return 0;
    case WM_RBUTTONUP:
    case WM_LBUTTONDBLCLK:
	lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
        if (lphl == NULL) return 0;
        SendMessage(lphl->hWndLogicParent, WM_COMMAND, wndPtr->wIDmenu,
		        	MAKELONG(hwnd, LBN_DBLCLK));
        printf("ListBox Send LBN_DBLCLK !\n");
	return 0;
    case WM_KEYDOWN:
        printf("ListBox WM_KEYDOWN wParam %X!\n", wParam);
	ListBoxFindNextMatch(hwnd, wParam);
	break;
    case WM_PAINT:
	wndPtr = WIN_FindWndPtr(hwnd);
	if ((wndPtr->dwStyle & LBS_OWNERDRAWFIXED) == LBS_OWNERDRAWFIXED) {
	    OwnerDrawListBox(hwnd);
	    break;
	    }
	if ((wndPtr->dwStyle & LBS_OWNERDRAWVARIABLE) == LBS_OWNERDRAWVARIABLE) {
	    OwnerDrawListBox(hwnd);
	    break;
	    }
	StdDrawListBox(hwnd);
	break;
    case WM_MOUSEMOVE:
        if ((wParam & MK_LBUTTON) != 0) {
            y = HIWORD(lParam);
	    if (y < 4) {
	        lphl = ListBoxGetStorageHeader(hwnd);
		if (lphl->FirstVisible > 1) {
		    lphl->FirstVisible--;
		    SetScrollPos(lphl->hWndScroll, WM_VSCROLL, lphl->FirstVisible, TRUE);
		    InvalidateRect(hwnd, NULL, TRUE);
		    UpdateWindow(hwnd);
		    break;
		    }
		}
	    GetClientRect(hwnd, &rect);
	    if (y > (rect.bottom - 4)) {
	        lphl = ListBoxGetStorageHeader(hwnd);
		if (lphl->FirstVisible < lphl->ItemsCount) {
		    lphl->FirstVisible++;
		    SetScrollPos(lphl->hWndScroll, WM_VSCROLL, lphl->FirstVisible, TRUE);
		    InvalidateRect(hwnd, NULL, TRUE);
		    UpdateWindow(hwnd);
		    break;
		    }
		}
	    if ((y > 0) && (y < (rect.bottom - 4))) {
		if ((y < rectsel.top) || (y > rectsel.bottom)) {
		    wRet = ListBoxFindMouse(hwnd, LOWORD(lParam), HIWORD(lParam));
		    ListBoxSetCurSel(hwnd, wRet);
		    ListBoxGetItemRect(hwnd, wRet, &rectsel);
		    InvalidateRect(hwnd, NULL, TRUE);
		    UpdateWindow(hwnd);
		    }
	        
		}
	    }
	break;

    case LB_RESETCONTENT:
        printf("ListBox LB_RESETCONTENT !\n");
	ListBoxResetContent(hwnd);
	return 0;
    case LB_DIR:
        printf("ListBox LB_DIR !\n");
	wRet = ListBoxDirectory(hwnd, wParam, (LPSTR)lParam);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
	return wRet;
    case LB_ADDSTRING:
	wRet = ListBoxAddString(hwnd, (LPSTR)lParam);
	return wRet;
    case LB_GETTEXT:
	wRet = ListBoxGetText(hwnd, wParam, (LPSTR)lParam);
        printf("ListBox LB_GETTEXT #%u '%s' !\n", wParam, (LPSTR)lParam);
	return wRet;
    case LB_INSERTSTRING:
	wRet = ListBoxInsertString(hwnd, wParam, (LPSTR)lParam);
	return wRet;
    case LB_DELETESTRING:
        printf("ListBox LB_DELETESTRING #%u !\n", wParam);
	wRet = ListBoxDeleteString(hwnd, wParam);
	return wRet;
    case LB_FINDSTRING:
	wRet = ListBoxFindString(hwnd, wParam, (LPSTR)lParam);
	return wRet;
    case LB_GETCARETINDEX:
	return wRet;
    case LB_GETCOUNT:
        lphl = ListBoxGetStorageHeader(hwnd);
	return lphl->ItemsCount;
    case LB_GETCURSEL:
	lphl = ListBoxGetStorageHeader(hwnd);
        printf("ListBox LB_GETCURSEL %u !\n", lphl->ItemSelected);
	if (lphl->ItemSelected == 0) return LB_ERR;
	return lphl->ItemSelected;
    case LB_GETHORIZONTALEXTENT:
	return wRet;
    case LB_GETITEMDATA:
	return wRet;
    case LB_GETITEMHEIGHT:
	return wRet;
    case LB_GETITEMRECT:
	return wRet;
    case LB_GETSEL:
	return wRet;
    case LB_GETSELCOUNT:
	return wRet;
    case LB_GETSELITEMS:
	return wRet;
    case LB_GETTEXTLEN:
	return wRet;
    case LB_GETTOPINDEX:
	return wRet;
    case LB_SELECTSTRING:
	return wRet;
    case LB_SELITEMRANGE:
	return wRet;
    case LB_SETCARETINDEX:
	return wRet;
    case LB_SETCOLUMNWIDTH:
	return wRet;
    case LB_SETHORIZONTALEXTENT:
	return wRet;
    case LB_SETITEMDATA:
	return wRet;
    case LB_SETTABSTOPS:
	return wRet;
    case LB_SETCURSEL:
#ifdef DEBUG_LISTBOX
        printf("ListBox LB_SETCURSEL wParam=%x !\n", wParam);
#endif
	wRet = ListBoxSetCurSel(hwnd, wParam);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
	return wRet;
    case LB_SETSEL:
        printf("ListBox LB_SETSEL wParam=%x lParam=%lX !\n", wParam, lParam);
	wRet = ListBoxSetSel(hwnd, wParam);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
	return wRet;
    case LB_SETTOPINDEX:
        printf("ListBox LB_SETTOPINDEX wParam=%x !\n", wParam);
        lphl = ListBoxGetStorageHeader(hwnd);
	lphl->FirstVisible = wParam;
	SetScrollPos(lphl->hWndScroll, WM_VSCROLL, lphl->FirstVisible, TRUE);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
	break;
    case LB_SETITEMHEIGHT:
#ifdef DEBUG_LISTBOX
        printf("ListBox LB_SETITEMHEIGHT wParam=%x lParam=%lX !\n", wParam, lParam);
#endif
	wRet = ListBoxSetItemHeight(hwnd, wParam, lParam);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
	return wRet;
	
    default:
	return DefWindowProc( hwnd, message, wParam, lParam );
    }
return 0;
}


LPHEADLIST ListBoxGetWindowAndStorage(HWND hwnd, WND **wndPtr)
{
    WND  *Ptr;
    LPHEADLIST lphl;
    *(wndPtr) = Ptr = WIN_FindWndPtr(hwnd);
    lphl = *((LPHEADLIST *)&Ptr->wExtra[1]);
    return lphl;
}


LPHEADLIST ListBoxGetStorageHeader(HWND hwnd)
{
    WND  *wndPtr;
    LPHEADLIST lphl;
    wndPtr = WIN_FindWndPtr(hwnd);
    lphl = *((LPHEADLIST *)&wndPtr->wExtra[1]);
    return lphl;
}


void StdDrawListBox(HWND hwnd)
{
	LPHEADLIST  lphl;
	LPLISTSTRUCT lpls;
	PAINTSTRUCT ps;
	HBRUSH hBrush;
	HWND	hWndParent;
	HDC hdc;
	RECT rect;
	UINT  i, h, h2;
	char	C[128];
	h = 0;
	hdc = BeginPaint( hwnd, &ps );
	GetClientRect(hwnd, &rect);
        lphl = ListBoxGetStorageHeader(hwnd);
	if (lphl == NULL) goto EndOfPaint;
	hBrush = SendMessage(lphl->hWndLogicParent, WM_CTLCOLOR, (WORD)hdc,
		    MAKELONG(hwnd, CTLCOLOR_LISTBOX));
	if (hBrush == (HBRUSH)NULL)  hBrush = GetStockObject(WHITE_BRUSH);
	if (lphl->ItemsCount > lphl->ItemsVisible) rect.right -= 16;
	FillRect(hdc, &rect, hBrush);
	if (lphl->ItemsCount == 0) goto EndOfPaint;
	lpls = lphl->lpFirst;
	if (lpls == NULL) goto EndOfPaint;
	lphl->ItemsVisible = 0;
	for(i = 1; i <= lphl->ItemsCount; i++) {
	    if (i >= lphl->FirstVisible) {
		h2 = lpls->dis.rcItem.bottom - lpls->dis.rcItem.top;
		lpls->dis.rcItem.top = h;
		lpls->dis.rcItem.bottom = h + h2;
		lpls->dis.rcItem.right = rect.right;
		TextOut(hdc, 5, h + 2, (char *)lpls->dis.itemData, 
			strlen((char *)lpls->dis.itemData));
		if (lpls->dis.itemState != 0) {
		    InvertRect(hdc, &lpls->dis.rcItem);
		    }
		h += h2;
		lphl->ItemsVisible++;
		if (h > rect.bottom) break;
		}
	    if (lpls->lpNext == NULL) goto EndOfPaint;
	    lpls = (LPLISTSTRUCT)lpls->lpNext;
	}
EndOfPaint:
    EndPaint( hwnd, &ps );
    if (lphl->ItemsCount > lphl->ItemsVisible) {
        InvalidateRect(lphl->hWndScroll, NULL, TRUE);
        UpdateWindow(lphl->hWndScroll);
 	}
}



void OwnerDrawListBox(HWND hwnd)
{
	LPHEADLIST  lphl;
	LPLISTSTRUCT lpls;
	HANDLE	hTemp;
	PAINTSTRUCT ps;
	HBRUSH 	hBrush;
	HWND	hWndParent;
	HDC hdc;
	RECT rect;
	UINT  i, h, h2;
	char	C[128];
	h = 0;
	hdc = BeginPaint( hwnd, &ps );
	GetClientRect(hwnd, &rect);
        lphl = ListBoxGetStorageHeader(hwnd);
	if (lphl == NULL) goto EndOfPaint;
	hBrush = SendMessage(lphl->hWndLogicParent, WM_CTLCOLOR, (WORD)hdc,
		    MAKELONG(hwnd, CTLCOLOR_LISTBOX));
	if (hBrush == (HBRUSH)NULL)  hBrush = GetStockObject(WHITE_BRUSH);
	if (lphl->ItemsCount > lphl->ItemsVisible) rect.right -= 16;
	FillRect(hdc, &rect, hBrush);
	if (lphl->ItemsCount == 0) goto EndOfPaint;
	lpls = lphl->lpFirst;
	if (lpls == NULL) goto EndOfPaint;
	lphl->ItemsVisible = 0;
	for(i = 1; i <= lphl->ItemsCount; i++) {
	    if (i >= lphl->FirstVisible) {
		lpls->dis.hDC = hdc;
		lpls->dis.itemID = i;
		h2 = lpls->dis.rcItem.bottom - lpls->dis.rcItem.top;
		lpls->dis.rcItem.top = h;
		lpls->dis.rcItem.bottom = h + h2;
		lpls->dis.rcItem.right = rect.right;
		lpls->dis.itemAction = ODA_DRAWENTIRE;
		if (lpls->dis.itemState != 0) {
		    lpls->dis.itemAction |= ODA_SELECT;
		    }
#ifdef DEBUT_LISTBOX
		printf("LBOX WM_DRAWITEM #%d left=%d top=%d right=%d bottom=%d !\n", 
			i, lpls->dis.rcItem.left, lpls->dis.rcItem.top, 
			lpls->dis.rcItem.right, lpls->dis.rcItem.bottom);
		printf("LBOX WM_DRAWITEM Parent=%X &dis=%lX CtlID=%u !\n", 
			hWndParent, (LONG)&lpls->dis, lpls->dis.CtlID);
#endif
		printf("LBOX WM_DRAWITEM '%s' !\n", lpls->dis.itemData);
		SendMessage(lphl->hWndLogicParent, WM_DRAWITEM, i, (LPARAM)&lpls->dis);
		GlobalUnlock(hTemp);
		GlobalFree(hTemp);
		if (lpls->dis.itemState != 0) {
		    InvertRect(hdc, &lpls->dis.rcItem);
		    }
		h += h2;
		lphl->ItemsVisible++;
		if (h > rect.bottom) break;
		}
	    if (lpls->lpNext == NULL) goto EndOfPaint;
	    lpls = (LPLISTSTRUCT)lpls->lpNext;
	}
EndOfPaint:
    EndPaint( hwnd, &ps );
    if (lphl->ItemsCount > lphl->ItemsVisible) {
        InvalidateRect(lphl->hWndScroll, NULL, TRUE);
        UpdateWindow(lphl->hWndScroll);
        }
}



int ListBoxFindMouse(HWND hwnd, int X, int Y)
{
LPHEADLIST 	lphl;
LPLISTSTRUCT	lpls;
RECT rect;
UINT  i, h, h2;
char	C[128];
h = 0;
lphl = ListBoxGetStorageHeader(hwnd);
if (lphl == NULL) return LB_ERR;
if (lphl->ItemsCount == 0) return LB_ERR;
lpls = lphl->lpFirst;
if (lpls == NULL) return LB_ERR;
for(i = 1; i <= lphl->ItemsCount; i++) {
    if (i >= lphl->FirstVisible) {
	h2 = h;
	h += lpls->dis.rcItem.bottom - lpls->dis.rcItem.top;
	if ((Y > h2) && (Y < h)) return(i);
	}
    if (lpls->lpNext == NULL) return LB_ERR;
    lpls = (LPLISTSTRUCT)lpls->lpNext;
    }
return(LB_ERR);
}



int CreateListBoxStruct(HWND hwnd)
{
    WND  *wndPtr;
    LPHEADLIST lphl;
    wndPtr = WIN_FindWndPtr(hwnd);
    lphl = (LPHEADLIST)malloc(sizeof(HEADLIST));
    lphl->lpFirst = NULL;
    *((LPHEADLIST *)&wndPtr->wExtra[1]) = lphl; 	/* HEAD of List */
    lphl->ItemsCount = 0;
    lphl->ItemsVisible = 0;
    lphl->FirstVisible = 1;
    lphl->StdItemHeight = 15;
    lphl->DrawCtlType = ODT_LISTBOX;
    return TRUE;
}


int ListBoxAddString(HWND hwnd, LPSTR newstr)
{
    WND  	*wndPtr;
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lplsnew;
    HANDLE 	hTemp;
    LPSTR 	str;
    lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
    if (lphl == NULL) return LB_ERR;
    hTemp = USER_HEAP_ALLOC(GMEM_MOVEABLE, sizeof(LISTSTRUCT));
    lplsnew = (LPLISTSTRUCT) USER_HEAP_ADDR(hTemp);
    lpls = lphl->lpFirst;
    if (lpls != NULL) {
	while(lpls->lpNext != NULL) {
	    lpls = (LPLISTSTRUCT)lpls->lpNext;
	    }
	lpls->lpNext = lplsnew;
 	}
    else
	lphl->lpFirst = lplsnew;
    lphl->ItemsCount++;
#ifdef DEBUG_LISTBOX
    printf("Items Count = %u\n", lphl->ItemsCount);
#endif
    hTemp = 0;
    if ((wndPtr->dwStyle & LBS_HASSTRINGS) != LBS_HASSTRINGS) {
	if (((wndPtr->dwStyle & LBS_OWNERDRAWFIXED) != LBS_OWNERDRAWFIXED) &&
	    ((wndPtr->dwStyle & LBS_OWNERDRAWVARIABLE) != LBS_OWNERDRAWVARIABLE)) {
	    hTemp = USER_HEAP_ALLOC(GMEM_MOVEABLE, strlen(newstr) + 1);
	    str = (LPSTR)USER_HEAP_ADDR(hTemp);
	    if (str == NULL) return LB_ERRSPACE;
	    strcpy(str, newstr);
	    newstr = str;
	    }
	}
    ListBoxDefaultItem(hwnd, wndPtr, lphl, lplsnew);
    lplsnew->hMem = hTemp;
    lplsnew->lpNext = NULL;
    lplsnew->dis.itemID = lphl->ItemsCount;
    lplsnew->dis.itemData = (DWORD)newstr;
    lplsnew->hData = hTemp;
    SetScrollRange(lphl->hWndScroll, WM_VSCROLL, 1, lphl->ItemsCount, TRUE);
    if (lphl->FirstVisible >= (lphl->ItemsCount - lphl->ItemsVisible)) {
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        }
    if ((lphl->ItemsCount - lphl->FirstVisible) == lphl->ItemsVisible) 
    	ShowWindow(lphl->hWndScroll, SW_NORMAL);
    return lphl->ItemsCount;
}


int ListBoxInsertString(HWND hwnd, UINT uIndex, LPSTR newstr)
{
    WND  	*wndPtr;
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lplsnew;
    HANDLE	hTemp;
    LPSTR	str;
    UINT	Count;
    lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
    if (lphl == NULL) return LB_ERR;
    if (uIndex < 1 || uIndex > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    if (uIndex > lphl->ItemsCount) return LB_ERR;
    for(Count = 1; Count < uIndex; Count++) {
	if (lpls->lpNext == NULL) return LB_ERR;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
    }
    hTemp = USER_HEAP_ALLOC(GMEM_MOVEABLE, sizeof(LISTSTRUCT));
    lplsnew = (LPLISTSTRUCT) USER_HEAP_ADDR(hTemp);
    ListBoxDefaultItem(hwnd, wndPtr, lphl, lplsnew);
    lplsnew->hMem = hTemp;
    lpls->lpNext = lplsnew;
    lphl->ItemsCount++;
    hTemp = 0;
    if ((wndPtr->dwStyle & LBS_HASSTRINGS) != LBS_HASSTRINGS) {
	if (((wndPtr->dwStyle & LBS_OWNERDRAWFIXED) != LBS_OWNERDRAWFIXED) &&
	    ((wndPtr->dwStyle & LBS_OWNERDRAWVARIABLE) != LBS_OWNERDRAWVARIABLE)) {
	    hTemp = USER_HEAP_ALLOC(GMEM_MOVEABLE, strlen(newstr) + 1);
	    str = (LPSTR)USER_HEAP_ADDR(hTemp);
	    if (str == NULL) return LB_ERRSPACE;
	    strcpy(str, newstr);
	    newstr = str;
	    }
	}
    lplsnew->lpNext = NULL;
    lplsnew->dis.itemID = lphl->ItemsCount;
    lplsnew->dis.itemData = (DWORD)newstr;
    lplsnew->hData = hTemp;
    SetScrollRange(lphl->hWndScroll, WM_VSCROLL, 1, lphl->ItemsCount, TRUE);
    if (((lphl->ItemsCount - lphl->FirstVisible) == lphl->ItemsVisible) && 
        (lphl->ItemsVisible != 0)) 
    	ShowWindow(lphl->hWndScroll, SW_NORMAL);
    if ((lphl->FirstVisible <= uIndex) &&
        ((lphl->FirstVisible + lphl->ItemsVisible) >= uIndex)) {
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        }
    return lphl->ItemsCount;
}


int ListBoxGetText(HWND hwnd, UINT uIndex, LPSTR OutStr)
{
    WND  	*wndPtr;
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls;
    UINT	Count;
    lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
    if (lphl == NULL) return LB_ERR;
    if (uIndex < 1 || uIndex > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    if (uIndex > lphl->ItemsCount) return LB_ERR;
    for(Count = 1; Count < uIndex; Count++) {
	if (lpls->lpNext == NULL) return LB_ERR;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
    }
    if (((wndPtr->dwStyle & LBS_OWNERDRAWFIXED) == LBS_OWNERDRAWFIXED) ||
	    ((wndPtr->dwStyle & LBS_OWNERDRAWVARIABLE) == LBS_OWNERDRAWVARIABLE)) {
	if ((wndPtr->dwStyle & LBS_HASSTRINGS) != LBS_HASSTRINGS) { 
	    *((long *)OutStr) = lpls->dis.itemData;
	    return 4;
	    }
	}
	
    strcpy(OutStr, (char *)lpls->dis.itemData);
    return strlen(OutStr);
}


int ListBoxDeleteString(HWND hwnd, UINT uIndex)
{
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lpls2;
    UINT	Count;
    lphl = ListBoxGetStorageHeader(hwnd);
    if (lphl == NULL) return LB_ERR;
    if (uIndex < 1 || uIndex > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    if (uIndex > lphl->ItemsCount) return LB_ERR;
    for(Count = 1; Count < uIndex; Count++) {
	if (lpls->lpNext == NULL) return LB_ERR;
	lpls2 = lpls;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
    }
    lpls2->lpNext = (LPLISTSTRUCT)lpls->lpNext;
    lphl->ItemsCount--;
    if (lpls->hData != 0) USER_HEAP_FREE(lpls->hData);
    if (lpls->hMem != 0) USER_HEAP_FREE(lpls->hMem);
    SetScrollRange(lphl->hWndScroll, WM_VSCROLL, 1, lphl->ItemsCount, TRUE);
    if (lphl->ItemsCount < lphl->ItemsVisible) 
    	ShowWindow(lphl->hWndScroll, SW_HIDE);
    if ((lphl->FirstVisible <= uIndex) &&
        ((lphl->FirstVisible + lphl->ItemsVisible) >= uIndex)) {
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        }
    return lphl->ItemsCount;
}


int ListBoxFindString(HWND hwnd, UINT nFirst, LPSTR MatchStr)
{
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls;
    UINT	Count;
    lphl = ListBoxGetStorageHeader(hwnd);
    if (lphl == NULL) return LB_ERR;
    if (nFirst < 1 || nFirst > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    Count = 1;
    while(lpls != NULL) {
    	if (strcmp((char *)lpls->dis.itemData, MatchStr) == 0)
	    return Count;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	Count++;
        }
    return LB_ERR;
}


int ListBoxResetContent(HWND hwnd)
{
    WND  *wndPtr;
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lpls2;
    UINT	i;
    lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
    if (lphl == NULL) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    for(i = 0; i <= lphl->ItemsCount; i++) {
	lpls2 = lpls;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	if (i != 0) {
#ifdef DEBUG_LISTBOX
	    printf("ResetContent #%u\n", i);
#endif
	    if (lpls2->hData != 0) USER_HEAP_FREE(lpls->hData);
	    if (lpls2->hMem != 0) USER_HEAP_FREE(lpls->hMem);
	    }  
	if (lpls == NULL)  break;
    }
    lphl->lpFirst = NULL;
    lphl->FirstVisible = 1;
    lphl->ItemsCount = 0;
    lphl->ItemSelected = 0;
    lphl->PrevSelected = 0;
    if ((wndPtr->dwStyle && LBS_NOTIFY) != 0)
	SendMessage(wndPtr->hwndParent, WM_COMMAND, 
    	    wndPtr->wIDmenu, MAKELONG(hwnd, LBN_SELCHANGE));

    SetScrollRange(lphl->hWndScroll, WM_VSCROLL, 1, lphl->ItemsCount, TRUE);
    ShowWindow(lphl->hWndScroll, SW_HIDE);
    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
    return TRUE;
}


int ListBoxSetCurSel(HWND hwnd, WORD wIndex)
{
    WND  *wndPtr;
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lpls2;
    UINT	i;
    lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
    if (lphl == NULL) return LB_ERR;
    lphl->ItemSelected = LB_ERR;
    if (wIndex < 1 || wIndex > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    for(i = 1; i <= lphl->ItemsCount; i++) {
	lpls2 = lpls;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	if (i == wIndex)
	    lpls2->dis.itemState = 1;
	else 
	    if (lpls2->dis.itemState != 0)
	        lpls2->dis.itemState = 0;
	if (lpls == NULL)  break;
    }
    lphl->ItemSelected = wIndex;
    if ((wndPtr->dwStyle && LBS_NOTIFY) != 0)
	SendMessage(wndPtr->hwndParent, WM_COMMAND, 
    	    wndPtr->wIDmenu, MAKELONG(hwnd, LBN_SELCHANGE));
    return LB_ERR;
}



int ListBoxSetSel(HWND hwnd, WORD wIndex)
{
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lpls2;
    UINT	i;
    lphl = ListBoxGetStorageHeader(hwnd);
    if (lphl == NULL) return LB_ERR;
    if (wIndex < 1 || wIndex > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    for(i = 1; i <= lphl->ItemsCount; i++) {
	lpls2 = lpls;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	if (i == wIndex) {
	    lpls2->dis.itemState = 1;
	    break;
	    }  
	if (lpls == NULL)  break;
    }
    return LB_ERR;
}


int ListBoxDirectory(HWND hwnd, UINT attrib, LPSTR filespec)
{
DIR           	*dirp;
struct dirent 	*dp;
struct stat	st;
char		str[128];
int		wRet;
dirp = opendir(".");
while ( (dp = readdir(dirp)) != NULL) 
    {
    stat(dp->d_name, &st);
#ifdef DEBUG_LBDIR
    printf("LB_DIR : st_mode=%lX / d_name='%s'\n", st.st_mode, dp->d_name);
#endif
    if S_ISDIR(st.st_mode) {
	sprintf(str, "[%s]", dp->d_name);
	}
    else
	strcpy(str, dp->d_name);
    wRet = ListBoxAddString(hwnd, str);
    if (wRet == LB_ERR) break;
    }
closedir(dirp);	
return wRet;
}


int ListBoxGetItemRect(HWND hwnd, WORD wIndex, LPRECT lprect)
{
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lpls2;
    UINT	i;
    lphl = ListBoxGetStorageHeader(hwnd);
    if (lphl == NULL) return LB_ERR;
    if (wIndex < 1 || wIndex > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    for(i = 0; i <= lphl->ItemsCount; i++) {
	lpls2 = lpls;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	if (i == wIndex) {
	    *(lprect) = lpls2->dis.rcItem;
	    break;
	    }  
	if (lpls == NULL)  break;
    }
    return LB_ERR;
}



int ListBoxSetItemHeight(HWND hwnd, WORD wIndex, long height)
{
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls, lpls2;
    UINT	i;
    lphl = ListBoxGetStorageHeader(hwnd);
    if (lphl == NULL) return LB_ERR;
    if (wIndex < 1 || wIndex > lphl->ItemsCount) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    for(i = 0; i <= lphl->ItemsCount; i++) {
	lpls2 = lpls;
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	if (i == wIndex) {
	    lpls2->dis.rcItem.bottom = lpls2->dis.rcItem.top + (short)height;
	    break;
	    }  
	if (lpls == NULL)  break;
    }
    return LB_ERR;
}





int ListBoxDefaultItem(HWND hwnd, WND *wndPtr, 
	LPHEADLIST lphl, LPLISTSTRUCT lpls)
{
    RECT	rect;
    GetClientRect(hwnd, &rect);
    SetRect(&lpls->dis.rcItem, 0, 0, rect.right, lphl->StdItemHeight);
    lpls->dis.CtlType = lphl->DrawCtlType;
    lpls->dis.CtlID = wndPtr->wIDmenu;
    lpls->dis.itemID = 0;
    lpls->dis.itemAction = 0;
    lpls->dis.itemState = 0;
    lpls->dis.hwndItem = hwnd;
    lpls->dis.hDC = 0;
    lpls->dis.itemData = 0;
}



int ListBoxFindNextMatch(HWND hwnd, WORD wChar)
{
    WND  	*wndPtr;
    LPHEADLIST 	lphl;
    LPLISTSTRUCT lpls;
    UINT	Count;
    lphl = ListBoxGetWindowAndStorage(hwnd, &wndPtr);
    if (lphl == NULL) return LB_ERR;
    lpls = lphl->lpFirst;
    if (lpls == NULL) return LB_ERR;
    if (wChar < ' ') return LB_ERR;
    if (((wndPtr->dwStyle & LBS_OWNERDRAWFIXED) == LBS_OWNERDRAWFIXED) ||
	    ((wndPtr->dwStyle & LBS_OWNERDRAWVARIABLE) == LBS_OWNERDRAWVARIABLE)) {
	if ((wndPtr->dwStyle & LBS_HASSTRINGS) != LBS_HASSTRINGS) { 
	    return LB_ERR;
	    }
	}
    Count = 1;
    while(lpls != NULL) {
        if (Count > lphl->ItemSelected) {
	    if (*((char *)lpls->dis.itemData) == (char)wChar) {
		lphl->FirstVisible = Count - lphl->ItemsVisible / 2;
		if (lphl->FirstVisible < 1) lphl->FirstVisible = 1;
		ListBoxSetCurSel(hwnd, Count);
		SetScrollPos(lphl->hWndScroll, WM_VSCROLL, lphl->FirstVisible, TRUE);
	        InvalidateRect(hwnd, NULL, TRUE);
	        UpdateWindow(hwnd);
	        return Count;
	        }
	    }
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	Count++;
	}
    Count = 1;
    lpls = lphl->lpFirst;
    while(lpls != NULL) {
	if (*((char *)lpls->dis.itemData) == (char)wChar) {
	    if (Count == lphl->ItemSelected)    return LB_ERR;
	    lphl->FirstVisible = Count - lphl->ItemsVisible / 2;
	    if (lphl->FirstVisible < 1) lphl->FirstVisible = 1;
	    ListBoxSetCurSel(hwnd, Count);
	    SetScrollPos(lphl->hWndScroll, WM_VSCROLL, lphl->FirstVisible, TRUE);
	    InvalidateRect(hwnd, NULL, TRUE);
	    UpdateWindow(hwnd);
	    return Count;
	    }
	lpls = (LPLISTSTRUCT)lpls->lpNext;
	Count++;
        }
    return LB_ERR;
}