//
//                 mxToolKit (c) 1999 by Mete Ciragan
//
// file:           mx.cpp
// implementation: Win32 API
// last modified:  Apr 18 1999, Mete Ciragan
// copyright:      The programs and associated files contained in this
//                 distribution were developed by Mete Ciragan. The programs
//                 are not in the public domain, but they are freely
//                 distributable without licensing fees. These programs are
//                 provided without guarantee or warrantee expressed or
//                 implied.
//
#include "mxtk/mx.h"
#include "mxtk/mxWindow.h"
#include "mxtk/mxEvent.h"
#include "mxtk/mxLinkedList.h"
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tier1/utlvector.h"


#define WM_MOUSEWHEEL                   0x020A

//#include <ostream.h"



void mxTab_resizeChild (HWND hwnd);



mxWindow *g_mainWindow = 0;
static mxLinkedList *g_widgetList = 0;
static mxWindow *g_idleWindow = 0;

static MSG msg;
static HWND g_hwndToolTipControl = 0;
static bool isClosing = false;
static HACCEL g_hAcceleratorTable = NULL;

void mx::createAccleratorTable( int numentries, Accel_t *entries )
{
	CUtlVector< ACCEL > accelentries;

	for ( int i = 0; i < numentries; ++i )
	{
		const Accel_t& entry = entries[ i ];

		ACCEL add;
		add.key = entry.key;
		add.cmd = entry.command;
		add.fVirt = 0;
		if ( entry.flags & ACCEL_ALT )
		{
			add.fVirt |= FALT;
		}
		if ( entry.flags & ACCEL_CONTROL )
		{
			add.fVirt |= FCONTROL;
		}
		if ( entry.flags & ACCEL_SHIFT )
		{
			add.fVirt |= FSHIFT;
		}
		if ( entry.flags & ACCEL_VIRTKEY )
		{
			add.fVirt |= FVIRTKEY;
		}

		accelentries.AddToTail( add );
	}

	g_hAcceleratorTable = ::CreateAcceleratorTable( accelentries.Base(), accelentries.Count() );
}



void
mx_addWidget (mxWidget *widget)
{
	if (g_widgetList)
		g_widgetList->add ((void *) widget);
}



void
mx_removeWidget (mxWidget *widget)
{
	if (g_widgetList)
		g_widgetList->remove ((void *) widget);
}



HWND
mx_CreateToolTipControl ()
{
	if (!g_hwndToolTipControl)
	{
		if (g_mainWindow)
		{
			g_hwndToolTipControl = CreateWindowEx (0, TOOLTIPS_CLASS, "", WS_POPUP | WS_EX_TOPMOST,
				0, 0, 0, 0, (HWND) g_mainWindow->getHandle (),
				(HMENU) NULL, (HINSTANCE) GetModuleHandle (NULL), NULL);
		}
	}

	return g_hwndToolTipControl;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *window - 
//			*event - 
// Output : static void
//-----------------------------------------------------------------------------
static void RecursiveHandleEvent( mxWindow *window, mxEvent *event )
{
	while ( window )
	{
		if ( window->handleEvent ( event ) )
			break;

		window = window->getParent();
	}
}

char const *translatecode( int code )
{
	switch ( code )
	{
	case NM_CLICK:
		return "NM_CLICK";
	case NM_CUSTOMDRAW:
		return "NM_CUSTOMDRAW";
	case NM_DBLCLK:
		return "NM_DBLCLK";
	case NM_KILLFOCUS:
		return "NM_KILLFOCUS";
	case NM_RCLICK:
		return "NM_RCLICK";
	case NM_RETURN:
		return "NM_RETURN";
	case NM_SETCURSOR:
		return "NM_SETCURSOR";
	case NM_SETFOCUS:
		return "NM_SETFOCUS"; 
	case TVN_BEGINDRAG:
		return "TVN_BEGINDRAG";
	case TVN_BEGINLABELEDIT:
		return "TVN_BEGINLABELEDIT";
	case TVN_BEGINRDRAG:
		return "TVN_BEGINRDRAG";
	case TVN_DELETEITEM:
		return "TVN_DELETEITEM";
	case TVN_ENDLABELEDIT:
		return "TVN_ENDLABELEDIT";
	case TVN_GETDISPINFO:
		return "TVN_GETDISPINFO";
	case TVN_GETINFOTIP:
		return "TVN_GETINFOTIP";
	case TVN_ITEMEXPANDED:
		return "TVN_ITEMEXPANDED";
	case TVN_ITEMEXPANDING:
		return "TVN_ITEMEXPANDING";
	case TVN_KEYDOWN :
		return "TVN_KEYDOWN";
	case TVN_SELCHANGED :
		return "TVN_SELCHANGED";
	case TVN_SELCHANGING :
		return "TVN_SELCHANGING";
	case TVN_SETDISPINFO :
		return "TVN_SETDISPINFO";
	case TVN_SINGLEEXPAND:
		return "TVN_SINGLEEXPAND";
	}

	return "Unknown!!!";
}
static LRESULT CALLBACK WndProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
	static bool bDragging = FALSE;

	switch (uMessage)
	{
	case WM_SETFOCUS:
	case WM_KILLFOCUS:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if ( window )
		{
			mxEvent event;
			event.event = mxEvent::Focus;
			event.widget = NULL;
			event.action = (uMessage == WM_SETFOCUS);
			RecursiveHandleEvent( window, &event );
			return 0;
		}
	}
	break;

	case WM_ACTIVATE:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if ( window )
		{
			mxEvent event;
			event.event = mxEvent::Activate;
			event.widget = NULL;
			event.action = (LOWORD( wParam ) != WA_INACTIVE);
			RecursiveHandleEvent( window, &event );
			return 0;
		}
	}
	break;

	case WM_COMMAND:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (LOWORD (wParam) > 0 && window)
		{
			WORD wNotifyCode = (WORD) HIWORD (wParam);
			HWND hwndCtrl = (HWND) lParam;
			mxEvent event;

			CHAR className[128];
			GetClassName (hwndCtrl, className, 128);
			if (!strcmpi (className, "edit"))
			{
				if (wNotifyCode != EN_CHANGE)
					break;
			}
			else if (!strcmpi (className, "combobox"))
			{
				if (wNotifyCode != CBN_SELCHANGE)
					break;
			}
			else if (!strcmpi (className, "listbox"))
			{
				if (wNotifyCode != LBN_SELCHANGE)
					break;
			}

			event.event = mxEvent::Action;
			event.widget = (mxWidget *) GetWindowLong ((HWND) lParam, GWL_USERDATA);
			event.action = (int) LOWORD (wParam);
			RecursiveHandleEvent( window, &event );
		}
	}
	break;

	case WM_NOTIFY:
	{
		if (isClosing)
			break;

		NMHDR *nmhdr = (NMHDR *) lParam;
		mxEvent event;

#if 0
		//if ( nmhdr->idFrom > 0 ) 
		{
			mxWidget *temp = (mxWidget *) GetWindowLong (nmhdr->hwndFrom, GWL_USERDATA);
			if ( temp && temp->getType() == MX_TREEVIEW )
			{
				NMTREEVIEW *nmt = ( NMTREEVIEW * )nmhdr;

				HTREEITEM hItem = TreeView_GetSelection (nmhdr->hwndFrom);

				char sz[ 256 ];
				sprintf( sz, "tree view receiving notify %i : %s action %i old %p new %p selection %p\n", nmhdr->code, translatecode( nmhdr->code ),
					nmt->action, nmt->itemOld, nmt->itemNew, hItem );
				
				OutputDebugString( sz );
			}
		}
#endif

		if (nmhdr->code == TVN_SELCHANGED)
		{
			if (nmhdr->idFrom > 0)
			{
				mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
				event.event = mxEvent::Action;
				event.widget = (mxWidget *) GetWindowLong (nmhdr->hwndFrom, GWL_USERDATA);
				event.action = (int) nmhdr->idFrom;

				RECT rc;
				HTREEITEM hItem = TreeView_GetSelection (nmhdr->hwndFrom);
				TreeView_GetItemRect (nmhdr->hwndFrom, hItem, &rc, TRUE);
				event.x = (int) rc.left;
				event.y = (int) rc.bottom;
				RecursiveHandleEvent( window, &event );

			}
		}
		else if (nmhdr->code == LVN_ITEMCHANGED)
		{
			if (nmhdr->idFrom > 0)
			{
				mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
				event.event = mxEvent::Action;
				event.widget = (mxWidget *) GetWindowLong (nmhdr->hwndFrom, GWL_USERDATA);
				event.action = (int) nmhdr->idFrom;

				RecursiveHandleEvent( window, &event );
			}
		}
		else if (nmhdr->code == NM_RCLICK)
		{
			if (nmhdr->idFrom > 0)
			{
				mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
				event.event = mxEvent::Action;
				event.widget = (mxWidget *) GetWindowLong (nmhdr->hwndFrom, GWL_USERDATA);
				event.action = (int) nmhdr->idFrom;
				event.flags = mxEvent::RightClicked;

				if ( event.widget )
				{
					if ( event.widget->getType () == MX_TREEVIEW )
					{
						RECT rc;
						HTREEITEM hItem = TreeView_GetSelection (nmhdr->hwndFrom);
						TreeView_GetItemRect (nmhdr->hwndFrom, hItem, &rc, TRUE);
						event.x = (int) rc.left;
						event.y = (int) rc.bottom;
					}
				}
				RecursiveHandleEvent( window, &event );
			}
		}
		else if (nmhdr->code == NM_DBLCLK)
		{
			if (nmhdr->idFrom > 0)
			{
				mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
				event.event = mxEvent::Action;
				event.widget = (mxWidget *) GetWindowLong (nmhdr->hwndFrom, GWL_USERDATA);
				event.action = (int) nmhdr->idFrom;
				event.flags = mxEvent::DoubleClicked;

				if (event.widget )
				{
					if ( event.widget->getType () == MX_TREEVIEW )
					{
						RECT rc;
						HTREEITEM hItem = TreeView_GetSelection (nmhdr->hwndFrom);
						TreeView_GetItemRect (nmhdr->hwndFrom, hItem, &rc, TRUE);
						event.x = (int) rc.left;
						event.y = (int) rc.bottom;
					}
				}

				RecursiveHandleEvent( window, &event );
				return TRUE;
			}
		}
		else if (nmhdr->code == TCN_SELCHANGING)
		{
			TC_ITEM ti;

			int index = TabCtrl_GetCurSel (nmhdr->hwndFrom);
			if (index >= 0)
			{
				ti.mask = TCIF_PARAM;
				TabCtrl_GetItem (nmhdr->hwndFrom, index, &ti);
				mxWindow *window = (mxWindow *) ti.lParam;
				if (window)
					window->setVisible (false);
			}
		}
		else if (nmhdr->code == TCN_SELCHANGE)
		{
			mxTab_resizeChild (nmhdr->hwndFrom);
			if (nmhdr->idFrom > 0)
			{
				mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
				event.event = mxEvent::Action;
				event.widget = (mxWidget *) GetWindowLong (nmhdr->hwndFrom, GWL_USERDATA);
				event.action = (int) nmhdr->idFrom;
				RecursiveHandleEvent( window, &event );
			}
		}
	}
	break;

	case WM_SIZE:
	{
		mxEvent event;

		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			event.event = mxEvent::Size;
			event.width = (int) LOWORD (lParam);
			event.height = (int) HIWORD (lParam);
			window->handleEvent (&event);
		}
	}
	break;
	case WM_WINDOWPOSCHANGED:
	{
		mxEvent event;

		
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			event.event = mxEvent::PosChanged;

			WINDOWPOS *wp = ( WINDOWPOS * )lParam;

			event.x			= wp->x;
			event.y			= wp->y;
			event.width		= wp->cx;
			event.height	= wp->cy;

			window->handleEvent (&event);
		}
	}
	break;

	case WM_ERASEBKGND:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			if (window->getType () == MX_GLWINDOW)
				return 0;
			if (window->getType () == MX_MATSYSWINDOW)
				return 0;

			if ( !isClosing && !window->PaintBackground() )
			{
				return 0;
			}
		}
	}
	break;

	case WM_HSCROLL:
	case WM_VSCROLL:
	{
		mxWidget *widget = (mxWidget *) GetWindowLong ((HWND) lParam, GWL_USERDATA);
		if (!widget)
		{
			break;
		}

		if (widget->getType() != MX_SCROLLBAR && widget->getType() != MX_SLIDER)
		{
			break;
		}


		switch (LOWORD (wParam))
		{
		case TB_LINEUP: // 	SB_LINEUP SB_LINELEFT
			break;
		case TB_LINEDOWN: // SB_LINEDOWN SB_LINERIGHT
			break;
		case TB_PAGEUP: // SB_PAGEUP SB_PAGELEFT
			break;
		case TB_PAGEDOWN: // SB_PAGEDOWN SB_PAGERIGHT
			break;
		case TB_THUMBPOSITION:  // SB_THUMBPOSITION
			break;
		case TB_THUMBTRACK: // SB_THUMBTRACK
			break;
		case TB_TOP: // SB_TOP SB_LEFT
			break;
		case TB_BOTTOM: // SB_BOTTOM SB_RIGHT
			break;
		case TB_ENDTRACK: // SB_ENDSCROLL
			break;
		default:
			break;
		}

		switch (LOWORD (wParam))
		{
		case TB_LINEUP: // 	SB_LINEUP SB_LINELEFT
		case TB_LINEDOWN: // SB_LINEDOWN SB_LINERIGHT
		case TB_PAGEUP: // SB_PAGEUP SB_PAGELEFT
		case TB_PAGEDOWN: // SB_PAGEDOWN SB_PAGERIGHT
		case TB_THUMBPOSITION:  // SB_THUMBPOSITION
		case TB_THUMBTRACK: // SB_THUMBTRACK
		case TB_TOP: // SB_TOP SB_LEFT
		case TB_BOTTOM: // SB_BOTTOM SB_RIGHT
		case TB_ENDTRACK: // SB_ENDSCROLL
		{
			mxEvent event;

			event.event = mxEvent::Action;
			event.widget = widget;
			event.action = widget->getId ();
			event.modifiers = LOWORD (wParam);
			event.height = HIWORD( wParam );
			mxWindow *window = widget->getParent ();

			if ( event.action > 0 )
			{
				RecursiveHandleEvent( window, &event );
			}
		}
		break;
		}
	}
	break;

	case WM_PAINT:
	{
		if ( !isClosing )
		{
			mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
			if (window)
			{
				window->redraw ();
			}
		}
	}
	break;

	case WM_PARENTNOTIFY:
		{
			mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
			if (window)
			{
				if ( wParam == WM_LBUTTONDOWN ||
					 wParam == WM_MBUTTONDOWN ||
					 wParam == WM_RBUTTONDOWN /*||
					 wParam & WM_XBUTTONDOWN*/ )
				{
					mxEvent event;
					event.event = mxEvent::ParentNotify;
					event.x = (short)LOWORD (lParam);
					event.y = (short)HIWORD (lParam);
					event.buttons = 0;
					event.modifiers = 0;

					if ( wParam == WM_LBUTTONDOWN )
						event.buttons |= mxEvent::MouseLeftButton;

					if ( wParam == WM_RBUTTONDOWN )
						event.buttons |= mxEvent::MouseRightButton;

					if ( wParam == WM_MBUTTONDOWN )
						event.buttons |= mxEvent::MouseMiddleButton;

					window->handleEvent (&event);
					RecursiveHandleEvent( window, &event );
					return 0;
				}
			}
		}
		break;

	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	{
		bDragging = TRUE;
		SetCapture (hwnd);
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);

		if (window)
		{
			mxEvent event;
			event.event = mxEvent::MouseDown;
			event.x = (short)LOWORD (lParam);
			event.y = (short)HIWORD (lParam);
			event.buttons = 0;
			event.modifiers = 0;

			if (uMessage == WM_MBUTTONDOWN)
				event.buttons |= mxEvent::MouseMiddleButton;
			else if (uMessage == WM_RBUTTONDOWN)
				event.buttons |= mxEvent::MouseRightButton;
			else
				event.buttons |= mxEvent::MouseLeftButton;

			if (wParam & MK_LBUTTON)
				event.buttons |= mxEvent::MouseLeftButton;

			if (wParam & MK_RBUTTON)
				event.buttons |= mxEvent::MouseRightButton;

			if (wParam & MK_MBUTTON)
				event.buttons |= mxEvent::MouseMiddleButton;

			if (wParam & MK_CONTROL)
				event.modifiers |= mxEvent::KeyCtrl;

			if (wParam & MK_SHIFT)
				event.modifiers |= mxEvent::KeyShift;

			window->handleEvent (&event);
		}
	}
	break;

	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;
			event.event = mxEvent::MouseUp;
			event.x = (short) LOWORD (lParam);
			event.y = (short) HIWORD (lParam);
			event.buttons = 0;
			event.modifiers = 0;

			if (uMessage == WM_MBUTTONUP)
				event.buttons |= mxEvent::MouseMiddleButton;
			else if (uMessage == WM_RBUTTONUP)
				event.buttons |= mxEvent::MouseRightButton;
			else
				event.buttons |= mxEvent::MouseLeftButton;

			if (wParam & MK_LBUTTON)
				event.buttons |= mxEvent::MouseLeftButton;

			if (wParam & MK_RBUTTON)
				event.buttons |= mxEvent::MouseRightButton;

			if (wParam & MK_MBUTTON)
				event.buttons |= mxEvent::MouseMiddleButton;

			if (wParam & MK_CONTROL)
				event.modifiers |= mxEvent::KeyCtrl;

			if (wParam & MK_SHIFT)
				event.modifiers |= mxEvent::KeyShift;

			window->handleEvent (&event);
		}
		bDragging = FALSE;
		ReleaseCapture ();
	}
	break;

	case WM_MOUSEMOVE:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;

			if (bDragging)
				event.event = mxEvent::MouseDrag;
			else
				event.event = mxEvent::MouseMove;

			event.x = (short) LOWORD (lParam);
			event.y = (short) HIWORD (lParam);
			event.buttons = 0;
			event.modifiers = 0;

			if (wParam & MK_LBUTTON)
				event.buttons |= mxEvent::MouseLeftButton;

			if (wParam & MK_RBUTTON)
				event.buttons |= mxEvent::MouseRightButton;

			if (wParam & MK_MBUTTON)
				event.buttons |= mxEvent::MouseMiddleButton;

			if (wParam & MK_CONTROL)
				event.modifiers |= mxEvent::KeyCtrl;

			if (wParam & MK_SHIFT)
				event.modifiers |= mxEvent::KeyShift;

			window->handleEvent (&event);
		}
	}
	break;
	case WM_NCLBUTTONDOWN:
	case WM_NCMBUTTONDOWN:
	case WM_NCRBUTTONDOWN:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);

		if (window)
		{
			mxEvent event;
			event.event = mxEvent::NCMouseDown;
			event.x = (short) LOWORD (lParam);
			event.y = (short) HIWORD (lParam);
			event.buttons = 0;
			event.modifiers = 0;

			if (uMessage == WM_NCMBUTTONDOWN)
				event.buttons |= mxEvent::MouseMiddleButton;
			else if (uMessage == WM_NCRBUTTONDOWN)
				event.buttons |= mxEvent::MouseRightButton;
			else
				event.buttons |= mxEvent::MouseLeftButton;

			window->handleEvent (&event);
		}
	}
	break;

	case WM_NCLBUTTONUP:
	case WM_NCMBUTTONUP:
	case WM_NCRBUTTONUP:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;
			event.event = mxEvent::NCMouseUp;
			event.x = (short) LOWORD (lParam);
			event.y = (short) HIWORD (lParam);
			event.buttons = 0;
			event.modifiers = 0;

			if (uMessage == WM_NCMBUTTONUP)
				event.buttons |= mxEvent::MouseMiddleButton;
			else if (uMessage == WM_NCRBUTTONUP)
				event.buttons |= mxEvent::MouseRightButton;
			else
				event.buttons |= mxEvent::MouseLeftButton;

			window->handleEvent (&event);
		}
	}
	break;

	case WM_NCMOUSEMOVE:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;

			event.event = mxEvent::NCMouseMove;

			event.x = (short) LOWORD (lParam);
			event.y = (short) HIWORD (lParam);
			event.buttons = 0;
			event.modifiers = 0;

			window->handleEvent (&event);
		}
	}
	break;

	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;
			event.event = mxEvent::KeyDown;
			event.key = (int) wParam;
			if ( window->handleEvent (&event) )
				return 0;
		}
	}
	break;

	case WM_CHAR:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;
			event.event = mxEvent::Char;
			event.key = (int) wParam;
			if ( window->handleEvent (&event) )
				return 0;
		}
	}
	break;

	case WM_SYSCHAR:
		return 0;
		break;

	case WM_KEYUP:
	case WM_SYSKEYUP:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;
			event.event = mxEvent::KeyUp;
			event.key = (int) wParam;
			if ( window->handleEvent (&event) )
				return 0;
		}
	}
	break;

	case WM_MOUSEWHEEL:
	{
		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;
			memset( &event, 0, sizeof( event ) );
			event.event = mxEvent::MouseWheeled;
			event.x = (short) LOWORD (lParam);
			event.y = (short) HIWORD (lParam);

			if (wParam & MK_LBUTTON)
				event.buttons |= mxEvent::MouseLeftButton;

			if (wParam & MK_RBUTTON)
				event.buttons |= mxEvent::MouseRightButton;

			if (wParam & MK_MBUTTON)
				event.buttons |= mxEvent::MouseMiddleButton;

			if (wParam & MK_CONTROL)
				event.modifiers |= mxEvent::KeyCtrl;

			if (wParam & MK_SHIFT)
				event.modifiers |= mxEvent::KeyShift;

			event.height = (short)HIWORD( wParam );;
			RecursiveHandleEvent( window, &event );
		}
	}
	break;
	case WM_TIMER:
	{
		if (isClosing)
			break;

		mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
		if (window)
		{
			mxEvent event;
			event.event = mxEvent::Timer;
			window->handleEvent (&event);
		}
	}
	break;

	case WM_CLOSE:
		if (g_mainWindow)
		{
			if ((void *) hwnd == g_mainWindow->getHandle ())
			{
				mx::quit ();
			}
			else
			{
				ShowWindow (hwnd, SW_HIDE);

				mxWindow *window = (mxWindow *) GetWindowLong (hwnd, GWL_USERDATA);
				if (window)
				{
					mxEvent event;
					event.event = mxEvent::Close;
					window->handleEvent( &event );
				}
			}
		}
		//else // shouldn't happen
			//DestroyWindow (hwnd);
		return 0;
/*
	case WM_DESTROY:
		if (g_mainWindow)
		{
			if ((void *) hwnd == g_mainWindow->getHandle ())
				mx::quit ();
		}
		break;
*/
	}

	return DefWindowProc (hwnd, uMessage, wParam, lParam);
}



int
mx::init(int argc, char **argv)
{
	WNDCLASS wc;
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = (HINSTANCE) GetModuleHandle (NULL);
    wc.hIcon = LoadIcon (wc.hInstance, "MX_ICON");
	wc.hCursor = LoadCursor (NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH) COLOR_WINDOW;
	wc.lpszMenuName = NULL;
	wc.lpszClassName = "mx_class";

	if (!wc.hIcon)
		wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);

	if (!RegisterClass (&wc))
		return 0;

	InitCommonControls ();

	g_widgetList = new mxLinkedList ();

	isClosing = false;

	return 1;
}



int
mx::run()
{
	int messagecount = 0;

	while (1)
	{
		bool doframe = false;
		if ( PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE) || !g_idleWindow )
		{
			if (!GetMessage (&msg, NULL, 0, 0))
			{
				doframe = false;
				break;
			}

			if ( !g_hAcceleratorTable ||
				!TranslateAccelerator( (HWND)g_mainWindow->getHandle (), g_hAcceleratorTable, &msg )) 
			{
				TranslateMessage( &msg );
				DispatchMessage( &msg );
			}
			messagecount++;

			if ( messagecount > 10 )
			{
				messagecount = 0;
				doframe = true;
			}
		}
		else if (g_idleWindow)
		{
			doframe = true;
			messagecount = 0;
		}

		if ( doframe && g_idleWindow )
		{
			mxEvent event;
			event.event = mxEvent::Idle;
			g_idleWindow->handleEvent (&event);
		}
	}

	return msg.wParam;
}



int
mx::check ()
{
	if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
	{
		if (GetMessage (&msg, NULL, 0, 0))
		{
			TranslateMessage (&msg);
			DispatchMessage (&msg);
		}

		return 1;
	}

	return 0;
}



void
mx::quit ()
{
	isClosing = true;

	mxWindow *mainwnd = getMainWindow();
	if ( mainwnd )
	{
		if ( !mainwnd->Closing() )
		{
			isClosing = false;
			return;
		}
	}

	if (g_widgetList)
	{
		// remove from back to front
		mxListNode *node = g_widgetList->getLast ();

		// Pass 1, see if anyone objects to closing
		while (node)
		{
			mxWidget *widget = (mxWidget *) g_widgetList->getData (node);
			node = g_widgetList->getPrev (node);

			bool canclose = true;
			if ( widget )
			{
				if ( !widget->CanClose() )
				{
					canclose = false;
				}
			}

			if ( !canclose )
			{
				isClosing = false;
				return;
			}
		}

		node = g_widgetList->getLast ();

		// Pass 2, call OnDelete to allow final cleanup
		while (node)
		{
			mxWidget *widget = (mxWidget *) g_widgetList->getData (node);
			node = g_widgetList->getPrev (node);

			if ( widget )
			{
				widget->OnDelete();
			}
		}

		node = g_widgetList->getLast ();

		// Pass 3, delete stuff
		while (node)
		{
			mxWidget *widget = (mxWidget *) g_widgetList->getData (node);
			node = g_widgetList->getPrev (node);

			// remove it!
			if ( widget )
			{
				delete widget;
			}
		}

		delete g_widgetList;
	}

	if (g_hwndToolTipControl)
		DestroyWindow (g_hwndToolTipControl);

	if ( g_hAcceleratorTable )
	{
		DestroyAcceleratorTable( g_hAcceleratorTable );
		g_hAcceleratorTable = 0;
	}

	PostQuitMessage (0);
	UnregisterClass ("mx_class", (HINSTANCE) GetModuleHandle (NULL));
}



int
mx::setDisplayMode (int w, int h, int bpp)
{
	DEVMODE dm;

	dm.dmSize = sizeof (DEVMODE);
	dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
	dm.dmBitsPerPel = bpp;
	dm.dmPelsWidth = w;
	dm.dmPelsHeight = h;

	if (w == 0 || h == 0 || bpp == 0)
		ChangeDisplaySettings (0, 0);
	else
		ChangeDisplaySettings (&dm, CDS_FULLSCREEN);

	return 0;
}



void
mx::setIdleWindow (mxWindow *window)
{
	g_idleWindow = window;
}



int
mx::getDisplayWidth ()
{
	return (int) GetSystemMetrics (SM_CXSCREEN);
}



int
mx::getDisplayHeight ()
{
	return (int) GetSystemMetrics (SM_CYSCREEN);
}



mxWindow*
mx::getMainWindow ()
{
	return g_mainWindow;
}



const char *
mx::getApplicationPath ()
{
	static char path[256];
	GetModuleFileName (0, path, 256);
	char *ptr = strrchr (path, '\\');
	if (ptr)
		*ptr = '\0';

	return path;
}



int
mx::getTickCount ()
{
	return (int) GetTickCount ();
}