//
//**************************************************************************
//
// Bhatch.cpp - An implementation of the hatched border around inplace
//              active windows.
//
// Modifications to OLE2UI version:
//              - C++ version
//              - removed restriction that parent must be non-null in CreateHatchWindow
//              - changed window from a child to a popup to allow null parent
//              - "extra bytes" removed in favor of member data
//              - added Paint() and InitWidth() to unclutter wnd proc
//              - added hit testing and correct cursors for handles
//              - added resizing for handles
//              - added NegotiateSize() to mediate inplace size negotiations between
//                the container and the server
//
// Copyright (c) 1993,94 by Borland International, Inc. All rights reserved
//
//**************************************************************************

#include "BOle.h"

// #define STRICT  1
#define STRICT
#include "OLE2UI.h"
#include "BHatch.h"

// class name of hatch window
#ifdef ANSI
  #define CLASS_HATCH "BOle Hatch Window"
#else
  #define CLASS_HATCH OLESTR("BOle Hatch Window")
#endif

// local function prototypes
LRESULT FAR PASCAL EXPORT BOleHatchWndProc (HWND, UINT, WPARAM, LPARAM);

// static initializer
UINT BOleHatchWindow::hatchWidth = 0;

// global functions
void SetRectExt (LPRECT r, UINT x, UINT y, UINT extent)
{
	r->left = x;
	r->top = y;
	r->right = x + extent;
	r->bottom = y + extent;
}

BOleHatchWindow::BOleHatchWindow(HWND hWndParent, HINSTANCE hInst, BOleSite *p)
{
  pBack = p; // assign BOleSite
#ifdef ANSI
  hWndHatch = ::CreateWindowExA(
    0,
    CLASS_HATCH,    // Class name
    "Hatch Window", // Window name
    WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
    0, 0, 0, 0,
    GetDesktopWindow(),
    (HMENU)NULL,
    hInst,
    this
  );
#else
  hWndHatch = ::CreateWindowEx(
    0,
    CLASS_HATCH,    // Class name
    OLESTR("Hatch Window"), // Window name
    WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
    0, 0, 0, 0,
    GetDesktopWindow(),
    (HMENU)NULL,
    hInst,
    this
  );
#endif

  hatchRect.left = hatchRect.top = hatchRect.bottom = hatchRect.right = 0;
  MoveRect.left = MoveRect.top = MoveRect.bottom = MoveRect.right = 0;
  nClippedFlag = 0;
  pSite = NULL;
  fInMouseDown = FALSE;
}

//***************************************************************************
// Destructor: ~BOleHatchWindow
//
// Purpose:
//              Public destructor to destroy the hWnd
//
//***************************************************************************
BOleHatchWindow::~BOleHatchWindow ()
{
	if (hWndHatch)
		::DestroyWindow (hWndHatch);
}

//***************************************************************************
// Static member function: Register
//
// Purpose:
//      Register the hatch window
//
// Parameters:
//      hInst                   Process instance
//
// Return Value:
//      TRUE                    if successful
//      FALSE                   if failed
//
//***************************************************************************
BOOL BOleHatchWindow::Register (HINSTANCE hInst)
{
#ifdef ANSI
  WNDCLASSA wc;
#else
  WNDCLASS wc;
#endif

  // Register Hatch Window Class
  wc.style = CS_BYTEALIGNWINDOW;
  wc.lpfnWndProc = BOleHatchWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = sizeof(BOleHatchWindow*);    // 'this' in extra bytes
  wc.hInstance = hInst;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = CLASS_HATCH;

#ifdef ANSI
  if (!::RegisterClassA(&wc))
#else
  if (!::RegisterClass(&wc))
#endif
    return FALSE;
  else
    return TRUE;
}

//***************************************************************************
// Static member function: InitWidth
//
// Purpose:
//              Initialize hatch border with from WIN.INI
//
//***************************************************************************
void BOleHatchWindow::InitWidth ()
{
#ifdef ANSI
  hatchWidth = GetProfileIntA("windows", "oleinplaceborderwidth",
			      DEFAULT_HATCHBORDER_WIDTH);
#else
  hatchWidth = GetProfileInt(OLESTR("windows"), OLESTR("oleinplaceborderwidth"),
			     DEFAULT_HATCHBORDER_WIDTH);
#endif
}

//***************************************************************************
// Member function: GetWidth
//
// Purpose:
//      Get width of hatch border
//
// Parameters:          None
//
// Return Value:
//    UINT                                      width of the hatch border
//***************************************************************************
UINT BOleHatchWindow::GetWidth ()
{
	if (!::IsWindow(hWndHatch))
		return 0;

	return hatchWidth;
}

//****************************************************************
// window messages related member functions
//
// Purpose:
//              Window Procedure dispatches all messages here.
//      Each member functions got the message name.
//              Do messages processing here.
//              Refer to windows help to get information about messages
//
//****************************************************************

//****************************************************************
// Member function: wmCreate
//
// Parameters:
//                      LPCREATESTRUCT  struct containing information about the window
//                                                                      to be created. Not used
//
// Return Value:
//                      LRESULT                         0L to continue creation process
//
//****************************************************************
#ifdef ANSI
LRESULT BOleHatchWindow::wmCreate (LPCREATESTRUCTA)
#else
LRESULT BOleHatchWindow::wmCreate (LPCREATESTRUCT)
#endif
{
	BOleHatchWindow::InitWidth ();
	return 0L;
}

//***************************************************************************
// Member function: wmPaint
//
// Purpose:
//              Draw hatched border and handles
//
// Parameters:                          None
//
// Return Value:
//                      LRESULT                         0L
//
//***************************************************************************
LRESULT BOleHatchWindow::wmPaint ()
{
	HDC hDC;
	PAINTSTRUCT ps;
	RECT rcHatchRect;

	hDC = ::BeginPaint(hWndHatch, &ps);
	// get hatchRect to paint hatch border
	rcHatchRect = hatchRect;
	SetRect((LPRECT)&rcHatchRect,rcHatchRect.left, rcHatchRect.top,
								rcHatchRect.right,
									rcHatchRect.bottom);
	OleUIDrawShading(&rcHatchRect, hDC, OLEUI_SHADE_BORDERIN,
			hatchWidth);
	// get the real visible rect to draw handles
	GetWindowRect(hWndHatch, (LPRECT)&rcHatchRect);
	SetRect((LPRECT)&rcHatchRect,0, 0, rcHatchRect.right-rcHatchRect.left,
									rcHatchRect.bottom-rcHatchRect.top);
	::InflateRect ((LPRECT)&rcHatchRect, -hatchWidth, -hatchWidth);
	OleUIDrawHandles (&rcHatchRect, hDC, OLEUI_HANDLES_OUTSIDE,
		hatchWidth+1, TRUE);
	::EndPaint (hWndHatch, &ps);

	return 0L;
}

//****************************************************************
// Member function: wmNCHitTest
//
// Purpose:
//                      Find out which handle the cursor is over
//
// Parameters:
//       LPPOINT                mouse position
//
// Return Value:
//       UINT                   a value indicating the position of the cursor
//
//****************************************************************
UINT BOleHatchWindow::wmNCHitTest (LPPOINT lpPoint)
{
	RECT handleRect;
	RECT rc; // contain hatch window rect

	// we want lpPoint to be in client coordinates
	::ScreenToClient (hWndHatch, lpPoint);

	GetWindowRect(hWndHatch, (LPRECT)&rc);
	SetRect((LPRECT)&rc,0,0,rc.right-rc.left,rc.bottom-rc.top);

	SetRectExt (&handleRect, rc.left, rc.top, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTTOPLEFT;

	SetRectExt (&handleRect, rc.left, (rc.top+rc.bottom-hatchWidth + 1)/2, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTLEFT;

	SetRectExt (&handleRect, rc.left, rc.bottom - hatchWidth + 1, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTBOTTOMLEFT;

	SetRectExt (&handleRect, (rc.left+rc.right-hatchWidth + 1)/2, rc.top, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTTOP;

	SetRectExt (&handleRect, (rc.left+rc.right-hatchWidth + 1)/2, rc.bottom - hatchWidth + 1, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTBOTTOM;

	SetRectExt (&handleRect, rc.right-hatchWidth + 1, rc.top, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTTOPRIGHT;

	SetRectExt (&handleRect, rc.right-hatchWidth + 1, (rc.top+rc.bottom-hatchWidth + 1)/2, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTRIGHT;

	SetRectExt (&handleRect, rc.right-hatchWidth + 1, rc.bottom-hatchWidth + 1, hatchWidth + 1);
	if (::PtInRect(&handleRect, *lpPoint))  return HTBOTTOMRIGHT;

	// If we've gotten this far, the cursor is not over a handle, but it
	// is over the hatch window (because we got the message). This means
	// the cursor is over the hatched border. It can't be over our client
	// area because the server object takes up the client area.
	//
	// So, by telling Windows that the cursor is over the caption bar, we
	// can piggy-back on Windows' code for dragging windows around.
	//
	return HTCAPTION;
}

//****************************************************************
// Member function: wmSetCursor
//
//      Purpose:
//                      Set the cursor bitmap to match the handle it's over.
//
// Parameters:
//                      HWND                    handle of the window with the cursor
//       UINT                   only used. Contains the value returned from wmNCHitTest
//                      UINT        Mouse message number
//
// Return Value:
//                      LRESULT         TRUE, to stop further processing
//
//****************************************************************
LRESULT BOleHatchWindow::wmSetCursor (HWND, UINT hitTestCode, UINT)
{

	switch (hitTestCode) {
		case HTTOPLEFT:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENWSE)));
			break;
		case HTLEFT:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZEWE)));
			break;
		case HTBOTTOMLEFT:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENESW)));
			break;
		case HTTOP:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENS)));
			break;
		case HTBOTTOM:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENS)));
			break;
		case HTTOPRIGHT:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENESW)));
			break;
		case HTRIGHT:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZEWE)));
			break;
		case HTBOTTOMRIGHT:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZENWSE)));
			break;
		default:
			::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE(IDC_ARROW)));
	}

	return (LRESULT)TRUE;
}

//****************************************************************
// Member function: wmMouseActivate
//
// Parameters:
//                      HWND                    handle of the top level parent. Not used
//       UINT                   hit test code. Not used
//                      UINT        Mouse message identifier. Not used
//
// Return Value:
//                      UINT                    MA_NOACTIVATE does not activate the window
//
//****************************************************************
UINT BOleHatchWindow::wmMouseActivate (HWND, UINT, UINT)
{
	return MA_NOACTIVATE;
}

//****************************************************************
// Member function: wmNCLButtonDown
//
// Parameters:
//                      UINT            hit test code as returned from WM_NCHITTEST
//                      LPOINT                  cursor coordinates
//
// Return Value:
//                      LRESULT         let it process by DefWindowProc
//
//****************************************************************
LRESULT BOleHatchWindow::wmNCLButtonDown (UINT nTestCode, LPPOINT lpPoint)
{
	// first resize window if needed
	if (nClippedFlag & CLIPPED) {
		RECT WinRect;
		// get the real (visible) window in client coordinates
		GetWindowRect( hWndHatch, (LPRECT)&WinRect);
		ScreenToClient( GetParent( hWndHatch), (LPPOINT)&WinRect);

		// if clipped left or top hatchRect adjust top and/or left
		if (nClippedFlag & CLIPPED_LEFT)
			WinRect.left += hatchRect.left;
		if (nClippedFlag & CLIPPED_TOP)
			WinRect.top += hatchRect.top;
		// move window
		MoveWindow( hWndHatch, WinRect.left, WinRect.top,
							hatchRect.right - hatchRect.left,
							hatchRect.bottom - hatchRect.top,
							FALSE);
	}

	// set flag indicating the user is moving the window and initiating
	// all space negotiation procedure
	fInMouseDown=TRUE;

	return ::DefWindowProc (hWndHatch, WM_NCLBUTTONDOWN, nTestCode, *((long *)lpPoint));
}

//****************************************************************
// Member function: wmGetMinMaxInfo
//
// Parameters:
//                      LPMINMAXINFO    struct containing resizing information
//
// Return Value:
//                      LRESULT                 0L when processing this message
//
//****************************************************************
LRESULT BOleHatchWindow::wmGetMinMaxInfo (MINMAXINFO* pMMI)
{
	int minWidth = (GetWidth() * 2) + 15;
	pMMI->ptMinTrackSize.x = minWidth;
	pMMI->ptMinTrackSize.y = minWidth;
	return 0L;
}

//****************************************************************
// Member function: wmWindowPosChanging
//
// Purpose:
//       Call negotiate size to activate the space negotiation
//                      procedure and, in case, changes the WINDOWPOS values
//                      so to accomplish the new size
//
// Parameters:
//       LPWINDOWPOS    structure containing information about
//                                                      resizing values
//
// Return Value:
//                      LRESULT
//
//****************************************************************
LRESULT BOleHatchWindow::wmWindowPosChanging (LPWINDOWPOS pWP)
{
		  if (hWndHatch == GetCapture()) {
				RECT TempRect;
				fInMouseDown = TRUE;
				::DefWindowProc (hWndHatch, WM_WINDOWPOSCHANGING, 0, (DWORD)(LPVOID)pWP);
				::InvalidateRect(hWndHatch, NULL, FALSE);
				::CopyRect((LPRECT)&TempRect, (LPRECT)&hatchRect);
				::InflateRect((LPRECT)&TempRect, -hatchWidth, -hatchWidth);
				::ValidateRect(hWndHatch, (LPRECT)&TempRect);
				return 0L;
		  }

		  if (fInMouseDown) {
					 int minWidth = (GetWidth() * 2) + 15;
					 if (pWP->cx < minWidth) {
								pWP->cx = minWidth;
					 }
					 if (pWP->cy < minWidth) {
								pWP->cy = minWidth;
					 }
					 NegotiateSize (pWP);
					 if (nClippedFlag & CLIPPED) {
								pWP->x = MoveRect.left;
								pWP->y = MoveRect.top;
								pWP->cx = MoveRect.right;
								pWP->cy = MoveRect.bottom;
								pWP->flags = 0;
					 }
					 ::InvalidateRect(hWndHatch, NULL, TRUE);
					 fInMouseDown = FALSE;
					 return 0L;
		  }

			return ::DefWindowProc (hWndHatch, WM_WINDOWPOSCHANGING, 0, (DWORD)(LPVOID)pWP);
}

//***************************************************************************
// Member function: Show
//
// Purpose:
//              Show or hide the hatch window; provide access to the site
//
// Parameters:
//              BOOL                                    TRUE to show the hatch window, FALSE to hide it
//              LPOLEINPLACESITE        Pointer to the in place site, which is used to
//                                              negotiate new size when a handle is dragged
//
//***************************************************************************
void BOleHatchWindow::Show (BOOL fShow, LPOLEINPLACESITE pS)
{
	pSite = pS;
	::ShowWindow (hWndHatch, fShow ? SW_SHOW : SW_HIDE);
}

//***************************************************************************
//      Member function: SetSize
//
// Purpose:
//              Move/size the HatchWindow correctly given the rect required by the
//              in-place server object window and the lprcClipRect imposed by the
//              in-place container. both rect's are expressed in the client coord.
//              of the in-place container's window (which is the parent of the
//              HatchWindow). When fInMouseDown is TRUE must not use MoveWindow
//                      but change the values in the WINDOWPOS struct inside the
//                      WM_WINDOWPOSCHANGING message (it seems Windows code is not re-entrant
//       there)
//
//              OLE2NOTE: the in-place server must honor the lprcClipRect specified
//              by its in-place container. it must NOT draw outside of the ClipRect.
//              in order to achieve this, the hatch window is sized to be
//              exactly the size that should be visible (rcVisRect). the
//              rcVisRect is defined as the intersection of the full size of
//              the HatchRect window and the lprcClipRect.
//              the ClipRect could infact clip the HatchRect on the
//              right/bottom and/or on the top/left. if it is clipped on the
//              right/bottom then it is sufficient to simply resize the hatch
//              window. but if the HatchRect is clipped on the top/left then
//              in-place server document window (child of HatchWindow) must be moved
//              by the delta that was clipped. the window origin of the
//              in-place server window will then have negative coordinates relative
//              to its parent HatchWindow.
//
// Parameters:
//              LPRECT                  full size of in-place server object window
//              LPRECT                  clipping rect imposed by in-place container
//              LPPOINT         offset required to position in-place server object
//                      window properly. caller should call:
//                                                              OffsetRect(&rcObjRect,lpptOffset->x,lpptOffset->y)
//
//      Return Value:                   None
//
//***************************************************************************
void BOleHatchWindow::SetSize(
		LPRECT      lprcIPObjRect,
		LPRECT      lprcClipRect,
		LPPOINT     lpptOffset)
{

	RECT        rcHatchRect;
	RECT        rcVisRect;
	UINT        uHatchWidth;
	POINT       ptOffset;

	if (!::IsWindow(hWndHatch))
		return;

	rcHatchRect = *lprcIPObjRect;
	uHatchWidth = GetWidth();
	::InflateRect((LPRECT)&rcHatchRect, uHatchWidth + 1, uHatchWidth + 1);

	// find the intersection between hatch (server) window and
	// clipping area (area where the object is allowed to paint)
	::IntersectRect (&rcVisRect, &rcHatchRect, lprcClipRect);

	// move the hatch window at the intersection rectangle if you are not
	// dragging the window around
	if (!fInMouseDown) {
		::MoveWindow(
				hWndHatch,
				rcVisRect.left,
				rcVisRect.top,
				rcVisRect.right-rcVisRect.left,
				rcVisRect.bottom-rcVisRect.top,
				TRUE    /* fRepaint */
		);
		::InvalidateRect(hWndHatch, NULL, TRUE);
	}
	else {
		// this information will be used in the WM_WINDOWPOSCHANGING
		// message to change WINDOWPOS structure values
					 ::SetRect(&MoveRect, rcVisRect.left, rcVisRect.top,
																rcVisRect.right-rcVisRect.left,
																					 rcVisRect.bottom-rcVisRect.top);
		  }


	nClippedFlag = 0; // clear clipped flag
	// store clipping information
	if (!(::EqualRect(&rcVisRect,&rcHatchRect))) {
		nClippedFlag |= CLIPPED;
		if (rcVisRect.left != rcHatchRect.left ) {
			nClippedFlag |= CLIPPED_LEFT;
		}
		if (rcVisRect.top != rcHatchRect.top) {
			nClippedFlag |= CLIPPED_TOP;
		}
	}

	// convert the rcHatchRect into the client coordinate system of the
	// HatchWindow itself
	ptOffset.x = - rcVisRect.left;
	ptOffset.y = - rcVisRect.top;
	::OffsetRect(&rcHatchRect, ptOffset.x, ptOffset.y);
	hatchRect = rcHatchRect;

	// calculate offset required to position in-place
	// server doc window (output value)
	lpptOffset->x = ptOffset.x;
	lpptOffset->y = ptOffset.y;

	return;
}

//***************************************************************************
// Member function: NegotiateSize
//
// Purpose:
//              When the user drags a handle of the hatch window, Windows attempts
//              to resize the window. This routine catches that attempt and validates
//              it with the client. We don't need to validate it with the server
//              because the client will call the server back from OnPosRectChange to
//              BOleSite::SetObjectRects.
//
//      Parameters:
//              WINDOWPOS*                              Pointer to Windows' WINDOWPOS structure
//
//***************************************************************************
void BOleHatchWindow::NegotiateSize (WINDOWPOS *pwp)
{
	RECT rcWnd; // The pre-resize size of the hatch window

	// As an optimization, get the current size of the hatch window
	// and bail out if it's the same as the new size. This saves the
	// potentially expensive renegotiation.
	//
	::GetWindowRect (hWndHatch, &rcWnd);
	HWND parent = ::GetParent (hWndHatch);
	::ScreenToClient (parent, (LPPOINT) &rcWnd.left);
	::ScreenToClient (parent, (LPPOINT) &rcWnd.right);
		  //if ( rcWnd.left == pwp->x && rcWnd.top == pwp->y &&
		  //        (rcWnd.bottom - rcWnd.top == pwp->cy) &&
		  //        (rcWnd.right - rcWnd.left == pwp->cx))
	//        return;

	// Ask the client to resize to the new size of the window
	//
	RECT rcNew;

	// The new size of the server's inplace window
	::SetRect (&rcNew, pwp->x, pwp->y, pwp->x + pwp->cx, pwp->y + pwp->cy);
	::InflateRect (&rcNew, -(hatchWidth + 1), -(hatchWidth + 1));
	if (pSite) {
		// Ask the server to show more/less of object
		//
		if (!SUCCEEDED(pBack->SetSiteRect(&rcNew))) {
			SIZE size;
			// undo resize of the server
			size.cx = rcWnd.right - rcWnd.left;
			size.cy = rcWnd.bottom - rcWnd.top;
			pBack->pPart->SetPartSize(&size);

			// If the client refuses the new size, give the pre-resize
			// size of the hatch window, back to Windows through the WINDOWPOS
			//
			pwp->x = rcWnd.left;
			pwp->y = rcWnd.top;
			pwp->cx = rcWnd.right - rcWnd.left;
			pwp->cy = rcWnd.bottom - rcWnd.top;
		}
	}
}

//***************************************************************************
//      BOleHatchWndProc
//
// Purpose:
//                      WndProc for hatch window
//
// Parameters:
//                      hWnd
//                      Message
//                      wParam
//                      lParam
//
// Return Value:
//                      message dependent
//***************************************************************************
LRESULT FAR PASCAL EXPORT BOleHatchWndProc (HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
#ifdef ANSI
  BOleHatchWindow *pHW  = (BOleHatchWindow*) ::GetWindowLongA(hWnd, 0);
#else
  BOleHatchWindow *pHW  = (BOleHatchWindow*) ::GetWindowLong(hWnd, 0);
#endif
  // if pHW is NULL, assign it if WM_NCCREATE otherwise
  // return DefWindowProc
  if (!pHW) {
    if (Message==WM_NCCREATE) {
#ifdef ANSI
      CREATESTRUCTA FAR* lpcs = (CREATESTRUCTA FAR*)lParam;
      ::SetWindowLongA(hWnd, 0, (DWORD)(LPVOID)(lpcs->lpCreateParams));
#else
      CREATESTRUCT FAR* lpcs = (CREATESTRUCT FAR*)lParam;
      ::SetWindowLong(hWnd, 0, (DWORD)(LPVOID)(lpcs->lpCreateParams));
#endif
    }
    return ::DefWindowProc (hWnd, Message, wParam, lParam);
  }

  switch (Message) {
    case WM_CREATE:
#ifdef ANSI
      return pHW->wmCreate((LPCREATESTRUCTA)lParam);
#else
      return pHW->wmCreate((LPCREATESTRUCT)lParam);
#endif

    case WM_PAINT:
      return pHW->wmPaint ();

    case WM_GETMINMAXINFO:
      return pHW->wmGetMinMaxInfo((MINMAXINFO*)lParam);

    // The cursor setting strategy here is to catch the WM_NCHITTEST
    // first to figure out which hit-test area the cursor is over.
    // Then Windows calls us back with WM_SETCURSOR so we can make
    // the cursor the correct shape for that hit-test area.
    //
    case WM_NCHITTEST:
      if (pHW) {
	POINT pt;
	pt.x = LOWORD(lParam);
	pt.y = HIWORD(lParam);
	return pHW->wmNCHitTest ((LPPOINT) &pt);
      }

    // Any window that is used during in-place activation
    // must handle the WM_SETCURSOR message or else the cursor
    // of the in-place parent will be used. if WM_SETCURSOR is
    // not handled, then DefWindowProc sends the message to the
    // window's parent.
    //
    case WM_SETCURSOR:
      return pHW->wmSetCursor((HWND)wParam, LOWORD(lParam), HIWORD(lParam));

    case WM_MOUSEACTIVATE:
      return pHW->wmMouseActivate((HWND)wParam, LOWORD(lParam), HIWORD(lParam));

    // The window move/resize strategy here is:
    // - Keep control so we can allow the OLE2 client app to refuse
    //   the new dimensions. Don't wait until the window has already
    //   been moved and then move it back.
    // - In order to keep control, we catch the WM_WINDOWPOSCHANGING
    //   and set appropriate flags in the WINDOWPOS struct.
    // - Since WM_WINDOWPOSCHANGING is sent for both user-interface
    //   initiated actions and for ::MoveWindow-initiated changes, we
    //   keep a flag which prevents changes during mousedowns
    //
    case WM_NCLBUTTONDOWN:
      return pHW->wmNCLButtonDown(wParam, (LPPOINT)&lParam);

    case WM_WINDOWPOSCHANGING:
      return pHW->wmWindowPosChanging((WINDOWPOS*) lParam);

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




