/*
 * SIMPUI.CPP
 *
 * Miscellaneous API's from the OLE 2.0 User Interface Support
 * Library sample code.  All code below comes from OBJFDBK.C,
 * HATCH.C, and OLEUTL.C.
 *
 * Copyright (c)1994 Microsoft Corporation, All Right Reserved
 */

#include "precomp.h"
#include "simpui.h"


// Borrowed from OBJFDBK.C

#define ropDPa  0x00A000C9L

static void DrawHandle(HDC hdc, int x, int y, UINT cSize, BOOL bInvert, BOOL fDraw);

/*
 * OleUIDrawHandles
 *
 * Purpose:
 *  Draw handles or/and boundary around Container Object when selected
 *
 * Parameters:
 *  lpRect      Dimensions of Container Object
 *  hdc         HDC of Container Object (MM_TEXT mapping mode)
 *  dwFlags-
 *      Exclusive flags
 *          OLEUI_HANDLES_INSIDE    Draw handles on inside of rect
 *          OLEUI_HANDLES_OUTSIDE   Draw handles on outside of rect
 *      Optional flags
 *          OLEUI_HANDLES_NOBORDER  Draw handles only, no rect
 *          OLEUI_HANDLES_USEINVERSE
 *              use invert for handles and rect, o.t. use COLOR_WINDOWTEXT
 *  cSize       size of handle box
 *  fDraw       Draw if TRUE, erase if FALSE
 *
 * Return Value: null
 *
 */
STDAPI_(void) OleUIDrawHandles(LPRECT  lpRect,
                               HDC     hdc,
                               DWORD   dwFlags,
                               UINT    cSize,
                               BOOL    fDraw)
{
	RECT    rc;
	int     bkmodeOld;
	BOOL    bInvert = (BOOL) (dwFlags & OLEUI_HANDLES_USEINVERSE);

	rc = *lpRect;
//	CopyRect(&rc, lpRect);

	bkmodeOld = SetBkMode(hdc, TRANSPARENT);

	if (dwFlags & OLEUI_HANDLES_OUTSIDE)
		InflateRect(&rc, cSize - 1, cSize - 1);

	// Draw the handles inside the rectangle boundary
	DrawHandle(hdc, rc.left, rc.top, cSize, bInvert, fDraw);
	DrawHandle(hdc, rc.left, rc.top + (rc.bottom - rc.top-cSize) / 2, cSize, bInvert, fDraw);
	DrawHandle(hdc, rc.left, rc.bottom - cSize, cSize, bInvert, fDraw);
	DrawHandle(hdc, rc.left + (rc.right - rc.left - cSize) / 2, rc.top, cSize, bInvert, fDraw);
	DrawHandle(hdc, rc.left + (rc.right - rc.left - cSize) / 2, rc.bottom - cSize, cSize, bInvert, fDraw);
	DrawHandle(hdc, rc.right - cSize, rc.top, cSize, bInvert, fDraw);
	DrawHandle(hdc, rc.right - cSize, rc.top + (rc.bottom - rc.top - cSize) / 2, cSize, bInvert, fDraw);
	DrawHandle(hdc, rc.right - cSize, rc.bottom - cSize, cSize, bInvert, fDraw);

	if (!(dwFlags & OLEUI_HANDLES_NOBORDER))
		FrameRect(hdc,
		          lpRect,
		          (HBRUSH)GetStockObject(fDraw ? BLACK_BRUSH : WHITE_BRUSH));

	SetBkMode(hdc, bkmodeOld);
}


/*
 * DrawHandle
 *
 * Purpose:
 *  Draw a handle box at the specified coordinate
 *
 * Parameters:
 *  hdc         HDC to be drawn into
 *  x, y        upper left corner coordinate of the handle box
 *  cSize       size of handle box
 *  bInvert     use InvertRect() if TRUE, otherwise use Rectangle()
 *  fDraw       Draw if TRUE, erase if FALSE, ignored if bInvert is TRUE
 *
 * Return Value: null
 *
 */
static void DrawHandle(HDC hdc, int x, int y, UINT cSize, BOOL bInvert, BOOL fDraw)
{
	HBRUSH  hbr;
	HBRUSH  hbrOld;
	HPEN    hpen;
	HPEN    hpenOld;
	RECT    rc;

	if (!bInvert)
	{
		if (fDraw)
		{
			hpen = (HPEN)  GetStockObject(BLACK_PEN);
			hbr  = (HBRUSH)GetStockObject(BLACK_BRUSH);
		}
		else
		{
			hpen = (HPEN)  GetStockObject(WHITE_PEN);
			hbr  = (HBRUSH)GetStockObject(WHITE_PEN);
		}

		hpenOld = (HPEN)  SelectObject(hdc, hpen);
		hbrOld  = (HBRUSH)SelectObject(hdc, hbr);
		Rectangle(hdc, x, y, x + cSize, y + cSize);
		SelectObject(hdc, hpenOld);
		SelectObject(hdc, hbrOld);
	}
	else
	{
		rc.left = x;
		rc.top = y;
		rc.right = x + cSize;
		rc.bottom = y + cSize;
		InvertRect(hdc, &rc);
	}
}


/*
 * OleUIDrawShading
 *
 * Purpose:
 *  Shade the object when it is in in-place editing. Borders are drawn
 *  on the Object rectangle. The right and bottom edge of the rectangle
 *  are excluded in the drawing.
 *
 * Parameters:
 *  lpRect      Dimensions of Container Object
 *  hdc         HDC for drawing
 *  dwFlags-
 *      Exclusive flags
 *          OLEUI_SHADE_FULLRECT    Shade the whole rectangle
 *          OLEUI_SHADE_BORDERIN    Shade cWidth pixels inside rect
 *          OLEUI_SHADE_BORDEROUT   Shade cWidth pixels outside rect
 *      Optional flags
 *          OLEUI_SHADE_USEINVERSE
 *              use PATINVERT instead of the hex value
 *  cWidth      width of border in pixel
 *
 * Return Value: null
 *
 */
STDAPI_(void) OleUIDrawShading(LPRECT lpRect, HDC hdc, DWORD dwFlags, UINT cWidth)
{
	HBRUSH  hbr;
	HBRUSH  hbrOld;
	HBITMAP hbm;
	RECT    rc;
	WORD    wHatchBmp[] = {0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88};
	COLORREF cvText;
	COLORREF cvBk;

	hbm = CreateBitmap(8, 8, 1, 1, wHatchBmp);
	hbr = CreatePatternBrush(hbm);
	hbrOld = (HBRUSH)SelectObject(hdc, hbr);

	rc = *lpRect;

	if (dwFlags == OLEUI_SHADE_FULLRECT)
	{
		cvText = SetTextColor(hdc, RGB(255, 255, 255));
		cvBk = SetBkColor(hdc, RGB(0, 0, 0));
		PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
			ropDPa);

	}
	else
	{    // either inside or outside rect

		if (dwFlags == OLEUI_SHADE_BORDEROUT)
			InflateRect(&rc, cWidth - 1, cWidth - 1);

		cvText = SetTextColor(hdc, RGB(255, 255, 255));
		cvBk = SetBkColor(hdc, RGB(0, 0, 0));
		PatBlt(hdc, rc.left, rc.top, rc.right - rc.left,
			cWidth, ropDPa);
		PatBlt(hdc, rc.left, rc.top, cWidth, rc.bottom - rc.top,
			ropDPa);
		PatBlt(hdc, rc.right - cWidth, rc.top, cWidth,
			rc.bottom - rc.top, ropDPa);
		PatBlt(hdc, rc.left, rc.bottom - cWidth, rc.right - rc.left,
			cWidth, ropDPa);
	}

	SetTextColor(hdc, cvText);
	SetBkColor(hdc, cvBk);
	SelectObject(hdc, hbrOld);
	DeleteObject(hbr);
	DeleteObject(hbm);
}


/*
 * OleUIShowObject
 *
 * Purpose:
 *  Draw the ShowObject effect around the object
 *
 * Parameters:
 *  lprc        rectangle for drawing
 *  hdc         HDC for drawing
 *  fIsLink     linked object (TRUE) or embedding object (FALSE)
 *
 * Return Value: null
 *
 */
STDAPI_(void) OleUIShowObject(LPCRECT lprc, HDC hdc, BOOL fIsLink)
{
    HPEN    hpen;
    HPEN    hpenOld;
    HBRUSH  hbrOld;

    if (!lprc || !hdc)
        return;

    hpen = fIsLink ? CreatePen(PS_DASH, 1, RGB(0,0,0)) :
                     (HPEN)GetStockObject(BLACK_PEN);

    if (!hpen)
        return;

    hpenOld = (HPEN)  SelectObject(hdc, hpen);
    hbrOld  = (HBRUSH)SelectObject(hdc, (HBRUSH)GetStockObject(NULL_BRUSH));

	Rectangle(hdc, lprc->left, lprc->top, lprc->right, lprc->bottom);

	SelectObject(hdc, hpenOld);
	SelectObject(hdc, hbrOld);

	if (fIsLink)
		DeleteObject(hpen);
}



// Borrowed from HATCH.C

// 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 CALLBACK EXPORT HatchWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);


/*
 * RegisterHatchWindowClass
 *
 * 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(short);   // 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;

   return (BOOL)RegisterClass(&wc);
}


/*
 * 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)
{
   if (!hWndParent || !hInst)
      return NULL;

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

/*
 *  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   = (int)(short)GetWindowWord(hWndHatch, EB_HATCHRECT_LEFT);
   lprcHatchRect->top    = (int)(short)GetWindowWord(hWndHatch, EB_HATCHRECT_TOP);
   lprcHatchRect->right  = (int)(short)GetWindowWord(hWndHatch, EB_HATCHRECT_RIGHT);
   lprcHatchRect->bottom = (int)(short)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)(short)lprcHatchRect->left);
   SetWindowWord(hWndHatch, EB_HATCHRECT_TOP,   (WORD)(short)lprcHatchRect->top);
   SetWindowWord(hWndHatch, EB_HATCHRECT_RIGHT, (WORD)(short)lprcHatchRect->right);
   SetWindowWord(hWndHatch, EB_HATCHRECT_BOTTOM,(WORD)(short)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(&rcHatchRect, uHatchWidth + 1, uHatchWidth + 1);

   IntersectRect(&rcVisRect, &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(&rcHatchRect, ptOffset.x, ptOffset.y);

   SetHatchRect(hWndHatch, &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 CALLBACK 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:
      {
         PAINTSTRUCT ps;
         RECT rcHatchRect;

         nBorderWidth = GetHatchWidth(hWnd);
         BeginPaint(hWnd, &ps);
         GetHatchRect(hWnd, &rcHatchRect);
         OleUIDrawShading(&rcHatchRect,
                          ps.hdc,
                          OLEUI_SHADE_BORDERIN,
                          nBorderWidth);
         InflateRect(&rcHatchRect, -nBorderWidth, -nBorderWidth);
         OleUIDrawHandles(&rcHatchRect,
                          ps.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;
}



// Borrowed from OLEUTL.C

#include <ctype.h>

/* GetWord
 * -------
 *
 * LPSTR lpszSrc - Pointer to a source string
 * LPSTR lpszDst - Pointer to destination buffer
 *
 * Will copy one space-terminated or null-terminated word from the source
 * string to the destination buffer.
 * returns: pointer to next character following the word.
 */
static LPSTR GetWord(LPSTR lpszSrc, LPSTR lpszDst)
{
	while (*lpszSrc && !isspace(*lpszSrc))
		*lpszDst++ = *lpszSrc++;

	*lpszDst = '\0';
	return lpszSrc;
}


/*
 * ParseCmdLine
 *
 * Parses the Windows command line which was passed to WinMain.
 * This function determines if the -Embedding switch has been given.
 *
 */

STDAPI_(void) ParseCmdLine(
	LPSTR lpszLine,
	BOOL FAR* lpfEmbedFlag,
	LPSTR szFileName)
{
	int i=0;
	char szBuf[256];

	if(lpfEmbedFlag)
		*lpfEmbedFlag = FALSE;
	szFileName[0]='\0';             // NULL string

	// skip blanks
	while(isspace(*lpszLine)) lpszLine++;

	if(!*lpszLine)   // No filename or options, so start a fresh document.
		return;

	// Check for "-Embedding" or "/Embedding" and set fEmbedding.
	if(lpfEmbedFlag && (*lpszLine == '-' || *lpszLine == '/')) {
		lpszLine++;
		lpszLine = GetWord(lpszLine, szBuf);
		*lpfEmbedFlag = !lstrcmp(szBuf, EMBEDDINGFLAG);
	}

	// skip blanks
	while(isspace(*lpszLine)) lpszLine++;

	// set szFileName to argument
	while(lpszLine[i]) {
		szFileName[i]=lpszLine[i];
		i++;
	}
	szFileName[i]='\0';
}
