/* Copyright 1994, 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_windDocdef[] =
		"$Id: windDocdef.c 63 2004-12-24 18:19:43Z ctm $";
#endif

#include "rsys/common.h"
#include "QuickDraw.h"
#include "CQuickDraw.h"
#include "WindowMgr.h"
#include "ToolboxUtil.h"

#include "rsys/cquick.h"
#include "rsys/wind.h"
#include "rsys/ctl.h"
#include "rsys/screen.h"
#include "rsys/fatal.h"

#include "rsys/image.h"

#include "zoom.c"
#include "go_away.c"
#include "active.c"
#include "ractive.c"
#include "grow.c"

#if 1

PUBLIC BOOLEAN ROMlib_window_zoomed(WindowPeek wp)
{
    BOOLEAN retval;
    const Rect *portrp, *staterp, *boundsrp;

    portrp   = &PORT_RECT (wp);
    staterp  = &MR (*(WStateData **) WINDOW_DATA (wp))->stdState;
    boundsrp = &PORT_BOUNDS (wp);

    retval = (WINDOW_SPARE_FLAG_X (wp)
	      && CW (portrp->top)  - CW (boundsrp->top)  == CW (staterp->top)
	      && CW (portrp->left) - CW (boundsrp->left) == CW (staterp->left)
	      && RECT_WIDTH(portrp) == RECT_WIDTH(staterp)
	      && RECT_HEIGHT(portrp) == RECT_HEIGHT(staterp) );
    return retval;
}

#define WINDOW_ZOOMED(w) (ROMlib_window_zoomed(w))

#endif

/* `color_p' is TRUE if the current call to the window definition
   function is for a while to be drawn in color */
static int color_p;

/* TRUE if the current window being serviced is a rounded window */
static int rounded_window_p;

/* colors to draw the current window with; has the appropriate
   black and white colors if `!color_p' */
static RGBColor *window_colors;

#define content (&window_colors[wContentColor])
/* FIXME: hack */
#undef frame
#define frame (&window_colors[wFrameColor])
#define text (&window_colors[wTextColor])
#undef hilite
#define hilite (&window_colors[wHiliteColor])
#define title_bar (&window_colors[wTitleBarColor])
#define title_bar (&window_colors[wTitleBarColor])
#define hilite_light (&window_colors[wHiliteColorLight])
#define hilite_dark (&window_colors[wHiliteColorDark])
#define title_bar_light (&window_colors[wTitleBarLight])
#define title_bar_dark (&window_colors[wTitleBarDark])
#define dialog_light (&window_colors[wDialogLight])
#define dialog_dark (&window_colors[wDialogDark])
#define tinge_light (&window_colors[wTingeLight])
#define tinge_dark (&window_colors[wTingeDark])

#define title_bar_bk (&window_colors[13])
#define title (&window_colors[14])
#define frame_outline (&window_colors[15])
#define half_tinge (&window_colors[16])
#define lines (&window_colors[17])
#define half_dialog (&window_colors[18])

/* aliases */
#define frame_shadow frame

RGBColor *
validate_colors_for_window (GrafPtr w)
{
  static RGBColor color_window_colors[19];
  /* just make the unused color something noticable */
#define UNUSED  { CWC (0xFFFF), CWC (0), CWC (0) }
#define WHITE   { CWC (0xFFFF), CWC (0xFFFF), CWC (0xFFFF) }
#define BLACK   { CWC (0), CWC (0), CWC (0) }
  static RGBColor bw_window_colors[19] =
    {
      WHITE,  /* content */
      BLACK,   /* frame */
      UNUSED,  /* text */
      UNUSED,  /* hilite */
      UNUSED,  /* title_bar */
      UNUSED,  /* hilite_light */
      UNUSED,  /* hilite_dark */
      UNUSED,  /* title_bar_light */
      UNUSED,  /* title_bar_dark */
      UNUSED,  /* dialog_light */
      BLACK,  /* dialog_dark */
      WHITE,   /* tinge_light */
      BLACK,   /* tinge_dark */
      WHITE,   /* title_bar_bk */
      BLACK,   /* title */
      BLACK,   /* frame_outline */
      UNUSED,  /* half_tinge */
      BLACK,   /* lines */
      UNUSED,  /* half_dialog */
    };
#undef BLACK
#undef WHITE
#undef UNUSED
  RGBColor *window_colors;
  AuxWinHandle t_aux_w;
  int i;
  
  /* FIXME: assuming that colors not specified come from the default
     color table; verify this */
  for (i = 0; i <= 12; i ++)
    color_window_colors[i] = default_color_win_ctab[i].rgb;
  
  t_aux_w = MR (*lookup_aux_win (w));
  if (t_aux_w && HxX (t_aux_w, awCTable))
    {
      CTabHandle w_ctab;
      ColorSpec *w_ctab_table;
      int w_ctab_size;
      
      w_ctab = HxP (t_aux_w, awCTable);
      w_ctab_table = CTAB_TABLE (w_ctab);
      /* this window definition function uses only the first 12
	 entries in the window color table */
      w_ctab_size = CTAB_SIZE (w_ctab);
      if (w_ctab_size > 12)
	{
	  warning_unexpected ("window color table with size `%d' > 12; ignored",
			      w_ctab_size);
	  w_ctab_size = 0;
	}
      
      /* blow off system6 style color tables */
      if (w_ctab_size <= 4 && 0) /* really blow off? Thresholder suggests
				    otherwise.  I'm leaving this code in
				    for now because there's probably a
				    reason it was in there in the first
				    place.  Perhaps the <= should be < */
	{
	  /* same content color */
	  for (i = 0; i <= w_ctab_size; i ++)
	    if (w_ctab_table[i].value == CW (wContentColor))
	      color_window_colors[wContentColor]
		= w_ctab_table[i].rgb;
	}
      else
	{
	  for (i = 0; i <= w_ctab_size; i ++)
	    {
	      ColorSpec *w_ctab_entry;
	      int w_ctab_entry_index;
	      
	      w_ctab_entry = &w_ctab_table[i];
	      w_ctab_entry_index = CW (w_ctab_entry->value);
	      if (w_ctab_entry_index < 0 || w_ctab_entry_index > 12)
		{
#if !defined (CYGWIN32) /* just gets in the way of debugging under windows */
		  warning_unexpected
		    ("window color table with index `%d' > 12 or < 0; ignored",
		     w_ctab_entry_index);
#endif
		  continue;
		}
	      color_window_colors[w_ctab_entry_index] = w_ctab_entry->rgb;
	    }
	}
    }

  bw_window_colors[wContentColor] = color_window_colors[wContentColor];
  
#define FAIL goto failure
#define DONE goto done
#define DO_BLOCK_WITH_FAILURE(try_block, fail_block)	\
  {							\
    { try_block }					\
    goto done;						\
  failure:						\
    { fail_block }					\
    /* fall through */					\
  done:;						\
  }

  DO_BLOCK_WITH_FAILURE
    ({
      RGBColor image_colors[5];
      RGBColor temp1;
      RGBColor temp2;
      RGBColor temp3;
      
      window_colors = color_window_colors;
      
      if (rounded_window_p)
	{
	  if (WINDOW_HILITED_X (w))
	    {
	      *title_bar_bk = *frame;
	      *title = *title_bar;	      
	    }
	  else
	    {
	      *title = *frame;
	      *title_bar_bk = *title_bar;
	    }
	  DONE;
	}
      
      if (!AVERAGE_COLOR (title_bar_light, title_bar_dark, 0xEEEE,
			  &temp1))
	FAIL;
      if (!AVERAGE_COLOR (hilite_light, &ROMlib_black_rgb_color, 0x8888,
			  &temp2))
	FAIL;
      if (!AVERAGE_COLOR (hilite_light, &ROMlib_black_rgb_color, 0x5555,
			  &temp3))
	FAIL;
      
      if (WINDOW_HILITED_X (w))
	{
	  *title_bar_bk = temp1;
	  *title = *text;
	  *frame_outline = *frame;
	}
      else
	{
	  *title = temp2;
	  *title_bar_bk = *content;
	  *frame_outline = temp3;
	}
      if (!AVERAGE_COLOR (tinge_light, tinge_dark, 0xAAAA,
			  half_tinge))
	FAIL;
      if (!AVERAGE_COLOR (title_bar_light, title_bar_dark, 0xAAAA,
			  lines))
	FAIL;
      if (!AVERAGE_COLOR (dialog_light, dialog_dark, 0x8888,
			  half_dialog))
	FAIL;
      
      /* now do the image colors */
      image_colors[0] = *title_bar_bk;
      image_colors[1] = *tinge_light;
      if (!AVERAGE_COLOR (title_bar_light, title_bar_dark, 0xAAAA,
			  &image_colors[2]))
	FAIL;
      image_colors[3] = *tinge_dark;
      /* used only in the grow icon */
      if (!AVERAGE_COLOR (title_bar_light, title_bar_dark, 0xBBBB,
			  &image_colors[4]))
	FAIL;

      image_update_ctab (go_away, image_colors, 3);
      image_update_ctab (zoom, image_colors, 3);
      image_update_ctab (active, image_colors, 3);
      image_update_ctab (grow, image_colors, 4);
      
      color_p = TRUE;
     },
     {
       gui_assert (!rounded_window_p);

       window_colors = bw_window_colors;
       color_p = FALSE;
     });
#undef DO_BLOCK_WITH_FAILURE
#undef FAIL

  return window_colors;
}

enum box_flag
{
  zoom_box_flag, go_away_box_flag,
};

void
toggle_box_active (enum box_flag which_box, Point origin)
{
  Rect dst_rect;
  pixel_image_t *box;

  /* compute the destination rectangle */
  dst_rect.top    = CW (origin.v);
  dst_rect.left   = CW (origin.h);
  dst_rect.bottom = CW (origin.v + 13);
  dst_rect.right  = CW (origin.h + 13);

  /* set box */
  if (rounded_window_p)
    color_p = FALSE;
  
  if (which_box == zoom_box_flag)
    box = zoom;
  else if (which_box == go_away_box_flag)
    box = go_away;
  else
    gui_abort ();  /* there are no more boxes */

  if (rounded_window_p)
    {
      /* invert the set bits of the box */
      PORT_FG_COLOR_X (thePort)
	= CL ((1 << PIXMAP_PIXEL_SIZE (CPORT_PIXMAP (thePort))) - 1);
      PORT_FG_COLOR_X (thePort) = CLC (0);
    }
  else
    {
      /* make sure the fg/bk colors are b/w so as not to confuse the
	 `CopyBits ()' */
      RGBForeColor (&ROMlib_black_rgb_color);
      RGBBackColor (&ROMlib_white_rgb_color);
    }

  image_copy (box, color_p, &dst_rect, srcXor);

  if (rounded_window_p)
    {
      RGBColor *target_color;

      /* this isn't quite what the mac does, but i couldn't figure
	 that one out.  it works for the common case (white on block
	 rounded window).  the mac function takes into account both
	 `title' and `title_bar' in some unknown fasion */
      if (((  title_bar->red
	    + title_bar->green
	    + title_bar->blue) / 3) > 0x8000)
	target_color = &ROMlib_black_rgb_color;
      else
	target_color = &ROMlib_white_rgb_color;

      PORT_FG_COLOR_X (thePort)
	= CL (Color2Index (target_color) ^ Color2Index (frame));
    }

  image_copy (rounded_window_p ? ractive : active,
	      color_p, &dst_rect, srcXor);
}


void
toggle_zoom_box (GrafPtr w)
{
  Point origin;
  
  origin.h = CW (PORT_RECT (w).right) - CW (PORT_BOUNDS (w).left) - 21;
  origin.v = CW (PORT_RECT (w).top)   - CW (PORT_BOUNDS (w).top) - 16;
  
  toggle_box_active (zoom_box_flag, origin);
}

void
toggle_go_away_box (GrafPtr w)
{
  Point origin;
  
  origin.h = CW (PORT_RECT (w).left) - CW (PORT_BOUNDS (w).left) + 8;
  origin.v = CW (PORT_RECT (w).top)  - CW (PORT_BOUNDS (w).top) - 16;

  toggle_box_active (go_away_box_flag, origin);
}

void
hilite_window (int left, int top, int right, int bottom)
{
  int l, r;

  /* we are called from `draw_frame ()' only, pen is (1, 1), patCopy,
     pattern black */

  /* draw title bar shadow/tinge */
  if (color_p)
    {
      /* bottom and right; we get both disputed corners */
      RGBForeColor (half_tinge);
      MoveTo (left, top - 2);
      LineTo (right - 1, top - 2);
      LineTo (right - 1, top - 18);

      /* top and left */
      RGBForeColor (tinge_light);
      MoveTo (right - 2, top - 18);
      LineTo (left, top - 18);
      LineTo (left, top - 3);
    }

  /* draw the lines */
  RGBForeColor (lines);

  l = left + 1;
  r = right - 2;
  MoveTo (l, top - 15);
  LineTo (r, top - 15);
  MoveTo (l, top - 13);
  LineTo (r, top - 13);
  MoveTo (l, top - 11);
  LineTo (r, top - 11);
  MoveTo (l, top - 9);
  LineTo (r, top - 9);
  MoveTo (l, top - 7);
  LineTo (r, top - 7);
  MoveTo (l, top - 5);
  LineTo (r, top - 5);
}

void
draw_title (GrafPtr w,
	    int go_away_drawn, int zoom_drawn)
{
  HIDDEN_GrafPtr tp;
  int left, top, right, bottom;
  StringHandle th;
  RgnHandle saveclip = NULL;
  int title_width;
  int title_start;
  int left_bound;

  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

/* #warning "clean up this port mess in draw_title ()" */
  GetPort(&tp);
  tp.p = MR(tp.p);
  
  if (go_away_drawn)
    left_bound = left + 28;
  else
    left_bound = left;
  
  title_width = WINDOW_TITLE_WIDTH (w);
  if (title_width)
    {
      title_start = left + (right - left - title_width) / 2 - 6;
      saveclip = PORT_CLIP_REGION (tp.p);
      PORT_CLIP_REGION_X (tp.p) = RM (NewRgn ());
      if (title_start >= left_bound)
	CopyRgn (saveclip, PORT_CLIP_REGION (tp.p));
      else
	{
	  title_start = left_bound;
	  SetRectRgn (PORT_CLIP_REGION (tp.p),
		      title_start, top - 16,
		      right - (zoom_drawn ? 28 : 0), top-3);
	  SectRgn (PORT_CLIP_REGION (tp.p), saveclip, PORT_CLIP_REGION (tp.p));
	}
      
      {
	RgnHandle additional_clip_rgn;

	additional_clip_rgn = NewRgn ();
	SetRectRgn (additional_clip_rgn, title_start, top - 16,
		    title_start + title_width + 12, top - 2);
	SectRgn (PORT_CLIP_REGION (tp.p), additional_clip_rgn,
		 PORT_CLIP_REGION (tp.p));
      
	/* erase the text to be drawn with the title bar background
	   color */
	RGBForeColor (title_bar_bk);
	PaintRgn (additional_clip_rgn);
	DisposeRgn (additional_clip_rgn);
      }
      
      RGBBackColor (title_bar_bk);
      RGBForeColor (title);
      
      th = WINDOW_TITLE (w);
      LOCK_HANDLE_EXCURSION_1
	(th,
	 {
	   PORT_TX_MODE_X (thePort) = CWC (srcCopy);
	   MoveTo (title_start + 6, top - 5);
	   DrawString (STARH (th));
	 });
      
      if (saveclip)
	{
	  DisposeRgn (PORT_CLIP_REGION (tp.p));
	  PORT_CLIP_REGION_X (tp.p) = RM (saveclip);
	}
    }
}

void
draw_go_away (int h, int v)
{
  Rect r;

  if (rounded_window_p)
    {
      RGBForeColor (title_bar);
      RGBBackColor (title_bar_bk);
    }
  else
    {
      /* make sure the fg/bk colors are b/w so as not to confuse the
	 `CopyBits ()' */
      RGBForeColor (&ROMlib_black_rgb_color);
      RGBBackColor (&ROMlib_white_rgb_color);
    }
  
  SetRect (&r, h + 8, v - 16, h + 21, v - 3);

  image_copy (go_away, rounded_window_p ? 0 : color_p, &r, srcCopy);
}

void
draw_zoom (int h, int v)
{
  Rect r;

  if (rounded_window_p)
    {
      RGBForeColor (title_bar);
      RGBBackColor (title_bar_bk);
    }
  else
    {
      /* make sure the fg/bk colors are b/w so as not to confuse the
	 `CopyBits ()' */
      RGBForeColor (&ROMlib_black_rgb_color);
      RGBBackColor (&ROMlib_white_rgb_color);
    }

  SetRect (&r, h - 21, v - 16, h - 8, v - 3);

  image_copy (zoom, rounded_window_p ? 0 : color_p, &r, srcCopy);
}

void
draw_frame (GrafPtr w, int draw_zoom_p, boolean_t goaway_override)
{
  int left, top, right, bottom;
  Rect r;

  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);
  
  PenSize (1, 1);
  /* draw with the current foreground color */
  PenPat (black);
  PenMode (patCopy);

  /* bounds of the title bar */
  SetRect (&r, left - 1, top - 19, right + 1, top);
  /* clear the title bar with the titlebar background color; title bar
     color is dependent on whether or not the window is hilited,
     `validate_colors_for_window ()' takes this into account */
  RGBForeColor (title_bar_bk);
  PaintRect (&r);

  /* draw the frame; these are drawn in the frame_outline_color */
  RGBForeColor (frame_outline);
  r.bottom = CW (bottom + 1);
  FrameRect (&r);
  MoveTo (left, top - 1);
  LineTo (right, top - 1);

  /* draw the `shadow' */
  RGBForeColor (frame_shadow);
  MoveTo (left, bottom + 1);
  LineTo (right + 1, bottom + 1);
  MoveTo (right + 1, top - 18);
  LineTo (right + 1, bottom);

  if (WINDOW_HILITED_X (w))
    {
      int draw_go_away_p = WINDOW_GO_AWAY_FLAG (w) && !goaway_override;
      
      hilite_window (left, top, right, bottom);
      if (draw_go_away_p)
	draw_go_away (left, top);
      if (draw_zoom_p)
	draw_zoom (right, top);
      draw_title (w, draw_go_away_p, draw_zoom_p);
    }
  else
    draw_title (w, FALSE, FALSE);

/* #warning "delete these, they shouldn't be necessary" */
  /* be a sneaky bastard, and set the fg/bk color to b/w */
  RGBForeColor (&ROMlib_black_rgb_color);
  RGBBackColor (&ROMlib_white_rgb_color);
}

void
draw_grow_lines (Rect *bounds)
{
  int left, top, right, bottom;
  Rect r;
  
  left   = CW (bounds->left);
  top    = CW (bounds->top);
  right  = CW (bounds->right);
  bottom = CW (bounds->bottom);
  
  PenSize (1, 1);
  SetRect (&r, left - 1, top - 19, right + 1, bottom + 1);
  FrameRect (&r);  /* outside */
  
  MoveTo (left, top - 1);
  LineTo (right, top - 1);   /* under title */
  
  MoveTo (left, bottom - 15);
  LineTo (right - 1, bottom - 15); /* at bottom */
  
  MoveTo (right - 15, top);
  LineTo (right - 15, bottom - 1); /* at side */
}

void
draw_dialog_box (GrafPtr w)
{
  int left, top, right, bottom;
  Rect r;
  RGBColor middle_color;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

  PenSize (1, 1);
  PenMode (patCopy);

  /* draw the outtermost rectangle; in the frame_shadow_color */
  RGBForeColor (frame_shadow);
  SetRect (&r, left - 8, top - 8, right + 8, bottom + 8);
  FrameRect (&r);
  
  /* draw the center rectangle */
  if (color_p)
    {
      middle_color.red   = 0xBBBB;
      middle_color.green = 0xBBBB;
      middle_color.blue  = 0xBBBB;
    }
  else
    middle_color = ROMlib_white_rgb_color;
  
  RGBForeColor (&middle_color);
  InsetRect (&r, 2, 2);
  FrameRect (&r);
  
  /* draw the innermost rectangle; in the dark dialog color */
  RGBForeColor (dialog_dark);
  InsetRect (&r, 2, 2);
  FrameRect (&r);

  /* FIXME:
     go in one more pixel, and white out a rect
     three pixels wide; having to do this worries
     me that the location of the border may be wrong,
     resolve this */
  InsetRect (&r, 1, 1);
  PenSize (3, 3);
  RGBForeColor (content);
  FrameRect (&r);
  PenSize (1, 1);
  
  if (color_p)
    {
      /* and now for the inbetween hiliting */
      RGBForeColor (dialog_light);
      MoveTo (left - 7, bottom + 6);
      LineTo (left - 7, top - 7);
      LineTo (right + 6, top - 7);
      
      RGBForeColor (half_dialog);
      LineTo (right + 6, bottom + 6);
      LineTo (left - 7, bottom + 6);
      
      
      /* same color; half_dialog_color */
      MoveTo (left - 5, bottom + 4);
      LineTo (left - 5, top - 5);
      LineTo (right + 4, top - 5);
      
      RGBForeColor (dialog_light);
      MoveTo (right + 4, top - 4);
      LineTo (right + 4, bottom + 4);
      LineTo (left - 4, bottom + 4);
    }
  else
    {
      RGBForeColor (&ROMlib_black_rgb_color);
      InsetRect (&r, -2, -2);
      FrameRect (&r);

      RGBForeColor (&ROMlib_white_rgb_color);
      InsetRect (&r, -2, -2);
      FrameRect (&r);
    }
}

void
draw_plain_dialog_box (GrafPtr w)
{
  int left, top, right, bottom;
  Rect r;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

  PenSize (1, 1);
  PenMode (patCopy);
  SetRect (&r, left - 1, top - 1, right + 1, bottom + 1);
  RGBForeColor (frame_outline);
  FrameRect (&r);
}

void
draw_alt_dialog_box (GrafPtr w)
{
  int left, top, right, bottom;
  Rect r;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);
  
  PenSize (1, 1);
  PenMode (patCopy);
  SetRect (&r, left-1, top-1, right+1, bottom+1);
  RGBForeColor (frame_outline);
  FrameRect (&r);
  
  PenSize (2, 2);
  RGBForeColor (frame_outline);
  MoveTo (left + 1, bottom + 1);
  LineTo (right + 1, bottom + 1);
  MoveTo (right + 1, top + 1);
  LineTo (right + 1, bottom);
  
  PenSize (1, 1);
}

void
draw_grow_icon (GrafPtr w)
{
  int left, top, right, bottom;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);
    
  PenPat (black);
  PenSize (1, 1);
  PenMode (patCopy);
    
  /* NOTE: IMI-302 says we should be drawing the lines here, but
     Microsoft Excel suggests otherwise (choose Number from the format
     menu and then type clover-/) */

  MoveTo (left, bottom - 15);
  LineTo (right - 1, bottom - 15);     /* line at bottom */
  
  MoveTo (right - 15, top);
  LineTo (right - 15, bottom - 1);     /* line at side */

  {
    Rect rect;

    rect.top = CW (bottom - 14);
    rect.left = CW (right - 14);
    rect.bottom  = CW (bottom);
    rect.right = CW (right);
    
    image_copy (grow, color_p, &rect, srcCopy);
  }
}

void
erase_grow_icon (GrafPtr w)
{
  int left, top, right, bottom;
  Rect r;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);
  
  PenPat (black);
  PenSize (1, 1);
  PenMode (patCopy);
  MoveTo (left, bottom - 15);
  LineTo (right - 1, bottom - 15); 
  MoveTo (right - 15, top);
  LineTo (right - 15, bottom - 1);
  SetRect (&r, right - 14, bottom - 14, right, bottom);
  FillRect (&r, white);
}

LONGINT
hit_doc (WindowPeek w, LONGINT parm, int growable_p,
	 boolean_t goaway_override)
{
  Point p;
  int left, top, right, bottom;
    
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

  p.v = HiWord (parm);
  p.h = LoWord (parm);
  if (WINDOW_HILITED_X (w))
    {
      if (PtInRgn (p, WINDOW_CONT_REGION (w)))
	{
	  if (!growable_p || (p.h < right - 16) || (p.v < bottom - 16))
	    return wInContent;
	  else
	    return wInGrow;
	}
      if ((p.h >= left - 1) && (p.h < right + 1)
	  && (p.v >= top - 19) && (p.v < top))
	{
	  if (WINDOW_GO_AWAY_FLAG_X (w) && !goaway_override
	      && (p.h > left + 8) && (p.h < left + 20)
	      && (p.v > top - 16) && (p.v < top - 4))
	    return wInGoAway;
	  else if (WINDOW_SPARE_FLAG_X (w)
		   && (p.h > right - 20) && (p.h < right - 8)
		   && (p.v > top - 16)   && (p.v < top - 4))
	    return WINDOW_ZOOMED ((WindowPeek) w) ? wInZoomIn : wInZoomOut;
	  else
	    return wInDrag;
	}
      else
	return wNoHit;
    }
  else
    {
      if (PtInRgn (p, WINDOW_CONT_REGION (w)))
	return wInContent;
      else if ((p.h >= left - 1) && (p.h < right + 1)
	       && (p.v >= top - 19) && (p.v < top))
	return wInDrag;
      else
	return wNoHit;
    }
}

LONGINT
hit_dialog_box (WindowPeek w, LONGINT parm)
{
  Point p;
    
  p.v = HiWord (parm);
  p.h = LoWord (parm);
  if (PtInRgn (p, WINDOW_CONT_REGION (w)))
    return wInContent;
  else
    return wNoHit;
}

void
calc_doc (GrafPtr w)
{
  RgnHandle rh;
  INTEGER *ip;
  int left, top, right, bottom;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

  SetRectRgn (WINDOW_CONT_REGION (w), left, top, right, bottom);

  rh = WINDOW_STRUCT_REGION (w); 
  ReallocHandle ((Handle) rh, (Size) 44);
  HxX (rh, rgnBBox.left)   = CW (left   -  1); 
  HxX (rh, rgnBBox.top)    = CW (top    - 19);  
  HxX (rh, rgnBBox.right)  = CW (right  +  2);   
  HxX (rh, rgnBBox.bottom) = CW (bottom +  2);
  HxX (rh, rgnSize) = CWC (44);
  ip = (INTEGER *) STARH (rh) + 5;

  *ip++ = CW(top   - 19); 
  *ip++ = CW(left  - 1);
  *ip++ = CW(right + 1);
  *ip++ = CWC(32767);
  
  *ip++ = CW(top   - 18);
  *ip++ = CW(right + 1);
  *ip++ = CW(right + 2);
  *ip++ = CWC(32767);
  
  *ip++ = CW(bottom + 1);
  *ip++ = CW(left   - 1);
  *ip++ = CW(left);
  *ip++ = CWC(32767);
  
  *ip++ = CW(bottom + 2);
  *ip++ = CW(left);
  *ip++ = CW(right  + 2);
  *ip++ = CWC(32767);
  
  *ip++ = CWC(32767);
}

void
calc_alt_dialog_box (GrafPtr w)
{
  RgnHandle rh;
  INTEGER *ip;
  int left, top, right, bottom;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

  SetRectRgn (WINDOW_CONT_REGION (w), left, top, right, bottom);

  rh = WINDOW_STRUCT_REGION (w); 

  ReallocHandle ((Handle) rh, (Size) 44);
  HxX (rh, rgnBBox.left)   = CW (left   - 1);
  HxX (rh, rgnBBox.top)    = CW (top    - 1);
  HxX (rh, rgnBBox.right)  = CW (right  + 3);   
  HxX (rh, rgnBBox.bottom) = CW (bottom + 3);
  HxX (rh, rgnSize) = CWC (44);
  ip = (INTEGER *) STARH (rh) + 5;
  
  *ip++ = CW(top   - 1);
  *ip++ = CW(left  - 1);
  *ip++ = CW(right + 1);
  *ip++ = CWC(32767);
  
  *ip++ = CW(top   + 1);
  *ip++ = CW(right + 1);
  *ip++ = CW(right + 3);
  *ip++ = CWC(32767);
  
  *ip++ = CW(bottom + 1);
  *ip++ = CW(left   - 1);
  *ip++ = CW(left   + 1);
  *ip++ = CWC(32767);
  
  *ip++ = CW(bottom + 3);
  *ip++ = CW(left   + 1);
  *ip++ = CW(right  + 3);
  *ip++ = CWC(32767);
  
  *ip++ = CWC(32767);
}

void
calc_dialog_box (GrafPtr w, INTEGER n)
{
  int left, top, right, bottom;
  
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

  SetRectRgn (WINDOW_CONT_REGION (w), left, top, right, bottom);
  SetRectRgn (WINDOW_STRUCT_REGION (w),
	      left - n, top - n, right + n, bottom + n);
}

struct draw_save
{
  PenState pen_state;
  RGBColor fg_color;
  RGBColor bk_color;
  LONGINT fg;
  LONGINT bk;
};

P4 (PUBLIC pascal, LONGINT, wdef0,
    INTEGER, varcode, WindowPtr, window,
    INTEGER, message, LONGINT, parm)
{
  WindowPeek w = (WindowPeek) window;
  draw_state_t draw_state;
  int zoom_bit;
  boolean_t draw_p;
  
  rounded_window_p = FALSE;
  
  /* extract and clean ZOOMBIT from varcode into zoom_bit */
  zoom_bit = varcode & ZOOMBIT;
  varcode &= ~ZOOMBIT;

  if (varcode > 4 && varcode != 5)
    varcode &= 3;

  draw_p = (message == wDraw
	    || message == wDrawGIcon
	    || message == wGrow);

  if (draw_p)
    {
      draw_state_save (&draw_state);
      
      window_colors = validate_colors_for_window ((GrafPtr) w);
    }
  
  switch (message)
    {
    case wDraw:
      if (!WINDOW_VISIBLE_X (w))
	return 0;
      PenNormal ();
      TRAPBEGIN ();
      switch (varcode)
	{
	case documentProc:
	case noGrowDocProc:
	case movableDBoxProc:
	  switch (parm)
	    {
	    case 0: /* draw entire window frame */
	      draw_frame ((GrafPtr) w, zoom_bit, varcode == movableDBoxProc);
	      break;
	    case wInGoAway:
	      toggle_go_away_box ((GrafPtr) w);
	      break;
	    case wInZoomIn:
	    case wInZoomOut:
	      toggle_zoom_box ((GrafPtr) w);
	      break;
	    }
	  break;
        case dBoxProc:
	  draw_dialog_box ((GrafPtr) w);
	  break;
        case plainDBox:
	  draw_plain_dialog_box ((GrafPtr) w);
	  break;
        case altDBoxProc:
	  draw_alt_dialog_box ((GrafPtr) w);
	  break;
        }
      TRAPEND ();
      break;
    case wHit:
      switch (varcode)
	{
	case documentProc:
	  return hit_doc (w, parm, TRUE, FALSE);
	case noGrowDocProc:
	case movableDBoxProc:
	  return hit_doc (w, parm, FALSE, varcode == movableDBoxProc);
	default:
	  return hit_dialog_box (w, parm);
	}
      /* control never gets here */
      gui_abort ();
    case wCalcRgns:
      switch(varcode)
	{
        case documentProc:
        case noGrowDocProc:
	case movableDBoxProc:
#warning TODO: correct frame for movableDBoxProc (toolbox essentials 4-10)
	  calc_doc ((GrafPtr) w);
	  break;
        case dBoxProc:
	  calc_dialog_box ((GrafPtr) w, 8);
	  break;
        case plainDBox:
	  calc_dialog_box ((GrafPtr) w, 1);
	  break;
        case altDBoxProc:
	  calc_alt_dialog_box ((GrafPtr) w);
	  break;
        }
      break;
    case wNew:
      if (zoom_bit)
	{
	  WStateData *wsp;
	  
	  WINDOW_DATA_X (w) = RM (NewHandle ((Size) sizeof (WStateData)));
	  wsp = MR (* (WStateData **) WINDOW_DATA (w));
	  
	  wsp->stdState = GD_BOUNDS (MR (TheGDevice));
	  InsetRect (&wsp->stdState, 3, 3);
	  wsp->stdState.top = CW (CW (wsp->stdState.top) + 38);
	  wsp->userState = PORT_RECT (w);
	  
	  /* local to global */
	  OffsetRect (&wsp->userState, -CW (PORT_BOUNDS (w).left),
		                       -CW (PORT_BOUNDS (w).top));
	  
	  WINDOW_SPARE_FLAG_X (w) = TRUE;
	}
      else
	WINDOW_SPARE_FLAG_X (w) = FALSE;
      break;
    case wDispose:
      if (WINDOW_SPARE_FLAG_X (w))
	DisposHandle (WINDOW_DATA (w));
      break;
    case wGrow:
      TRAPBEGIN ();
      draw_grow_lines ((Rect *) (long) SYN68K_TO_US(parm));
      TRAPEND ();
      break;
    case wDrawGIcon:
      {
	/* clip the growicon to the window vis/clip regions */
	RgnHandle temp_rgn;
	RgnHandle save_clip;
	
	temp_rgn = NewRgn ();
	save_clip = NewRgn ();
	
	SectRgn (PORT_VIS_REGION (w), PORT_CLIP_REGION (w),
		 temp_rgn);
	
	OffsetRgn (temp_rgn,
		   - CW (PORT_BOUNDS (w).left),
		   - CW (PORT_BOUNDS (w).top));

	CopyRgn (PORT_CLIP_REGION (thePort), save_clip);
	SectRgn (PORT_CLIP_REGION (thePort), temp_rgn,
		 PORT_CLIP_REGION (thePort));
	
	TRAPBEGIN();
	if (varcode == documentProc)
	  {
	    if (WINDOW_HILITED_X (w))
	      draw_grow_icon ((GrafPtr) w);
	    else
	      erase_grow_icon ((GrafPtr) w);
	  }
	TRAPEND();

	CopyRgn (save_clip, PORT_CLIP_REGION (thePort));
	
	DisposeRgn (temp_rgn);
	DisposeRgn (save_clip);
	
	break;
      }
    }

  if (draw_p)
    draw_state_restore (&draw_state);
  
  return 0;
}

/* begin rounded window definition functions */

void
hilite_rounded_window (WindowPeek w,
		       INTEGER left,  INTEGER top,
		       INTEGER right, INTEGER bottom)
{
  RgnHandle rh1, rh2;

  rh1 = NewRgn ();
  rh2 = NewRgn ();
  CopyRgn (WINDOW_STRUCT_REGION (w), rh1);
  InsetRgn (rh1, 1, 1);
  SetRectRgn (rh2, left, top - 18, right, top - 1);
  SectRgn (rh1, rh2, rh1);

  RGBForeColor (title_bar_bk);
  PenPat (black);
  PaintRgn (rh1);
  
  DisposeRgn (rh2);
  DisposeRgn (rh1);
}

void
draw_rounded_doc (GrafPtr w)
{
  int left, top, right, bottom;
  int draw_go_away_p;
  Rect r;

  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);

  SetRect (&r, left - 1, top - 19, right + 1, top);
  FillRect (&r, white);
  RGBForeColor (frame);
  FrameRgn (WINDOW_STRUCT_REGION (w));
  PenSize (1, 1);
  PenMode (patCopy);
  MoveTo (left, top - 1);
  LineTo (right, top - 1);

  hilite_rounded_window ((WindowPeek) w, left, top, right, bottom);
  if (WINDOW_HILITED_X (w))
    {
      draw_go_away_p = WINDOW_GO_AWAY_FLAG (w);
      if (draw_go_away_p)
	draw_go_away (left, top);
      draw_title (w, draw_go_away_p, FALSE);
    }
  else
    draw_title (w, FALSE, FALSE);
}

LONGINT
hit_rounded_doc (GrafPtr w, LONGINT param)
{
  Point p;
  int left, top, right, bottom;
    
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);
  
  p.v = HiWord (param);
  p.h = LoWord (param);
  if (WINDOW_HILITED_X (w))
    {
      if (PtInRgn (p, WINDOW_CONT_REGION (w)))
	return wInContent;
      if (p.h >= left-1 && p.h < right+1 && p.v >= top-19 && p.v < top)
	{
	  if (WINDOW_GO_AWAY_FLAG (w)
	      && p.h > left+8 && p.h < left+20
	      && p.v > top-16 && p.v < top-4)
	    return wInGoAway;
	  else
	    return wInDrag;
	}
      else
	return wNoHit;
    }
  else
    {
      if (PtInRgn (p, WINDOW_CONT_REGION (w)))
	return wInContent;
      else if (p.h >= left-1 && p.h < right+1
	       && p.v >= top-19 && p.v < top)
	return wInDrag;
      else
	return wNoHit;
    }
}


void
calc_rounded_doc (GrafPtr w, INTEGER curve_code)
{
  int left, top, right, bottom;
  int curve;
  static const int curve_code_to_curve[] =
    {
      4, 6, 8, 10, 12, 20, 24,
    };
  RgnHandle rh;
  Rect r;
    
  left   = CW (PORT_RECT (w).left)   - CW (PORT_BOUNDS (w).left);
  top    = CW (PORT_RECT (w).top)    - CW (PORT_BOUNDS (w).top);
  right  = CW (PORT_RECT (w).right)  - CW (PORT_BOUNDS (w).left);
  bottom = CW (PORT_RECT (w).bottom) - CW (PORT_BOUNDS (w).top);
  
  SetRectRgn (WINDOW_CONT_REGION (w), left, top, right, bottom);

  if (curve_code >= 1 && curve_code <= 7)
    curve = curve_code_to_curve[curve_code - 1];
  else
    curve = 16;

  r.left   = CW(left   - 1);
  r.top    = CW(top    - 19);
  r.right  = CW(right  + 1);
  r.bottom = CW(bottom + 1);
  OpenRgn ();
  FrameRoundRect (&r, curve, curve);
  CloseRgn (WINDOW_STRUCT_REGION (w));
  CopyRgn (WINDOW_STRUCT_REGION (w), WINDOW_CONT_REGION (w));
  InsetRgn (WINDOW_CONT_REGION (w), 1, 1);
  rh = NewRgn ();
  SetRectRgn (rh, left, top, right, bottom);
  SectRgn (rh, WINDOW_CONT_REGION (w), WINDOW_CONT_REGION (w));
  DisposeRgn (rh);
}

P4 (PUBLIC pascal, LONGINT, wdef16,
    INTEGER, varcode, WindowPtr, wp,
    INTEGER, message, LONGINT, param)
{
  WindowPeek w = (WindowPeek) wp;
  draw_state_t draw_state;
  boolean_t draw_p;
  
  rounded_window_p = TRUE;

  /* mask out zoom bit; it will be ignored */
  varcode &= ~ZOOMBIT;
  
  draw_p = (message == wDraw);
  
  if (draw_p)
    {
      draw_state_save (&draw_state);
      
      window_colors = validate_colors_for_window ((GrafPtr) w);
    }

  switch (message)
    {
    case wDraw:
      if (!WINDOW_VISIBLE_X (w))
	break;
      TRAPBEGIN ();
      switch (param)
	{
	case 0:
	  draw_rounded_doc ((GrafPtr) w);
	  break;

	case wInGoAway:
	  toggle_go_away_box ((GrafPtr) w);
	  break;
	}
      TRAPEND ();
      break;

    case wHit:
      return hit_rounded_doc ((GrafPtr) w, param);
      break;
      
    case wCalcRgns:
      calc_rounded_doc ((GrafPtr) w, varcode);
      break;
      
    case wNew:
      WINDOW_SPARE_FLAG_X (w) = FALSE;
      break;
      
    case wDispose:
      break;
      
    case wGrow:
    case wDrawGIcon:
      break;
    }
  
  if (draw_p)
    draw_state_restore (&draw_state);
  
  return 0;
}
 
