#include <windows.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <commdlg.h>
#include <math.h>
#include "mswint.h"

/*
 *****************************************************************************
 *
 * List Boxes
 *
 *****************************************************************************
 */

static XWNDPROC oldListProc = NULL;
static XWNDPROC newListProc;
static XWNDPROC oldComboProc = NULL;
static XWNDPROC newComboProc;

struct wList_t {
		WOBJ_COMMON
		int count;
		int last;
		long * valueP;
		wListCallBack_p action;
		wBool_t editable;
		int colCnt;
		wWinPix_t * colWidths;
		wBool_t * colRightJust;
		const char * * colTitles;
		wWinPix_t maxWidth;
		wWinPix_t scrollPos;
		HWND hScrollWnd;
		wWinPix_t scrollH;
		wWinPix_t dragPos;
		int dragCol;
		wWinPix_t dragColWidth;
		};


typedef struct {
		void * itemContext;
		wIcon_p bm;
		wBool_t selected;
		} listData;

static int LIST_HEIGHT = 19;
static int listTitleHeight = 16;


void wListClear(
		wList_p b )
{
	UINT msg;
	if (b->type==B_LIST)
		msg = LB_RESETCONTENT;
	else
		msg = CB_RESETCONTENT;
	SendMessage( b->hWnd, msg, (WPARAM)0, (LPARAM)0 );
	b->last = -1;
	b->count = 0;
}



void wListSetSize( wList_p bl, wWinPix_t w, wWinPix_t h )
{
	int rc;
	RECT rect;
	wWinPix_t y;

	bl->w = w;
	bl->h = h;
	y = bl->y;	  
	if ( bl->hScrollWnd && bl->maxWidth > bl->w )
		h -= bl->scrollH;
	if ( bl->colTitles ) {
		h -= listTitleHeight;
		y += listTitleHeight;
	}
	rc = SetWindowPos( bl->hWnd, HWND_TOP, 0, 0,
		w, h, SWP_NOMOVE|SWP_NOZORDER);
	if ( bl->hScrollWnd ) {
		if ( bl->maxWidth > bl->w ) {
			GetClientRect( bl->hWnd, &rect );
			rc = SetWindowPos( bl->hScrollWnd, HWND_TOP, bl->x, y+rect.bottom+2,
				bl->w, bl->scrollH, SWP_NOZORDER);
			ShowWindow( bl->hScrollWnd, SW_SHOW );
		} else {
			ShowWindow( bl->hScrollWnd, SW_HIDE );
		}
	}

}


void wListSetIndex(
		wList_p bl,
		int index )
{
	listData * ldp;

	wListGetCount(bl);
	if ( index >= bl->count )
		index = bl->count-1;
	if ( bl->last == index && index == -1 )
		return;
	if ( bl->type==B_LIST && (bl->option&BL_MANY) != 0 ) {
		if ( bl->last != -1 )
			SendMessage( bl->hWnd, LB_SETSEL, (WPARAM)0, (LPARAM)bl->last );
		if ( index >= 0 )
			SendMessage( bl->hWnd, LB_SETSEL, (WPARAM)1, (LPARAM)index );
	} else {
		SendMessage( bl->hWnd,
				bl->type==B_LIST?LB_SETCURSEL:CB_SETCURSEL, (WPARAM)index, (LPARAM)0 );
	}
	if ( bl->last >= 0 ) {
		ldp = (listData*)SendMessage( bl->hWnd,
				(bl->type==B_LIST?LB_GETITEMDATA:CB_GETITEMDATA),
			    (WPARAM)bl->last, (LPARAM)0 );
		if ( ldp && ldp!=(void*)LB_ERR )
			ldp->selected = FALSE;
	}
	if ( index >= 0 ) {
		ldp = (listData*)SendMessage( bl->hWnd,
				(bl->type==B_LIST?LB_GETITEMDATA:CB_GETITEMDATA),
			    (WPARAM)index, (LPARAM)0 );
		if ( ldp && ldp!=(void*)LB_ERR )
			ldp->selected = TRUE;
	}
	/*if (b->option&BL_ICON)*/
		InvalidateRect( bl->hWnd, NULL, FALSE );
	bl->last = index;
}


wIndex_t wListGetIndex(
		wList_p b )
{
	return b->last;
}


void wListSetActive(
		wList_p b,
		int inx,
		wBool_t active )
{
}


void wListSetEditable(
		wList_p b,
		wBool_t editable )
{
	b->editable = editable;
}


void wListSetValue(
		wList_p bl,
		const char * val )
{
	if ( bl->type == B_DROPLIST ) {
		SendMessage( bl->hWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)val );
		bl->last = -1;
	}
}


wIndex_t wListFindValue(
		wList_p bl,
		const char * val )
{
	wIndex_t inx;
	WORD cnt;
	wListGetCount(bl);
	for ( inx = 0; inx < bl->count ; inx++ ) {
		cnt = (int)SendMessage( bl->hWnd,
				(bl->type==B_LIST?LB_GETTEXT:CB_GETLBTEXT), (WPARAM)inx,
				(LPARAM)mswTmpBuff );
		mswTmpBuff[cnt] = '\0';
		if ( strcmp( val, mswTmpBuff ) == 0 )
			return inx;
	}
	return -1;
}


wIndex_t wListGetValues(
		wList_p bl,
		char * s,
		int siz,
		void * * listContextRef,
		void * * itemContextRef )
{
	WORD cnt;
	WORD msg;
	WORD inx = bl->last;
	listData *ldp = NULL;
	if ( bl->type==B_DROPLIST && bl->last < 0 ) {
		msg = WM_GETTEXT;
		inx = sizeof mswTmpBuff;
	} else {
		if ( bl->last < 0 )
			goto EMPTY;
		if ( bl->type==B_LIST ) {
			msg = LB_GETTEXT;
		} else {
			msg = CB_GETLBTEXT;
		}
	}
	cnt = (int)SendMessage( bl->hWnd, msg, (WPARAM)inx, (LPARAM)mswTmpBuff );
	mswTmpBuff[cnt] = '\0';
	if (s) {
		strncpy(s, mswTmpBuff, siz);
		s[siz-1] = '\0';
	}
	if (bl->last >= 0) {
		ldp = (listData*)SendMessage( bl->hWnd,
				(bl->type==B_LIST?LB_GETITEMDATA:CB_GETITEMDATA),
			    (WPARAM)bl->last, (LPARAM)0 );
		if ( ldp==(listData*)LB_ERR )
			ldp = NULL;
	} else {
		ldp = NULL;
	}
EMPTY:
	if (itemContextRef)
		*itemContextRef = (ldp?ldp->itemContext:NULL);
	if (listContextRef)
		*listContextRef = bl->data;
	return bl->last;
}

wBool_t wListSetValues(
		wList_p b,
		wIndex_t inx,
		const char * labelStr,
		wIcon_p bm,
		void * itemData )
{
	listData * ldp;		
	WORD curSel = -1;
	ldp = (listData*)malloc( sizeof *ldp );
	ldp->itemContext = itemData;
	ldp->bm = bm;
	ldp->selected = FALSE;
	if ( (b->option&BL_MANY) == 0 )
		curSel = (WORD)SendMessage( b->hWnd,
				(UINT)b->type==B_LIST?LB_GETCURSEL:CB_GETCURSEL,
				(WPARAM)0,
				(LPARAM)0 );
	SendMessage( b->hWnd,
				(UINT)b->type==B_LIST?LB_DELETESTRING:CB_DELETESTRING,
				(WPARAM)inx,
				(LPARAM)0 );
	inx = (wIndex_t)SendMessage( b->hWnd,
				(UINT)b->type==B_LIST?LB_INSERTSTRING:CB_INSERTSTRING,
				(WPARAM)inx,
		        (LPARAM)labelStr );
	SendMessage( b->hWnd,
				(UINT)b->type==B_LIST?LB_SETITEMDATA:CB_SETITEMDATA,
				(WPARAM)inx,
		        (LPARAM)ldp );
	if ( (b->option&BL_MANY) == 0 && curSel == (WORD)inx)
			SendMessage( b->hWnd,
					(UINT)b->type==B_LIST?LB_SETCURSEL:CB_SETCURSEL,
					(WPARAM)inx,
					(LPARAM)0 );
	/*if (b->option&BL_ICON)*/
		InvalidateRect( b->hWnd, NULL, FALSE );
	return TRUE;
}


void wListDelete(
		wList_p b,
		wIndex_t inx )
{
	SendMessage( b->hWnd,
				(UINT)b->type==B_LIST?LB_DELETESTRING:CB_DELETESTRING,
				(WPARAM)inx,
				(LPARAM)0 );
}


/**
 * Select all items in list.
 *
 * \param bl IN list handle
 * \return
 */

void wListSelectAll( wList_p bl )
{
	wIndex_t inx;
	listData *ldp;

	// mark all items selected
	SendMessage( bl->hWnd,
				 LB_SETSEL,
				 (WPARAM)TRUE,
				 (LPARAM)-1 );

	// and synchronize the internal data structures 
	wListGetCount(bl);
	for ( inx=0; inx<bl->count; inx++ ) {
		ldp = (listData*)SendMessage( bl->hWnd,
									  (bl->type==B_LIST?LB_GETITEMDATA:CB_GETITEMDATA),
									   (WPARAM)inx, (LPARAM)0 );
		ldp->selected = TRUE;
		SendMessage( bl->hWnd,
					(UINT)bl->type==B_LIST?LB_SETITEMDATA:CB_SETITEMDATA,
					(WPARAM)inx,
					(LPARAM)ldp );
	}
}


wIndex_t wListGetCount(
		wList_p bl )
{			  
	bl->count = (int)SendMessage( bl->hWnd, (UINT)bl->type==B_LIST?LB_GETCOUNT:CB_GETCOUNT, (WPARAM)0, (LPARAM)0 );
	return bl->count;
}


void * wListGetItemContext(
		wList_p bl,
		wIndex_t inx )
{
	listData * ldp;
	wListGetCount(bl);
	if ( inx < 0 || inx >= bl->count ) return NULL;
	ldp = (listData*)SendMessage( bl->hWnd,
				(bl->type==B_LIST?LB_GETITEMDATA:CB_GETITEMDATA),
				(WPARAM)inx, (LPARAM)0 );
	return ((ldp&&ldp!=(void*)LB_ERR)?ldp->itemContext:NULL);
}


wBool_t wListGetItemSelected(
		wList_p bl,
		wIndex_t inx )
{
	listData * ldp;
	wListGetCount(bl);
	if ( inx < 0 || inx >= bl->count ) return FALSE;
	ldp = (listData*)SendMessage( bl->hWnd,
				(bl->type==B_LIST?LB_GETITEMDATA:CB_GETITEMDATA),
				(WPARAM)inx, (LPARAM)0 );
	return ((ldp&&ldp!=(void*)LB_ERR)?ldp->selected:FALSE);
}


wIndex_t wListGetSelectedCount(
		wList_p bl )
{
	wIndex_t selcnt, inx;
	wListGetCount(bl);
	for ( selcnt=inx=0; inx<bl->count; inx++ )
		if ( wListGetItemSelected( bl, inx ) )
			selcnt++;
	return selcnt;
}




wIndex_t wListAddValue(
		wList_p b,
		const char * value,
		wIcon_p bm,
		void * itemContext )
{
	int nindex;
	listData * ldp;
	ldp = (listData*)malloc( sizeof *ldp );
	ldp->itemContext = itemContext;
	ldp->bm = bm;
	ldp->selected = FALSE;
	if ( value == NULL )
		value = "";
	b->count++;
	nindex = (int)SendMessage(
				b->hWnd,
				(UINT)b->type==B_LIST?LB_ADDSTRING:CB_ADDSTRING,
				(WPARAM)0,
				(LPARAM)value );
	if (nindex == 0) {
		SendMessage( b->hWnd,
				(UINT)b->type==B_LIST?LB_SETCURSEL:CB_SETCURSEL,
				(WPARAM)nindex,
			    (LPARAM)0 );
		b->last = 0;
	}
	SendMessage( b->hWnd,
				(UINT)b->type==B_LIST?LB_SETITEMDATA:CB_SETITEMDATA,
				(WPARAM)nindex,
		        (LPARAM)ldp );
	return nindex;
}


int wListGetColumnWidths(
		wList_p bl,
		int colCnt,
		wWinPix_t * colWidths )
{
	wIndex_t inx;

	if ( bl->type != B_LIST )
		return 0;
	if ( bl->colWidths == NULL )
		return 0;
	for ( inx=0; inx<colCnt; inx++ ) {
		if ( inx < bl->colCnt )
			colWidths[inx] = bl->colWidths[inx];
		else
			colWidths[inx] = 0;
	}
	return bl->colCnt;
}


static void listSetBusy(
		wControl_p b,
		BOOL_T busy)
{
	wList_p bl = (wList_p)b;

	EnableWindow( bl->hWnd, !(BOOL)busy );
	if ( bl->hScrollWnd )
		EnableWindow( bl->hScrollWnd, !(BOOL)busy );
}

static void listShow(
		wControl_p b,
		BOOL_T show)
{
	wList_p bl = (wList_p)b;

	ShowWindow( bl->hWnd, show?SW_SHOW:SW_HIDE );
	if ( bl->hScrollWnd && bl->maxWidth > bl->w )
		ShowWindow( bl->hScrollWnd, show?SW_SHOW:SW_HIDE );
#ifdef SHOW_DOES_SETFOCUS
	if ( show && (bl->option&BO_READONLY)==0 )
		hWnd = SetFocus( bl->hWnd );
#endif
}

static void listSetPos(
		wControl_p b,
		wWinPix_t x,
		wWinPix_t y )
{
	wList_p bl = (wList_p)b;
	wWinPix_t x1, y1;
	RECT rect;
	
	bl->x = x1 = x;
	bl->y = y1 = y;
	if ( bl->colTitles )
		y1 += listTitleHeight;
	if (!SetWindowPos( b->hWnd, HWND_TOP, x1, y1,
				CW_USEDEFAULT, CW_USEDEFAULT,
				SWP_NOSIZE|SWP_NOZORDER))
				mswFail("listSetPos");
	if ( bl->hScrollWnd && bl->maxWidth > bl->w ) {
		GetClientRect( bl->hWnd, &rect );
		if (!SetWindowPos( bl->hScrollWnd, HWND_TOP, x1, y1+rect.bottom+2,
				CW_USEDEFAULT, CW_USEDEFAULT,
				SWP_NOSIZE|SWP_NOZORDER))
				mswFail("listSetPos2");
	}
}


static void listRepaintLabel(
		HWND hWnd,
		wControl_p b )
{
	wList_p bl = (wList_p)b;
	HDC hDc;
	RECT rc;
	HFONT hFont;
	HPEN hPen0, hPen1, hPen2, hPen3;
	HBRUSH hBrush;
	const char * * title;
	int inx;
	int start;
	wWinPix_t colWidth;

	mswRepaintLabel( hWnd, b );
	if ( bl->colTitles == NULL )
		return;
	hDc = GetDC( hWnd );
	start = bl->x-bl->scrollPos+2;
	rc.top = bl->y;
	rc.bottom = bl->y+listTitleHeight;
	rc.left = bl->x-1;
	rc.right = bl->x+bl->w;
	hBrush = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ) );
	FillRect( hDc, &rc, hBrush );
	SetBkColor( hDc, GetSysColor( COLOR_BTNFACE ) );
	
	hFont = SelectObject( hDc, mswLabelFont );
	hPen1 = CreatePen( PS_SOLID, 0, GetSysColor( COLOR_BTNTEXT ) );
	hPen2 = CreatePen( PS_SOLID, 0, GetSysColor( COLOR_BTNHIGHLIGHT ) );
	hPen3 = CreatePen( PS_SOLID, 0, GetSysColor( COLOR_BTNSHADOW ) );
	hPen0 = SelectObject( hDc, hPen1 );
	MoveTo( hDc, rc.left, rc.top );
	LineTo( hDc, rc.right, rc.top );
	LineTo( hDc, rc.right, rc.bottom );
	LineTo( hDc, rc.left, rc.bottom );
	LineTo( hDc, rc.left, rc.top );
	SelectObject( hDc, hPen2 );
	MoveTo( hDc, rc.left+1, rc.bottom-1 );
	LineTo( hDc, rc.left+1, rc.top+1 );
	LineTo( hDc, rc.right-1, rc.top+1 );
	SelectObject( hDc, hPen3 );
	MoveTo( hDc, rc.left+2, rc.bottom-1 );
	LineTo( hDc, rc.right-1, rc.bottom-1 );
	LineTo( hDc, rc.right-1, rc.top+1 );
	rc.top += 2;
	rc.bottom -= 1;
	for ( inx=0,title=bl->colTitles; inx<bl->colCnt&&*title&&start<bl->x+bl->w; inx++ ) {
		colWidth = bl->colWidths[inx];
		if ( start+colWidth >= 3 ) {
			rc.left = start;
			if ( rc.left < bl->x+2 )
				rc.left = bl->x+2;
			rc.right = start+colWidth;
			if ( rc.right > bl->x+bl->w-1 )
				rc.right = bl->x+bl->w-1;
			ExtTextOut( hDc, start+1, rc.top+0,
				ETO_CLIPPED|ETO_OPAQUE, &rc,
				*title, (int)(strlen(*title)), NULL );
			if ( start-bl->x >= 3 ) {
			SelectObject( hDc, hPen1 );
			MoveTo( hDc, start-1, rc.top-1 );
			LineTo( hDc, start-1, rc.bottom+3 );
			SelectObject( hDc, hPen2 );
			MoveTo( hDc, start, rc.top );
			LineTo( hDc, start, rc.bottom+1 );
			SelectObject( hDc, hPen3 );
			MoveTo( hDc, start-2, rc.top );
			LineTo( hDc, start-2, rc.bottom+1 );
			}
		}
		title++;
		start += colWidth;
	}
	SelectObject( hDc, hPen0 );
	SelectObject( hDc, hFont );
	DeleteObject( hBrush );
	DeleteObject( hPen1 );
	DeleteObject( hPen2 );
	DeleteObject( hPen3 );
}


#ifdef LATER
static void listHandleSelectionState( LPDRAWITEMSTRUCT lpdis, LPRECT rc )
{
	int oldROP;
	oldROP = SetROP2( lpdis->hDC, R2_NOT );
	Rectangle( lpdis->hDC, rc->left, rc->top, rc->right, rc->bottom );
	SetROP2( lpdis->hDC, oldROP );
	/*InvertRect( lpdis->hDC, rc );*/
}
#endif

static void listHandleFocusState( LPDRAWITEMSTRUCT lpdis, LPRECT rc )
{
	DrawFocusRect( lpdis->hDC, rc );
}


LRESULT listProc(
		wControl_p b,
		HWND hWnd,
		UINT message,
		WPARAM wParam,
		LPARAM lParam )
{						
	wList_p bl = (wList_p)b;
	int cnt, inx, selected;
	size_t len;
	listData * ldp;
	HDC hDc;
	LPMEASUREITEMSTRUCT lpmis;
	TEXTMETRIC tm;
	LPDRAWITEMSTRUCT lpdis;
	RECT rc, rc1;
	char * cp0, * cp1;
	wWinPix_t x;
	int colWidth;
	int nPos;
	HFONT hFont;
	HPEN hPen;
	HBRUSH hBrush;
	WPARAM notification;
	COLORREF col;

	if (bl) switch( message ) {
	
	case WM_COMMAND:
		notification = WCMD_PARAM_NOTF;
		switch (bl->type) {
		case B_LIST:
			switch (notification) {
			case LBN_SELCHANGE:
			case LBN_DBLCLK:
				if ( (bl->option&BL_DBLCLICK)!=0 ?
						notification!=LBN_DBLCLK :
						notification==LBN_DBLCLK )
					break;
				if ( (bl->option&BL_MANY) ) {
					wListGetCount(bl);
					for ( inx=0; inx<bl->count; inx++ ) {
					ldp = (listData*)SendMessage( bl->hWnd, LB_GETITEMDATA, (WPARAM)inx, (LPARAM)0 );
					if ( ldp != NULL && ldp != (void*)LB_ERR ) {
						selected = ((long)SendMessage( bl->hWnd, LB_GETSEL, (WPARAM)inx, (LPARAM)0 ) != 0L );
						if ( selected != ldp->selected ) {
							ldp->selected = selected;
							if ( selected ) {
								bl->last = inx;
								cnt = (int)SendMessage( bl->hWnd, LB_GETTEXT, (WPARAM)bl->last, (LPARAM)mswTmpBuff );
								mswTmpBuff[cnt] = '\0';
							} else {
								mswTmpBuff[0] = '\0';
							}
							if ( bl->action )
								bl->action( inx, mswTmpBuff, selected?1:2, bl->data, ldp->itemContext );
							if ( selected && bl->valueP )
								*bl->valueP = bl->last;
						}
					}
					}
				} else {
					bl->last = (int)SendMessage( bl->hWnd, LB_GETCURSEL, (WPARAM)0, (LPARAM)0 );
					cnt = (int)SendMessage( bl->hWnd, LB_GETTEXT, (WPARAM)bl->last,
										(LPARAM)mswTmpBuff );
					mswTmpBuff[cnt] = '\0';
					if (bl->action) {
						ldp = (listData*)SendMessage( bl->hWnd, LB_GETITEMDATA,
							(WPARAM)bl->last, (LPARAM)0 );
						bl->action( bl->last, mswTmpBuff, 1, bl->data,
							((bl->last>=0&&ldp&&ldp!=(void*)LB_ERR)?ldp->itemContext:NULL) );
					}
					if (bl->valueP) {
						*bl->valueP = bl->last;
					}
				}
				break;

			case LBN_KILLFOCUS:
				if ( ( bl->option&BL_MANY ) == 0 &&
					 bl->last != (int)SendMessage( bl->hWnd, LB_GETCURSEL, (WPARAM)0, (LPARAM)0 ) )
					(void)SendMessage( bl->hWnd, LB_SETCURSEL, (WPARAM)bl->last, (LPARAM)0 );
				break;
			}
			break;

		case B_DROPLIST:
		case B_COMBOLIST:
			switch (notification) {
			case CBN_SELCHANGE:
			case CBN_DBLCLK:
				if ( (bl->type == B_DROPLIST) ||
					 ( (bl->option&BL_DBLCLICK)!=0 ?
						notification!=CBN_DBLCLK :
						notification==CBN_DBLCLK) )
					break;

			case CBN_CLOSEUP:
				bl->last = (int)SendMessage( bl->hWnd, CB_GETCURSEL, (WPARAM)0, (LPARAM)0 );
				if (bl->last < 0)
					break;
				if (bl->action) {
					cnt = (int)SendMessage( bl->hWnd, CB_GETLBTEXT, 
						(WPARAM)bl->last, (LPARAM)mswTmpBuff );
					ldp = (listData*)SendMessage( bl->hWnd, CB_GETITEMDATA,
						(WPARAM)bl->last, (LPARAM)0 );
					mswTmpBuff[cnt] = '\0';
					bl->action( bl->last, mswTmpBuff, 1, bl->data,
						((bl->last>=0&&ldp&&ldp!=(void*)LB_ERR)?ldp->itemContext:NULL) );
				}
				if (bl->valueP) {
					*bl->valueP = bl->last;
				}
				mswAllowBalloonHelp = TRUE;
				/*SendMessage( bl->bWnd, CB_SETCURSEL, bl->last, 0L );*/
				break;

			case CBN_KILLFOCUS:
				inx = (int)SendMessage( bl->hWnd, CB_GETCURSEL, (WPARAM)0, (LPARAM)0 );
				if ( bl->last != inx )
					(void)SendMessage( bl->hWnd, CB_SETCURSEL, (WPARAM)bl->last, (LPARAM)0 );
				break;

			case CBN_DROPDOWN:
				mswAllowBalloonHelp = FALSE;
				break;

			case CBN_EDITCHANGE:
				bl->last = -1;
				if (bl->action) {
					cnt = (int)SendMessage( bl->hWnd, WM_GETTEXT, (WPARAM)sizeof mswTmpBuff,
						(LPARAM)mswTmpBuff );
					mswTmpBuff[cnt] = '\0';
					bl->action( -1, mswTmpBuff, 1, bl->data, NULL );
				}
				break;
			}
			break;
		}
		break;

	case WM_MEASUREITEM:
		lpmis = (LPMEASUREITEMSTRUCT)lParam;
		hDc = GetDC( hWnd );
		if ( bl->type == B_LIST )
			hFont = SelectObject( hDc, mswLabelFont );
		GetTextMetrics( hDc, &tm );
		lpmis->itemHeight = tm.tmHeight;			  
		if ( bl->type == B_LIST )
			SelectObject( hDc, hFont );
		ReleaseDC( hWnd, hDc );
		break;

	case WM_DRAWITEM:
		lpdis = (LPDRAWITEMSTRUCT)lParam;
		if (lpdis->itemID == -1) {
			listHandleFocusState(lpdis, &lpdis->rcItem);
			return TRUE;
		}
		ldp = (listData*)SendMessage( bl->hWnd,
						(bl->type==B_LIST?LB_GETITEMDATA:CB_GETITEMDATA),
						(WPARAM)lpdis->itemID, (LPARAM)0);
		rc = lpdis->rcItem;
		if (lpdis->itemAction & (ODA_DRAWENTIRE|ODA_SELECT|ODA_FOCUS)) {
			if( bl->type == B_LIST )
				hFont = SelectObject( lpdis->hDC, mswLabelFont );
			cnt = (int)SendMessage( lpdis->hwndItem, 
				(bl->type==B_LIST?LB_GETTEXT:CB_GETLBTEXT),
				(WPARAM)lpdis->itemID, (LPARAM)mswTmpBuff );
			mswTmpBuff[cnt] = '\0';
			if ( lpdis->itemState & ODS_SELECTED ) {
				SetTextColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
				SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
			} else {
				SetTextColor( lpdis->hDC, GetSysColor( COLOR_WINDOWTEXT ) );
				SetBkColor( lpdis->hDC, GetSysColor( COLOR_WINDOW ) );
			}
			rc1 = rc;
			rc1.left -= bl->scrollPos;
			for ( inx=0,cp0=mswTmpBuff; inx<bl->colCnt&&cp0&&rc1.left<rc.right; inx++ ) {
				if ( inx>=bl->colCnt-1 || (cp1=strchr(cp0,'\t')) == NULL ) {
					len = strlen( cp0 );
					cp1=cp0 + len; // JBB, to avoid an MSC error below where cp1 has not been defined.
				} else {
					len = cp1-cp0;
					cp1 ++;
				}
				if ( bl->colWidths ) {
					colWidth = bl->colWidths[inx];
				} else {
					colWidth = rc.right;
				}
				if ( inx == 0 && ldp && ldp!=(void*)LB_ERR && ldp->bm ) {
					if (mswPalette) {
						SelectPalette( lpdis->hDC, mswPalette, 0 );
						cnt = RealizePalette( lpdis->hDC );
					}
					hPen = SelectObject( lpdis->hDC, CreatePen( PS_SOLID, 0, GetSysColor( COLOR_WINDOW ) ) );
					hBrush = SelectObject( lpdis->hDC, CreateSolidBrush( GetSysColor( COLOR_WINDOW ) ) );
					Rectangle( lpdis->hDC, rc1.left, rc1.top, rc1.right, rc1.bottom );
					DeleteObject( SelectObject( lpdis->hDC, hPen ) );
					DeleteObject( SelectObject( lpdis->hDC, hBrush ) );

					col = RGB(	(ldp->bm->colormap[ 1 ]).rgbRed, 
								(ldp->bm->colormap[ 1 ]).rgbGreen,
								(ldp->bm->colormap[ 1 ]).rgbBlue );
					mswDrawIcon( lpdis->hDC, rc1.left+2, rc.top+0, ldp->bm, 0, col, col);

					rc1.left += ldp->bm->w+6;
					colWidth -= ldp->bm->w+6;
				}
				if ( inx>=bl->colCnt-1 || (rc1.right = rc1.left + colWidth) > rc.right )
					 rc1.right = rc.right;
				if ( rc1.right > 0 && rc1.left+3 < rc.right ) {
					ExtTextOut( lpdis->hDC, rc1.left+3, rc1.top+1,
						ETO_CLIPPED | ETO_OPAQUE, &rc1,
						(LPSTR)cp0, (int)len, NULL );
				}
				rc1.left = rc1.right;
				cp0 = cp1;
			}
			if ( lpdis->itemState & ODS_SELECTED ) {
				SetTextColor( lpdis->hDC, GetSysColor( COLOR_WINDOWTEXT ) );
				SetBkColor( lpdis->hDC, GetSysColor( COLOR_WINDOW ) );
			}
			if (lpdis->itemState & ODS_FOCUS) {
				DrawFocusRect( lpdis->hDC, &rc );
			}
			if ( bl->type == B_LIST)
				SelectObject( lpdis->hDC, hFont );
			return (LRESULT)TRUE;
		}
		
		break;

	case WM_HSCROLL:
		len = ((long)bl->maxWidth)-((long)bl->w);
		if ( len <= 0 )
			return (LRESULT)0;
		switch ( WSCROLL_PARAM_CODE ) {
		case SB_LEFT:
			if ( bl->scrollPos == 0 )
				return (LRESULT)0;
			bl->scrollPos = 0;
			break;
		case SB_LINELEFT:
		case SB_PAGELEFT:
			if ( bl->scrollPos == 0 )
				return (LRESULT)0;
			for ( inx=colWidth=0; inx<bl->colCnt; inx++ ) {
				if ( colWidth+bl->colWidths[inx] >= bl->scrollPos ) {
					bl->scrollPos = colWidth; 
					break;
				}
				colWidth += bl->colWidths[inx];
			}
			break;
		case SB_LINERIGHT:
		case SB_PAGERIGHT:
			if ( bl->scrollPos >= len )
				return (LRESULT)0;
			for ( inx=colWidth=0; inx<bl->colCnt; inx++ ) {
				if ( colWidth >= bl->scrollPos ) {
					bl->scrollPos = colWidth+bl->colWidths[inx];
					break;
				}
				colWidth += bl->colWidths[inx];
			}
			break;
		case SB_RIGHT:
			if ( bl->scrollPos >= len )
				return (LRESULT)0;
			bl->scrollPos = (int)len;
			break;
		case SB_THUMBTRACK:	  
			return (LRESULT)0;
		case SB_THUMBPOSITION:
			nPos = (int)WSCROLL_PARAM_NPOS;
			bl->scrollPos = (int)(len*nPos/100);
			break;
		case SB_ENDSCROLL:
			return (LRESULT)0;
		}
		if ( bl->scrollPos > len ) bl->scrollPos = (int)len;
		if ( bl->scrollPos < 0 ) bl->scrollPos = 0;
		nPos = (int)(((long)bl->scrollPos)*100L/len+0.5);
		SetScrollPos( bl->hScrollWnd, SB_CTL, nPos, TRUE );
		InvalidateRect( bl->hWnd, NULL, FALSE );
		listRepaintLabel( ((wControl_p)(bl->parent))->hWnd, (wControl_p)bl );
		return (LRESULT)0;

	case WM_LBUTTONDOWN:
		if ( bl->type != B_LIST )
			break;
		if ( bl->colCnt <= 1 )
			break;
		x = bl->dragPos = LOWORD(lParam)+bl->scrollPos-4;
		bl->dragCol = -1;
		for ( inx=0; inx<bl->colCnt; inx++ ) {
			x -= bl->colWidths[inx];
			if ( x < -5 ) break;
			if ( x <= 0 ) { bl->dragCol = inx; break; }
			if ( x > bl->colWidths[inx+1] ) continue;
			if ( x <= 10 ) { bl->dragCol = inx; break; }
		}
		if ( bl->dragCol >= 0 )
			bl->dragColWidth = bl->colWidths[inx];
		return (LRESULT)0;

#ifdef LATER
	case WM_MOUSEMOVE:
		if ( (wParam&MK_LBUTTON) == 0 )
			break;
		if ( bl->type != B_LIST )
			break;
		if ( bl->colCnt <= 1 )
			break;
		x = LOWORD(lParam)+bl->scrolPos;
		for ( inx=0; inx<bl->colCnt; inx++ ) {
			x -= bl->colWidths[inx];
			if ( x <= 0 )
				break;
		}
		return (LRESULT)0;
#endif

	case WM_MOUSEMOVE:
		if ( (wParam&MK_LBUTTON) == 0 )
			break;
	case WM_LBUTTONUP:
		if ( bl->type != B_LIST )
			break;
		if ( bl->colCnt <= 1 )
			break;
		if ( bl->dragCol < 0 )
			break;
		x = LOWORD(lParam)+bl->scrollPos-4-bl->dragPos;			/* WIN32??? */
		bl->colWidths[bl->dragCol] = bl->dragColWidth+x;
		if ( bl->colWidths[bl->dragCol] < 0 )
			bl->colWidths[bl->dragCol] = 0;
		for ( bl->maxWidth=inx=0; inx<bl->colCnt; inx++ )
			bl->maxWidth += bl->colWidths[inx];
		if ( bl->maxWidth <= bl->w ) {
			x = bl->w - bl->maxWidth;
			bl->colWidths[bl->colCnt-1] += x;
			bl->maxWidth = bl->w;
			bl->scrollPos = 0;
		} else {
			if ( bl->scrollPos+bl->w > bl->maxWidth ) {
				bl->scrollPos = bl->maxWidth - bl->w;
			}
		}
		InvalidateRect( bl->hWnd, NULL, FALSE );
		listRepaintLabel( ((wControl_p)(bl->parent))->hWnd, (wControl_p)bl );
		return (LRESULT)0;

	}													  
	
	return DefWindowProc( hWnd, message, wParam, lParam );
}					

LRESULT FAR PASCAL _export pushList(
		HWND hWnd,
		UINT message,
		WPARAM wParam,
		LPARAM lParam )
{
	/* Catch <Return> and cause focus to leave control */
	wIndex_t inx = (wIndex_t)GetWindowLongPtr(hWnd, GWL_ID);
	wControl_p b = mswMapIndex(inx);
#ifdef OLDCODE
	long inx = GetWindowLong( hWnd, GWL_ID );
	wControl_p b = mswMapIndex( inx );
#endif

	switch (message) {
	case WM_CHAR:
		if ( b != NULL) {
			switch( WCMD_PARAM_ID ) {
			case 0x0D:
			case 0x1B:
			case 0x09:
				SetFocus( ((wControl_p)(b->parent))->hWnd ); 
				SendMessage( ((wControl_p)(b->parent))->hWnd, WM_CHAR,
						wParam, lParam );
				/*SendMessage( ((wControl_p)(b->parent))->hWnd, WM_COMMAND,
						inx, MAKELONG( hWnd, EN_KILLFOCUS ) );*/
				return (LRESULT)0;
			}
		}
		break;
	}
	return CallWindowProc( oldListProc, hWnd, message, wParam, lParam );
}

LRESULT FAR PASCAL _export pushCombo(
		HWND hWnd,
		UINT message,
		WPARAM wParam,
		LPARAM lParam )
{
	/* Catch <Return> and cause focus to leave control */
	wIndex_t inx = (wIndex_t)GetWindowLongPtr( hWnd, GWL_ID );
	wControl_p b = mswMapIndex( inx );

	switch (message) {
	case WM_CHAR:
		if ( b != NULL) {
			switch( WCMD_PARAM_ID ) {
			case 0x0D:
			case 0x1B:
			case 0x09:
				SetFocus( ((wControl_p)(b->parent))->hWnd ); 
				SendMessage( ((wControl_p)(b->parent))->hWnd, WM_CHAR,
						wParam, lParam );
				/*SendMessage( ((wControl_p)(b->parent))->hWnd, WM_COMMAND,
						inx, MAKELONG( hWnd, EN_KILLFOCUS ) );*/
				return (LRESULT)0;
			}
		}
		break;
	}
	return CallWindowProc( oldComboProc, hWnd, message, wParam, lParam );
}

static callBacks_t listCallBacks = {
		listRepaintLabel,
		NULL,
		listProc,
		listSetBusy,
		listShow,
		listSetPos };


static wList_p listCreate(
		int		typ,
		const char	*className,
		long	style,
		wWin_p	parent,
		wWinPix_t	x,
		wWinPix_t	y,
		const char	* helpStr,
		const char	* labelStr,
		long	option,
		long	number,
		wWinPix_t	width,
		long	*valueP,
		wListCallBack_p action,
		void	*data,
		wBool_t addFocus,
		int		*indexR )
{ 
	wList_p b;
	RECT rect;	
	int index;

	b = (wList_p)mswAlloc( parent, typ, mswStrdup(labelStr), sizeof *b, data, &index );
	mswComputePos( (wControl_p)b, x, y );
	b->option = option;
	b->count = 0;
	b->last = -1;
	b->valueP = valueP;
	b->labelY += 4;
	b->action = action;
	b->maxWidth = 0;
	b->scrollPos = 0;
	b->scrollH = 0;
	b->dragPos = 0;
	b->dragCol = -1;

	b->hWnd = CreateWindow( className, NULL,
				style | WS_CHILD | WS_VISIBLE | mswGetBaseStyle(parent), b->x, b->y,
				width, LIST_HEIGHT*(int)number,
				((wControl_p)parent)->hWnd, (HMENU)(UINT_PTR)index, mswHInst, NULL );
	if (b->hWnd == NULL) {
		mswFail("CreateWindow(LIST)");
		return b;
	}

	GetWindowRect( b->hWnd, &rect );
	b->w = rect.right - rect.left;
	b->h = rect.bottom - rect.top;
	b->colCnt = 1;
	b->colWidths = NULL;
	b->colTitles = NULL;

	mswAddButton( (wControl_p)b, TRUE, helpStr );
	mswCallBacks[typ] = &listCallBacks;
	if (addFocus) {
		mswChainFocus( (wControl_p)b );
		if (b->type == B_LIST) {
			newListProc = MakeProcInstance((XWNDPROC)pushList, mswHInst);
			oldListProc = (XWNDPROC)GetWindowLongPtr(b->hWnd, GWLP_WNDPROC);
			SetWindowLongPtr(b->hWnd, GWLP_WNDPROC, (LONG_PTR)newListProc);
#ifdef _OLDCODE
			oldListProc = (XWNDPROC)GetWindowLong(b->hWnd, GWL_WNDPROC);
			SetWindowLong(b->hWnd, GWL_WNDPROC, (LONG)newListProc);
#endif
		}
		else {
			newComboProc = MakeProcInstance((XWNDPROC)pushCombo, mswHInst);
			oldComboProc = (XWNDPROC)GetWindowLongPtr(b->hWnd, GWLP_WNDPROC);
			SetWindowLongPtr(b->hWnd, GWLP_WNDPROC, (LONG_PTR)newComboProc);
#ifdef _OLDCODE
			oldComboProc = (XWNDPROC)GetWindowLong(b->hWnd, GWL_WNDPROC);
			SetWindowLong(b->hWnd, GWL_WNDPROC, (LONG)newComboProc);
#endif
		}
	}
	if ( indexR )
		*indexR = index;
	if ( !mswThickFont )
		SendMessage( b->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, (LPARAM)0 );
	return b;
}


wList_p wListCreate(
		wWin_p	parent,
		wWinPix_t	x,
		wWinPix_t	y,
		const char	* helpStr,
		const char	* labelStr,
		long	option,
		long	number,
		wWinPix_t	width,
		int		colCnt,
		wWinPix_t	* colWidths,
		wBool_t * colRightJust,
		const char	* * colTitles,
		long	*valueP,
		wListCallBack_p action,
		void	*data )
{ 
	long bs;
	wList_p bl;
	static int dbu = 0;
	RECT rect;
	int index;
	int i;

	bs = LBS_NOTIFY | WS_VSCROLL | WS_BORDER | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS;
	if (option & BL_MANY)
		bs |= LBS_MULTIPLESEL|LBS_EXTENDEDSEL;
	if (option & BL_SORT)
		bs |= LBS_SORT;
	if ( colCnt > 1 )
		bs |= WS_HSCROLL;
	if ( colTitles ) {
		y += listTitleHeight;
		number -= 1;
	}
	bl = listCreate( B_LIST, "LISTBOX", bs, parent, x, y, helpStr,
						labelStr, option, number, width, valueP, action, data, TRUE, &index );
	if ( colTitles ) {
		bl->y -= listTitleHeight;
		bl->h += listTitleHeight;
	}	 
	if ( colCnt > 1 ) {
		bl->colCnt = colCnt;
		bl->colWidths = (wWinPix_t*)malloc( colCnt * sizeof *bl->colWidths );
		bl->colRightJust = (wBool_t*)malloc( colCnt * sizeof *bl->colRightJust );
		bl->colTitles = colTitles;
		bl->maxWidth = 0;
		memcpy( bl->colWidths, colWidths, colCnt * sizeof *bl->colWidths );
		for ( i=0; i<colCnt; i++ ) {
			bl->colWidths[i] = colWidths[i];
			bl->maxWidth += bl->colWidths[i];
		}
		bl->hScrollWnd = CreateWindow( "ScrollBar", NULL,
				SBS_HORZ | SBS_BOTTOMALIGN | WS_CHILD | WS_VISIBLE | mswGetBaseStyle(parent), bl->x, bl->y,
				width, CW_USEDEFAULT,
				((wControl_p)parent)->hWnd, (HMENU)(UINT_PTR)index, mswHInst, NULL );
		if (bl->hScrollWnd == NULL)
			mswFail("CreateWindow(LISTSCROLL)");
		SetScrollRange( bl->hScrollWnd, SB_CTL, 0, 100, TRUE );
		GetWindowRect( bl->hScrollWnd, &rect );
		bl->scrollH = rect.bottom - rect.top+2;
	}
	return bl;
}


wList_p wDropListCreate(
		wWin_p	parent,
		wWinPix_t	x,
		wWinPix_t	y,
		const char	* helpStr,
		const char	* labelStr,
		long	option,
		long	number,
		wWinPix_t	width,
		long	*valueP,
		wListCallBack_p action,
		void	*data )
{
	long bs;

	if ( (option&BL_EDITABLE) != 0 )
		bs = CBS_DROPDOWN;
	else
		bs = CBS_DROPDOWNLIST;
	bs |= WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS;
	if (option & BL_SORT)
		bs |= CBS_SORT;
	return listCreate( B_DROPLIST, "COMBOBOX", bs, parent, x, y, helpStr,
						labelStr, option, number, width, valueP, action, data, TRUE, NULL );
}

wList_p wComboListCreate(
		wWin_p	parent,
		wWinPix_t	x,
		wWinPix_t	y,
		const char	* helpStr,
		const char	* labelStr,
		long	option,
		long	number,
		wWinPix_t	width,
		long	*valueP,
		wListCallBack_p action,
		void	*data )
{
	long bs;

	bs = CBS_SIMPLE | WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS;
	if (option & BL_SORT)
		bs |= CBS_SORT;
	return listCreate( B_COMBOLIST, "COMBOBOX", bs, parent, x, y, helpStr,
						labelStr, option, number, width, valueP, action, data, FALSE, NULL );
}
