//=======================================================================
//		File:	SERVER.CPP
//		Date:	4-1-97
//		Name:
//
//		Desc:	This file provides the framework for the sample server
//				application.  This application consists of a single
//				window with seven icons that can be selected, a push
//				button, and a status bar.  It is intended to be simple
//				and rather inaccessible.
//
//		Copyright (C) 1997 by Microsoft Corporation.  All rights reserved.
//		See bottom of file for disclaimer.
//=======================================================================

//=======================================================================
//		Include Files
//=======================================================================

#include <windows.h>
#include <initguid.h>
#include <objbase.h>
#include <objerror.h>
#include <tchar.h>
#include "resource.h"
#include "server.h"
#include "status.h"

/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  MSAA SUPPORT ADDITION:                                             //
//                                                                     //
//  Include header file of class implementing IAccessible.             //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
#include "access.h"



//=======================================================================
//		Private Defines
//=======================================================================

#define	BUF_SIZE			64

#define	NO_ICON_HAS_FOCUS	-1
#define	NO_CHILD_HAS_FOCUS	-2



//=======================================================================
//		Global Variables
//=======================================================================

HWND		g_hWndApp;				// window handle for the App
HWND		g_hWndStatusBar;		// window handle for the status bar
HWND		g_hWndButton;			// window handle for push button
HINSTANCE	g_hInst;				// current instance of the App
TCHAR		g_szAppClass[BUF_SIZE];	// window class name for App
TCHAR		g_szAppName[BUF_SIZE];	// window name for App
ICON_INFO	g_rgIcons[NUM_ICONS];	// array of icon information elements
int         g_dxMargin;				// horizontal margin spacing for icon placement
int			g_dyMargin;				// vertical margin spacing for icon placement
int			g_nLastChildWithFocus;	// ID of last child with the focus (queried
									//   only in SetFocusHandler)

/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  MSAA SUPPORT ADDITION:                                             //
//                                                                     //
//  A boolean flag to indicate whether or not the application is ready //
//  to process WM_GETOBJECTs and a global pointer to the Accessible    //
//  object for the application.                                        //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
BOOL        g_bReadyForWMGETOBJECT = FALSE;
CAccClient*	g_pAccClient = NULL;



//=======================================================================
//		Private Function Prototypes
//=======================================================================

BOOL	RegisterWindowClasses( HINSTANCE hInstance );
BOOL	InitInstance( HINSTANCE hInstance );
void	TermInstance( void );
void	PaintHandler( HWND hWnd );
void	LMBDownHandler( WPARAM wParam, LPARAM lParam );
int		HasAnIconBeenClicked( LPARAM lp );
int		GetIconSelectionAnchor( void );
BOOL	IsIconInRange( int nIcon, int nIconIndex1, int nIconIndex2 );
LRESULT	ActivationHandler( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
void	SetFocusHandler( HWND hWndLostFocus );
void	KillFocusHandler( HWND hWndGetsFocus );
void	DrawButtonHandler( LPDRAWITEMSTRUCT lpDI );
BOOL	ButtonNotifyMsgHandler( WORD wNotifyCode );
void	UpdateWindowAndStatusBar( void );
void	UpdateStatusBarMessage( BOOL bButtonHasFocus );

LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  MSAAA SUPPORT ADDITION:                                            //
//                                                                     //
//  Prototype of function used to handle WM_GETOBJECT messages and     //
//  prototype of function used to handle WM_DESTROY messages.          //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
LRESULT GetObjectHandler( HWND hWnd, WPARAM wParam, LPARAM lParam );
void	DestroyHandler( HWND hWnd );



//=======================================================================
//		WinMain Definition
//=======================================================================

//-----------------------------------------------------------------------
//	WinMain()
//
//	DESCRIPTION:
//
//		The WinMain function is called by the system as the initial entry
//		point for a Windows-based application. 
//
//	PARAMETERS:
//
//		hInst			Identifies the current instance of the application.
//
//		hPrevInst		Identifies the previous instance of the application.
//						  For a Win32-based application, this parameter is
//						  always NULL.
//
//		szCmdLine		Points to a null-terminated string specifying the
//						  command line for the application.
//
//		nCmdShow		Specifies how the window is to be shown.
//
//	RETURNS:
//
//		int				Zero is returned in all cases.
//
//-----------------------------------------------------------------------

int WINAPI
WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nCmdShow )
{
	MSG     msg;


	//-----------------------------------------------------
	//	Register the necessary window classes and
	//	  initialize this instance of the application.
	//-----------------------------------------------------

	if( !RegisterWindowClasses( hInst ) )
		return 0;

    if( !InitInstance( hInst ) )
		return 0;


	//-----------------------------------------------------
	//	Enter the message loop.
	//-----------------------------------------------------

	while ( GetMessage( &msg, NULL, 0, 0 ) == TRUE )
	{
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}


	//-----------------------------------------------------
	//	Perform any cleanup for the application.
	//-----------------------------------------------------

    TermInstance();


    return 0;
}



//=======================================================================
//		Private Function Definitions
//=======================================================================

//-----------------------------------------------------------------------
//	RegisterWindowClasses()
//
//	DESCRIPTION:
//
//		Registers the application window class and calls
//		StatusBar_RegisterWndClass() to register the status bar
//		window class.
//
//	PARAMETERS:
//
//		hInstance		Identifies the current instance of the application. 
//
//	RETURNS:
//
//		BOOL			TRUE if all window classes were registered
//						  successfully; FALSE otherwise.
//
//-----------------------------------------------------------------------

BOOL RegisterWindowClasses( HINSTANCE hInstance )
{
    WNDCLASSEX      wc;


	//-----------------------------------------------------
    //	Load the window class name from the string table.
	//-----------------------------------------------------

	if ( LoadString( hInstance, IDS_APPWINDOWCLASSNAME,
					 (LPTSTR) g_szAppClass, BUF_SIZE ) < 1 )
		return FALSE;


	//-----------------------------------------------------
    //	Register the class.
	//-----------------------------------------------------

    wc.cbSize			= sizeof(wc);
    wc.style			= 0;
    wc.lpfnWndProc		= MainWndProc;
    wc.cbClsExtra		= 0;
    wc.cbWndExtra		= 0;
    wc.hInstance		= hInstance;
    wc.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SERVER));
    wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName		= NULL;
    wc.lpszClassName    = g_szAppClass;
    wc.hIconSm			= NULL;

    if( !RegisterClassEx( &wc ) )
        return FALSE;


	//-----------------------------------------------------
    //	Register the status bar window class.
	//-----------------------------------------------------

	if( !StatusBar_RegisterWndClass( hInstance ) )
		return FALSE;


    return TRUE;
}



//-----------------------------------------------------------------------
//	InitInstance()
//
//	DESCRIPTION:
//
//		Performs all initializations necessary for this instance of
//		the application.
//
//	PARAMETERS:
//
//		hInstance		Identifies the current instance of the application. 
//
//	RETURNS:
//
//		BOOL			TRUE if all application initializations succeed;
//						  FALSE otherwise.
//
//	NOTES:
//
//		The initializations include the following:
//
//			1) Determining how large the client area of our window
//				should be initially to accommodate all the icons,
//				the push button, and the status bar
//
//			2) Creating the main window
//
//			3) Creating the status bar
//
//			4) Initializing each icon's entry in the array of icon
//				information and drawing each icon
//
//			5) Creating the button
//
//			6) Initializing the COM library
//
//-----------------------------------------------------------------------

BOOL InitInstance( HINSTANCE hInstance )
{
    RECT            rcClient;
    int             i, x, y, dxItem, dyItem;
	HRESULT			hr;
	TCHAR			szButtonClass[BUF_SIZE];
	TCHAR			szButtonName[BUF_SIZE];


	//-----------------------------------------------------
    //	Initialize the application instance handle.
	//-----------------------------------------------------

    g_hInst = hInstance;


	//-----------------------------------------------------
    //	Initialize our icon margin and dimension values.
	//-----------------------------------------------------

    g_dxMargin = GetSystemMetrics( SM_CXBORDER );
    g_dyMargin = GetSystemMetrics( SM_CYBORDER );

    dxItem = GetSystemMetrics( SM_CXICON ) + 2 * g_dxMargin;
    dyItem = GetSystemMetrics( SM_CYICON ) + 2 * g_dyMargin;


	//-----------------------------------------------------
    //	Determine how large the client area of our window
	//	  must be so all the icons, the push button, and
	//	  the status bar fit in nicely.
	//-----------------------------------------------------

    rcClient.left = 0;
    rcClient.top = 0;
    rcClient.right = (2 * NUM_ICONS * dxItem) + 3 * dxItem;
    rcClient.bottom = dyItem + (2 * dyItem) + STATUS_HEIGHT;
    if ( !AdjustWindowRectEx(
				&rcClient,
				WS_OVERLAPPEDWINDOW,
				FALSE,
				WS_EX_WINDOWEDGE | WS_EX_APPWINDOW ) )
		return FALSE;


	//-----------------------------------------------------
    //	Load the window name from the string table.
	//-----------------------------------------------------

	if ( LoadString( hInstance, IDS_APPNAME,
					 (LPTSTR) g_szAppName, BUF_SIZE ) < 1 )
		return FALSE;


	//-----------------------------------------------------
    //	Create the main window.
	//-----------------------------------------------------

    g_hWndApp = CreateWindowEx(
					WS_EX_WINDOWEDGE | WS_EX_APPWINDOW,
					g_szAppClass,
					g_szAppName,
					WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
					CW_USEDEFAULT,
					0,
					rcClient.right - rcClient.left,
					rcClient.bottom - rcClient.top,
					NULL,
					NULL,
					g_hInst,
					NULL );
        
    if ( g_hWndApp == NULL )
        return FALSE;


	//-----------------------------------------------------
    //	Create the status bar.
	//-----------------------------------------------------

    g_hWndStatusBar = StatusBar_CreateWnd( g_hWndApp, g_hInst );
        
    if ( g_hWndStatusBar == NULL )
        return FALSE;


	//-----------------------------------------------------
    //	Initialize the icon information array.  This
	//	  involves loading each icon, calculating where
	//	  all the icons fit within the client area of
	//	  the application window, and initializing
	//	  the focus, selection, and selection anchor
	//	  indicators to FALSE.
	//-----------------------------------------------------

    if ( !GetClientRect( g_hWndApp, &rcClient ) )
		return FALSE;

    x = rcClient.left + dxItem;
    y = rcClient.top + (dyItem / 2);

    for ( i = 0; i < NUM_ICONS; i++ )
    {
        g_rgIcons[i].hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_JEWELS + i) );

		g_rgIcons[i].rcBounds.left = x;
		g_rgIcons[i].rcBounds.top = y;
		g_rgIcons[i].rcBounds.right = x + dxItem;
		g_rgIcons[i].rcBounds.bottom = y + dyItem;

        x += 2 * dxItem;
        if (x >= rcClient.right - (dxItem/2))
        {
            x = rcClient.left + dxItem;
            y += 2 * dyItem;
        }

		g_rgIcons[i].bIsSelected = FALSE;
		g_rgIcons[i].bIsSelAnchor = FALSE;
		g_rgIcons[i].bHasFocus = FALSE;
    }


	//-----------------------------------------------------
    //	Load the button window class name and the button
	//	  window name from the string table.
	//-----------------------------------------------------

	if ( LoadString( hInstance, IDS_BUTTONWINDOWCLASSNAME,
					 (LPTSTR) szButtonClass, BUF_SIZE ) < 1 )
		return FALSE;

	if ( LoadString( hInstance, IDS_BUTTONWINDOWNAME,
					 (LPTSTR) szButtonName, BUF_SIZE ) < 1 )
		return FALSE;


	//-----------------------------------------------------
    //	Create the push button one icon spot to the right
	//	  of the rightmost icon.
	//-----------------------------------------------------

    g_hWndButton = CreateWindowEx(
						0,
						szButtonClass,
						szButtonName,
						WS_VISIBLE | WS_CHILD | BS_OWNERDRAW | BS_PUSHBUTTON | BS_NOTIFY,
						g_rgIcons[NUM_ICONS - 1].rcBounds.left + (2 * dxItem),
						dyItem - g_dyMargin,
						dxItem + 2 * g_dxMargin,
						dyItem + 2 * g_dyMargin,
						g_hWndApp,
						(HMENU)ID_BUTTON,
						g_hInst,
						NULL );

    if ( g_hWndButton == NULL )
        return FALSE;


	//-----------------------------------------------------
	//	Initialize the child object focus identifier.
	//-----------------------------------------------------

	g_nLastChildWithFocus = NO_CHILD_HAS_FOCUS;


	//-----------------------------------------------------
	//	Initialize the COM library.
	//-----------------------------------------------------

	hr = CoInitialize(NULL);
	if ( FAILED( hr ) )
		return FALSE;


	//-----------------------------------------------------
	//	Show our window and paint its client area.
	//-----------------------------------------------------

    ShowWindow( g_hWndApp, SW_SHOW );
    UpdateWindow( g_hWndApp );


	//-----------------------------------------------------
	//	Update the status bar text with the current
	//	  object focus information.
	//-----------------------------------------------------

	UpdateStatusBarMessage( FALSE );


    return TRUE;
}



//-----------------------------------------------------------------------
//	TermInstance()
//
//	DESCRIPTION:
//
//		Performs all clean-up necessary for this instance of the
//		application.  At this time, this simply means closing the
//		COM library.
//
//	PARAMETERS:
//
//		None.
//
//	RETURNS:
//
//		None
//
//-----------------------------------------------------------------------

void TermInstance( void )
{
	//-----------------------------------------------------
	//	Close the COM library.
	//-----------------------------------------------------

	CoUninitialize();


	return;
}



//-----------------------------------------------------------------------
//	MainWndProc()
//
//	DESCRIPTION:
//
//		Window procedure for the application window as registered
//		with the window class.
//
//	PARAMETERS:
//
//		hWnd			Window handle of the application window.
//
//		uMsg			Message identifier.
//
//		wParam			Specifies additional message information,
//						contents depend on the value of uMsg.
//
//		lParam			Specifies additional message information,
//						contents depend on the value of uMsg.
//
//
//	RETURNS:
//
//		LRESULT			Value depends upon the processing of
//						  the uMsg parameter.
//
//-----------------------------------------------------------------------

LRESULT CALLBACK
MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg )
    {
        case WM_PAINT:
            PaintHandler( hWnd );
            break;


        case WM_DESTROY:
			/////////////////////////////////////////////////////////////////
			//                                                             //
			//  MSAA SUPPORT ADDITION:                                     //
			//                                                             //
			//  The handling of WM_DESTORY has been moved into a separate  //
			//  function to highlight the changes made to support          //
			//  Accessibility.                                             //
			//                                                             //
			/////////////////////////////////////////////////////////////////
            DestroyHandler( hWnd );
            break;


        case WM_CLOSE:
			/////////////////////////////////////////////////////////////////
			//                                                             //
			//  MSAA SUPPORT ADDITION:                                     //
			//                                                             //
			//  The application is in the process of closing down, so      //
			//  set g_bReadyForWMGETOBJECT to FALSE to indicate that the   //
			//  application will no longer process WM_GETOBJECT messages.  //
			//                                                             //
			/////////////////////////////////////////////////////////////////
			g_bReadyForWMGETOBJECT = FALSE;

			StatusBar_DestroyWnd( &g_hWndStatusBar );
			DestroyWindow( hWnd );
			break;


        case WM_LBUTTONDOWN:
			LMBDownHandler( wParam, lParam );
			break;

        
		case WM_SIZE:
			StatusBar_ResizeWnd( hWnd, g_hWndStatusBar );
			return DefWindowProc( hWnd, uMsg, wParam, lParam );


		/////////////////////////////////////////////////////////////////////
		//                                                                 //
		//  MSAA SUPPORT ADDITION:                                         //
		//                                                                 //
		//  Handle the WM_SHOWWINDOW messages to the extent that the       //
		//  g_bReadyForWMGETOBJECT flag is set to TRUE to indicate that    //
		//  the application is now ready to process WM_GETOBJECT messages. //
		//  By doing this, we insure that the application window is fully  //
		//  created before we try to create its Accessible object, namely  //
		//  CAccClient.                                                    //
		//                                                                 //
		//  Recall that the Accessible object's lifetime for this          //
		//  application is bounded by the first WM_GETOBJECT that the      //
		//  application can handle successfully (creation) and the close   //
		//  of the application (destruction).  Thus, once it is set to     //
		//  TRUE by the first WM_SHOWWINDOW, g_bReadyForWMGETOBJECT will   //
		//  not be set to FALSE until the application receives a WM_CLOSE  //
		//  (see above).                                                   //
		//                                                                 //
		/////////////////////////////////////////////////////////////////////
		case WM_SHOWWINDOW:
			if ( !g_bReadyForWMGETOBJECT )
				g_bReadyForWMGETOBJECT = TRUE;

			NotifyWinEvent( wParam ? EVENT_OBJECT_SHOW : EVENT_OBJECT_HIDE,
			                hWnd,
							OBJID_CLIENT,
							CHILDID_SELF );

			return DefWindowProc( hWnd, uMsg, wParam, lParam );


		/////////////////////////////////////////////////////////////////////
		//                                                                 //
		//  MSAA  SUPPORT ADDITION:                                        //
		//                                                                 //
		//  Pass the WM_GETOBJECT messages to our custom handler.          //
		//                                                                 //
		/////////////////////////////////////////////////////////////////////
		case WM_GETOBJECT:
			return GetObjectHandler( hWnd, wParam, lParam );


		case WM_ACTIVATE:
			return ActivationHandler( hWnd, uMsg, wParam, lParam );

			
		case WM_SETFOCUS:
			SetFocusHandler( (HWND) wParam );
			break;

			
		case WM_KILLFOCUS:
			KillFocusHandler( hWnd );
            break;


		case WM_DRAWITEM:
			DrawButtonHandler( (LPDRAWITEMSTRUCT) lParam );
			break;


        case WM_COMMAND:
            if ( LOWORD( wParam ) == ID_BUTTON )
                if ( ButtonNotifyMsgHandler( HIWORD( wParam ) ) )
					return 0L;


		default:
			return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }

	return 0L;
}



//-----------------------------------------------------------------------
//	PaintHandler()
//
//	DESCRIPTION:
//
//		Handles the WM_PAINT messages for the application window.
//
//	PARAMETERS:
//
//		hWnd			Window handle of the application window.
//
//	RETURNS:
//
//		None.
//
//	NOTES:
//
//		The painting of client area of the application window consists
//		of drawing the icons and their correct selection, focus, and
//		selection anchor state.
//
//-----------------------------------------------------------------------

void PaintHandler( HWND hWnd )
{
	PAINTSTRUCT		ps;
	BOOL			bWndHasFocus;


	//-----------------------------------------------------
	//	Call BeginPaint() to initialize the PAINTSTRUCT
	//	  for this window.
	//-----------------------------------------------------

    if ( BeginPaint( hWnd, &ps ) == NULL )
		return;


	//-----------------------------------------------------
	//	Determine if our window has the focus.
	//-----------------------------------------------------

	bWndHasFocus = ( GetFocus() == g_hWndApp );


	//-----------------------------------------------------
	//	Draw each icon.  If the icon is selected, invert
	//	  its rectangle.  If our window has the input
	//	  focus and if the icon has the focus, draw a
	//	  focus rectangle around it.  If the icon is the
	//	  selection anchor, draw a bar beneath it.
	//-----------------------------------------------------

    for ( int i = 0; i < NUM_ICONS; i++ )
    {
		//-------------------------------------------------
        //	Draw the icon.
		//-------------------------------------------------

		DrawIcon( ps.hdc,
		          g_rgIcons[i].rcBounds.left + g_dxMargin,
		          g_rgIcons[i].rcBounds.top + g_dxMargin,
		          g_rgIcons[i].hIcon );


		//-------------------------------------------------
		//	If the icon is selected, invert its rectangle.
		//-------------------------------------------------

		if ( g_rgIcons[i].bIsSelected )
		{
			InvertRect( ps.hdc, &g_rgIcons[i].rcBounds );
		}


		//-------------------------------------------------
		//	If the window has the focus and if the icon has
		//	  the focus, draw a focus rect about the icon.
		//-------------------------------------------------

		if ( bWndHasFocus  &&  g_rgIcons[i].bHasFocus )
            DrawFocusRect( ps.hdc, &g_rgIcons[i].rcBounds );


		//-------------------------------------------------
		//	Is the icon the selection anchor?  If so,
		//	  draw a red bar under the icon.
		//-------------------------------------------------

		if ( g_rgIcons[i].bIsSelAnchor )
		{
			RECT		rc;
			HBRUSH		hBrush, hBrushOld;
			LOGBRUSH	lb;

			//---------------------------------------------
			//	Set the dimensions of the bar.
			//---------------------------------------------

			rc.top    = g_rgIcons[i].rcBounds.bottom - 4 * g_dyMargin;
			rc.bottom = g_rgIcons[i].rcBounds.bottom - 2 * g_dyMargin;
			rc.left   = g_rgIcons[i].rcBounds.left + 3 * g_dxMargin;
			rc.right  = g_rgIcons[i].rcBounds.right - 3 * g_dxMargin;


			//---------------------------------------------
			//	Create a red, solid brush.
			//---------------------------------------------

			lb.lbStyle = BS_SOLID;
			lb.lbColor = RGB( 255, 0, 0 );
			lb.lbHatch = 0L; 
			hBrush = CreateBrushIndirect( &lb );


			//---------------------------------------------
			//	If the brush cannot be created, use the
			//	  light gray brush to draw the bar.
			//---------------------------------------------

			if ( hBrush == NULL )
				FillRect( ps.hdc, &rc, GetStockObject( DKGRAY_BRUSH ) );
			else
			{
				//---------------------------------------------
				//	The brush was created successfully, so
				//	  select it.  If the selection fails, use
				//	  the light gray brush to draw the bar.
				//---------------------------------------------

				hBrushOld = SelectObject( ps.hdc, (HGDIOBJ)hBrush );

				if ( hBrushOld == NULL )
					FillRect( ps.hdc, &rc, GetStockObject( DKGRAY_BRUSH ) );
				else
				{
					//---------------------------------------------
					//	The brush was selected successfully,
					//	  so draw the bar and deselect the brush.
					//---------------------------------------------

					FillRect( ps.hdc, &rc, hBrush );
					SelectObject( ps.hdc, (HGDIOBJ)hBrushOld );
				}

				//---------------------------------------------
				//	Release the brush.
				//---------------------------------------------

				DeleteObject( (HGDIOBJ)hBrush );
			}
		} // end of if icon is selection anchor
    } // end of for loop


	//-----------------------------------------------------
	//	Call EndPaint() to terminating painting.
	//-----------------------------------------------------

    EndPaint( hWnd, &ps );


	return;
}



//-----------------------------------------------------------------------
//	LMBDownHandler()
//
//	DESCRIPTION:
//
//		Handles the WM_LBUTTONDOWN messages for the application window.
//
//	PARAMETERS:
//
//		wParam			The wParam value as passed to the window
//						  procedure.  Indicates whether various
//						  virtual keys (CTRL and SHIFT) are down.
//
//		lParam			The lParam value as passed to the window
//						  procedure.  The low-order word specifies
//						  the x-coordinate of the cursor.  The
//						  high-order word specifies the y-coordinate
//						  of the cursor.  The coordinates are
//						  relative to the upper-left corner of the
//						  client area.
//
//	RETURNS:
//
//		None.
//
//	NOTES:
//
//		Since the icons may be selected and focused by clicking
//		them with the left mouse button, this handler must determine
//		if a mouse click occurred over an icon and then update
//		the icons' states appropriately.
//
//-----------------------------------------------------------------------

void LMBDownHandler( WPARAM wParam, LPARAM lParam )
{
	int		nIcon;


	//-----------------------------------------------------
	//	Determine if an icon was clicked.
	//-----------------------------------------------------

	nIcon = HasAnIconBeenClicked( lParam );


	//-----------------------------------------------------
	//	Update the selection, focus, and anchor of all
	//	  the icons based on whether or not an icon was
	//	  clicked.
	//-----------------------------------------------------

	UpdateIconSelection( nIcon, wParam );


	return;
}
	
	
	
	
//-----------------------------------------------------------------------
//	HasAnIconBeenClicked()
//
//	DESCRIPTION:
//
//		Determines whether or not a mouse button click occurred
//		over an icon.
//
//	PARAMETERS:
//
//		lParam			The lParam value as passed to the window
//						  procedure for the WM_LBUTTONDOWN
//						  message.  The low-order word specifies
//						  the x-coordinate of the cursor.  The
//						  high-order word specifies the y-coordinate
//						  of the cursor.  The coordinates are
//						  relative to the upper-left corner of the
//						  client area.
//
//	RETURNS:
//
//		int			The return from HasAnIconBeenClicked( POINT ).
//
//	NOTES:
//
//		This function uses the LPARAM parameter to create a POINT
//		structure containing the location of the mouse cursor
//		when it was clicked.  Then, it passes this POINT to the
//		overloaded HasAnIconBeenClicked( POINT ) to actually
//		determine if the mouse click occurred over an icon.
//
//-----------------------------------------------------------------------

int HasAnIconBeenClicked( LPARAM lp )
{
	POINT	pt;

	pt.x = (long) LOWORD(lp);
	pt.y = (long) HIWORD(lp);

	return HasAnIconBeenClicked( pt );
}



//-----------------------------------------------------------------------
//	GetIconSelectionAnchor()
//
//	DESCRIPTION:
//
//		Determines which icon, if any, is the current icon selection
//		anchor.
//
//	PARAMETERS:
//
//		None.
//
//	RETURNS:
//
//		int			The index into the icon information array of
//					  the icon that is the selection anchor or
//					  NO_ICON_SEL_ANCHOR.
//
//	NOTES:
//
//		This function simply loops through the icon information
//		array looking for the first icon (because there will be
//		only one) whose bIsSelAnchor flag is TRUE.  If one is
//		found, its index is returned; otherwise NO_ICON_SEL_ANCHOR
//		is returned.
//
//-----------------------------------------------------------------------

int GetIconSelectionAnchor( void )
{
	for ( int i = 0; i < NUM_ICONS; i++ )
		if ( g_rgIcons[i].bIsSelAnchor )
			return i;


	return NO_ICON_SEL_ANCHOR;
}



//-----------------------------------------------------------------------
//	IsIconInRange()
//
//	DESCRIPTION:
//
//		Determines if the icon index specified by the first parameter
//		is within the inclusive range established by the second and
//		third parameters.
//
//	PARAMETERS:
//
//		nIcon			The index of the icon for which we wish to know
//						  is it inclusively within the range specified
//						  by nIconIndex1 and nIconIndex2.
//
//		nIconIndex1		One of the bounds of the testing range.
//
//		nIconIndex2		The other bound of the testing range.
//
//	RETURNS:
//
//		BOOL			TRUE if nIcon is greater than or equal to the
//						  smaller of nIconIndex1 and IconIndex2 AND if
//						  nIcon is less than or equal to the larger of
//						  nIconIndex1 and nIconIndex2, FALSE otherwise.
//
//	NOTES:
//
//		nIconIndex1 is not necessarily less than nIconIndex2, and
//		it may actually be equal to nIconIndex2.
//
//-----------------------------------------------------------------------

BOOL IsIconInRange( int nIcon, int nIconIndex1, int nIconIndex2 )
{
	int		nIndexMax, nIndexMin;


	nIndexMax = max( nIconIndex1, nIconIndex2 );
	nIndexMin = min( nIconIndex1, nIconIndex2 );


	return ( nIndexMin <= nIcon  &&  nIcon <= nIndexMax );
}



//-----------------------------------------------------------------------
//	ActivationHandler()
//
//	DESCRIPTION:
//
//		Handles the WM_ACTIVATE messages for the application window.
//
//	PARAMETERS:
//
//		hWnd			Window handle of the application window.
//
//		uMsg			WM_ACTIVATE.
//
//		wParam			The wParam as passed to the window procedure
//						  for the WM_ACTIVATE message.  The low-order
//						  word specifies whether the window is being
//						  activated or deactivated.  The high-order
//						  word specifies the minimized state of the
//						  window being activated or deactivated.
//						  A nonzero value indicates the window is
//						  minimized.
//
//		lParam			The lParam as passed to the window procedure
//						  for the WM_ACTIVATE message.  Identifies
//						  the window being activated or deactivated,
//						  depending on the value of the low-order word
//						  of wParam.
//
//	RETURNS:
//
//		LRESULT			If the WM_ACTIVATE message is processed, 0L is
//						  returned.  Otherwise, the message is passed
//						  to DefWindowProc() and its return value is
//						  returned.
//
//	NOTES:
//
//		WM_ACTIVATE messages are sent to a window as it is being
//		activated, but before it receives the input focus.
//		When DefWindowProc() processes this message, it sets the
//		keyboard focus to the window if that window is being activated
//		and is not minimized.  Generally, this behavior is acceptable
//		for the application.  However, when the application window is
//		being activated and the window is not minimized and the push
//		button should have the focus, the default processing must be
//		circumvented to give the focus to the button.
//
//-----------------------------------------------------------------------

LRESULT	ActivationHandler( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	//-----------------------------------------------------
	//	Is the window being activated AND is the window
	//	  not minimized AND should the push button be
	//	  given the input focus?  If so, set the focus
	//	  to the push button and return 0L; otherwise
	//	  pass the message of to the default window
	//	  procedure.
	//-----------------------------------------------------

	if ( LOWORD( wParam ) != WA_INACTIVE  &&
	     HIWORD( wParam ) != TRUE         &&
	     g_nLastChildWithFocus == ID_BUTTON )
	{
		Button_SetFocus();
		return 0L;
	}
	else
		return DefWindowProc( hWnd, uMsg, wParam, lParam );
}



//-----------------------------------------------------------------------
//	SetFocusHandler()
//
//	DESCRIPTION:
//
//		Handles the WM_SETFOCUS messages for the application window.
//
//	PARAMETERS:
//
//		hWndLostFocus	Window handle of the window that lost the focus.
//
//	RETURNS:
//
//		None.
//
//	NOTES:
//
//		If one of the icons had the focus when the window was
//		deactivated, then that icon needs to be given the focus again
//		when the window actually receives it.  For the icon, this
//		means updating its bHasFocus flag, redrawing it so that the
//		focus rect is visible around it, and updating the status bar.
//
//		Note that if the button had the focus when the window was
//		deactivated, it receives the focus again during the processing
//		of the WM_ACTIVATE message, not here.  The focus is returned
//		to the button there so that there is no circular focus-pocus.
//
//-----------------------------------------------------------------------

void SetFocusHandler( HWND hWndLostFocus )
{
	//-----------------------------------------------------
	//	If the button lost the focus to the application
	//	  window, g_nLastChildWithFocus should be set to
	//	  NO_CHILD_HAS_FOCUS.  Why?  Well, because the
	//	  button is losing the focus, so
	//	  g_nLastChildWithFocus should not be set to
	//	  ID_BUTTON.  Since the button most recently had
	//	  the focus, none of the icons should receive i.
	//	  And, if this setting of the focus to the window
	//	  resulted because an icon was clicked,
	//	  g_nLastChildWithFocus will be updated in the
	//	  appropriate mouse button handler.
	//-----------------------------------------------------

	if ( hWndLostFocus == g_hWndButton )
		g_nLastChildWithFocus = NO_CHILD_HAS_FOCUS;


	//-----------------------------------------------------
	//	Otherwise, if an icon had the focus, give it
	//	  back to it by setting its bHasFocus flag to
	//	  TRUE and repainting the window.  Then,
	//	  update the status bar to reflect the current
	//	  focus/selection state.
	//-----------------------------------------------------

	else if ( g_nLastChildWithFocus >= ICON_JEWELS  &&
	          g_nLastChildWithFocus <= ICON_CLOTHING )
	{
		g_rgIcons[g_nLastChildWithFocus].bHasFocus = TRUE;
		InvalidateRect( g_hWndApp, NULL, TRUE );
		UpdateWindow( g_hWndApp );

		UpdateStatusBarMessage( FALSE );


		/////////////////////////////////////////////////////////////////////
		//                                                                 //
		//  MSAA SUPPORT ADDITION:                                         //
		//                                                                 //
		//	Send a WinEvent because icon g_nLastChildWithFocus received    //
		//	  the focus.                                                   //
		//                                                                 //
		/////////////////////////////////////////////////////////////////////

		if ( g_pAccClient != NULL )
			g_pAccClient->SendWinEventForChild( EVENT_OBJECT_FOCUS, g_nLastChildWithFocus );
	}


	return;
}

			
			
			
//-----------------------------------------------------------------------
//	KillFocusHandler()
//
//	DESCRIPTION:
//
//		Handles the WM_KILLFOCUS messages for the application window.
//
//	PARAMETERS:
//
//		hWndGetsFocus	Window handle of the window that will receive
//						  the focus.
//
//	RETURNS:
//
//		None.
//
//	NOTES:
//
//		Since the main window is losing the focus, it must be
//		determined if an icon has the focus.  If so, certain steps
//		need to be taken to ensure that the icon appears defocused
//		and that the icon receives the focus again when the window
//		actually gets the focus.
//
//		If the window is losing the focus to the button,
//		g_nLastChildWithFocus and the status bar text will be
//		updated appropriately in the BN_SETFOCUS handling portion
//		of ButtonMsgHandler().
//
//-----------------------------------------------------------------------

void KillFocusHandler( HWND hWndGetsFocus )
{
	//-----------------------------------------------------
	//	The main window is losing the focus, so determine
	//	  if an icon has the focus.
	//-----------------------------------------------------

	for ( int i = 0; i < NUM_ICONS; i++ )
	{
		//-----------------------------------------------------
		//	If an icon has the focus, ...
		//-----------------------------------------------------

		if ( g_rgIcons[i].bHasFocus )
		{
			//-----------------------------------------------------
			//	update g_nLastChildWithFocus with the index of
			//	  the icon, ...
			//-----------------------------------------------------

			g_nLastChildWithFocus = i;


			//-----------------------------------------------------
			//	set the bHasFocus member of the icon's structure
			//	  to FALSE, ...
			//-----------------------------------------------------

			g_rgIcons[i].bHasFocus = FALSE;


			//-----------------------------------------------------
			//	repaint the window to remove the focus rect from
			//	  the icon, and ...
			//-----------------------------------------------------

			InvalidateRect( g_hWndApp, NULL, TRUE );
			UpdateWindow( g_hWndApp );


			//-----------------------------------------------------
			//	if the window is not losing the focus to the
			//	  button, update the status bar text.
			//-----------------------------------------------------

			if ( hWndGetsFocus != g_hWndButton )
				UpdateStatusBarMessage( FALSE );


			//-----------------------------------------------------
			//	Since at most one icon has the focus, terminate
			//	  the for loop because that icon was found.
			//-----------------------------------------------------

			break;
		}
	} // end of for loop


	return;
}

			
			
			
//-----------------------------------------------------------------------
//	DrawButtonHandler()
//
//	DESCRIPTION:
//
//		Processes the WM_DRAWITEM messages for the application window.
//		In response to these messages, the button is drawn in the state
//		specified by the DRAWITEMSTRUCT pointed to by the in parameter.
//
//	PARAMETERS:
//
//		lpDI  			A pointer to the DRAWITEMSTRUCT to be used to
//						  draw the button.
//
//	RETURNS:
//
//		None.
//
//-----------------------------------------------------------------------

void DrawButtonHandler( LPDRAWITEMSTRUCT lpDI )
{
	BOOL		bButtonHasFocus, bButtonPushed;
	int			x, y;


	//-----------------------------------------------------
	//	Determine the button's state.
	//-----------------------------------------------------

	bButtonPushed = lpDI->itemState & ODS_SELECTED;
	bButtonHasFocus = lpDI->itemState & ODS_FOCUS;


	//-----------------------------------------------------
	//	Draw the button's frame in the appropriate "pushed"
	//	  or "unpushed" state.
	//-----------------------------------------------------

	DrawFrameControl( lpDI->hDC, &lpDI->rcItem, DFC_BUTTON,
			                  bButtonPushed ? (DFCS_BUTTONPUSH | DFCS_PUSHED) : DFCS_BUTTONPUSH);


	//-----------------------------------------------------
	//	Prepare to draw the button's icon by setting the
	//	  background drawing mode to transparent.
	//-----------------------------------------------------

	SetBkMode( lpDI->hDC, TRANSPARENT );


	//-----------------------------------------------------
	//	Determine the upper left corner of the button's
	//	  icon based on the bounding rectangle of the button.
	//-----------------------------------------------------

	x = lpDI->rcItem.left + 2 * g_dxMargin + ( bButtonPushed ? 1 : 0 );
	y = lpDI->rcItem.top + 2 * g_dyMargin + ( bButtonPushed ? 1 : 0 );


	//-----------------------------------------------------
	//	Draw the button's icon.
	//-----------------------------------------------------

	DrawIcon( lpDI->hDC, x, y, LoadIcon(NULL, IDI_QUESTION) );


	//-----------------------------------------------------
	//	Prepare to draw the focus rectangle by setting
	//	  the background drawing mode to opaque.
	//-----------------------------------------------------

	SetBkMode( lpDI->hDC, OPAQUE );


	//-----------------------------------------------------
	//	If the button has the focus, draw the focus
	//	  rectangle on the button just slightly smaller
	//	  than the button's bounding rectangle.
	//-----------------------------------------------------

	if ( bButtonHasFocus )
	{
		InflateRect( &lpDI->rcItem, -g_dxMargin, -g_dyMargin );
		DrawFocusRect( lpDI->hDC, &lpDI->rcItem );
	}


	return;
}




//-----------------------------------------------------------------------
//	ButtonNotifyMsgHandler()
//
//	DESCRIPTION:
//
//		Processes specific push button notification messages.
//
//	PARAMETERS:
//
//		wNotifyCode		The button notification message.
//
//	RETURNS:
//
//		LRESULT			TRUE if the notification message was processed,
//						  FALSE otherwise.
//
//	NOTES:
//
//		The following button notification message are processed:
//
//		BN_CLICKED
//		BN_SETFOCUS
//		BN_KILLFOCUS
//
//-----------------------------------------------------------------------

BOOL ButtonNotifyMsgHandler( WORD wNotifyCode )
{
	TCHAR		szStr[BUF_SIZE];


	switch ( wNotifyCode )
	{
		case BN_CLICKED:
			//-----------------------------------------------------
			//	The button was clicked, so load the help message
			//	  from the string table and display a message box.
			//-----------------------------------------------------

			LoadString( g_hInst, IDS_HELPBUTTONMSG, (LPTSTR) szStr, BUF_SIZE );

			MessageBox( g_hWndApp, szStr, g_szAppName, MB_ICONEXCLAMATION | MB_OK );

			break;


		case BN_SETFOCUS:
			//-----------------------------------------------------
			//	The button is gaining the focus, so update
			//	  both the g_nLastChildWithFocus identifier and
			//	  the status bar message with TRUE (the button
			//	  has the focus).
			//-----------------------------------------------------

			g_nLastChildWithFocus = ID_BUTTON;

			UpdateStatusBarMessage( TRUE );


			/////////////////////////////////////////////////////////////////////
			//                                                                 //
			//  MSAA SUPPORT ADDITION:                                         //
			//                                                                 //
			//	Send a WinEvent because the button received the focus.         //
			//                                                                 //
			/////////////////////////////////////////////////////////////////////

			if ( g_pAccClient != NULL )
				g_pAccClient->SendWinEventForChild( EVENT_OBJECT_FOCUS, ID_BUTTON );

            break;


		case BN_KILLFOCUS:
			//-----------------------------------------------------
			//	The button is losing the focus, so update
			//	  the status bar message with FALSE (the button
			//	  does not have the focus).
			//-----------------------------------------------------
			UpdateStatusBarMessage( FALSE );

			break;


		default:
			//-----------------------------------------------------
			//	The button notification message was not processed,
			//	  so return FALSE.
			//-----------------------------------------------------
			return FALSE;
	}


	//-----------------------------------------------------
	//	The button notification message was processed,
	//	  so return TRUE.
	//-----------------------------------------------------
    return TRUE;
}




//-----------------------------------------------------------------------
//	UpdateWindowAndStatusBar()
//
//	DESCRIPTION:
//
//		Repaints the client area of the main window and calls
//		UpdateStatusBarMessage() to redraw the status bar text.
//
//	PARAMETERS:
//
//		None.
//
//	RETURNS:
//
//		None.
//
//-----------------------------------------------------------------------

void UpdateWindowAndStatusBar( void )
{
	//-----------------------------------------------------
	//	Repaint the window.
	//-----------------------------------------------------

	InvalidateRect( g_hWndApp, NULL, TRUE );
	UpdateWindow( g_hWndApp );


	//-----------------------------------------------------
	//	Update the status bar text.
	//-----------------------------------------------------

	UpdateStatusBarMessage( GetFocus() == g_hWndButton );


	return;
}




//-----------------------------------------------------------------------
//	UpdateStatusBarMessage()
//
//	DESCRIPTION:
//
//		Updates the status bar text based on which object (icon or
//		button) has the input focus and how many, if any, icons are
//		selected.
//
//	PARAMETERS:
//
//		bButtonHasFocus		Flag specifying whether or not the button
//							  has the input focus (or is to be treated
//							  as if it had the input focus for the
//							  duration of this function).
//
//	RETURNS:
//
//		None.
//
//	NOTES:
//
//		The following are various text strings that will be
//		displayed in the status bar:
//
//			Object with focus: none.
//			Object with focus: none (1 icon selected).
//			Object with focus: none (X icons selected).
//			Object with focus: push button.
//			Object with focus: push button (1 icon selected).
//			Object with focus: push button (X icons selected).
//			Object with focus: Y icon (1 icon selected).
//			Object with focus: Y icon (X icons selected).
//
//		where X is a digit between 2 and 7 inclusive (there are
//		seven icons) and Y is the name of an icon (ring, money,
//		art, mansion, person,plane, or clothing).
//
//		All strings are built dynamically based on the focus and
//		selection states of the icons and the button.  All
//		sub-strings comprising the final status bar text are loaded
//		from a string table resource.
//
//-----------------------------------------------------------------------

void UpdateStatusBarMessage( BOOL bButtonHasFocus )
{
	TCHAR	szStatusMsg[BUF_SIZE * 4];
	TCHAR	szTmp[BUF_SIZE];
	int		i, nObjSelCnt;
	int		nStrIDIconWithFocus = NO_ICON_HAS_FOCUS;


	//-----------------------------------------------------
	//	Load the initial status bar message portion from
	//	  the string table.
	//-----------------------------------------------------

	LoadString( g_hInst, IDS_SBTEXT_OBJFOCUS, (LPTSTR) szStatusMsg, BUF_SIZE * 4 );


	//-----------------------------------------------------
	//	Append a space.
	//-----------------------------------------------------

	lstrcat( szStatusMsg, _TEXT(" ") );

	
	//-----------------------------------------------------
	//	If the push button has the focus, append the
	//	  button string.
	//-----------------------------------------------------

	if ( bButtonHasFocus )
	{
		LoadString( g_hInst, IDS_SBTEXT_PUSHBUTTON, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
	}


	//-----------------------------------------------------
	//	If the button doesn't have the focus, determine
	//	  if any icon does.
	//-----------------------------------------------------

	else
	{
		for ( i = 0; i < NUM_ICONS; i++ )
			if ( g_rgIcons[i].bHasFocus )
			{
				nStrIDIconWithFocus = IDS_SBTEXT_ICONRING + i;
				break;
			}
	}


	//-----------------------------------------------------
	//	Determine how many, if any, icons are selected.
	//-----------------------------------------------------

	nObjSelCnt = 0;

	for ( i = 0; i < NUM_ICONS; i++ )
		if ( g_rgIcons[i].bIsSelected )
			nObjSelCnt++;


	//-----------------------------------------------------
	//	If an icon has the focus, append its name to the
	//	  string.
	//-----------------------------------------------------

	if ( nStrIDIconWithFocus != NO_ICON_HAS_FOCUS )
	{
		LoadString( g_hInst, nStrIDIconWithFocus, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
		lstrcat( szStatusMsg, _TEXT(" ") );
		LoadString( g_hInst, IDS_SBTEXT_ICONICON, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
	}

	//-----------------------------------------------------
	//	Otherwise, if the button does not have the focus
	//	  as well, append "none" to the string.
	//-----------------------------------------------------

	else if ( !bButtonHasFocus )
	{
		LoadString( g_hInst, IDS_SBTEXT_NONE, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
	}


	//-----------------------------------------------------
	//	If there is at least one icon selected, append
	//	  the count of selected icons to the string.
	//-----------------------------------------------------

	if ( nObjSelCnt )
	{
		lstrcat( szStatusMsg, _TEXT(" ") );
		LoadString( g_hInst, IDS_SBTEXT_OPENPARAN, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
		wsprintf( szTmp, _TEXT("%i"), nObjSelCnt );
		lstrcat( szStatusMsg, szTmp );
		lstrcat( szStatusMsg, _TEXT(" ") );
		LoadString( g_hInst, IDS_SBTEXT_ICONICON, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
		if ( nObjSelCnt > 1 )
		{
			LoadString( g_hInst, IDS_SBTEXT_SINGLE_S, (LPTSTR) szTmp, BUF_SIZE );
			lstrcat( szStatusMsg, szTmp );
		}
		lstrcat( szStatusMsg, _TEXT(" ") );
		LoadString( g_hInst, IDS_SBTEXT_SELECTED, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
		LoadString( g_hInst, IDS_SBTEXT_CLOSEPARAN, (LPTSTR) szTmp, BUF_SIZE );
		lstrcat( szStatusMsg, szTmp );
	}


	//-----------------------------------------------------
	//	Append a period to the string.
	//-----------------------------------------------------

	LoadString( g_hInst, IDS_SBTEXT_PERIOD, (LPTSTR) szTmp, BUF_SIZE );
	lstrcat( szStatusMsg, szTmp );


	//-----------------------------------------------------
	//	If the newly constructed status bar text is
	//	  different from the current, set the new text
	//	  to the status bar.
	//-----------------------------------------------------

	if ( lstrcmp( StatusBar_GetText(), (LPTSTR) szStatusMsg ) )
	{
		StatusBar_SetText( g_hWndStatusBar, (LPTSTR) szStatusMsg );

		//////////////////////////////////////////////////////////////////
		//                                                              //
		//  MSAA SUPPORT ADDITION:                                      //
		//                                                              //
		//  Send a VALUECHANGE WinEvent for the status bar.             //
		//                                                              //
		//////////////////////////////////////////////////////////////////
		if ( g_pAccClient != NULL )
			g_pAccClient->SendWinEventForChild( EVENT_OBJECT_VALUECHANGE, ID_STATUSBAR );
	}


	return;
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  MSAA SUPPORT ADDITION:                                             //
//                                                                     //
//  Function definition for GetObjectHandler().                        //
//                                                                     //
/////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------
//	GetObjectHandler()
//
//	DESCRIPTION:
//
//		Processes the WM_GETOBJECT messages for the application window.
//
//	PARAMETERS:
//
//		hWnd			Handle to the application window.
//
//		wParam			The wParam as passed to the window procedure
//						  for the WM_GETOBJECT message.  This value
//						  contains additional information about the
//						  message and is to be passed to the
//						  LresultFromObject() function as the wParam
//						  parameter.
//
//		lParam			The lParam as passed to the window procedure
//						  for the WM_GETOBJECT message.  This value
//						  is the identifier of the object whose
//						  IAccessible pointer is desired.  This value
//						  can be one of the object identifier constants
//						  or a custom object identifier.
//
//	RETURNS:
//
//		LRESULT			A value specifying a reference to the desired
//						  IAccessible object, a request for a standard
//						  Accessible object, or a COM error (see NOTES
//						  below).
//
//	NOTES:
//
//		MSAA queries the server for HWND-related Accessible objects
//		by sending it the WM_GETOBJECT message with the identifier
//		of the desired object.  If the object identifier is one that
//		is supported (this server only supports the OBJID_CLIENT ID),
//		a pointer to that object's IAccessible interface is returned
//		by using the the LresultFromObject() API.  For all other
//		object IDs, zero is returned to request a standard Accessible
//		object from the system.  If an error occurs while processing
//		the WM_GETOBJECT message, the associated COM error code is
//		returned.
//
//-----------------------------------------------------------------------

LRESULT GetObjectHandler( HWND hWnd, WPARAM wParam, LPARAM lParam )
{
	LRESULT	lr;
	HRESULT	hr;


	//-----------------------------------------------------
	//	If we aren't ready to process the WM_GETOBJECT
	//	  message, because either the application window
	//	  has not been shown yet or the application is
	//	  shutting down, return the COM error E_FAIL.
	//	  (The MSAA documentation for WM_GETOBJECT does
	//	  not specify which error code to return in this
	//	  case.  E_FAIL, meaning "unspecified error,"
	//	  seems as reasonable as any other COM error code.)
	//-----------------------------------------------------

	if ( !g_bReadyForWMGETOBJECT )
		return E_FAIL;


	switch ( lParam )
	{
		//-----------------------------------------------------
		//	We process the OBJID_CLIENT object identifier;
		//	  this is the client area of our application
		//	  window.
		//-----------------------------------------------------

		case OBJID_CLIENT:

			//-----------------------------------------------------
			//	If this is the first WM_GETOBJECT that we've
			//	  processed, create our Accessible object.
			//-----------------------------------------------------

			if ( g_pAccClient == NULL )
			{
				//-----------------------------------------------------
				//	Allocate our Accessible object.  If the allocation
				//	  fails, return E_OUTOFMEMORY.
				//-----------------------------------------------------

				g_pAccClient = new CAccClient();

				if ( g_pAccClient == NULL )
					return (LRESULT)E_OUTOFMEMORY;


				//-----------------------------------------------------
				//	Initialize our Accessible object.  If the
				//	  initialization fails, delete the Accessible
				//	  object and return the failure code.
				//-----------------------------------------------------

				hr = g_pAccClient->Initialize( hWnd, g_hInst );

				if ( FAILED(hr) )
				{
					delete g_pAccClient;
					g_pAccClient = NULL;
					return (LRESULT)hr;
				}


				///////////////////////////////////////////////////////
				//                                                   //
				//  MSAA  SUPPORT ADDITION:                          //
				//                                                   //
				//  Send an object creation WinEvent for the         //
				//	  creation of the CAccClient object.
				//                                                   //
				///////////////////////////////////////////////////////

				//-----------------------------------------------------
				//	Send an EVENT_OBJECT_CREATE WinEvent for the
				//	  creation of the Accessible object for the
				//	  client area.
				//-----------------------------------------------------

				NotifyWinEvent( EVENT_OBJECT_CREATE, hWnd, OBJID_CLIENT, CHILDID_SELF );
			}


			//-----------------------------------------------------
			//	Call LresultFromObject() to create reference to
			//	  our Accessible object that MSAA will marshal to
			//	  the client.
			//-----------------------------------------------------

			lr = LresultFromObject( IID_IAccessible,
			                        wParam,
			                        (IAccessible *) g_pAccClient );
			break;


		//-----------------------------------------------------
		//	For any object identifier that we don't process,
		//	  return zero to request a standard accessible
		//	  object from MSAA.
		//-----------------------------------------------------

		default:
			lr = (LRESULT)0L;
	}


	return lr;
}




/////////////////////////////////////////////////////////////////////////////
//                                                                         //
//  MSAA  SUPPORT ADDITION:                                                //
//                                                                         //
//  Function definition for DestroyHandler().                              //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------
//	DestroyHandler()
//
//	DESCRIPTION:
//
//		Handles the WM_DESTROY messages for the application window.
//
//	PARAMETERS:
//
//		hWnd			Window handle of the application window.
//
//	RETURNS:
//
//		None.
//
//	NOTES:
//
//		The main task of this function is to destroy the Accessible
//		object and post the quit message.
//
//-----------------------------------------------------------------------

void DestroyHandler( HWND hWnd )
{
	//---------------------------------------------------------
	//	If an Accessible object was created, destroy it now.
	//---------------------------------------------------------

	if ( g_pAccClient != NULL )
	{
		//-----------------------------------------------------
		//	At this point, we are destroying to the
		//	  application window.  Thus, we also need to
		//	  destroy the Accessible object.  (Remember that
		//	  the Accessible's object lifetime is bounded by
		//	  the application window.)
		//
		//	Recall that g_bReadyForWMGETOBJECT has been set
		//	  to FALSE in the processing of WM_CLOSE so we
		//	  are no longer processing WM_GETOBJECT messages.
		//	  However, one or more clients still might be
		//	  using an interface pointer of the Accessible
		//	  object.  So, we will call CoDisconnectObject()
		//	  to terminate all remote connections on our
		//	  object.
		//-----------------------------------------------------

		CoDisconnectObject( (LPUNKNOWN) g_pAccClient, 0L );


		//-----------------------------------------------------
		//	Now that no clients are using our Accessible
		//	  object, we can delete it.
		//-----------------------------------------------------

		delete g_pAccClient;
		g_pAccClient = NULL;


		//-----------------------------------------------------
		//	Send an EVENT_OBJECT_DESTROY WinEvent for the
		//	  destruction of the Accessible object for the
		//	  client area.
		//-----------------------------------------------------

		NotifyWinEvent( EVENT_OBJECT_DESTROY, hWnd, OBJID_CLIENT, CHILDID_SELF );
	}


	//-----------------------------------------------------
	//	The status bar has been destroyed already and
	//	  its corresponding global window handle has been
	//	  NULLed.  Since we are destroying the application
	//	  window and since Windows will destroy the button
	//	  shortly, we can set both of those global handles
	//	  to NULL;
	//-----------------------------------------------------

	g_hWndButton = NULL;
	g_hWndApp = NULL;


	//-----------------------------------------------------
	//	Post the quit message to terminate the
	//	  application's message loop.
	//-----------------------------------------------------

	PostQuitMessage(0);


	return;
}




//===========================================================================
//  Public Function Definitions
//===========================================================================

//-----------------------------------------------------------------------
//	HasAnIconBeenClicked()
//
//	DESCRIPTION:
//
//		Determines whether or not a mouse button click occurred
//		over an icon.
//
//	PARAMETERS:
//
//		pt			POINT structure representing the location of
//					  of the mouse cursor.
//
//	RETURNS:
//
//		int			If the POINT representing the mouse cursor
//					  location is within a rectangle representing
//					  the location of an icon, return the index
//					  into g_rgIcons of the corresponding icon.
//					  Otherwise, return NO_ICON_CLICKED.
//
//-----------------------------------------------------------------------

int HasAnIconBeenClicked( POINT pt )
{
	for ( int i = 0; i < NUM_ICONS; i++ )
		if ( PtInRect( &g_rgIcons[i].rcBounds, pt ) )
			return i;


	return NO_ICON_CLICKED;
}



//-----------------------------------------------------------------------
//	UpdateIconSelection()
//
//	DESCRIPTION:
//
//		Updates the selection, focus, and selection anchor states
//		for all icons based upon which icon was mouse clicked and
//		which virtual keys were held down when the icon was clicked.
//
//	PARAMETERS:
//
//		nIcon		The index or ID of the icon that has been
//					  mouse clicked (this could be NO_ICON_CLICKED).
//
//		fwKeys		Indicates whether various virtual keys are down.
//
//	RETURNS:
//
//		None.
//
//-----------------------------------------------------------------------

void UpdateIconSelection( int nIcon, ULONG fwKeys )
{
	int		i, nSelAnchor;


	//-----------------------------------------------------
	//	If an icon wasn't clicked, clear the selection,
	//	  the focus, and the selection anchor from all
	//	  icons.
	//-----------------------------------------------------

	if ( nIcon == NO_ICON_CLICKED )
	{
		for ( i = 0; i < NUM_ICONS; i++ )
		{
			g_rgIcons[i].bIsSelected = FALSE;
			g_rgIcons[i].bIsSelAnchor = FALSE;
			g_rgIcons[i].bHasFocus = FALSE;
		}


		//-----------------------------------------------------
		//	Since no icon was clicked, if the push button
		//	  does not have the input focus, the
		//	  g_nLastChildWithFocus identifier must be cleared
		//	  (set to NO_CHILD_HAS_FOCUS).
		//-----------------------------------------------------

		if ( GetFocus() != g_hWndButton )
			g_nLastChildWithFocus = NO_CHILD_HAS_FOCUS;
	}


	//-----------------------------------------------------
	//	Otherwise, an icon was clicked, so...
	//-----------------------------------------------------

	else
	{
		//-----------------------------------------------------
		//	Determine if there is a selection anchor.
		//-----------------------------------------------------

		nSelAnchor = GetIconSelectionAnchor();


		//-----------------------------------------------------
		//	For any icon, we need to determine if it is
		//	  selected, does it have the focus, and is it
		//	  the selection anchor?
		//-----------------------------------------------------

		for ( i = 0; i < NUM_ICONS; i++ )
		{
			//-----------------------------------------------------
			//	If the icon was clicked, it gets the focus;
			//	  otherwise, no focus.
			//-----------------------------------------------------

			g_rgIcons[i].bHasFocus = (i == nIcon);


			//-----------------------------------------------------
			//	If there is no current selection anchor, or if no
			//	  virtual key (CTRL or SHIFT) was held down when
			//	  the mouse click occurred, the clicked icon is
			//	  selected and becomes the anchor; all other icons
			//	  are not selected.
			//-----------------------------------------------------

			if ( nSelAnchor == NO_ICON_SEL_ANCHOR  ||
				 !((fwKeys & MK_CONTROL) || (fwKeys & MK_SHIFT)) )
			{
				g_rgIcons[i].bIsSelected = (i == nIcon);
				g_rgIcons[i].bIsSelAnchor = (i == nIcon);
			}


			//-----------------------------------------------------
			//	Otherwise, there is an selection anchor, so
			//	  update the icons' selection states based on
			//	  which virtual key(s) was(were) held down.
			//-----------------------------------------------------

			else if ( fwKeys & MK_SHIFT )
			{
				//-----------------------------------------------------
				//	The SHIFT key was held down, so the selection
				//	  anchor remains the same and all icons within
				//	  the inclusive "clicked icon/anchor" range must
				//	  be added to the selection.  What about those
				//	  icons outside of the "clicked icon/anchor" range?
				//	  If the CTRL key was also held down, those icons'
				//	  selection state does not change; otherwise,
				//	  those icons are deselected.
				//-----------------------------------------------------

				if ( IsIconInRange( i, nSelAnchor, nIcon ) )
					g_rgIcons[i].bIsSelected = TRUE;
				else if ( !(fwKeys & MK_CONTROL) )
					g_rgIcons[i].bIsSelected = FALSE;

			}
			else if ( fwKeys & MK_CONTROL )
			{
				//-----------------------------------------------------
				//	Only the CTRL key was held down, so the clicked
				//	  icon becomes the selection anchor and its
				//	  selection state inverts.  The selection state
				//	  of all unclicked icons remains unchanged.
				//-----------------------------------------------------

				g_rgIcons[i].bIsSelAnchor = (i == nIcon);

				if ( i == nIcon )
					g_rgIcons[i].bIsSelected = !(g_rgIcons[i].bIsSelected);

			}
		} // end of for loop


		//-----------------------------------------------------
		//	Update the g_nLastChildWithFocus identifier with
		//	  the ID of the clicked icon.
		//-----------------------------------------------------

		g_nLastChildWithFocus = nIcon;


		//-----------------------------------------------------
		//	If the button currently has the focus, explicitly
		//	  set the focus to the main window so it appears
		//	  as though the clicked icon has it.
		//-----------------------------------------------------

		if ( GetFocus() == g_hWndButton )
		{
			SetFocus( NULL );
			SetFocus( g_hWndApp );
		}

	} // end of else statement that complements "if ( nIcon == NO_ICON_CLICKED )"


	//-----------------------------------------------------
	//	Repaint the window and update the status bar.
	//-----------------------------------------------------

	UpdateWindowAndStatusBar();


	/////////////////////////////////////////////////////////////////////
	//                                                                 //
	//  MSAA SUPPORT ADDITION:                                         //
	//                                                                 //
	//	Send a WinEvent because icon nIcon received the focus.         //
	//                                                                 //
	/////////////////////////////////////////////////////////////////////

	if ( g_pAccClient != NULL )
		g_pAccClient->SendWinEventForChild( EVENT_OBJECT_FOCUS, nIcon );


	return;
}




//-----------------------------------------------------------------------
//	FocusAndAnchorIcon()
//
//	DESCRIPTION:
//
//		Assigns the focus and the selection anchor to the specified
//		icon, but does not alter the selection state of any icon.
//
//	PARAMETERS:
//
//		nIcon		[in]  The index or ID of the icon that is to be
//							focused and ancored.
//
//	RETURNS:
//
//		None.
//
//-----------------------------------------------------------------------

void FocusAndAnchorIcon( int nIcon )
{
	//-----------------------------------------------------
	//	The icon referred to by nIcon must be focused
	//	  and anchored; all other icons are defocused
	//	  and deanchored.  The selection state of all
	//	  icons remains unchanged.
	//-----------------------------------------------------

	for ( int i = 0; i < NUM_ICONS; i++ )
	{
		g_rgIcons[i].bHasFocus = ( i == nIcon );
		g_rgIcons[i].bIsSelAnchor = ( i == nIcon );
	}


	//-----------------------------------------------------
	//	Repaint the window and update the status bar.
	//-----------------------------------------------------

	UpdateWindowAndStatusBar();


	/////////////////////////////////////////////////////////////////////
	//                                                                 //
	//  MSAA SUPPORT ADDITION:                                         //
	//                                                                 //
	//	Send a WinEvent because icon nIcon received the focus.         //
	//                                                                 //
	/////////////////////////////////////////////////////////////////////

	if ( g_pAccClient != NULL )
		g_pAccClient->SendWinEventForChild( EVENT_OBJECT_FOCUS, nIcon );


	return;
}




//-----------------------------------------------------------------------
//	SelectIconOnly()
//
//	DESCRIPTION:
//
//		Selects the icon referred to by the in-parameter.
//		All other icons are deselected.
//
//	PARAMETERS:
//
//		nIcon		[in]  The index or ID of the icon that is to be
//							selected.
//
//	RETURNS:
//
//		None.
//
//-----------------------------------------------------------------------

void SelectIconOnly( int nIcon )
{
	//-----------------------------------------------------
	//	The icon referred to by nIcon must be selected;
	//	  all other icons are deselected.  The focus and
	//	  the selection anchor are not updated except in
	//	  the case where there is no selection anchor.
	//	  Then, the icon being selected becomes the
	//	  selection anchor.
	//-----------------------------------------------------

	for ( int i = 0; i < NUM_ICONS; i++ )
		g_rgIcons[i].bIsSelected = ( i == nIcon );

	if ( GetIconSelectionAnchor() == NO_ICON_SEL_ANCHOR )
		g_rgIcons[nIcon].bIsSelAnchor = TRUE;


	//-----------------------------------------------------
	//	Repaint the window and update the status bar.
	//-----------------------------------------------------

	UpdateWindowAndStatusBar();


	return;
}
	
	
	
	
//-----------------------------------------------------------------------
//	Button_HasFocus()
//
//	DESCRIPTION:
//
//		Returns a boolean value indicating whether or not the push
//		  button has the input focus.
//
//	PARAMETERS:
//
//		None.
//
//	RETURNS:
//
//		BOOL		TRUE if the button (g_hWndButton) has the input
//					  focus, FALSE otherwise.
//
//-----------------------------------------------------------------------

BOOL Button_HasFocus( void )
{
	return ( GetFocus() == g_hWndButton );
}



//-----------------------------------------------------------------------
//	Button_IsPushed()
//
//	DESCRIPTION:
//
//		Returns a boolean value indicating whether or not the push
//		  button is currently pressed down.
//
//	PARAMETERS:
//
//		None.
//
//	RETURNS:
//
//		BOOL		TRUE if the button (g_hWndButton) is pushed,
//					  FALSE otherwise.
//
//-----------------------------------------------------------------------
BOOL Button_IsPushed( void )

{
	LRESULT		lrButtonState;

	lrButtonState = SendMessage( g_hWndButton, BM_GETSTATE, 0, 0L );

	return ( lrButtonState & BST_PUSHED );
}



//-----------------------------------------------------------------------
//	Button_IsEqualHWND()
//
//	DESCRIPTION:
//
//		Determines if the handle to the button is the same as the
//		  handle passed in as a parameter.
//
//	PARAMETERS:
//
//		hWnd		Window handle to comparison test with the
//					  button's window handle.
//
//	RETURNS:
//
//		BOOL		TRUE if button window handle is the same as the
//					  parameter; FALSE otherwise.
//
//-----------------------------------------------------------------------
BOOL Button_IsEqualHWND( HWND hWnd )

{
	return ( g_hWndButton == hWnd );
}



//-----------------------------------------------------------------------
//	Button_Click()
//
//	DESCRIPTION:
//
//		Simulates the user clicking the button.
//
//	PARAMETERS:
//
//		None.
//
//	RETURNS:
//
//		None.
//
//-----------------------------------------------------------------------

void Button_Click( void )
{
	SendMessage( g_hWndButton, BM_CLICK, 0, 0 );
}



//-----------------------------------------------------------------------
//	Button_Click()
//
//	DESCRIPTION:
//
//		Sets the input focus to the button.
//
//	PARAMETERS:
//
//		None.
//
//	RETURNS:
//
//		None.
//
//-----------------------------------------------------------------------

void Button_SetFocus( void )
{
	SetFocus( g_hWndButton );
}



//-----------------------------------------------------------------------
//	Button_GetWindowRect()
//
//	DESCRIPTION:
//
//		Retrieves the dimensions of the bounding rectangle of the
//		  button in screen coordinates.
//
//	PARAMETERS:
//
//		lpRect		Points to the RECT structure that receives the
//					  screen coordinates of the upper-left and
//					  lower-right corners of the button. 
//
//	RETURNS:
//
//		BOOL		TRUE if GetWindowRect() succeeds, FALSE otherwise.
//
//-----------------------------------------------------------------------

BOOL Button_GetWindowRect( LPRECT lpRect )
{
	return GetWindowRect( g_hWndButton, lpRect );
}



//-----------------------------------------------------------------------
//	StatusBar_IsEqualHWND()
//
//	DESCRIPTION:
//
//		Determines if the handle to the status bar is the same
//		  as the handle passed in as a parameter.
//
//	PARAMETERS:
//
//		hWnd		Window handle to comparison test with the
//					  status bar's window handle.
//
//	RETURNS:
//
//		BOOL		TRUE if status bar window handle is the same
//					  as the parameter; FALSE otherwise.
//
//-----------------------------------------------------------------------

BOOL StatusBar_IsEqualHWND( HWND hWnd )
{
	return ( g_hWndStatusBar == hWnd );
}



//-----------------------------------------------------------------------
//	StatusBar_GetWindowRect()
//
//	DESCRIPTION:
//
//		Retrieves the dimensions of the bounding rectangle of the
//		  status bar in screen coordinates.
//
//	PARAMETERS:
//
//		lpRect		Points to the RECT structure that receives the
//					  screen coordinates of the upper-left and
//					  lower-right corners of the status bar.
//
//	RETURNS:
//
//		BOOL		TRUE if GetWindowRect() succeeds, FALSE otherwise.
//
//-----------------------------------------------------------------------

BOOL StatusBar_GetWindowRect( LPRECT lpRect )
{
	return GetWindowRect( g_hWndStatusBar, lpRect );
}




/*************************************************************************
    THE INFORMATION AND CODE PROVIDED HEREUNDER (COLLECTIVELY REFERRED TO
    AS "SOFTWARE") IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN
    NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR
    ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL,
    CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF
    MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR
    LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE
    FOREGOING LIMITATION MAY NOT APPLY.
*************************************************************************/


//----  End of SERVER.CPP  ----