/*
 * HATCH.C
 *
 * Miscellaneous API's to generate hatch window for in-place active
 * objects. This is part of the OLE 2.0 User Interface Support Library.
 *
 * Copyright (c)1993 Microsoft Corporation, All Right Reserved
 */

#define STRICT  1
#include "ole2ui.h"

// offsets in the extra bytes stored with the hatch window
#define EB_HATCHWIDTH       0
#define EB_HATCHRECT_LEFT   2
#define EB_HATCHRECT_TOP    4
#define EB_HATCHRECT_RIGHT  6
#define EB_HATCHRECT_BOTTOM 8

// class name of hatch window
#define CLASS_HATCH     "Hatch Window"

// local function prototypes
LRESULT FAR PASCAL EXPORT HatchWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);


/*
 * HatchRegisterClass
 *
 * Purpose:
 *  Register the hatch window
 *
 * Parameters:
 *  hInst           Process instance
 *
 * Return Value:
 *  TRUE            if successful
 *  FALSE           if failed
 *
 */
STDAPI_(BOOL) RegisterHatchWindowClass(HINSTANCE hInst)
{
   WNDCLASS wc;

   // Register Hatch Window Class
   wc.style = CS_BYTEALIGNWINDOW;
   wc.lpfnWndProc = HatchWndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 5 * sizeof(int);    // extra bytes stores
                              //     uHatchWidth
                              //     rcHatchRect
   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;

   if (!RegisterClass(&wc))
      return FALSE;
   else
      return TRUE;
}


/*
 * CreateHatchWindow
 *
 * Purpose:
 *  Create the hatch window
 *
 * Parameters:
 *  hWndParent          parent of hatch window
 *  hInst               instance handle
 *
 * Return Value:
 *  pointer to hatch window         if successful
 *  NULL                            if failed
 *
 */
STDAPI_(HWND) CreateHatchWindow(HWND hWndParent, HINSTANCE hInst)
{
   HWND         hWnd;

   if (!hWndParent || !hInst)
      return NULL;

   hWnd = CreateWindow(
      CLASS_HATCH,
      "Hatch Window",
      WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
      0, 0, 0, 0,
      hWndParent,
      (HMENU)NULL,
      hInst,
      0L
   );

   if (!hWnd)
      return NULL;

   return hWnd;
}

/*
 *  GetHatchWidth
 *
 *  Purpose:
 *      Get width of hatch border
 *
 *  Parameters:
 *      hWndHatch       hatch window handle
 *
 *  Return Value:
 *      width of the hatch border
 */
STDAPI_(UINT) GetHatchWidth(HWND hWndHatch)
{
   if (!IsWindow(hWndHatch))
      return 0;

   return (UINT)GetWindowWord(hWndHatch, EB_HATCHWIDTH);
}

/*
 *  GetHatchRect
 *
 *  Purpose:
 *      Get hatch rect. this is the size of the hatch window if it were
 *      not clipped by the ClipRect.
 *
 *  Parameters:
 *      hWndHatch       hatch window handle
 *      lprcHatchRect   hatch rect
 *
 *  Return Value:
 *      none
 */
STDAPI_(void) GetHatchRect(HWND hWndHatch, LPRECT lprcHatchRect)
{
   if (!IsWindow(hWndHatch)) {
      SetRect(lprcHatchRect, 0, 0, 0, 0);
      return;
   }

   lprcHatchRect->left = GetWindowWord(hWndHatch, EB_HATCHRECT_LEFT);
   lprcHatchRect->top = GetWindowWord(hWndHatch, EB_HATCHRECT_TOP);
   lprcHatchRect->right = GetWindowWord(hWndHatch, EB_HATCHRECT_RIGHT);
   lprcHatchRect->bottom = GetWindowWord(hWndHatch, EB_HATCHRECT_BOTTOM);
}


/* SetHatchRect
 *
 *
 *  Purpose:
 *      Store hatch rect with HatchRect window.
 *      this rect is the size of the hatch window if it were
 *      not clipped by the ClipRect.
 *
 *  Parameters:
 *      hWndHatch       hatch window handle
 *      lprcHatchRect   hatch rect
 *
 *  Return Value:
 *      none
 */
STDAPI_(void) SetHatchRect(HWND hWndHatch, LPRECT lprcHatchRect)
{
   if (!IsWindow(hWndHatch))
      return;

   SetWindowWord(hWndHatch, EB_HATCHRECT_LEFT,  (WORD)lprcHatchRect->left);
   SetWindowWord(hWndHatch, EB_HATCHRECT_TOP,   (WORD)lprcHatchRect->top);
   SetWindowWord(hWndHatch, EB_HATCHRECT_RIGHT, (WORD)lprcHatchRect->right);
   SetWindowWord(hWndHatch, EB_HATCHRECT_BOTTOM,(WORD)lprcHatchRect->bottom);
}


/* SetHatchWindowSize
 *
 *
 *  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).
 *
 *      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:
 *      hWndHatch       hatch window handle
 *      lprcIPObjRect   full size of in-place server object window
 *      lprcClipRect    clipping rect imposed by in-place container
 *      lpptOffset      offset required to position in-place server object
 *                      window properly. caller should call:
 *                          OffsetRect(&rcObjRect,lpptOffset->x,lpptOffset->y)
 *
 *  Return Value:
 *      none
 */
STDAPI_(void) SetHatchWindowSize(
      HWND        hWndHatch,
      LPRECT      lprcIPObjRect,
      LPRECT      lprcClipRect,
      LPPOINT       lpptOffset
)
{
   RECT        rcHatchRect;
   RECT        rcVisRect;
   UINT        uHatchWidth;
   POINT       ptOffset;

   if (!IsWindow(hWndHatch))
      return;

   rcHatchRect = *lprcIPObjRect;
   uHatchWidth = GetHatchWidth(hWndHatch);
   InflateRect((LPRECT)&rcHatchRect, uHatchWidth + 1, uHatchWidth + 1);

   IntersectRect((LPRECT)&rcVisRect, (LPRECT)&rcHatchRect, lprcClipRect);
   MoveWindow(
         hWndHatch,
         rcVisRect.left,
         rcVisRect.top,
         rcVisRect.right-rcVisRect.left,
         rcVisRect.bottom-rcVisRect.top,
         TRUE    /* fRepaint */
   );
   InvalidateRect(hWndHatch, NULL, TRUE);

   ptOffset.x = -rcHatchRect.left + (rcHatchRect.left - rcVisRect.left);
   ptOffset.y = -rcHatchRect.top + (rcHatchRect.top - rcVisRect.top);

   /* convert the rcHatchRect into the client coordinate system of the
   **    HatchWindow itself
   */
   OffsetRect((LPRECT)&rcHatchRect, ptOffset.x, ptOffset.y);

   SetHatchRect(hWndHatch, (LPRECT)&rcHatchRect);

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


/*
 *  HatchWndProc
 *
 *  Purpose:
 *      WndProc for hatch window
 *
 *  Parameters:
 *      hWnd
 *      Message
 *      wParam
 *      lParam
 *
 *  Return Value:
 *      message dependent
 */
LRESULT FAR PASCAL EXPORT HatchWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
   int nBorderWidth;

   switch (Message) {

      case WM_CREATE:
         nBorderWidth = GetProfileInt(
            "windows",
            "oleinplaceborderwidth",
            DEFAULT_HATCHBORDER_WIDTH
         );
         SetWindowWord(hWnd, EB_HATCHWIDTH, (WORD)nBorderWidth);
         break;

      case WM_PAINT:
      {
         HDC hDC;
         PAINTSTRUCT ps;
         RECT rcHatchRect;

         nBorderWidth = GetHatchWidth(hWnd);
         hDC = BeginPaint(hWnd, &ps);
         GetHatchRect(hWnd, (LPRECT)&rcHatchRect);
         OleUIDrawShading(&rcHatchRect, hDC, OLEUI_SHADE_BORDERIN,
               nBorderWidth);
         InflateRect((LPRECT)&rcHatchRect, -nBorderWidth, -nBorderWidth);
         OleUIDrawHandles(&rcHatchRect, hDC, OLEUI_HANDLES_OUTSIDE,
               nBorderWidth+1, TRUE);
         EndPaint(hWnd, &ps);
         break;
      }

      /* OLE2NOTE: 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:
         SetCursor(LoadCursor( NULL, MAKEINTRESOURCE(IDC_ARROW) ) );
         return (LRESULT)TRUE;

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

   return 0L;
}
