/*
 * GIZMOBAR.C
 * GizmoBar Version 1.00, March 1993
 *
 * Contains the main window procedure of the GizmoBar control
 * that handles mouse logic and Windows messages.
 *
 * Copyright (c)1992 Microsoft Corporation, All Rights Reserved
 *
 * Kraig Brockschmidt, Software Design Engineer
 * Microsoft Systems Developer Relations
 *
 * Internet  :  kraigb@microsoft.com
 * Compuserve:  >INTERNET:kraigb@microsoft.com
 */

#ifdef WIN32
#define _INC_OLE
#define __RPC_H__
#endif


#define STRICT
#include <windows.h>
#include "gizmoint.h"



/*
 * GizmoBarWndProc
 *
 * Purpose:
 *  Window Procedure for the GizmoBar custom control.  Handles all
 *  messages like WM_PAINT just as a normal application window would.
 *  Any message not processed here should go to DefWindowProc.
 *
 * Parameters:
 *  Standard
 *
 * Return Value:
 *  Standard
 */

LRESULT FAR PASCAL GizmoBarWndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
	{
	BOOL            fSuccess;
	BOOL            fTemp;
	LPCREATESTRUCT  pCreate;
	PGIZMOBAR       pGB;
	PGIZMO          pGizmo;
	RECT            rc;
	short           x, y;
	COLORREF        cr;

	COMMANDPARAMS(wID, wCode, hWndMsg);

	//This will be valid for all messages except WM_NCCREATE
	pGB=(PGIZMOBAR)GetWindowLong(hWnd, GBWL_STRUCTURE);

	//Pass control messages onto another function for processing.
	if (iMsg >= WM_USER)
		return GBMessageHandler(hWnd, iMsg, wParam, lParam, pGB);

	//Handle standard Windows messages.
	switch (iMsg)
		{
		case WM_NCCREATE:
			pCreate=(LPCREATESTRUCT)lParam;

			pGB=GizmoBarPAllocate((LPINT)&fSuccess, hWnd, pCreate->hInstance
								  , pCreate->hwndParent, pCreate->style, 0);

			if (!fSuccess)
				{
				GizmoBarPFree(pGB);
				return -1L;
				}
			else
				SetWindowLong(hWnd, GBWL_STRUCTURE, (LONG)pGB);

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


		case WM_DESTROY:
			/*
			 * We want to clean up before DestroyWindow nukes all the
			 * children, so WM_DESTROY is a better to do it than
			 * WM_NCDESTROY.
			 */
			GizmoBarPFree(pGB);
			break;


		case WM_ERASEBKGND:
			/*
			 * Eat this message to avoid erasing portions that
			 * we are going to repaint in WM_PAINT.  Part of a
			 * change-state-and-repaint strategy is to rely on
			 * WM_PAINT to do anything visual, which includes
			 * erasing invalid portions.  Letting WM_ERASEBKGND
			 * erase the background is redundant.
			 */
			return TRUE;

#ifdef WIN32
		case WM_CTLCOLORBTN:
		case WM_CTLCOLORSTATIC:
#else
		case WM_CTLCOLOR:
			/*
			 * Change the color of static text on the GizmoBar.
			 */
			if (HIWORD(lParam)==CTLCOLOR_STATIC || HIWORD(lParam)==CTLCOLOR_BTN)
#endif
				{
				cr=GetSysColor(COLOR_BTNFACE);
				SetTextColor((HDC)wParam, GetSysColor(COLOR_BTNTEXT));
				SetBkColor((HDC)wParam, cr);

				/*
				 * If the system colors have changed, then crFace will
				 * not be equal to COLOR_BTNFACE, so we reinitialize the
				 * background brush.  This scheme handles system color
				 * changes appropriately without processing WM_WININICHANGE
				 * and without blindly creating a new brush on every
				 * WM_CTLCOLOR message.
				 */
				if (cr!=pGB->crFace)
					{
					pGB->crFace=cr;

					if (NULL!=pGB->hBrFace)
						DeleteObject(pGB->hBrFace);

					pGB->hBrFace=CreateSolidBrush(pGB->crFace);
					}

				return (LONG)(UINT)pGB->hBrFace;
				}

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


		case WM_PAINT:
			GizmoBarPaint(hWnd, pGB);
			break;


		case WM_SETFONT:
			/*
			 * wParam has the new font that we now send to all other
			 * windows controls in us.  We control repaints here to
			 * prevent a lot of repainting for each control.
			 */
			DefWindowProc(hWnd, WM_SETREDRAW, FALSE, 0L);
#ifdef WIN32
			if ( (WPARAM)NULL != wParam)
#else
			if ((WPARAM)NULL!=wParam && IsGDIObject((HFONT)wParam))
#endif
				{
				pGB->hFont=(HFONT)wParam;
				GizmoPEnum(&pGB->pGizmos, FEnumChangeFont, (DWORD)(LPSTR)pGB);

				DefWindowProc(hWnd, WM_SETREDRAW, TRUE, 0L);
				InvalidateRect(hWnd, NULL, FALSE);
				UpdateWindow(hWnd);
				}
			break;


		case WM_GETFONT:
			return (LRESULT)(UINT)pGB->hFont;


		case WM_ENABLE:
			/*
			 * wParam has the new enable flag that we use to enable
			 * or disable ALL controls in us at one time.  We also turn
			 * the redraw off to prevent a lot of flicker.
			 */
			DefWindowProc(hWnd, WM_SETREDRAW, FALSE, 0L);

			pGB->fEnabled=(BOOL)wParam;
			GizmoPEnum(&pGB->pGizmos, FEnumEnable, (DWORD)(LPSTR)pGB);

			DefWindowProc(hWnd, WM_SETREDRAW, TRUE, 0L);
			InvalidateRect(hWnd, NULL, FALSE);
			UpdateWindow(hWnd);
			break;


		case WM_CANCELMODE:
			pGizmo=pGB->pGizmoTrack;

			pGB->fTracking=FALSE;
			pGB->fMouseOut=FALSE;

			if (NULL!=pGizmo)
				GizmoPStateSet(hWnd, pGizmo, COMMANDBUTTON_UP);

			ReleaseCapture();
			break;


		case WM_LBUTTONDBLCLK:
		case WM_LBUTTONDOWN:
			//Get the mouse coordinates.
			x=LOWORD(lParam);
			y=HIWORD(lParam);


			/*
			 * See if we hit a command or attribute gizmo or not.  Anything
			 * else that is a control will get the message instead of
			 * us anyway, so we don't have to check.  FEnumHitTest also
			 * validates drawn gizmos, enabled, and visible, so we don't.
			 */
			pGizmo=GizmoPEnum(&pGB->pGizmos, FEnumHitTest, lParam);

			if (NULL==pGizmo)
				break;          //Didn't hit one matching our needs.

			/*
			 * Inform the associate that a command was hit like a menu item.
			 */
			if (NULL!=pGB->hWndAssociate)
				{
				if (pGizmo->fNotify)
					SendMenuSelect(pGB->hWndAssociate, pGizmo->uID, 0, 0);
				}

			/*
			 * We hit a button.  If it's a command or attribute, then change
			 * the state and start tracking.
			 */
			pGB->fTracking=TRUE;
			pGB->pGizmoTrack=pGizmo;
			pGB->fMouseOut=FALSE;
			SetCapture(hWnd);

			pGizmo->uStateOrg=pGizmo->uState;
			GizmoPStateSet(hWnd, pGizmo, ATTRIBUTEBUTTON_MOUSEDOWN);

			break;


		case WM_MOUSEMOVE:
				{
				POINT point;
#ifdef  WIN32
				point.x = (LONG)(SHORT)LOWORD(lParam);
				point.y = (LONG)(SHORT)HIWORD(lParam);
#else
				point.x = LOWORD(lParam);
				point.y = HIWORD(lParam);
#endif
			if (!pGB->fTracking)
				break;

			pGizmo=pGB->pGizmoTrack;
			SetRect(&rc, pGizmo->x, pGizmo->y, pGizmo->x+pGizmo->dx, pGizmo->y+pGizmo->dy);

			fTemp=pGB->fMouseOut;
			pGB->fMouseOut=!PtInRect(&rc, point);

			//If the mouse went out, change state to the original.
			if (!fTemp && pGB->fMouseOut)
				{
				GizmoPStateSet(hWnd, pGizmo, pGizmo->uStateOrg);

				if (NULL!=pGB->hWndAssociate)
					{
					//Notify that we left the button
					if (pGizmo->fNotify)
						SendMenuSelect(pGB->hWndAssociate, 0x0000, 0xFFFF, 0);
					}
				}

			if (fTemp && !pGB->fMouseOut)
				{
				GizmoPStateSet(hWnd, pGizmo, ATTRIBUTEBUTTON_MOUSEDOWN);

				if (NULL!=pGB->hWndAssociate)
					{
					//Notify that we pressed down again
					if (pGizmo->fNotify)
						SendMenuSelect(pGB->hWndAssociate, pGizmo->uID, 0x0000, 0);
					}
				}

			break;
				}

		case WM_LBUTTONUP:
			if (!pGB->fTracking)
				break;

			pGB->fTracking=FALSE;
			pGizmo=pGB->pGizmoTrack;
			ReleaseCapture();


			/*
			 * Repaint if we were actually below the mouse when this
			 * occurred.  For command buttons, pop the button up.  For
			 * attributes, either toggle the state (inclusive buttons)
			 * or check the selected one (exclusive buttons).
			 */

			if (!pGB->fMouseOut)
				{
				//Command buttons always come up.
				if (GIZMOTYPE_BUTTONCOMMAND==pGizmo->iType)
					GizmoPStateSet(hWnd, pGizmo, COMMANDBUTTON_UP);

				//Attribute inclusive buttons toggle
				if (GIZMOTYPE_BUTTONATTRIBUTEIN==pGizmo->iType)
					GizmoPCheck(hWnd, pGizmo, !(BUTTONGROUP_DOWN & pGizmo->uStateOrg));

				//Attribure exclusive buttons are always checked.
				if (GIZMOTYPE_BUTTONATTRIBUTEEX==pGizmo->iType)
					GizmoPCheck(hWnd, pGizmo, TRUE);

				//Only send messages if notify is ON.
				if (NULL!=pGB->hWndAssociate && pGizmo->fNotify)
					{
					SendMenuSelect(pGB->hWndAssociate, 0, 0xFFFF, 0);
					SendCommand(pGB->hWndAssociate, pGizmo->uID, BN_CLICKED, hWnd);
					}
				}

			break;


		case WM_COMMAND:
			//Pass control messages on if the gizmo's notify is ON.
			if (NULL!=pGB->hWndAssociate)
				{
				pGizmo=PGizmoFromHwndID(hWnd, wID);

				if (NULL!=pGizmo)
					{
					if (pGizmo->fNotify)
						SendMessage(pGB->hWndAssociate, iMsg, wParam, lParam);
					}
				}
			break;

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

	return 0L;
	}





/*
 * FEnumChangeFont
 *
 * Purpose:
 *  Enumeration callback for all the gizmos we know about in order to
 *  send a new font to them that's stored in PGIZMOBAR in dw.
 *
 * Parameters:
 *  pGizmo          PGIZMO to draw.
 *  iGizmo          UINT index on the GizmoBar of this gizmo.
 *  dw              DWORD extra data passed to GizmoPEnum, in our case
 *                  the GizmoBar's pGB.
 *
 * Return Value:
 *  BOOL            TRUE to continue the enumeration, FALSE otherwise.
 */

BOOL FAR PASCAL FEnumChangeFont(PGIZMO pGizmo, UINT iGizmo, DWORD dw)
	{
	PGIZMOBAR   pGB=(PGIZMOBAR)dw;

	//We only need to change fonts in windowed controls using WM_SETFONT
	if (NULL!=pGizmo->hWnd)
		SendMessage(pGizmo->hWnd, WM_SETFONT, (WPARAM)pGB->hFont, 1L);

	return TRUE;
	}






/*
 * FEnumEnable
 *
 * Purpose:
 *  Enumeration callback for all the gizmos we know about in order to
 *  enable or disable them from the WM_ENABLE message.
 *
 * Parameters:
 *  pGizmo          PGIZMO to draw.
 *  iGizmo          UINT index on the GizmoBar of this gizmo.
 *  dw              DWORD extra data passed to GizmoPEnum, in our case
 *                  the GizmoBar's pGB.
 *
 * Return Value:
 *  BOOL            TRUE to continue the enumeration, FALSE otherwise.
 */

BOOL FAR PASCAL FEnumEnable(PGIZMO pGizmo, UINT iGizmo, DWORD dw)
	{
	PGIZMOBAR   pGB=(PGIZMOBAR)dw;
	BOOL        fEnable=pGB->fEnabled;

	//NOTE:  This code is duplicated in GBGizmoEnable in API.C
	if (NULL!=pGizmo->hWnd)
		EnableWindow(pGizmo->hWnd, fEnable);
	else
		{
		//If we're not down, command and attribute buttons act the same.
		if (!(BUTTONGROUP_DOWN & pGizmo->uState))
			{
			GizmoPStateSet(pGB->hWnd, pGizmo
				, fEnable ? COMMANDBUTTON_UP : COMMANDBUTTON_DISABLED);
			}
		else
			{
			//Attribute buttons are a little more sensitive with DOWNDISABLED
			GizmoPStateSet(pGB->hWnd, pGizmo
				, fEnable ? ATTRIBUTEBUTTON_DOWN : ATTRIBUTEBUTTON_DOWNDISABLED);
			}
		}

	return TRUE;
	}







/*
 * FEnumHitTest
 *
 * Purpose:
 *  Enumeration callback for all the gizmos we know about in order to
 *  hit-test them.
 *
 * Parameters:
 *  pGizmo          PGIZMO to draw.
 *  iGizmo          UINT index on the GizmoBar of this gizmo.
 *  dw              DWORD extra data passed to GizmoPEnum, in our case
 *                  the hDC on which to draw.
 *
 * Return Value:
 *  BOOL            TRUE to continue the enumeration, FALSE otherwise.
 */

BOOL FAR PASCAL FEnumHitTest(PGIZMO pGizmo, UINT iGizmo, DWORD dw)
	{
	RECT  rc;
	 POINT point;
#ifdef  WIN32
	 point.x = (LONG)(SHORT)LOWORD(dw);
	 point.y = (LONG)(SHORT)HIWORD(dw);
#else
	 point.x = LOWORD(dw);
	 point.y = HIWORD(dw);
#endif


	//Hit tests have to happen on visible, enabled, and drawn controls only.
	if (GIZMOTYPE_DRAWN & pGizmo->iType
		&& !pGizmo->fHidden && !(BUTTONGROUP_DISABLED & pGizmo->uState))
		{
		SetRect(&rc, pGizmo->x, pGizmo->y, pGizmo->x+pGizmo->dx, pGizmo->y+pGizmo->dy);

		//Stop enumeration if we have a hit.
		return !PtInRect(&rc, point);
		}

	return TRUE;
	}
