/****************************************************************************
*                pvtiny.c
*
*  This module implements Windows 'tiny caption' non-client drawing routines.
*
*  Authors : See comment block below. Taken from Microsoft's ITSYBITSY demo.
*
*****************************************************************************/

// This code is not commented enough or as much as I'd like. It also needs
// a major cleanup. However, after all, this is volunteer work and I've
// not got the time to do so now. Rather than further delay the release,
// here it is.

#define POVWIN_FILE

/////////////////////////////////////////////////////////////////////////
//
// Project:     ItsyBitsy window support module
// Module:      itsybits.c
//
//
//    ItsyBitsy is a support module that allows you to create windows
//    that look and act very much like a popup window with a system
//    menu and caption bar, except everything is scaled to about 2/3
//    scale.
//
//    For documentation on how to use ItsyBits, read the document
//    ITSYBITS.DOC.
//
//   Revisions:
//      9/27/91     Charlie Kindel (cek/ckindel)
//                          Wrote and documented it.
//
//      1/14/93     cek
//      2/23/93     cek     Added minimize/maximize buttons.
//      3/18/93     cek     Fixed system menu bug where system menu
//                          popped back up if you clicked on the
//                          icon again while it was up.
//      3/24/93     cek     More comments.  Fixed DS_MODALDIALOG style
//                          problem.  Use auto precompiled headers
//                          in MSVC.
//      1/02/96     cjc     revised to suit POV-Ray for Windows.
//
//////////////////////////////////////////////////////////////////////////

#include <math.h>
#include <setjmp.h>
#include <string.h>
#include <windows.h>
#include "frame.h"
#include "colour.h"
#include "povray.h"
#include "povproto.h"
#include "optout.h"
#include "userio.h"
#pragma hdrstop

#include <windowsx.h>
#include "pvengine.h"
#include "pvclient.h"

#define TestWinStyle(hWnd, dwStyleBit) \
          (((DWORD) GetWindowLong (hWnd, GWL_STYLE) & (DWORD) dwStyleBit) ? TRUE : FALSE)

#define HASCAPTION(hwnd)      (TestWinStyle (hwnd, IBS_VERTCAPTION) || \
                               TestWinStyle (hwnd, IBS_HORZCAPTION))

#define ISNEWWINDOW(hwnd)     (TestWinStyle (hwnd, IBS_VERTCAPTION) && \
                               TestWinStyle (hwnd, IBS_HORZCAPTION))

#define SETCAPTIONSIZE(h,i)   SetProp(h,"ibSize", (HANDLE) i)
#define GETCAPTIONSIZE(h)     GetProp(h,"ibSize")
#define FREECAPTIONSIZE(h)    RemoveProp(h,"ibSize")

#define SETMENUWASUPFLAG(h,i) SetProp(h,"ibFlag", (HANDLE) i)
#define GETMENUWASUPFLAG(h)   GetProp(h,"ibFlag")
#define FREEMENUWASUPFLAG(h)  RemoveProp(h,"ibFlag")

/////////////////////////////////////////////////////////////////////
// Little known fact:
//    ExtTextOut() is the fastest way to draw a filled rectangle
//    in Windows (if you don't want dithered colors or borders).
//
//    Unfortunately there is a bug in the Windows 3.0 8514 driver
//    in using ExtTextOut() to a memory DC.  If you are drawing
//    to an off screen bitmap, then blitting that bitmap to the
//    display, do not #define USE_EXTTEXTOUT below.
//
//    The following macro (DRAWFASTRECT) draws a filled rectangle
//    with no border and a solid color.  It uses the current back-
//    ground color as the fill color.
//////////////////////////////////////////////////////////////////////

#define USE_EXTTEXTOUT
#ifdef USE_EXTTEXTOUT
   #define DRAWFASTRECT(hdc,lprc) ExtTextOut(hdc,0,0,ETO_OPAQUE,lprc,NULL,0,NULL)
#else
   #define DRAWFASTRECT(hdc,lprc) {\
   HBRUSH hbr = CreateSolidBrush (GetBkColor (hdc ) ) ;\
   hbr = SelectObject(hdc, hbr) ;\
   PatBlt(hdc,(lprc)->left,(lprc)->top,(lprc)->right-(lprc)->left,(lprc)->bottom-(lprc)->top,PATCOPY) ;\
   hbr = SelectObject(hdc, hbr) ;\
   DeleteObject (hbr ) ;\
   }
#endif

// The DrawArrow function takes the following to indicate what
// kind of arrow to draw.

#define ARROW_UP        0
#define ARROW_DOWN      1
#define ARROW_RESTORE   2

BOOL NEAR PASCAL DepressMinMaxButton (HWND hWnd, UINT uiHT, LPRECT) ;
BOOL NEAR PASCAL DoMenu (HWND hWnd) ;
void NEAR PASCAL SetupSystemMenu (HWND hWnd, HMENU hMenu) ;
BOOL NEAR PASCAL GetCaptionRect (HWND hWnd, LPRECT lprc) ;
BOOL NEAR PASCAL GetIconRect (HWND hWnd, LPRECT lprc) ;
BOOL NEAR PASCAL GetButtonRect (HWND hWnd, UINT nPos, LPRECT lprc) ;
BOOL NEAR PASCAL GetMinButtonRect (HWND hWnd, LPRECT lprc) ;
BOOL NEAR PASCAL GetMaxButtonRect (HWND hWnd, LPRECT lprc) ;
BOOL NEAR PASCAL ibDrawCaption (HDC hDC, HWND hWnd, LPRECT lprc,
                                BOOL fVert, BOOL fSysMenu,
                                BOOL fMin, BOOL fMax, BOOL fActive) ;
VOID NEAR PASCAL DrawSysMenu (HDC hDC, HWND hWnd, BOOL fInvert) ;
VOID NEAR PASCAL DrawButton (HDC hDC, HWND hWnd, BOOL fMin, BOOL fDepressed) ;
VOID NEAR PASCAL DrawArrow (HDC hdc, LPRECT lprc, UINT uiStyle) ;

// Global vars
//
static BOOL fWin31 ;

///////////////////////////////////////////////////////////////////////
// External/Public functions
///////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////
//  UINT WINAPI ibGetCaptionSize (HWND hWnd)
//
//  Description:
//
//    Gets the size of the caption (height if horz, width if
//    vertical).
//
//  Comments:
//
///////////////////////////////////////////////////////////////
UINT WINAPI ibGetCaptionSize (HWND hWnd )
{
    return (UINT) GETCAPTIONSIZE (hWnd) + 1 ;
} // ibGetCaptionSize()

/////////////////////////////////////////////////////////////////
//  UINT WINAPI ibSetCaptionSize (HWND hWnd, UINT nSize)
//
//  Description:
//
//    Changes the size of the caption (height if horz, width if
//    vertical).
//
//  Comments:
//
//////////////////////////////////////////////////////////////////
UINT WINAPI ibSetCaptionSize (HWND hWnd, UINT nSize)
{
    UINT ui ;

    if (nSize <= 0)
        return 0 ;

    nSize-- ;
    ui = (UINT) SETCAPTIONSIZE (hWnd, nSize) ;

    // Once we change the window style, we need a WM_NCCALCRECT
    // to be generated.
    //
    // SWP_FRAMECHANGED is not documented in the 3.1 SDK docs,
    // but *is* in WINDOWS.H.
    //

    // CJC on my system, when this was called, it seemed to cause the
    // window position to move by the amount of the caption. since I
    // only call it when my window is hidden, it's not important.

//  SetWindowPos (hWnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED |
//                      SWP_NOSIZE | SWP_NOMOVE |
//                      SWP_NOACTIVATE | SWP_NOZORDER) ;

    return ui ;

} // ibSetCaptionSize()

/////////////////////////////////////////////////////////////////
//  LRESULT WINAPI ibDefWindowProc (HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
//
//  Description:
//
//    This function should be called instead of DefWindowProc() for
//    windows that want to have itsybitsy captions.
//
//  Comments:
//
//////////////////////////////////////////////////////////////////
LRESULT WINAPI ibDefWindowProc (HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    UINT      nCapSize ;
    RECT      rc ;
    DWORD     style = GetWindowLong (hWnd, GWL_STYLE) ;
    LRESULT   lRet ;

    switch (uiMsg)
    {
      case WM_SYSCHAR:
           // If ALT-SPACE
           // was hit then pop up the menu
           //
           if (HASCAPTION (hWnd) && (wParam == VK_SPACE))
           {
             DoMenu (hWnd) ;
             break ;
           }

           // FALL THROUGH!!!!
           //

      case WM_SYSKEYDOWN:
      case WM_SYSKEYUP:
      case WM_KEYDOWN:
      case WM_KEYUP:
           // Fool DefWindowProc into thinking we do not have
           // a system menu.  Otherwise it will try to
           // pop up its own.
           //
           SetWindowLong (hWnd, GWL_STYLE, style & ~WS_SYSMENU) ;
           lRet = DefWindowProc (hWnd, uiMsg, wParam, lParam) ;
           SetWindowLong (hWnd, GWL_STYLE, style) ;
           return (lRet) ;

      case WM_COMMAND:
           // The menu that is popped up for the system menu with
           // TrackPopupMenu() sends it's notifications as WM_COMMAND
           // messages.  We need to translate these into
           // WM_SYSCOMMAND messages.  All standard WM_SYSCOMMAND
           // ids are greater than 0xF000.
           //
           // This could be a possible cause of confusion if the
           // itsybitsy window had children that used SC_MOVE or SC_CLOSE
           // as their IDs.  Take note and be careful.
           //
           // Also, because ibDefWindowProc looks at WM_COMMAND messages,
           // you will need to be careful to call ibDefWindowProc() for
           // any wm_command messages that you would normally ignore.
           // Otherwise the system menu won't work.
           //
           if (wParam >= 0xF000)
           {
               // Call PostMessage() here instead of SendMessage!
               // Here's why:
               //      Our menu was created by TrackPopupMenu().  TPM() does
               //      not return until after the menu has been destroyed
               //      (and thus the command associated with the menu selection
               //      sent).  Therefore when we get here, we are still
               //      *inside* TPM().  If we Send WM_SYSCOMMAND, SC_CLOSE
               //      to the window, the window will be destroyed before
               //      TPM() returns to our code within DoMenu() below.
               //      We do stuff with the window handle after DoMenu()
               //      returns (namely GetProp()).  Since the window has
               //      been destroyed, this is bad.
               PostMessage (hWnd, WM_SYSCOMMAND, wParam, lParam) ;
           }
           return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;

      case WM_GETMINMAXINFO:
           nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;
           if (HASCAPTION (hWnd) && TestWinStyle (hWnd, WS_THICKFRAME))
           {
             LPPOINT lppt = (LPPOINT) lParam ;
             RECT    rcMenu ;
             RECT    rcMin ;
             RECT    rcMax ;
             int     nX ;
             int     cx, cy ;    // window frame/border width

             if (TestWinStyle (hWnd, WS_THICKFRAME))
             {
               cx = GetSystemMetrics (SM_CXFRAME) ;
               cy = GetSystemMetrics (SM_CYFRAME) ;
             }
             else
               if (TestWinStyle (hWnd, WS_BORDER))
               {
                 cx = GetSystemMetrics (SM_CXBORDER) ;
                 cy = GetSystemMetrics (SM_CYBORDER) ;
               }

             GetIconRect (hWnd, &rcMenu) ;
             GetMinButtonRect (hWnd, &rcMin) ;
             GetMaxButtonRect (hWnd, &rcMax) ;

             // nX is the total pixels our bitmaps take up in the X direction
             nX = (rcMenu.right - rcMenu.left) +
                  (rcMin.right - rcMin.left)   +
                  (rcMax.right - rcMax.left) ;

             // lppt [3] is the minimum tracking size
             if (TestWinStyle (hWnd, IBS_VERTCAPTION))
             {
               lppt[3].x = nCapSize + cx * 2 - 1 ;
               lppt[3].y = nX + (2 * nCapSize) ;
             }
             else
             {
               lppt[3].x = nX + (2 * nCapSize) ;
               lppt[3].y = nCapSize + cy * 2 - 1 ;
             }
           }
           return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;

      /////////////////////////////////////////////////////////////////////
      // Non-client area messages.  These are used to allow the
      // minature caption bar to be handled correctly.
      //
      case WM_NCCREATE:
           // We have two things that we need to store somewhere:
           //     1) The caption height (width).
           // and 2) A flag indicating whether the sysmenu was
           //        just up or not.
           //
           // CAPTIONXY is a macro that calls GetSystemMetrics.
           //
           SETCAPTIONSIZE (hWnd, CAPTIONXY) ;

           // Set our flag that tells us whether the system menu was
           // 'just up'.
           //
           SETMENUWASUPFLAG (hWnd, FALSE) ;

           // Are we in 3.1?  If so we have some neat features
           // we can use like rotated TrueType fonts.
           //
           fWin31 = TRUE ;

           // If IBS_????CAPTION was specified and the WS_DLGFRAME (or
           // WS_DLGFRAME 'cause it == WS_CAPTION | WS_BORDER)
           // was specified the caller made a mistake. Things get really
           // ugly if DefWindowProc sees WS_DLGFRAME, so we strip
           // the WS_DLGFRAME part off!
           //
           if ((style & IBS_VERTCAPTION || style & IBS_HORZCAPTION) && style & WS_DLGFRAME)
           {
             style &= (DWORD) ~WS_DLGFRAME ;
             SetWindowLong (hWnd, GWL_STYLE, style) ;
           }
           return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;

      case WM_NCDESTROY:
           // We store the caption size in a window prop. so we
           // must remove props.
           //
           FREECAPTIONSIZE (hWnd) ;
           FREEMENUWASUPFLAG (hWnd) ;
           return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;

      case WM_NCCALCSIZE:
           // This is sent when the window manager wants to find out
           // how big our client area is to be.  If we have a mini-caption
           // then we trap this message and calculate the client area rect,
           // which is the client area rect calculated by DefWindowProc()
           // plus the width/height of the mini-caption bar
           //
           lRet = DefWindowProc (hWnd, uiMsg, wParam, lParam) ;
           if (!IsIconic (hWnd) && HASCAPTION (hWnd))
           {
             nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;

             if (TestWinStyle (hWnd, IBS_VERTCAPTION))
               ((LPRECT)lParam)->left += nCapSize ;
             else
               ((LPRECT)lParam)->top += nCapSize ;
           }
           return (lRet) ;

      case WM_NCHITTEST:
           // This message is sent whenever the mouse moves over us.
           // We will depend on DefWindowProc for everything unless
           // there is a mini-caption, in which case we will
           // return HTCAPTION or HTSYSMENU.  When the user clicks
           // or double clicks, NC_LBUTTON/ message are sent with
           // wParam equal to what we return here.
           // This means that this is an ideal place to figure out
           // where we are!
           //
           // let defwindowproc handle the standard borders etc...
           //
           lRet = DefWindowProc (hWnd, uiMsg, wParam, lParam) ;
           if (!IsIconic (hWnd) && HASCAPTION (hWnd) && lRet == HTNOWHERE)
           {
             RECT  rc ;
             RECT  rcMenu ;
             RECT  rcMinButton ;
             RECT  rcMaxButton ;
             POINT pt ;

//           nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;

             pt.x = LOWORD (lParam) ;
             pt.y = HIWORD (lParam) ;

             GetCaptionRect (hWnd, &rc) ;  // window coords
             if (PtInRect (&rc, pt))
             {
               lRet = HTCAPTION ;

               // rely on the fact that Get???Rect() return an invalid
               // (empty) rectangle if the menu/buttons don't exist
               //
               GetIconRect (hWnd, &rcMenu) ;
               GetMinButtonRect (hWnd, &rcMinButton) ;
               GetMaxButtonRect (hWnd, &rcMaxButton) ;

               if (PtInRect (&rcMenu, pt))
                 lRet = HTSYSMENU ;
               else
                 if (PtInRect (&rcMinButton, pt))
                   lRet = HTMINBUTTON ;
               else
                 if (PtInRect (&rcMaxButton, pt))
                   lRet = HTMAXBUTTON ;
             }
           }
           if (lRet != HTSYSMENU)
             SETMENUWASUPFLAG (hWnd, FALSE) ;
           return (lRet) ;

      case WM_NCLBUTTONDBLCLK:
           // Windows recieve WM_NC?BUTTONDBLCLK messages whether they
           // have CS_DBLCLKS or not.  We watch for one of these
           // to see if the user double clicked on the system menu (to
           // close the window) or on the caption (to maximize the window).
           //
           if (!IsIconic (hWnd) && HASCAPTION (hWnd) && wParam == HTSYSMENU)
           {
             SendMessage (hWnd, WM_CLOSE, 0, 0L) ;
             break ;
           }
           return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;

      case WM_NCLBUTTONDOWN:
           // If we're iconic or we don't have a caption then
           // DefWindowProc will do the job just fine.
           //
           if (IsIconic (hWnd) || !HASCAPTION (hWnd))
             return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;

           // Here's were we handle the system menu, the min and max buttons.
           // If you wanted to change the behavior of the min/max buttons to
           // do something like swap tool palettes or something, you
           // would change the SendMessage() calls below.
           //
           switch (wParam)
           {
             case HTSYSMENU:
                  if (GETMENUWASUPFLAG (hWnd) == FALSE && DoMenu (hWnd))
                    SETMENUWASUPFLAG (hWnd, TRUE) ;
                  else
                    SETMENUWASUPFLAG (hWnd, FALSE) ;
                   break ;

             case HTMINBUTTON:
                  GetMinButtonRect (hWnd, &rc) ;
                  // Note that DepressMinMaxButton() goes into
                  // a PeekMessage() loop waiting for the mouse
                  // to come back up.
                  //
                  if (DepressMinMaxButton (hWnd, wParam, &rc))
                    SendMessage (hWnd, WM_SYSCOMMAND, SC_MINIMIZE, lParam) ;
                  break ;

             case HTMAXBUTTON:
                  GetMaxButtonRect (hWnd, &rc) ;
                  // Note that DepressMinMaxButton() goes into
                  // a PeekMessage() loop waiting for the mouse
                  // to come back up.
                  //
                  if (DepressMinMaxButton (hWnd, wParam, &rc))
                  {
                    if (IsZoomed(hWnd))
                      SendMessage (hWnd, WM_SYSCOMMAND, SC_RESTORE, lParam) ;
                    else
                      SendMessage (hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, lParam) ;
                  }
                  break ;

             default:
                 // Well, it appears as though the user clicked somewhere other
                 // than the buttons.  We let DefWindowProc do it's magic.
                 // This is where things like dragging and sizing the
                 // window happen.
                 //
                 return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;
           }
           break ;

      case WM_SETTEXT:
      case WM_NCPAINT:
      case WM_NCACTIVATE:
           if (IsIconic (hWnd))
             return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;

           // Paint the non-client area here.  We will call DefWindowProc
           // after we are done so it can paint the borders and so
           // forth...
           //
           lRet = DefWindowProc (hWnd, uiMsg, wParam, lParam) ;
           if (HASCAPTION (hWnd))
           {
             RECT  rcCap ;
             RECT  rc ;
             HDC   hDC = GetWindowDC (hWnd) ;
             BOOL  fActive ;

             GetCaptionRect (hWnd, &rcCap) ;    // Convert to window coords
             GetWindowRect (hWnd, &rc) ;
             OffsetRect (&rcCap, -rc.left, -rc.top) ;

//           if (uiMsg == WM_NCPAINT)
//               fActive = (hWnd == GetForegroundWindow ()) ;
//           else
//               fActive = wParam ;

             fActive = (hWnd == GetForegroundWindow ()) ;
             ibDrawCaption (hDC, hWnd, &rcCap,
                            TestWinStyle (hWnd, IBS_VERTCAPTION),
                            TestWinStyle (hWnd, WS_SYSMENU),
                            TestWinStyle (hWnd, WS_MINIMIZEBOX),
                            TestWinStyle (hWnd, WS_MAXIMIZEBOX),
                            fActive) ;

             ReleaseDC (hWnd, hDC) ;
           }
           return (lRet) ;

      default:
           return (DefWindowProc (hWnd, uiMsg, wParam, lParam)) ;
  }
  return (0L) ;
} // ibDefWindowProc()

//  ibAdjustWindowRect (HWND hWnd, LPRECT lprc)
//
//    Does the same thing as the USER function AdjustWindowRect(),
//    but knows about itsybitsy windows.  AdjustWindowRect() is
//    bogus for stuff like this.
//
VOID WINAPI ibAdjustWindowRect (LPRECT lprc, UINT flags, UINT nCapSize)
{
  short    cx = 0, cy = 0 ;

  // First check Windows's styles, then our own.
  //
  if ((flags & WS_THICKFRAME) != 0)
  {
    cx = GetSystemMetrics (SM_CXFRAME) ;
    cy = GetSystemMetrics (SM_CYFRAME) ;
  }
  else
    if ((flags & DS_MODALFRAME) != 0)
    {
      cx = GetSystemMetrics (SM_CXDLGFRAME) + GetSystemMetrics (SM_CXBORDER) ;
      cy = GetSystemMetrics (SM_CYDLGFRAME) + GetSystemMetrics (SM_CYBORDER) ;
    }
    else
      if ((flags & WS_BORDER) != 0)
      {
        cx = GetSystemMetrics (SM_CXBORDER) ;
        cy = GetSystemMetrics (SM_CYBORDER) ;
      }
  
  InflateRect (lprc, cx, cy) ;
  
  if ((flags & IBS_VERTCAPTION) != 0)
    lprc->left -= nCapSize ;
  else
    if ((flags & IBS_HORZCAPTION) != 0)
      lprc->top -= nCapSize ;

} // ibAdjustWindowRect() 

//  ibAdjustWindowRectHWND  (HWND hWnd, LPRECT lprc)
//
//    Does the same thing as the USER function AdjustWindowRect(),
//    but knows about itsybitsy windows.  AdjustWindowRect() is
//    bogus for stuff like this.
//
VOID WINAPI ibAdjustWindowRectHWND (HWND hWnd, LPRECT lprc)
{
  short    cx = 0, cy = 0 ;
  UINT     nCapSize ;
  
  nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;
  
  // First check Windows's styles, then our own.
  //
  if (TestWinStyle (hWnd, WS_THICKFRAME))
  {
    cx = GetSystemMetrics (SM_CXFRAME) ;
    cy = GetSystemMetrics (SM_CYFRAME) ;
  }
  else
    if (TestWinStyle(hWnd, DS_MODALFRAME))
    {
      cx = GetSystemMetrics (SM_CXDLGFRAME) + GetSystemMetrics (SM_CXBORDER) ;
      cy = GetSystemMetrics (SM_CYDLGFRAME) + GetSystemMetrics (SM_CYBORDER) ;
    }
    else
      if (TestWinStyle(hWnd, WS_BORDER))
      {
        cx = GetSystemMetrics (SM_CXBORDER) ;
        cy = GetSystemMetrics (SM_CYBORDER) ;
      }

  InflateRect (lprc, cx, cy) ;
  
  if (TestWinStyle (hWnd, IBS_VERTCAPTION))
    lprc->left -= nCapSize ;
  else
    if (TestWinStyle (hWnd, IBS_HORZCAPTION))
      lprc->top -= nCapSize ;

} // ibAdjustWindowRectHWND() 

///////////////////////////////////////////////////////////////////////
// Internal functions
///////////////////////////////////////////////////////////////////////

// DepressMinMaxButton()
//
// This function is called when the user has pressed either the min or
// max button (i.e. WM_NCLBUTTONDOWN).  We go into a Peekmessage() loop,
// waiting for the mouse to come back up.  This allows us to make the
// button change up/down state like a real button does.
//
// lprc points to the rectangle that describes the button the
// user has clicked on.
//
BOOL NEAR PASCAL DepressMinMaxButton (HWND hWnd, UINT uiHT, LPRECT lprc)
{             
  BOOL    fDepressed = TRUE ;
  MSG     msg ;
                    
  // Draw button in down state
  DrawButton (NULL, hWnd, uiHT == HTMINBUTTON, TRUE) ;
  SetCapture (hWnd) ;
  
  while (TRUE)
  {
    if (PeekMessage ((LPMSG) &msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
    {
      switch (msg.message)
      {
        case WM_LBUTTONUP:
             if (fDepressed)
               DrawButton (NULL, hWnd, uiHT == HTMINBUTTON, FALSE) ;
             ReleaseCapture () ;
             return (PtInRect (lprc, msg.pt)) ;
        
        case WM_MOUSEMOVE:
             if (PtInRect (lprc, msg.pt))
             {
               if (!fDepressed)
                 DrawButton (NULL, hWnd, uiHT == HTMINBUTTON, fDepressed = TRUE) ;
             }
             else
             {
               if (fDepressed)
                 DrawButton (NULL, hWnd, uiHT == HTMINBUTTON, fDepressed = FALSE) ;
             }
             break ;
      }
    }
  }
} // DepressMinMaxButton()  

// DrawCaption (HDC hDC, HWND hWnd, LPRECT lprc,
//                   BOOL fVert, BOOL fSysMenu, BOOL fActive)
//
//    This function draws an itsy bitsy caption bar with or
//    without system menu to the dc specified by hDC.  The
//    caption is drawn to fit within the lprc RECT and is
//    drawn without borders.
//
BOOL NEAR PASCAL ibDrawCaption (HDC hDC, HWND hWnd, LPRECT lprc,
                                BOOL fVert, BOOL fSysMenu, BOOL fMin,
                                BOOL fMax, BOOL fActive)
{
  RECT        rc ;
  RECT        rcCap ;
  COLORREF    rgbCaptionBG ;
  COLORREF    rgbWindowFrame ;
  HBRUSH      hbrCaption ;
  UINT        ui ;
  UINT        nCapSize ;
  
  nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;
  
  // Get the colors.
  //
  rgbWindowFrame = GetSysColor (COLOR_WINDOWFRAME) ;
  rgbCaptionBG = GetSysColor (fActive ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION) ;

  SetBkMode (hDC, TRANSPARENT) ;
  SelectObject (hDC, GetStockObject (NULL_BRUSH)) ;
  SelectObject (hDC, GetStockObject (NULL_PEN)) ;
  
  rcCap = *lprc ;
  
  if (fSysMenu)
  {
    if (fVert)
      rcCap.top += nCapSize ;
    else
      rcCap.left += nCapSize ;
  }
  
  if (fMax)
  {
    if (fVert)
      rcCap.bottom -= nCapSize ;
    else
      rcCap.right -= nCapSize ;
  }
  
  if (fMin)
  {
    if (fVert)
      rcCap.bottom -= nCapSize ;
    else
      rcCap.right -= nCapSize ;
  }
  
  if (fVert)
  {
    rc.left = lprc->right - 1 ;
    rc.right = lprc->right ;
    rc.top = lprc->top ;
    rc.bottom = lprc->bottom ;
  }
  else
  {
    rc.left = lprc->left ;
    rc.right = lprc->right ;
    rc.bottom = lprc->bottom ;
    rc.top = rc.bottom - 1 ;
  }
  
  SetBkColor (hDC, rgbWindowFrame) ;
  DRAWFASTRECT (hDC, &rc) ;

  hbrCaption = CreateSolidBrush (rgbCaptionBG) ;
  hbrCaption = SelectObject (hDC, hbrCaption) ;
  SelectObject (hDC, GetStockObject (NULL_PEN)) ;
  if (fVert)
    Rectangle (hDC, rcCap.left, rcCap.top, rcCap.right, rcCap.bottom + 1) ;
  else
    Rectangle (hDC, rcCap.left, rcCap.top, rcCap.right+1, rcCap.bottom) ;
  hbrCaption = SelectObject (hDC, hbrCaption) ;
  DeleteObject (hbrCaption) ;

  ui = GetWindowTextLength (hWnd) ;
  if (fWin31)
  {
    HFONT          hFont ;
    LPSTR          lpsz ;
    LOGFONT        lf ;
    TEXTMETRIC     tm ;
    int            cx ;
    int            cy ;
    int            colour ;
    SIZE           Size ;

    if ((lpsz = GlobalAllocPtr (GHND, ui + 2)) != NULL)
    {
      UINT    nBkMode ;

      GetWindowText (hWnd, lpsz, ui + 1) ;
      nBkMode = SetBkMode (hDC, TRANSPARENT) ;

      memset (&lf, '\0', sizeof (LOGFONT)) ;

      lf.lfHeight = -(int) (nCapSize - 3) ;
      lf.lfCharSet = ANSI_CHARSET ;
      lf.lfQuality = DEFAULT_QUALITY ;
      lf.lfClipPrecision = CLIP_LH_ANGLES | CLIP_STROKE_PRECIS ;
      if (nCapSize >= 20)
        lf.lfWeight = FW_BOLD ;

      if (fVert)
      {
        // Can only rotate true type fonts (well, ok, we could
        // try and use "modern").
        lstrcpy (lf.lfFaceName, "Arial") ;
        lf.lfPitchAndFamily = FF_SWISS | 0x04 ;
        lf.lfEscapement = 900 ;

        // Note:  The Win 3.1 documentation for CreateFont() say's
        // that the lfOrientation member is ignored.  It appears,
        // that on Windows 16 3.1 this is true, but when running
        // as a 16 bit WinApp on WindowsNT 3.1 the lfOrientation
        // must be set or the text does not rotate!
        //
        lf.lfOrientation = 900 ;

        hFont = CreateFontIndirect (&lf) ;
        hFont = SelectObject (hDC, hFont) ;

        GetTextExtentPoint (hDC, lpsz, ui, &Size) ;
        cx = rcCap.bottom - ((rcCap.bottom - rcCap.top - Size.cx) / 2) ;
        cy = rcCap.left - 1 + ((rcCap.right - rcCap.left - Size.cy) / 2) ;

        // Make sure we got a rotatable font back.
        //
        GetTextMetrics (hDC, &tm) ;
        colour = SetTextColor (hDC, RGBWHITE) ;
        if (tm.tmPitchAndFamily & TMPF_VECTOR || tm.tmPitchAndFamily & TMPF_TRUETYPE)
        {
          ExtTextOut (hDC,
                      cy,
                      min (cx, rcCap.bottom),
                      ETO_CLIPPED, &rcCap,
                      lpsz, ui, NULL) ;
        }
        SetTextColor (hDC, colour) ;
        hFont = SelectObject (hDC, hFont) ;
        DeleteObject (hFont) ;
      }
      else
      {
        // Use small fonts always for the horizontal. Cause it looks
        // more like "System" than Arial.
        //
        lstrcpy (lf.lfFaceName, "Ariel") ;
        lf.lfPitchAndFamily = FF_SWISS ;

        hFont = CreateFontIndirect (&lf) ;
        hFont = SelectObject (hDC, hFont) ;

        GetTextExtentPoint (hDC, lpsz, ui, &Size) ;
        cx = rcCap.left + ((rcCap.right - rcCap.left - Size.cx) / 2) ;
        cy = rcCap.top + ((rcCap.bottom - rcCap.top - Size.cy) / 2) ;

        colour = SetTextColor (hDC, RGBWHITE) ;
        // Figger out how big the string is
        //
        ExtTextOut (hDC,
                    max (cx, rcCap.left),
                    cy,
                    ETO_CLIPPED, &rcCap,
                    lpsz, ui, NULL) ;

        SetTextColor (hDC, colour) ;
        hFont = SelectObject (hDC, hFont) ;
        DeleteObject (hFont) ;
      }

      // Unsetup the DC
      //
      SetBkMode (hDC, nBkMode) ;

      GlobalUnlockPtr (lpsz) ;
      GlobalFree (GlobalPtrHandle (lpsz)) ;
    }
  }

  if (fSysMenu)
    DrawSysMenu (hDC, hWnd, FALSE) ;

  if (fMin)
    DrawButton (hDC, hWnd, TRUE, FALSE) ;

  if (fMax)
    DrawButton (hDC, hWnd, FALSE, FALSE) ;

  return (TRUE) ;
} // DrawCaption()

// DrawSysMenu (HDC hDC, hWnd, BOOL fInvert)
//
// Draws the little system menu icon.
//
VOID NEAR PASCAL DrawSysMenu (HDC hDC, HWND hWnd, BOOL fInvert)
{
  RECT         rcIcon ;
  RECT         rcTemp ;
  RECT         rc ;
  COLORREF     rgbIconFace ;
  COLORREF     rgbWindowFrame ;
  BOOL         fDC ;
  UINT         nCapSize ;
      
  nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;
  
  if (!hDC)
  {
    fDC = TRUE ;
    hDC = GetWindowDC (hWnd) ;
  }
  else
    fDC = FALSE ;
  
  if (hDC)
  {
    rgbIconFace    = GetNearestColor (hDC, RGBLTGRAY) ;
    rgbWindowFrame = GetSysColor (COLOR_WINDOWFRAME) ;

    GetIconRect (hWnd, &rcIcon) ;
    GetWindowRect (hWnd, &rc) ;
       
    OffsetRect (&rcIcon, -rc.left, -rc.top) ;
    
    rcTemp = rcIcon ;
    
    if (TestWinStyle (hWnd, IBS_VERTCAPTION))
    {
      rc = rcIcon ;      // separator line
      rc.top = ++rc.bottom - 1 ;
    }
    else
    {
      rc = rcIcon ;      // separator line
      rc.left = ++rc.right - 1 ;
    }

    // Fill
    SetBkColor (hDC, rgbIconFace) ;
    DRAWFASTRECT (hDC, &rcTemp) ;
    
    // Draw separator line
    SetBkColor (hDC, rgbWindowFrame) ;
    DRAWFASTRECT (hDC, &rc) ;
    
    if (nCapSize > 4)
    {
      // Draw the little horizontal doo-hickey
      //
      rcTemp.top = rcIcon.top + ((nCapSize-1) / 2) ;
      rcTemp.bottom = rcTemp.top + 3 ;
      rcTemp.left  = rcTemp.left + 3 ; 
      rcTemp.right = rcTemp.right - 1 ;
          
      SetBkColor (hDC, RGBGRAY) ;
      DRAWFASTRECT (hDC, &rcTemp) ;
          
      rc = rcTemp ;
      OffsetRect (&rc, -1, -1) ;
      SetBkColor (hDC, RGBBLACK) ;
      DRAWFASTRECT (hDC, &rc) ;
          
      InflateRect (&rc, -1, -1) ;
      SetBkColor (hDC, RGBWHITE) ;
      DRAWFASTRECT (hDC, &rc) ;
    }
    
    if (fInvert)
        InvertRect (hDC, &rcIcon) ;

    if (fDC)
        ReleaseDC (hWnd, hDC) ;
  }

} // DrawSysMenu()

// DoMenu (HWND hWnd)
//
//    Pops up the system menu.
//
BOOL NEAR PASCAL DoMenu (HWND hWnd)
{
  HDC    hDC ;
  RECT   rcIcon ;
  RECT   rc ;
  POINT  pt ;
  HMENU  hMenu ;
  DWORD  dw ;

  if (!TestWinStyle (hWnd, WS_SYSMENU))
    return FALSE ;
  
  if ((hDC = GetWindowDC (hWnd)) != NULL)
  {
    // Invert the icon
    //
    DrawSysMenu (hDC, hWnd, TRUE) ;
    
    // Pop up the menu
    //
    if (TestWinStyle (hWnd, IBS_VERTCAPTION))
    {
      pt.x = -1 ;
      pt.y = 0 ;
    }
    else
    {
      pt.x = 0 ;
      pt.y = -1 ;
    }
    
    GetIconRect (hWnd, &rcIcon) ;
    GetWindowRect (hWnd, &rc) ;
    OffsetRect (&rcIcon, -rc.left, -rc.top) ;
    
    ClientToScreen (hWnd, &pt) ;
    ClientToScreen (hWnd, (LPPOINT) &rc.right) ;
    
    dw = GetWindowLong (hWnd, GWL_STYLE) ;
    SetWindowLong (hWnd, GWL_STYLE, dw | WS_SYSMENU) ;
    
    hMenu = GetSystemMenu (hWnd, FALSE) ;
    SetupSystemMenu (hWnd, hMenu) ;
    
    SetWindowLong (hWnd, GWL_STYLE, dw) ;

    TrackPopupMenu (hMenu, 0, // TPM_LEFTALIGN,
                    pt.x,
                    pt.y,
                    0,
                    hWnd,
                    &rc) ;

    DrawSysMenu (hDC, hWnd, FALSE) ;
    ReleaseDC (hWnd, hDC) ;
  }
  return (TRUE) ;
} // DoMenu()

// SetupSystemMenu (HWND hWnd, HMENU hMenu)
//
//    Enables/Disables the appropriate menu items on the
//    menu passed for the window passed.
//
void NEAR PASCAL SetupSystemMenu (HWND hWnd, HMENU hMenu)
{
  UINT     wMove ;        
  UINT     wSize ;
  UINT     wMinBox ;
  UINT     wMaxBox ;
  UINT     wRestore ;
  
  // Assume all should be grayed.
  //
  wSize = wMove = wMinBox = wMaxBox = wRestore = MF_GRAYED ;
  
  if (TestWinStyle (hWnd, WS_MAXIMIZEBOX) || IsIconic (hWnd))
    wMaxBox = MF_ENABLED ;
  
  if (TestWinStyle (hWnd, WS_MINIMIZEBOX))
    wMinBox = MF_ENABLED ;
  
  if (IsZoomed (hWnd))
    wRestore = MF_ENABLED ;
  
  if (TestWinStyle (hWnd, WS_THICKFRAME) && !(IsIconic (hWnd) || IsZoomed (hWnd)))
    wSize = MF_ENABLED ;
  
  if (!IsZoomed (hWnd) && !IsIconic (hWnd) && TestWinStyle (hWnd, WS_CAPTION))
    wMove = MF_ENABLED ;
  
  EnableMenuItem (hMenu, SC_MOVE,     wMove) ;
  EnableMenuItem (hMenu, SC_SIZE,     wSize) ;
  EnableMenuItem (hMenu, SC_MINIMIZE, wMinBox) ;
  EnableMenuItem (hMenu, SC_MAXIMIZE, wMaxBox) ;
  EnableMenuItem (hMenu, SC_RESTORE,  wRestore) ;
                      
} // SetupSystemMenu()

// GetCaptionRect (HWND hWnd, LPRECT lprc)
//
//    calculates the rectangle of the mini-caption in screen coords.
//
BOOL NEAR PASCAL GetCaptionRect (HWND hWnd, LPRECT lprc)
{
  UINT     nCapSize ;
  
  nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;
  
  if (!HASCAPTION (hWnd))
  {
    SetRectEmpty (lprc) ;
    return FALSE ;
  }
  
  GetWindowRect (hWnd, lprc) ;
  
  // the window might have other non-client components like
  // borders 
  //
  if (TestWinStyle (hWnd, WS_THICKFRAME))
  {
    lprc->left += GetSystemMetrics (SM_CXFRAME) ;
    lprc->top  += GetSystemMetrics (SM_CYFRAME) ;
    lprc->right -= GetSystemMetrics (SM_CXFRAME) ;
    lprc->bottom -= GetSystemMetrics (SM_CYFRAME) ;
  }
  else
    if (TestWinStyle (hWnd, DS_MODALFRAME))  // if it's a dialog box
    {   
      lprc->left += GetSystemMetrics (SM_CXDLGFRAME) + GetSystemMetrics (SM_CXBORDER) ;
      lprc->top  += GetSystemMetrics (SM_CYDLGFRAME) + GetSystemMetrics (SM_CYBORDER) ;
      lprc->right -= GetSystemMetrics (SM_CXDLGFRAME) + GetSystemMetrics (SM_CXBORDER) ;
      lprc->bottom -= GetSystemMetrics (SM_CYDLGFRAME) + GetSystemMetrics (SM_CYBORDER) ;
    }
    else
      if (TestWinStyle (hWnd, WS_BORDER))
      {   
        lprc->left += GetSystemMetrics (SM_CXBORDER) ;
        lprc->top  += GetSystemMetrics (SM_CYBORDER) ;
        lprc->right -= GetSystemMetrics (SM_CXBORDER) ;
        lprc->bottom -= GetSystemMetrics (SM_CYBORDER) ;
      }
  
  if (TestWinStyle (hWnd, IBS_VERTCAPTION))
    lprc->right = lprc->left + nCapSize ;
  else
    lprc->bottom = lprc->top + nCapSize ;
  
  return (TRUE) ;
} // GetCaptionRect()

// GetIconRect (HWND hWnd, LPRECT lprc)
//
//    Calculates the rect of the icon in screen coordinates.
//
BOOL NEAR PASCAL GetIconRect (HWND hWnd, LPRECT lprc)
{
  UINT    nCapSize ;
  BOOL    fMenu, fVert ;

  fMenu = TestWinStyle (hWnd, WS_SYSMENU) ;
  fVert = TestWinStyle (hWnd, IBS_VERTCAPTION) ;

  if (!GetCaptionRect (hWnd, lprc))   // window coords
    return (FALSE) ;

  if (!fMenu)
  {
    SetRectEmpty (lprc) ;
    return (FALSE) ;
  }

  nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;

  if (fVert)
    lprc->bottom = lprc->top + nCapSize ;
  else
    lprc->right = lprc->left + nCapSize ;
  
  lprc->bottom-- ;
  lprc->right-- ;
  
  return (TRUE) ;

} // GetIconRect()

// GetMinButtonRect()
//
// Calculates the rect of the minimize button in screen
// coordinates.
//
// For horizontal captions, we have the following situation ('Y' is minimize
// and '^' is maximize or restore):
//
//   +---------------------------------+
//   | - |                     | Y | ^ |
//   +---------------------------------+
//   |                         |.......| <-- This is the width (nSize)
//
// For vertical captions, we have the following:
//
//   |  |
//   |  |
//   |  |
//   |  |
//   |  |
//   |  |
//   |--|--
//   | Y| .
//   |--| . <-- This is the height of the rectangle (nSize)
//   | ^| .
//   +--+--
//
// In order to figure out where the minimize button goes, we first need
// to know if there is a maximize button.  If so, use GetMaxButtonRect()
// to place...
//
BOOL NEAR PASCAL GetMinButtonRect (HWND hWnd, LPRECT lprc)
{
  if (!TestWinStyle (hWnd, WS_MINIMIZEBOX)) 
  {
    SetRectEmpty (lprc) ;
    return (FALSE) ;
  }

  // The minimize button can be in either position 1 or 2.  If there
  // is a maximize button, it's in position 2.
  //
  if (TestWinStyle (hWnd, WS_MAXIMIZEBOX))
    return (GetButtonRect (hWnd, 2, lprc)) ;
  else
    return (GetButtonRect (hWnd, 1, lprc)) ;
}

// GetMaxButtonRect()
//
// Calculates the rect of the maximize button in screen
// coordinates.
//
// The maximize button, if present, is always to the far right
// or bottom.
//
BOOL NEAR PASCAL GetMaxButtonRect (HWND hWnd, LPRECT lprc)
{
  //The maximize button can only be in position 1.
  //
  if (!TestWinStyle (hWnd, WS_MAXIMIZEBOX))
  {
    SetRectEmpty (lprc) ;
    return FALSE ;
  }
  else
    return (GetButtonRect (hWnd, 1, lprc)) ;
}

// Get the rect where a button would go. 
// 
// This function does not care if it's a min or max, just whether
// it is the first from the right/bottom or second from the right/bottom
// and so on..
//
BOOL NEAR PASCAL GetButtonRect (HWND hWnd, UINT nPos, LPRECT lprc)
{
  UINT    nSize ;

  if (!GetCaptionRect (hWnd, lprc))   // window coords
    return (FALSE) ;

  nSize = (UINT) GETCAPTIONSIZE (hWnd) ;

  if (TestWinStyle (hWnd, IBS_VERTCAPTION))
  {
    lprc->bottom -= nSize * (nPos - 1) ;
    lprc->top = lprc->bottom - nSize + 1 ;
  }
  else
  {
    lprc->right -= nSize * (nPos - 1) ;
    lprc->left = lprc->right - nSize + 1 ;
  }

  return (TRUE) ;
} // GetButtonRect()

// DrawButton (HDC hDC, HWND hWnd, BOOL fMin, BOOL fDepressed)
// 
// Draws either the min, max, or restore buttons. If fMin is FALSE then it
// will draw either the Max or Restore button.  If fDepressed is TRUE it will
// draw the button in a down state.
//
VOID NEAR PASCAL DrawButton (HDC hDC, HWND hWnd, BOOL fMin, BOOL fDepressed)
{
  RECT            rcButton ;
  RECT            rc ;
  COLORREF        rgbWindowFrame ;
  BOOL            fDC ;
  UINT            nCapSize ;
  UINT            nOffset ;
  int             n ;

  nCapSize = (UINT) GETCAPTIONSIZE (hWnd) ;

  // If you look at the standard Windows' min/max buttons, you will notice
  // that they have two pixels of 'shadow' to the bottom and right.  Since
  // our buttons can be really, really small, we only want one pixel of 
  // shadow when they are small.  I arbitrarily decided that if the 
  // caption size is greater than or equal to 20 we will use two
  // pixels.  That's what this THRESHOLD stuff does.
  //
  #define THRESHOLD   20
  nOffset = (nCapSize >= THRESHOLD) ? 2 : 1 ;    

  if (!hDC)
  {
    fDC = TRUE ;
    hDC = GetWindowDC (hWnd) ;
  }
  else
    fDC = FALSE ;
  
  if (hDC)
  {
    rgbWindowFrame = GetSysColor (COLOR_WINDOWFRAME) ;
    
    if (fMin)
      GetMinButtonRect (hWnd, &rcButton) ;
    else
      GetMaxButtonRect (hWnd, &rcButton) ;
    
    GetWindowRect (hWnd, &rc) ;
    OffsetRect (&rcButton, -rc.left, -rc.top) ;
    
    rc = rcButton ;
    if (TestWinStyle (hWnd, IBS_VERTCAPTION))
    {
      rc = rcButton ;     // separator line
      rc.bottom = --rc.top + 1 ;
      rcButton.right-- ;
    }
    else
    {
      rc = rcButton ;     // separator line
      rc.right = --rc.left + 1 ;
      rcButton.bottom-- ;
    }
    
    // Draw separator line
    SetBkColor (hDC, rgbWindowFrame) ;
    DRAWFASTRECT (hDC, &rc) ;
    
    // Fill
    SetBkColor (hDC, RGBLTGRAY) ;
    DRAWFASTRECT (hDC, &rcButton) ;
    
    if (!fDepressed)
    {
      // The normal min/max buttons have one pixel on the top and left
      // sides for the highlight, and two pixels on the bottom and
      // right side for the shadow.
      //
      // When our caption is 'small' we only use one pixel on all
      // sides.
      //
      SetBkColor (hDC, RGBWHITE) ;
      // Draw left side
      rc = rcButton ;
      rc.right = rc.left + 1 ;
      DRAWFASTRECT (hDC, &rc) ;

      // Draw Top
      rc = rcButton ;
      rc.bottom = rc.top + 1 ;
      DRAWFASTRECT (hDC, &rc) ;

      SetBkColor (hDC, RGBGRAY) ;
      // Draw right side
      rc = rcButton ;
      rc.left = rc.right - 1 ;
      DRAWFASTRECT (hDC, &rc) ;
      if (nCapSize > THRESHOLD)
      {
        rc.left-- ;
        rc.top++ ;
        DRAWFASTRECT (hDC, &rc) ;
      }

      // Draw bottom
      rc = rcButton ;
      rc.top = rc.bottom - 1 ;
      DRAWFASTRECT (hDC, &rc) ;
      if (nCapSize > THRESHOLD)
      {
        rc.top-- ;
        rc.left++ ;
        DRAWFASTRECT (hDC, &rc) ;
      }

      rcButton.left++ ;
      rcButton.top++ ;
      rcButton.right -= nOffset ;
      rcButton.bottom -= nOffset ;
    }
    else
    {
      // Draw depressed state

      SetBkColor (hDC, RGBGRAY) ;
      // Draw left side
      rc = rcButton ;
      rc.right = rc.left + nOffset ;
      DRAWFASTRECT (hDC, &rc) ;

      // Draw Top
      rc = rcButton ;
      rc.bottom = rc.top + nOffset ;
      DRAWFASTRECT (hDC, &rc) ;

      rcButton.left += 2 * nOffset ;
      rcButton.top += 2 * nOffset ;
    }

    // Now draw the arrows.  We do not want the
    // arrows to grow too large when we have a bigger than
    // normal caption, so we restrict their size.
    //
    // rcButton now represents where we can place our
    // arrows.  
    //
    // The maximum size of our arrows (i.e. the width of rcButton)
    // has been empirically determined to be SM_CYCAPTION / 2
    //
    n = ((GetSystemMetrics (SM_CYCAPTION)) / 2) - (rcButton.right - rcButton.left) ;
    if (n < 1)
      InflateRect (&rcButton, n / 2 - 1, n / 2 - 1) ;

    if (fMin)
      DrawArrow (hDC, &rcButton, ARROW_DOWN) ;
    else 
      if (IsZoomed (hWnd))
        DrawArrow (hDC, &rcButton, ARROW_RESTORE) ;
      else
        DrawArrow (hDC, &rcButton, ARROW_UP) ;
    
    if (fDC)
      ReleaseDC (hWnd, hDC) ;
  }
} // DrawButton()

// DrawArrow
//
// Draws either a up or down arrow.  The arrow is bound by the rectangle
//
VOID NEAR PASCAL DrawArrow (HDC hdc, LPRECT lprc, UINT uiStyle)
{
  int     row ;
  int     xTip ;
  int     yTip ;
  RECT    rc ;
  int     nMax = (lprc->bottom - lprc->top) >> 1 ;

  SetBkColor (hdc, RGBBLACK) ;

  // We draw the arrow by drawing a series of horizontal lines
  //
  xTip = lprc->left + ((lprc->right - lprc->left+1) >> 1) ;
  switch (uiStyle)
  {
    case ARROW_UP:
         yTip = lprc->top + ((lprc->bottom - lprc->top-1) >> 2) ;
         for  (row = nMax ; row > 0 ; row--)
         {
           rc.left = xTip - row ;
           rc.right = xTip + row - 1 ;
           rc.top = yTip + row ;
           rc.bottom = rc.top + 1 ;
           DRAWFASTRECT (hdc, &rc) ;
         }
         break ;

    case ARROW_DOWN:
         yTip = lprc->bottom - ((lprc->bottom - lprc->top-1) >> 2) ;
         for  (row = nMax ; row > 0 ; row--)
         {
           rc.left = xTip - row ;
           rc.right = xTip + row - 1 ;
           rc.top = yTip - row ;
           rc.bottom = rc.top + 1 ;
           DRAWFASTRECT (hdc, &rc) ;
         }
         break ;

    case ARROW_RESTORE:
    default:
         yTip = lprc->top + ((lprc->bottom - lprc->top-1) >> 3) - 2;
         for (row = 1 ; row <= nMax ; row++)
         {
           rc.left = xTip - row ;
           rc.right = xTip + row - 1 ;
           rc.top = yTip + row ;
           rc.bottom = rc.top + 1 ;
           DRAWFASTRECT (hdc, &rc) ;
         }
 
         yTip += (nMax+1) * 2 ;
         for  (row = nMax ; row > 0 ; row--)
         {
           rc.left = xTip - row ;
           rc.right = xTip + row - 1 ;
           rc.top = yTip - row ;
           rc.bottom = rc.top + 1 ;
           DRAWFASTRECT (hdc, &rc) ;
         }
         break ;
  }
}  // DrawArrow()   


