// -*-C++-*- 

/*  src/platform/Wq.cpp  */

/*
 * Author: Philogelos A. <Philogelos@yahoo.com>
 * Maintainer: Philogelos A.
 * Keywords: C++, library, containers
 *
 * Copyright (C) 1999 Philogelos A.
 *
 * This file is part of Quercus Robusta.
 *
 * Quercus Robusta is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this software; see the file COPYING.LIB.  If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */



/* $Id$ */
#include "platform/Wq.hpp"

#if defined( USE_WQ )

#if !defined(_INLINE)
static char cvsid[] = "@(#)$Id$";
static char debugFileId[] = __FILE__;
#endif

#include "Debug.hpp"
#include "exceptions/Errno.hpp"
#include "PGuard.hpp"
#include "OGuard.hpp"
#include "platform/threads/MGuard.hpp"
#include "containers/lists/List.hpp"
#include "containers/Pair.hpp"
#include "DataWrapper.hpp"

#define GUI_WRAP( call, testValue, methodName ) do \
	if( ( ( call ) == testValue ) && \
		( GetLastError() != ERROR_SUCCESS ) && \
		!std::uncaught_exception() ) \
	{ \
		throw new Errno( #call " failed in " #methodName " (" __FILE__ ")", null, GetLastError() ); \
	} while( false )

#define CREATE_OBJECT( className, call, methodName ) \
	HANDLE handle; \
	GUI_WRAP( ( handle = ( call ) ), NULL, methodName ); \
	return new className( handle );





Aq::Painter *Wq::MSWindowSystem::createPainter( Aq::GraphicContext *aContext, 
												Aq::Drawable *aDrawable ) const
{
  return new Wq::WPainter
	( TCAST( aContext, Wq::GC ), TCAST( aDrawable, Wq::Drawable ) );
}

Wq::MSWindowSystem *Wq::MSWindowSystem::msWindowSystem = 
TCAST( ( new Wq::MSWindowSystem() ) -> dontManage(), 
	   Wq::MSWindowSystem );


Wq::Drawable::Drawable() :
  Drawable( MSWindowSystem::msWindowSystem )
{}

Wq::Window::Window( HANDLE aHandle ) : Win32Object( aHandle ),
  Window( MSWindowSystem::msWindowSystem )
{
  test_( IsWindow( getHandle() ) );
  eventLoop = NULL;
  events = NULL;
  FINIT( triggers, new List() );
}

Wq::Window::~Window()
{
  test_( terminateMessage != 0 );
  PostMessage( getHandle(), terminateMessage, 0, 0 );
  FDONE( triggers );
  FCDONE( cursor );

  if( eventLoop != NULL )
	{
	  eventLoop -> join();
	  FDONE( eventLoop );
	}
  FCDONE( events );
  setHandle( NULL );
}

String Wq::Window::getDefaultClassName()
{
  static char *defClassName = "Wq::Window::defClassName";

  if( !defaultClassRegistered )
	{
	  WNDCLASS classInfo;

		
	  classInfo.style = CS_DBLCLKS | CS_GLOBALCLASS | CS_OWNDC; 
	  classInfo.lpfnWndProc = &Wq::Window::defWinProc; 
	  classInfo.cbClsExtra = 0xff; 
	  classInfo.cbWndExtra = 0xff; 
	  classInfo.hInstance = GetModuleHandle( NULL ); 
	  classInfo.hIcon = NULL; 
	  classInfo.hCursor = NULL; 
	  classInfo.hbrBackground = ( void * ) COLOR_WINDOW; 
	  classInfo.lpszMenuName = NULL; 
	  classInfo.lpszClassName = defClassName; 

	  GUI_WRAP( RegisterClass( &classInfo ), 0, 
				Wq::Window::getDefaultClassName() );
	  defaultClassRegistered = true;
	}
  if( terminateMessage == 0 )
	{
	  GUI_WRAP( terminateMessage = RegisterWindowMessage
				( "Wq::terminateMessage" ), 0, 
				Wq::Window::getDefaultClassName() );
	}
  if( wakeUpMessage == 0 )
	{
	  GUI_WRAP( wakeUpMessage = RegisterWindowMessage
				( "Wq::wakeUpMessage" ), 0, 
				Wq::Window::getDefaultClassName() );
	}
  return defClassName;
}

void Wq::Window::show() const
{
  ShowWindow( getHandle(), SW_RESTORE );
  GUI_WRAP( UpdateWindow( getHandle() ), 0, Wq::Window::show() );
	GUI_WRAP( SetActiveWindow( getHandle() ), 0, Wq::Window::show() );
}

Wq::DC *Wq::Window::getDC() const
{
  CREATE_OBJECT( Wq::DC, GetDC( getHandle() ), Wq::Window::getDC() );
}

void Wq::Window::ungetDC( Wq::DC *aDC ) const
{
  preC_( aDC != NULL );

  OGuard _dc( aDC, this );
  ReleaseDC( getHandle(), aDC -> getHandle() );
}

void Wq::Window::setCursor( Wq::Cursor *aCursor )
{
  preC_( aCursor != NULL );

  OGuard _cursor( aCursor, this );
  OGuard _self( this, this );

  FMOVE( cursor, aCursor );
  PostMessage( getHandle(), wakeUpMessage, 0, ( LPARAM ) this );
}

void Wq::Window::showCursor( boolean toShow )
{
  ShowCursor( toShow );
}

Aq::EventSource *Wq::Window::getEventSource() const
{
  return getWEventSource();
}

Wq::WindowEventSource *Wq::Window::getWEventSource() const
{
  return events;
}

static Mutex *winCreateLock = NULL;
static Semaphore *winCreateSema = NULL;
static Wq::Window *freshWindow = NULL;

void Wq::initClass()
{
  if( winCreateLock == NULL )
	{
	  winCreateLock = THR_SYSTEM -> mutex();
	  TCAST( winCreateLock, Object ) -> dontManage();
	}

  if( winCreateSema == NULL )
	{
	  winCreateSema = THR_SYSTEM -> semaphore( 0 );
	  TCAST( winCreateSema, Object ) -> dontManage();
	}
}

Wq::Window *Wq::Window::create()
{
  Wq::Window *result;

  initClass();
  MGuard _lock( winCreateLock, winCreateLock );

  test_( freshWindow == NULL );
  THR_FACTORY -> createFromFunc( &winLoop, NULL );
  winCreateSema -> dec();
  test_( freshWindow != NULL );
  result = freshWindow;
  freshWindow = NULL;
  return result;
}


LRESULT CALLBACK Wq::Window::defWinProc
( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  Wq::Window *win;

  SetLastError( 0 );
  GUI_WRAP( win = ( Wq::Window * ) GetWindowLong( hwnd, 0 ), 
			0, Wq::Window::defWinProc );

  if( uMsg == terminateMessage )
	{
	  throw new Wq::CloseWindowException();
	}
  if( uMsg == wakeUpMessage )
	{
	  test_( win != NULL );
	  test_( win == ( Wq::Window * ) lParam );
	  test_( wParam == NULL );
	  test_( win -> getHandle() == hwnd );
	  while( ! win -> triggers -> isEmpty() )
		{
		  Pair *trigger;

		  trigger = TCAST( win -> triggers -> behead(), Pair );
		  OGuard _trigger( trigger, win );
		  ( ( void ( * )( void * ) ) 
			( TCAST( trigger -> getAt( 1 ), DataWrapper ) 
			  -> getValue() ) )
			( TCAST( trigger -> getAt( 2 ), DataWrapper ) 
			  -> getValue() );
		}
	}
  if( win != NULL )
	{
	  if( win -> cursor != NULL )
		{
		  SetCursor( win -> cursor -> getHandle() );
		}
	}
  return DefWindowProc( hwnd, uMsg, wParam, lParam );
}

boolean Wq::Window::defaultClassRegistered = false;
UINT  Wq::Window::terminateMessage = 0;
UINT  Wq::Window::wakeUpMessage = 0;

void *Wq::Window::winLoop( void * )
{
  return( ( new Wq::WindowEventSource() ) -> run() );
}


boolean Wq::Window::isCursorWithin() const
{
  POINT cursorPosition;
  RECT winRect;

  GUI_WRAP( GetCursorPos( &cursorPosition ), 0, 
			Wq::Window::isCursorWithin() );
  GUI_WRAP( GetClientRect( getHandle(), &winRect ), 0,
			Wq::Window::isCursorWithin() );
  return( PtInRect( &winRect, cursorPosition ) );
}

void Wq::Window::move( Aq::CoordX aX, Aq::CoordY aY )
{
  GUI_WRAP( SetWindowPos( 
						 getHandle(), 
						 HWND_TOP , 
						 aX, aY,
						 0, 0,
						 SWP_NOSIZE | SWP_NOZORDER ), 0, Wq::Window::move() );
}

void Wq::Window::setSize( Aq::Size aWidth, 
						  Aq::Size aHeight )
{
	NONCLIENTMETRICS metrics;

	metrics.cbSize = sizeof metrics;
	GUI_WRAP( SystemParametersInfo( 
		SPI_GETNONCLIENTMETRICS, 
		sizeof metrics, &metrics, 0 ), 0, Wq::Window::setSize );

	test_( metrics.iBorderWidth >= 0 );
	test_( metrics.iCaptionWidth >= 0 );
	test_( metrics.iCaptionHeight >= 0 );

	GUI_WRAP( SetWindowPos( 
						 getHandle(), HWND_TOP, 
						 0, 0,
						 aWidth + 2 * metrics.iBorderWidth, 
						 aHeight + 
							2 * metrics.iBorderWidth +
							metrics.iCaptionHeight,
						 SWP_NOMOVE | SWP_NOZORDER ), 0, Wq::Window::move() );
}

void Wq::Window::setTitle( String aTitle )
{
  char *title;

  title = aTitle.getChars();
  if( !SetWindowText( getHandle(), title ) )
	{
	  delete []title;
	  throw new Errno( "SetWindowText() in Wq::Window::setTitle()", null,
					   GetLastError() );
	}
  delete []title;
}

void Wq::Window::makeForeground()
{
  GUI_WRAP( SetForegroundWindow( getHandle() ), 0, Wq::Window::makeForeground() );
}

Wq::CloseWindowException::CloseWindowException() :
  Exception( "Window is about to be closed", null )
{}

Wq::CloseWindowException::~CloseWindowException()
{}


Wq::GC::GC( COLORREF aFore, COLORREF aBack, 
			Wq::Pen *aPen, Wq::Brush *aBrush, Wq::Font *aFont ) :
  GraphicContext( MSWindowSystem::msWindowSystem )
{
  preC_( aPen != NULL );
  preC_( aBrush != NULL );
  preC_( aFont != NULL );

  fg = aFore;
  bg = aBack;
  
  FNNINIT( pen, aPen );
  FNNINIT( brush, aBrush );
  FNNINIT( font, aFont );
  FINIT( cleanBrush, Wq::Brush::createSolid( aBack ) );
}

Wq::GC::~GC()
{
  FDONE( cleanBrush );
  FDONE( font );
  FDONE( brush );
  FDONE( pen );
}

void Wq::GC::flushTo( Wq::DC *aDC )
{
  preC_( aDC != NULL );

  aDC -> setPen( pen );
  aDC -> setBrush( brush );
  aDC -> setFont( font );
  aDC -> setTextForeground( fg );
  aDC -> setTextBackground( bg );
}

Wq::WPainter::WPainter( Wq::GC *aGC, Wq::Drawable *aWindow ) :
  Painter( aGC, aWindow )
{
  dc = NULL;
  FNNINIT( arena, aWindow );
}

Wq::WPainter::~WPainter()
{
  FDONE( arena );
  test_( dc == NULL );
}

void Wq::WPainter::init()
{
  OGuard _self( this, this );

  test_( dc == NULL );
  FINIT( dc, getWDrawable() -> getDC() );
  getGC() -> flushTo( dc );
}

void Wq::WPainter::done()
{
  OGuard _self( this, this );

  test_( dc != NULL );
  getWDrawable() -> ungetDC( dc );
  FDONE( dc );
  dc = NULL;
}

void Wq::WPainter::drawLine( Aq::CoordX aStartX, 
							 Aq::CoordY aStartY, 
							 Aq::CoordX anEndX, 
							 Aq::CoordY anEndY ) const
{
  OGuard _self( this, this );

  test_( dc != NULL );
  //  dc -> drawLine( aStartX, aStartY, anEndX, anEndY );
}

void Wq::WPainter::drawPoint( Aq::CoordX aX, Aq::CoordY aY ) const
{
  OGuard _self( this, this );

  test_( dc != NULL );
  //  dc -> drawPoint( aX, aY );
}

void Wq::WPainter::drawCString( Aq::CoordX aX, Aq::CoordY aY, 
								char *aString ) const
{
  OGuard _self( this, this );

  test_( dc != NULL );
  dc -> drawCString( aX, aY, aString );
}

void Wq::WPainter::fillRectangle( Aq::CoordX aX, 
								  Aq::CoordY aY,
								  Aq::Size aWidth, 
								  Aq::Size aHeight ) const
{
  OGuard _self( this, this );

  test_( dc != NULL );
  dc -> fillRectangle( aX, aY, aWidth, aHeight, getGC() -> brush );
}

void Wq::WPainter::clearRectangle( Aq::CoordX aX, 
								   Aq::CoordY aY,
								   Aq::Size aWidth, 
								   Aq::Size aHeight ) const
{
  OGuard _self( this, this );

  test_( dc != NULL );
  dc -> fillRectangle( aX, aY, aWidth, aHeight, getGC() -> cleanBrush );
}

void Wq::WPainter::copyFrom( Aq::CoordX aSourceX, 
							 Aq::CoordY aSourceY, 
							 Aq::Size aWidth, Aq::Size aHeight,
							 Aq::Drawable *aDrawable,
							 Aq::CoordX aDestX, 
							 Aq::CoordY aDestY ) const
{
  OGuard _self( this, this );

  test_( dc != NULL );
  test_( aDrawable -> isCompatible( this ) );

  TCAST( aDrawable, Wq::Drawable ) -> getDC() -> copyTo
    ( aSourceX, aSourceY, aWidth, aHeight, dc, aDestX, aDestY, 
	  SRCCOPY );
}

void Wq::WPainter::setDrawable( Aq::Drawable *aDrawable )
{
  //  Aq::Painter::setDrawable( aDrawable );
  arena = TCAST( aDrawable, Wq::Drawable );
}

void Wq::WPainter::setWDrawable( Wq::Drawable *aDrawable )
{
  preC_( arena != NULL );

  FMOVE( arena, aDrawable );
}

Wq::Drawable *Wq::WPainter::getWDrawable() const
{
  return arena;
}

Wq::GC *Wq::WPainter::getGC() const
{
  return TCAST( getContext(), Wq::GC );
}


Wq::WqEventSource::WqEventSource()
{
  doneWithEvents = false;
}

void  Wq::WqEventSource::processEvent( Aq::AqEvent *anEvent )
{
  preC_( anEvent != NULL );

  WqEvent *event;
  
  event = TCAST( anEvent, Wq::WqEvent );
  OGuard _event( event, this );

  TranslateMessage( &( event -> event ) ); 
  DispatchMessage( &( event -> event ) ); 
}

Wq::WindowEventSource::WindowEventSource()
{
  selectMouseMotion( true );
}

void Wq::WindowEventSource::selectMouseMotion( boolean isSelected )
{
  generateMouseMotionEvents = isSelected;
}

boolean Wq::WindowEventSource::init()
{
  char *className;
  HANDLE handle;

  className = Wq::Window::getDefaultClassName().getChars();
  handle = CreateWindow( 
						className, 
						"NONAME", 
						WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | 
						WS_MINIMIZEBOX |
						WS_OVERLAPPED | WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
						CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
						NULL, NULL, GetModuleHandle( NULL ), NULL );
  delete []className;

  if( handle == NULL )
	{
	  throw new Errno( "CreateWindow() in Wq::Window::winLoop()", null,
					   GetLastError() );
	}

  hostWindow = new Wq::Window( handle );

  SetLastError( 0 );
  GUI_WRAP( SetWindowLong( handle, 0, ( long ) ( void * ) hostWindow ), 
			0, Wq::WindowEventSource::init() );
  hostWindow -> eventLoop = THR_SYSTEM -> getSelf();
  LinkManager::reg( hostWindow, hostWindow -> eventLoop );
	
  hostWindow -> events = this;
  LinkManager::reg( hostWindow, hostWindow -> events );

  freshWindow = hostWindow;
  winCreateSema -> inc();

  return true;
}

void Wq::WindowEventSource::done()
{
  GUI_WRAP( DestroyWindow( hostWindow -> getHandle() ), 0, 
			Wq::WindowEventSource::done() );
}

boolean Wq::WindowEventSource::ignorableEvent( MSG *aMsg ) const
{
  preC_( aMsg != NULL );

  Wq::WqEvent::initTypes();
  test_( Wq::WqEvent::types != NULL );

  return( ( 0 <= aMsg -> message ) && ( aMsg -> message <= WM_PENWINLAST ) &&
		  ( !generateMouseMotionEvents ) && 
		  ( Wq::WqEvent::types[ aMsg -> message ].type == 
			Wq::WqEvent::mouse ) );
}

Aq::AqEvent *Wq::WindowEventSource::getNextTypedElement()
{
  int retCode;
  MSG msg;
  Wq::WqEvent *result;

  try
	{
	  do
		{
		  retCode = 
			GetMessage( &msg, ( HWND ) hostWindow -> getHandle(), 0, 0 );
		  if( ( retCode == -1 ) || ( retCode == 0 ) || 
			  ( msg.message == WM_DESTROY ) || ( msg.message == WM_QUIT ) ||
			  ( msg.message == Wq::Window::terminateMessage ) )
			{
			  doneWithEvents = true;
			  return NULL;
			}
		}
	  while( ignorableEvent( &msg ) );
	  return Wq::WqEvent::create( &msg );
	}
  catch( Wq::CloseWindowException *aMessenger )
	{
	  OGuard _page( aMessenger, this );
	  doneWithEvents = true;
	  return NULL;
	}
}

Wq::WqEvent::WqEvent( MSG *anEvent )
{
  PLAF -> memcpy( &event, anEvent, sizeof event );
}

String Wq::WqEvent::toString() const
{
  String result;

  result = Object::toString() + " [" + getEventName( event.message ) + "]";
  result += String
	( " hwnd: 0x%x message: %li wParam: %li lParam: %li"
	  " time: %li x: %i y: %i" ).
	sprintf( 1, event.hwnd, ( long ) event.message,
			 ( long ) event.wParam, ( long ) event.lParam,
			 ( long ) event.time, event.pt.x, event.pt.y );
  return result;
}

Wq::WqEvent::MessageType *Wq::WqEvent::types = NULL;

void Wq::WqEvent::initTypes()
{
  if( types == NULL )
	{
	  types = new Wq::WqEvent::MessageType[ WM_PENWINLAST + 1 ];

#define F( MSG, NAME, TYPE ) types[ MSG ].type = TYPE ; types[ MSG ].name = NAME

	F( WM_VKEYTOITEM, "WM_VKEYTOITEM", Wq::WqEvent::key );
	F( WM_CHARTOITEM, "WM_CHARTOITEM", Wq::WqEvent::key );
	F( WM_KEYDOWN, "WM_KEYDOWN", Wq::WqEvent::key );
	F( WM_KEYUP, "WM_KEYUP", Wq::WqEvent::key );
	F( WM_CHAR, "WM_CHAR", Wq::WqEvent::key );
	F( WM_DEADCHAR, "WM_DEADCHAR", Wq::WqEvent::key );
	F( WM_SYSKEYDOWN, "WM_SYSKEYDOWN", Wq::WqEvent::key );
	F( WM_SYSKEYUP, "WM_SYSKEYUP", Wq::WqEvent::key );
	F( WM_SYSCHAR, "WM_SYSCHAR", Wq::WqEvent::key );
	F( WM_SYSDEADCHAR, "WM_SYSDEADCHAR", Wq::WqEvent::key );
	F( WM_KEYLAST, "WM_KEYLAST", Wq::WqEvent::key );
	F( WM_MENUCHAR, "WM_MENUCHAR", Wq::WqEvent::key );
	F( WM_HOTKEY, "WM_HOTKEY", Wq::WqEvent::key );
	F( WM_LBUTTONDOWN, "WM_LBUTTONDOWN", Wq::WqEvent::button );
	F( WM_LBUTTONUP, "WM_LBUTTONUP", Wq::WqEvent::button );
	F( WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK", Wq::WqEvent::button );
	F( WM_RBUTTONDOWN, "WM_RBUTTONDOWN", Wq::WqEvent::button );
	F( WM_RBUTTONUP, "WM_RBUTTONUP", Wq::WqEvent::button );
	F( WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK", Wq::WqEvent::button );
	F( WM_MBUTTONDOWN, "WM_MBUTTONDOWN", Wq::WqEvent::button );
	F( WM_MBUTTONUP, "WM_MBUTTONUP", Wq::WqEvent::button );
	F( WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK", Wq::WqEvent::button );
	F( WM_SETCURSOR, "WM_SETCURSOR", Wq::WqEvent::mouse );
	F( WM_MOUSEACTIVATE, "WM_MOUSEACTIVATE", Wq::WqEvent::mouse );
	F( WM_MOUSEMOVE, "WM_MOUSEMOVE", Wq::WqEvent::mouse );
#if(_WIN32_WINNT >= 0x0400)
	F( WM_MOUSEWHEEL, "WM_MOUSEWHEEL", Wq::WqEvent::mouse );
	F( WM_MOUSEHOVER, "WM_MOUSEHOVER", Wq::WqEvent::mouse );
	F( WM_MOUSELEAVE, "WM_MOUSELEAVE", Wq::WqEvent::mouse );
#endif /* _WIN32_WINNT >= 0x0400 */
	F( WM_SETFOCUS, "WM_SETFOCUS", Wq::WqEvent::focus );
	F( WM_KILLFOCUS, "WM_KILLFOCUS", Wq::WqEvent::focus );
	F( WM_ENABLE, "WM_ENABLE", Wq::WqEvent::focus );
	F( WM_CREATE, "WM_CREATE", Wq::WqEvent::window );
	F( WM_MOVE, "WM_MOVE", Wq::WqEvent::window );
	F( WM_ACTIVATE, "WM_ACTIVATE", Wq::WqEvent::window );
	F( WM_SETTEXT, "WM_SETTEXT", Wq::WqEvent::window );
	F( WM_PAINT, "WM_PAINT", Wq::WqEvent::window );
	F( WM_QUERYOPEN, "WM_QUERYOPEN", Wq::WqEvent::window );
	F( WM_ERASEBKGND, "WM_ERASEBKGND", Wq::WqEvent::window );
	F( WM_SYSCOLORCHANGE, "WM_SYSCOLORCHANGE", Wq::WqEvent::window );
	F( WM_SHOWWINDOW, "WM_SHOWWINDOW", Wq::WqEvent::window );
	F( WM_PAINTICON, "WM_PAINTICON", Wq::WqEvent::window );
	F( WM_ICONERASEBKGND, "WM_ICONERASEBKGND", Wq::WqEvent::window );
	F( WM_DRAWITEM, "WM_DRAWITEM", Wq::WqEvent::window );
#if( WINVER >= 0x0400 )
	F( WM_MOVING, "WM_MOVING", Wq::WqEvent::window );
#endif /* WINVER >= 0x0400 */
	F( WM_DESTROY, "WM_DESTROY", Wq::WqEvent::destroy );
	F( WM_CLOSE, "WM_CLOSE", Wq::WqEvent::destroy );
	F( WM_QUERYENDSESSION, "WM_QUERYENDSESSION", Wq::WqEvent::destroy );
	F( WM_QUIT, "WM_QUIT", Wq::WqEvent::destroy );
	F( WM_ENDSESSION, "WM_ENDSESSION", Wq::WqEvent::destroy );
	F( WM_SIZE, "WM_SIZE", Wq::WqEvent::resize );
	F( WM_GETMINMAXINFO, "WM_GETMINMAXINFO", Wq::WqEvent::resize );
#if( WINVER >= 0x0400 )
	F( WM_SIZING, "WM_SIZING", Wq::WqEvent::resize );
#endif /* WINVER >= 0x0400 */
	F( WM_CUT, "WM_CUT", Wq::WqEvent::selection );
	F( WM_COPY, "WM_COPY", Wq::WqEvent::selection );
	F( WM_PASTE, "WM_PASTE", Wq::WqEvent::selection );
	F( WM_CLEAR, "WM_CLEAR", Wq::WqEvent::selection );
	F( WM_RENDERFORMAT, "WM_RENDERFORMAT", Wq::WqEvent::selection );
	F( WM_RENDERALLFORMATS, "WM_RENDERALLFORMATS", Wq::WqEvent::selection );
	F( WM_DESTROYCLIPBOARD, "WM_DESTROYCLIPBOARD", Wq::WqEvent::selection );
	F( WM_DRAWCLIPBOARD, "WM_DRAWCLIPBOARD", Wq::WqEvent::selection );
	F( WM_PAINTCLIPBOARD, "WM_PAINTCLIPBOARD", Wq::WqEvent::selection );
	F( WM_VSCROLLCLIPBOARD, "WM_VSCROLLCLIPBOARD", Wq::WqEvent::selection );
	F( WM_SIZECLIPBOARD, "WM_SIZECLIPBOARD", Wq::WqEvent::selection );
	F( WM_ASKCBFORMATNAME, "WM_ASKCBFORMATNAME", Wq::WqEvent::selection );
	F( WM_CHANGECBCHAIN, "WM_CHANGECBCHAIN", Wq::WqEvent::selection );
	F( WM_HSCROLLCLIPBOARD, "WM_HSCROLLCLIPBOARD", Wq::WqEvent::selection );
	F( WM_NULL, "WM_NULL", Wq::WqEvent::other );
	F( WM_SETREDRAW, "WM_SETREDRAW", Wq::WqEvent::other );
	F( WM_GETTEXT, "WM_GETTEXT", Wq::WqEvent::other );
	F( WM_GETTEXTLENGTH, "WM_GETTEXTLENGTH", Wq::WqEvent::other );
#if( WINVER >= 0x0400 )
	F( WM_SETTINGCHANGE, "WM_SETTINGCHANGE", Wq::WqEvent::other );
#else
	F( WM_WININICHANGE, "WM_WININICHANGE", Wq::WqEvent::other );
#endif /* WINVER >= 0x0400 */
	F( WM_DEVMODECHANGE, "WM_DEVMODECHANGE", Wq::WqEvent::other );
	F( WM_ACTIVATEAPP, "WM_ACTIVATEAPP", Wq::WqEvent::other );
	F( WM_FONTCHANGE, "WM_FONTCHANGE", Wq::WqEvent::other );
	F( WM_TIMECHANGE, "WM_TIMECHANGE", Wq::WqEvent::other );
	F( WM_CANCELMODE, "WM_CANCELMODE", Wq::WqEvent::other );
	F( WM_CHILDACTIVATE, "WM_CHILDACTIVATE", Wq::WqEvent::other );
	F( WM_QUEUESYNC, "WM_QUEUESYNC", Wq::WqEvent::other );
	F( WM_NEXTDLGCTL, "WM_NEXTDLGCTL", Wq::WqEvent::other );
	F( WM_SPOOLERSTATUS, "WM_SPOOLERSTATUS", Wq::WqEvent::other );
	F( WM_MEASUREITEM, "WM_MEASUREITEM", Wq::WqEvent::other );
	F( WM_DELETEITEM, "WM_DELETEITEM", Wq::WqEvent::other );
	F( WM_SETFONT, "WM_SETFONT", Wq::WqEvent::other );
	F( WM_GETFONT, "WM_GETFONT", Wq::WqEvent::other );
	F( WM_SETHOTKEY, "WM_SETHOTKEY", Wq::WqEvent::other );
	F( WM_GETHOTKEY, "WM_GETHOTKEY", Wq::WqEvent::other );
	F( WM_QUERYDRAGICON, "WM_QUERYDRAGICON", Wq::WqEvent::other );
	F( WM_COMPAREITEM, "WM_COMPAREITEM", Wq::WqEvent::other );
	F( WM_COMPACTING, "WM_COMPACTING", Wq::WqEvent::other );
	F( WM_COMMNOTIFY, "WM_COMMNOTIFY", Wq::WqEvent::other );
	F( WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING", Wq::WqEvent::other );
	F( WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED", Wq::WqEvent::other );
	F( WM_POWER, "WM_POWER", Wq::WqEvent::other );
	F( WM_COPYDATA, "WM_COPYDATA", Wq::WqEvent::other );
	F( WM_CANCELJOURNAL, "WM_CANCELJOURNAL", Wq::WqEvent::other );
	  
#if( WINVER >= 0x0400 )
	F( WM_NOTIFY, "WM_NOTIFY", Wq::WqEvent::other );
	F( WM_INPUTLANGCHANGEREQUEST, "WM_INPUTLANGCHANGEREQUEST", Wq::WqEvent::other );
	F( WM_INPUTLANGCHANGE, "WM_INPUTLANGCHANGE", Wq::WqEvent::other );
	F( WM_TCARD, "WM_TCARD", Wq::WqEvent::other );
	F( WM_HELP, "WM_HELP", Wq::WqEvent::other );
	F( WM_USERCHANGED, "WM_USERCHANGED", Wq::WqEvent::other );
	F( WM_NOTIFYFORMAT, "WM_NOTIFYFORMAT", Wq::WqEvent::other );
	F( WM_CONTEXTMENU, "WM_CONTEXTMENU", Wq::WqEvent::other );
	F( WM_STYLECHANGING, "WM_STYLECHANGING", Wq::WqEvent::other );
	F( WM_STYLECHANGED, "WM_STYLECHANGED", Wq::WqEvent::other );
	F( WM_DISPLAYCHANGE, "WM_DISPLAYCHANGE", Wq::WqEvent::other );
	F( WM_GETICON, "WM_GETICON", Wq::WqEvent::other );
	F( WM_SETICON, "WM_SETICON", Wq::WqEvent::other );
#endif /* WINVER >= 0x0400 */
	  
	F( WM_NCCREATE, "WM_NCCREATE", Wq::WqEvent::other );
	F( WM_NCDESTROY, "WM_NCDESTROY", Wq::WqEvent::other );
	F( WM_NCCALCSIZE, "WM_NCCALCSIZE", Wq::WqEvent::other );
	F( WM_NCHITTEST, "WM_NCHITTEST", Wq::WqEvent::other );
	F( WM_NCPAINT, "WM_NCPAINT", Wq::WqEvent::other );
	F( WM_NCACTIVATE, "WM_NCACTIVATE", Wq::WqEvent::other );
	F( WM_GETDLGCODE, "WM_GETDLGCODE", Wq::WqEvent::other );
	  
	F( WM_NCMOUSEMOVE, "WM_NCMOUSEMOVE", Wq::WqEvent::other );
	F( WM_NCLBUTTONDOWN, "WM_NCLBUTTONDOWN", Wq::WqEvent::other );
	F( WM_NCLBUTTONUP, "WM_NCLBUTTONUP", Wq::WqEvent::other );
	F( WM_NCLBUTTONDBLCLK, "WM_NCLBUTTONDBLCLK", Wq::WqEvent::other );
	F( WM_NCRBUTTONDOWN, "WM_NCRBUTTONDOWN", Wq::WqEvent::other );
	F( WM_NCRBUTTONUP, "WM_NCRBUTTONUP", Wq::WqEvent::other );
	F( WM_NCRBUTTONDBLCLK, "WM_NCRBUTTONDBLCLK", Wq::WqEvent::other );
	F( WM_NCMBUTTONDOWN, "WM_NCMBUTTONDOWN", Wq::WqEvent::other );
	F( WM_NCMBUTTONUP, "WM_NCMBUTTONUP", Wq::WqEvent::other );
	F( WM_NCMBUTTONDBLCLK, "WM_NCMBUTTONDBLCLK", Wq::WqEvent::other );
	  
	F( WM_INITDIALOG, "WM_INITDIALOG", Wq::WqEvent::other );
	F( WM_COMMAND, "WM_COMMAND", Wq::WqEvent::other );
	F( WM_SYSCOMMAND, "WM_SYSCOMMAND", Wq::WqEvent::other );
	F( WM_TIMER, "WM_TIMER", Wq::WqEvent::other );
	F( WM_HSCROLL, "WM_HSCROLL", Wq::WqEvent::other );
	F( WM_VSCROLL, "WM_VSCROLL", Wq::WqEvent::other );
	F( WM_INITMENU, "WM_INITMENU", Wq::WqEvent::other );
	F( WM_INITMENUPOPUP, "WM_INITMENUPOPUP", Wq::WqEvent::other );
	F( WM_MENUSELECT, "WM_MENUSELECT", Wq::WqEvent::other );
	F( WM_ENTERIDLE, "WM_ENTERIDLE", Wq::WqEvent::other );
	  
	F( WM_CTLCOLORMSGBOX, "WM_CTLCOLORMSGBOX", Wq::WqEvent::other );
	F( WM_CTLCOLOREDIT, "WM_CTLCOLOREDIT", Wq::WqEvent::other );
	F( WM_CTLCOLORLISTBOX, "WM_CTLCOLORLISTBOX", Wq::WqEvent::other );
	F( WM_CTLCOLORBTN, "WM_CTLCOLORBTN", Wq::WqEvent::other );
	F( WM_CTLCOLORDLG, "WM_CTLCOLORDLG", Wq::WqEvent::other );
	F( WM_CTLCOLORSCROLLBAR, "WM_CTLCOLORSCROLLBAR", Wq::WqEvent::other );
	F( WM_CTLCOLORSTATIC, "WM_CTLCOLORSTATIC", Wq::WqEvent::other );
	  
	F( WM_PARENTNOTIFY, "WM_PARENTNOTIFY", Wq::WqEvent::other );
	F( WM_ENTERMENULOOP, "WM_ENTERMENULOOP", Wq::WqEvent::other );
	F( WM_EXITMENULOOP, "WM_EXITMENULOOP", Wq::WqEvent::other );
	  
#if(WINVER >= 0x0400)
	F( WM_NEXTMENU, "WM_NEXTMENU", Wq::WqEvent::other );
	F( WM_CAPTURECHANGED, "WM_CAPTURECHANGED", Wq::WqEvent::other );
	F( WM_POWERBROADCAST, "WM_POWERBROADCAST", Wq::WqEvent::other );
	F( WM_DEVICECHANGE, "WM_DEVICECHANGE", Wq::WqEvent::other );
#endif /* WINVER >= 0x0400 */
	  
	F( WM_MDICREATE, "WM_MDICREATE", Wq::WqEvent::other );
	F( WM_MDIDESTROY, "WM_MDIDESTROY", Wq::WqEvent::other );
	F( WM_MDIACTIVATE, "WM_MDIACTIVATE", Wq::WqEvent::other );
	F( WM_MDIRESTORE, "WM_MDIRESTORE", Wq::WqEvent::other );
	F( WM_MDINEXT, "WM_MDINEXT", Wq::WqEvent::other );
	F( WM_MDIMAXIMIZE, "WM_MDIMAXIMIZE", Wq::WqEvent::other );
	F( WM_MDITILE, "WM_MDITILE", Wq::WqEvent::other );
	F( WM_MDICASCADE, "WM_MDICASCADE", Wq::WqEvent::other );
	F( WM_MDIICONARRANGE, "WM_MDIICONARRANGE", Wq::WqEvent::other );
	F( WM_MDIGETACTIVE, "WM_MDIGETACTIVE", Wq::WqEvent::other );
	  
	F( WM_MDISETMENU, "WM_MDISETMENU", Wq::WqEvent::other );
	F( WM_ENTERSIZEMOVE, "WM_ENTERSIZEMOVE", Wq::WqEvent::other );
	F( WM_EXITSIZEMOVE, "WM_EXITSIZEMOVE", Wq::WqEvent::other );
	F( WM_DROPFILES, "WM_DROPFILES", Wq::WqEvent::other );
	F( WM_MDIREFRESHMENU, "WM_MDIREFRESHMENU", Wq::WqEvent::other );
	  
	F( WM_UNDO, "WM_UNDO", Wq::WqEvent::other );
	F( WM_QUERYNEWPALETTE, "WM_QUERYNEWPALETTE", Wq::WqEvent::other );
	F( WM_PALETTEISCHANGING, "WM_PALETTEISCHANGING", Wq::WqEvent::other );
	F( WM_PALETTECHANGED, "WM_PALETTECHANGED", Wq::WqEvent::other );
#if(WINVER >= 0x0400)
	F( WM_PRINT, "WM_PRINT", Wq::WqEvent::other );
	F( WM_PRINTCLIENT, "WM_PRINTCLIENT", Wq::WqEvent::other );
	  
	F( WM_HANDHELDFIRST, "WM_HANDHELDFIRST", Wq::WqEvent::other );
	F( WM_HANDHELDLAST, "WM_HANDHELDLAST", Wq::WqEvent::other );
	  
	F( WM_AFXFIRST, "WM_AFXFIRST", Wq::WqEvent::other );
	F( WM_AFXLAST, "WM_AFXLAST", Wq::WqEvent::other );
#endif /* WINVER >= 0x0400 */
	F( WM_PENWINFIRST, "WM_PENWINFIRST", Wq::WqEvent::other );
	F( WM_PENWINLAST, "WM_PENWINLAST", Wq::WqEvent::other );

#undef F
	}
  test_( types != NULL );
}

String Wq::WqEvent::getEventName( unsigned anId )
{
  initTypes();
  test_( types != NULL );

  if( ( 0 <= anId ) && ( anId <= WM_PENWINLAST ) )
	{
	  return types[ anId ].name;
	}
  else
	{
	  return "";
	}
}

String Wq::WqEvent::getClassName() const
{
  return "WqEvent";
}

Wq::WqEvent *Wq::WqEvent::create( MSG *anEvent )
{
  preC_( anEvent != NULL );

  Wq::WqEvent *result;

  if( ( 0 <= anEvent -> message ) && ( anEvent -> message <= WM_PENWINLAST ) )
	{
	  switch( types[ anEvent -> message ].type )
		{
		case Wq::WqEvent::key:
		  result = new Wq::KeyEvent( anEvent );
		  break;
		case Wq::WqEvent::button:
		  result = new Wq::ButtonEvent( anEvent );
		  break;
		case Wq::WqEvent::mouse:
		  result = new Wq::MouseMotionEvent( anEvent );
		  break;
		case Wq::WqEvent::focus:
		  result = new Wq::FocusEvent( anEvent );
		  break;
		case Wq::WqEvent::window:
		  result = new Wq::WindowEvent( anEvent );
		  break;
		case Wq::WqEvent::destroy:
		  result = new Wq::DestroyEvent( anEvent );
		  break;
		case Wq::WqEvent::resize:
		  result = new Wq::ResizeEvent( anEvent );
		  break;
		case Wq::WqEvent::selection:
		  result = new Wq::SelectionEvent( anEvent );
		  break;
		case Wq::WqEvent::other:
		  result = new Wq::WqEvent( anEvent );
		  break;
		default:
		  test( false, "Unknown or impossible message type" );
		}
	}
  else
	{
	  result = new Wq::WqEvent( anEvent );
	}
  test_( result != NULL );
  return result;
}

Wq::KeyEvent::KeyEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::KeyEvent::getClassName() const
{
  return "KeyEvent";
}

Wq::ButtonEvent::ButtonEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::ButtonEvent::getClassName() const
{
  return "ButtonEvent";
}

Wq::MouseMotionEvent::MouseMotionEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::MouseMotionEvent::getClassName() const
{
  return "MouseMotionEvent";
}

Wq::FocusEvent::FocusEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::FocusEvent::getClassName() const
{
  return "FocusEvent";
}

Wq::WindowEvent::WindowEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::WindowEvent::getClassName() const
{
  return "WindowEvent";
}

Wq::DestroyEvent::DestroyEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::DestroyEvent::getClassName() const
{
  return "DestroyEvent";
}

Wq::ResizeEvent::ResizeEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::ResizeEvent::getClassName() const
{
  return "ResizeEvent";
}

Wq::SelectionEvent::SelectionEvent( MSG *anEvent ) :
  WqEvent( anEvent )
{}

String Wq::SelectionEvent::getClassName() const
{
  return "SelectionEvent";
}


Wq::DC::DC( HANDLE aHandle ) : Win32Object( aHandle )
{}

Wq::DC::~DC()
{
  //	ReleaseDC( getHandle() );
  handle = NULL;
}

Wq::DC *Wq::DC::create( Window *aWindow )
{
  preC_( aWindow != NULL );

  OGuard _window( aWindow, aWindow );

  CREATE_OBJECT( Wq::DC, GetDC( aWindow -> getHandle() ), Wq::DC::create( Window *aWindow ) );
}

Wq::DC *Wq::DC::createInMemory( HANDLE aHandle )
{
  CREATE_OBJECT( Wq::DC, CreateCompatibleDC( aHandle ), 
				 Wq::DC::createInMemory() );
}

void Wq::DC::drawString( Aq::CoordX aX, Aq::CoordY aY, 
						 String aString )
{
  char *text;

  text = aString.getChars();
  drawCString( aX, aY, text );
  delete []text;
}

void Wq::DC::drawCString( Aq::CoordX aX, Aq::CoordY aY, 
						  char *aString )
{
  preC_( aString != NULL );

  GUI_WRAP( TextOut( getHandle(), aX, aY, aString, 
					 PLAF -> strlen( aString ) ), 0, Wq::DC::drawCString() );
}

void Wq::DC::fillRectangle( Aq::CoordX aX, Aq::CoordY aY, 
							Aq::Size aWidth, Aq::Size aHeight, Brush *aBrush )
{
  preC_( aBrush != NULL );

  OGuard _brush( aBrush, this );

  RECT rect;

  rect.left = aX;
  rect.top = aY;
  rect.right = aX + aWidth;
  rect.bottom = aY + aHeight;

  GUI_WRAP( FillRect( getHandle(), &rect, aBrush -> getHandle() ), 0, Wq::DC::fillRectangle() );
}

void Wq::DC::copyTo( Aq::CoordX aSourceX, Aq::CoordY aSourceY,
					 Aq::Size aWidth, Aq::Size aHeight,
					 DC *aDC, 
					 Aq::CoordX aDestX, Aq::CoordY aDestY,
					 DWORD anOp )
{
  preC_( aDC != NULL );

  OGuard _dc( aDC, this );

  GUI_WRAP( BitBlt( aDC -> getHandle(), aDestX, aDestY, aWidth, aHeight, 
					getHandle(), aSourceX, aSourceY, anOp ), NULL, Wq::DC::copyTo );
}

void Wq::DC::setFont( Font *aFont )
{
  selectObject( aFont );
}

void Wq::DC::setBitmap( Bitmap *aBitmap )
{
  selectObject( aBitmap );
}

void Wq::DC::setBrush( Brush *aBrush )
{
  selectObject( aBrush );
}

void Wq::DC::setPen( Pen *aPen )
{
  selectObject( aPen );
}

void Wq::DC::selectObject( Win32Object *anObject )
{
  preC_( anObject != NULL );

  OGuard _obj( anObject, this );

  GUI_WRAP( SelectObject( getHandle(), anObject -> getHandle() ), NULL, Wq::DC::selectObject() );
}

Wq::Font   *Wq::DC::getFont() const
{
  return new Wq::Font( getObject( OBJ_FONT ) );
}

Wq::Bitmap *Wq::DC::getBitmap() const
{
  return new Wq::Bitmap( getObject( OBJ_BITMAP ) );
}

Wq::Brush  *Wq::DC::getBrush() const
{
  return new Wq::Brush( getObject( OBJ_BRUSH ) );
}

Wq::Pen    *Wq::DC::getPen() const
{
  return new Wq::Pen( getObject( OBJ_PEN ) );
}

HANDLE  Wq::DC::getObject( UINT anObjType ) const
{
  HANDLE result;

  GUI_WRAP( result = GetCurrentObject( getHandle(), anObjType ), NULL, Wq::DC::getObject() );
  return result;
}

void Wq::DC::setTextForeground( COLORREF aColor )
{
  GUI_WRAP( SetTextColor( getHandle(), aColor ), CLR_INVALID, Wq::DC::setTextForeground() );
}

void Wq::DC::setTextBackground( COLORREF aColor )
{
  GUI_WRAP( SetBkColor( getHandle(), aColor ), CLR_INVALID, Wq::DC::setTextBackground() );
}

COLORREF Wq::DC::getTextForeground() const
{
  COLORREF result;

  GUI_WRAP( result = GetTextColor( getHandle() ), CLR_INVALID, Wq::DC::getTextForeground() );
  return result;
}

COLORREF Wq::DC::getTextBackground() const
{
  COLORREF result;

  GUI_WRAP( result = GetBkColor( getHandle() ), CLR_INVALID, 
			Wq::DC::getTextBackground() );
  return result;
}

Wq::Bitmap::Bitmap( HANDLE aHandle ) : Win32Object( aHandle )
{
  dc = NULL;
}

Wq::Bitmap::~Bitmap()
{
  FCDONE( dc );
  DeleteObject( getHandle() );
  handle = NULL;
}

Aq::Size Wq::Bitmap::getWidth() const
{
  SIZE size;

  GUI_WRAP( GetBitmapDimensionEx( getHandle(), &size ), 0, 
			Wq::Bitmap::getWidth() );
  return size.cx;
}

void Wq::Bitmap::setSize( Aq::Size aWidth, Aq::Size aHeight )
{
  SIZE size;

  GUI_WRAP( SetBitmapDimensionEx( getHandle(), aWidth, aHeight, &size ), 0, 
			Wq::Bitmap::getWidth() );
}

Aq::Size Wq::Bitmap::getHeight() const
{
  SIZE size;

  GUI_WRAP( GetBitmapDimensionEx( getHandle(), &size ), 0, 
			Wq::Bitmap::getWidth() );
  return size.cy;
}

void Wq::Bitmap::setDC( DC *aDC )
{
  FCDONE( dc );
  if( aDC != NULL )
	{
	  OGuard _dc( aDC, this );

	  ( ( Wq::DC * ) dc ) = Wq::DC::createInMemory
		( aDC -> getHandle() );
	}
  else
	{
	  ( ( Wq::DC * ) dc ) = Wq::DC::createInMemory( NULL );
	}
  PGuard _self( this, this );

  OREG( dc );
  dc -> setBitmap( ( Wq::Bitmap * ) this );
}

Wq::DC *Wq::Bitmap::getDC() const
{
  if( dc == NULL )
	{
	  const_cast< Wq::Bitmap * >( this ) -> setDC( NULL );
	  test_( dc != NULL );
	}
  //  dc -> fillRectangle( 0, 0, 10, 10, 
  //	  Wq::Brush::createSolid( RGB( 0xff, 0, 0 ) ) );
  return dc;
}

void Wq::Bitmap::ungetDC( DC *aDC ) const
{}

Wq::Bitmap *Wq::Bitmap::create( DC *aDC, Aq::Size aWidth,
								Aq::Size aHeight )
{
  preC_( aDC != NULL );

  OGuard _dc( aDC, aDC );

  CREATE_OBJECT( Wq::Bitmap, 
				 CreateCompatibleBitmap( aDC -> getHandle(),
										 aWidth, aHeight ), 
				 Wq::Bitmap::create() );
}

Wq::Bitmap *Wq::Bitmap::load( int aResource )
{
  CREATE_OBJECT( Wq::Bitmap, 
				 LoadBitmap( GetModuleHandle( NULL ), 
							 MAKEINTRESOURCE( aResource ) ), 
				 Wq::Bitmap::load() );
}

Wq::Brush::Brush( HANDLE aHandle ) : Win32Object( aHandle )
{}

Wq::Brush::~Brush()
{
  DeleteObject( getHandle() );
  handle = NULL;
}

Wq::Brush *Wq::Brush::createSolid( COLORREF aColor )
{
  CREATE_OBJECT( Wq::Brush, 
				 CreateSolidBrush( aColor ), 
				 Wq::Brush::createSolid() );
}

Wq::Brush *Wq::Brush::createStock( int aStockBrush )
{
  CREATE_OBJECT( Wq::Brush, 
				 GetStockObject( aStockBrush ), 
				 Wq::Brush::createStock() );
}


Wq::Font::Font( HANDLE handle ) : Win32Object( handle )
{}

Wq::Font::~Font()
{
  DeleteObject( getHandle() );
  handle = NULL;
}

Wq::Font *Wq::Font::create( 
						   int aHeight, 
						   int aWidth, 
						   int anEscapement, 
						   int anOrientation, 
						   int aWeight, 
						   DWORD anItalic, 
						   DWORD anUnderline, 
						   DWORD aStrikeOut, 
						   DWORD aCharSet, 
						   DWORD anOutputPrecision, 
						   DWORD anwClipPrecision, 
						   DWORD aQuality, 
						   DWORD aPitchAndFamily, 
						   LPCTSTR aFace )
{
  CREATE_OBJECT( Wq::Font, 
				 CreateFont(
							aHeight, 
							aWidth, 
							anEscapement, 
							anOrientation, 
							aWeight, 
							anItalic, 
							anUnderline, 
							aStrikeOut, 
							aCharSet, 
							anOutputPrecision, 
							anwClipPrecision, 
							aQuality, 
							aPitchAndFamily, 
							aFace ), 
				 Wq::Font::create() );
}
 
Wq::Font *Wq::Font::createDefault()
{
  return create( 0, 0, 0, 0, FW_DONTCARE, false, false, false, 
				 DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
				 DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE, NULL );
}

static int CALLBACK fontEnumProc(
								 ENUMLOGFONTW FAR *lpelf, 
								 NEWTEXTMETRICW FAR *lpntm, 
								 int FontType, 
								 LPARAM lParam )
{
  preC_( lParam != NULL );
  preC_( lpelf != NULL );

  Iterable *block;

  block = ( Iterable * ) lParam;

  OGuard _block( block, block );

  block -> fireOn( new String( ( unichar * ) ( lpelf -> elfFullName ) ) );

  return block -> isMore();
}

Wq::FontIterator::FontIterator( DC *aDC )
{
  FNNINIT( dc, aDC );
}

Wq::FontIterator::~FontIterator()
{
  FDONE( dc );
}

void Wq::FontIterator::iterate( Iterable *aBlock )
{
  preC_( aBlock != NULL );

  OGuard _block( aBlock, ( Top * ) this );

  aBlock -> initIterations();
  EnumFontFamiliesW( dc -> getHandle(), 
					 NULL, ( FONTENUMPROC ) fontEnumProc, 
					 ( LPARAM ) aBlock ); 
  aBlock -> doneIterations();
}


Wq::Cursor::Cursor( HANDLE aHandle ) : Win32Object( aHandle )
{}

Wq::Cursor::~Cursor()
{
  DestroyCursor( getHandle() );
  handle = NULL;
}

Wq::Cursor *Wq::Cursor::load( char *aResource )
{
  return loadGlobal( GetModuleHandle( NULL ), aResource );
}

Wq::Cursor *Wq::Cursor::loadGlobal( HANDLE aModule, char *aResource )
{
  CREATE_OBJECT( Wq::Cursor, 
				 LoadCursor( aModule, aResource ), 
				 Wq::Cursor::load() );
}

Wq::Cursor *Wq::Cursor::create( Aq::Size aWidth, Aq::Size aHeight, 
								Aq::CoordX aHotSpotX, Aq::CoordY aHotSpotY, 
								void *anANDMask, void *aXORMask )
{
  CREATE_OBJECT( Wq::Cursor, 
				 CreateCursor( GetModuleHandle( NULL ), 
							   aHotSpotX, aHotSpotY, aWidth, aHeight, anANDMask, aXORMask ), 
				 Wq::Cursor::create() );
}

Wq::Pen::Pen( HANDLE aHandle ) : Win32Object( aHandle )
{}

Wq::Pen::~Pen()
{
  DeleteObject( getHandle() );
  handle = NULL;
}

Wq::Pen *Wq::Pen::create( int aStyle, int aWidth, COLORREF aColor )
{
  CREATE_OBJECT( Wq::Pen, 
				 CreatePen( aStyle, aWidth, aColor ), 
				 Wq::Pen::create() );
}

#if defined( TESTING )

#include "observe/EchoCondition.hpp"
#include "observe/ObservationContext.hpp"
#include "observe/EchoObserver.hpp"
#include "iter/iterables/EchoIterable.hpp"

#include "resource.h"

#define cursor_00_xbm_width 64
#define cursor_00_xbm_height 64
#define cursor_00_xbm_x_hot 32
#define cursor_00_xbm_y_hot 32
static unsigned char cursor_00_xbm_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x14,
  0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0xa1, 0x00, 0x00, 0x02, 0x00,
  0x00, 0x00, 0x0a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x20, 0x00, 0x04, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x80, 0x01, 0x08, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x06, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x01, 0x00, 0x19, 0x00, 0x00, 0x20, 0x00, 0x80, 0x00, 0xe0, 0xe4,
  0x01, 0x30, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x40, 0x80, 0x00,
  0x40, 0x00, 0x0a, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
  0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x60, 0x80, 0x00, 0x00,
  0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x00,
  0xc0, 0x01, 0x01, 0x00, 0x08, 0x20, 0x00, 0x3a, 0x00, 0x00, 0x01, 0x02,
  0x00, 0x08, 0x40, 0xc5, 0x01, 0x01, 0x02, 0x01, 0x00, 0x08, 0x40, 0x00,
  0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x03, 0x01, 0x01, 0x02,
  0x00, 0x04, 0x18, 0x00, 0x06, 0x02, 0x02, 0x00, 0x00, 0x02, 0x04, 0x00,
  0x04, 0x02, 0x02, 0x02, 0x00, 0x02, 0x04, 0x00, 0x04, 0x02, 0x01, 0x01,
  0x00, 0x00, 0x06, 0xf8, 0x04, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x0c,
  0x05, 0x01, 0x01, 0x01, 0x80, 0x00, 0x01, 0x87, 0xc3, 0x01, 0x01, 0x02,
  0x00, 0x01, 0x01, 0x41, 0x61, 0x80, 0x80, 0x00, 0x00, 0x00, 0x01, 0x41,
  0x3e, 0xc0, 0x00, 0x00, 0x00, 0x01, 0x81, 0x40, 0x00, 0x40, 0x80, 0x00,
  0x80, 0x80, 0x80, 0x40, 0x00, 0x40, 0x80, 0x00, 0x00, 0x80, 0x80, 0xc0,
  0x00, 0x30, 0x40, 0x00, 0x80, 0x00, 0x01, 0x81, 0x01, 0x14, 0x00, 0x00,
  0x00, 0x00, 0x80, 0x00, 0x01, 0x04, 0x20, 0x00, 0x00, 0x81, 0x00, 0x01,
  0x47, 0x05, 0x20, 0x00, 0x80, 0x00, 0x01, 0x00, 0xb8, 0x00, 0x08, 0x20,
  0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x01, 0x00,
  0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x02, 0x04, 0x08, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x70,
  0x00, 0xa0, 0x00, 0x04, 0x00, 0x02, 0x04, 0xc0, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x04, 0x18, 0x00, 0x4f, 0x0e, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00,
  0x30, 0x01, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00,
  0x00, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x0a, 0x00,
  0x00, 0x40, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xb0,
  0x00, 0xa0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x09, 0x00, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static char *cursor_00_xbm_xor = new char[ sizeof cursor_00_xbm_bits ];

class TestObserver : public virtual PassiveObserver,
					 public virtual Object
{
public: HandlerResult fire( Condition *aCondition, 
							ObservationContext *aContext )
	{
	  Wq::WqEvent *event;

	  event = TCAST( aCondition, Wq::WqEvent );
	  OGuard _event( event, this );

	  if( event -> event.message == WM_SETCURSOR )
		{
		  DLOG( "WM_SETCURSOR!" );
		  SetCursor( cursorHandle );
		}
	  return 0;
	}
public: HANDLE cursorHandle;
};

boolean Wq::tester( int )
{
  Wq::Window *window;
  Wq::Brush *brush;
  Wq::Pen *pen;
  Wq::Font *font;
  Wq::FontIterator *iter;
  Wq::Bitmap *ball;
  //  Wq::DC *dc;
  Wq::Cursor *cursor;
  Wq::GC *GC;

  Wq::initClass();

  window = Wq::Window::create();
  OGuard _window( window, window );

  brush = Wq::Brush::createSolid( COLOR_WINDOW );
  OGuard _brush ( brush , brush );

  pen = Wq::Pen::create( PS_SOLID, 0, RGB( 0, 0xff, 0 ) );
  OGuard _pen( pen, pen );

  font = Wq::Font::createDefault();
  OGuard _font( font, font );

  ball = Wq::Bitmap::load( IDB_BALL );
  OGuard _ball( ball, ball );

  //  dc = Wq::DC::createInMemory();
  //  OGuard _dc( dc, dc );

  //  dc -> setBitmap( ball );

  GC = new Wq::GC( 
				  RGB( 0xff, 0xff, 0 ), RGB( 0, 0, 0xff ),
				  pen, brush, font );
  OGuard _gc( GC, GC );

  //  cursor = Wq::Cursor::create( cursor_00_xbm_width, cursor_00_xbm_height,
  //	  cursor_00_xbm_x_hot, cursor_00_xbm_y_hot, 
  //	  cursor_00_xbm_bits, cursor_00_xbm_xor );
  cursor = Wq::Cursor::loadGlobal( NULL, IDC_APPSTARTING );
  OGuard _cursor( cursor, cursor );
  
  window -> move( 50, 50 );
  window -> setSize( 300, 300 );
  window -> setTitle( "XONIX [ Win32 Edition ]" );
  window -> show();

  //  iter = new Wq::FontIterator( dc );
  //  OGuard _iter( iter, iter );

  //  iter -> iterate( new EchoIterable() );


  TestObserver *obs;
	
  obs = new TestObserver();
  OGuard _obs( obs, obs );

  obs -> cursorHandle = cursor -> getHandle();

  //  window -> getEventSource() -> addPassive( new EchoObserver() );
  window -> getEventSource() -> addPassive( obs );

  for( int i = 0 ; i < 1000 ; )
	{
	  Wq::WPainter *helloWorld = new Wq::WPainter( GC, window );
	  Aq::PainterGuard paint( helloWorld, helloWorld );

	  helloWorld -> fillRectangle
		( i % 300 , i % 300, 16, 16 );
	  ++i;
	  helloWorld -> copyFrom( 0, 0, 16, 16, ball, i % 300 , i % 300 );
	  helloWorld -> drawString( 10, 10, String( "Hello, world: " ) + 
								String( i ) );
	  //	  DLOG( "draw!" );
	  window -> setCursor( cursor );
	  //	  window -> showCursor( true );
	  THR_SYSTEM -> sleep( 0, 1000000 );
	}
  return true;
}
#endif

#if defined(_INLINE)
#include "../src/Debug.ipp"
#endif

/* USE_WQ */
#endif

/* $Log$ */
