/*	XGDraw.cpp
 *
 *		This is the stack object which provides proper serial access
 *	to drawing stuff
 *
 *	*** Note to X Users:
 *
 *		Views are emulated in the YAAF library by setting the clipping
 *	and the origin of the drawing context to the rectangle the XGView
 *	class resides in. (This is done normally in XGDraw::CreateContext.)
 *	The problem is that there is no way in X to set the origin of
 *	a graphic context (at least that I'm aware of).
 *
 *		That's why the 'fXOrigin' and 'fYOrigin' values; these give
 *	the offset in the graphics context where the view should be
 *	drawn. Normally as all drawing goes through the XGDraw drawing
 *	routines, this isn't a problem. But you should keep this fact in
 *	mind if you extend the XGDraw code with new drawing routines.
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XError.h>
#include <XView.h>
#include <XWindow.h>
#include <XDraw.h>
#include <XPrinter.h>
#include <stdio.h>

#if OPT_WINOS
#include <math.h>
#endif

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

#if OPT_WINOS == 1
HFONT		GDefaultFont;				// a default font for my stuff.
#endif

#if (OPT_XWIN == 1) || (OPT_WINOS == 1)
XRect		*XGDraw::gUpdate = NULL;	// update rectangle
#endif

#if OPT_XWIN == 1
Region		XGDraw::gRegion;
#endif

XGDraw *XGDraw::gDraw = NULL;

#if OPT_WINOS == 1
/* this fixes a bug on Windows, where if two threads tried to
 * have a draw object at the same time, the draw stack would
 * get screwed up.  This critical section serializes access to
 * draw objects 9although one thread can still have multiple
 * simultansous draw objects, since critical sections are
 * "re-entrant' on Windows).
 */
CRITICAL_SECTION *XGDraw::gCritSec = NULL;

extern "C" {
	extern HINSTANCE _GInstance;
}
#endif

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark Construction/Destruction
#endif

/*$	XGDraw::XGDraw
 *
 *		Construct a drawing object from the specified view. This is used
 *	to initiate drawing inside of a view.
 *
 *		The #view# parameter is a pointer to the current view; commonly
 *	for maintaining an XGView class object you write, you would pass
 *	the #this# pointer. The #lc# parameter indicates if the local coordinate
 *	system should be used. Normally you would pass the default (true)
 *	value--this parameter is used internally by YAAF.
 *
 *		Thus, to set up the drawing, normally you would write
\begin{verbatim}
		XGDraw draw(this);
\end{verbatim}
 */

XGDraw::XGDraw(XGView *view, bool lc)
{
	/*
	 *	Initialize this drawing state object
	 */

#if OPT_WINOS == 1
	if (!gCritSec) {
		gCritSec = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
		InitializeCriticalSection(gCritSec);
	}
	if (gCritSec)
		EnterCriticalSection(gCritSec);
#endif

#if OPT_MACOS == 1
	fGDCache = NULL;
#endif

	fLast = gDraw;						// and insert me into the linked list
	gDraw = this;
	fView = view;
	fPrinter = NULL;
	fFontPtr = NULL;

	if (fLast) fLast->SaveContext();	// Save old drawing context
	CreateContext(lc);
}

/*$	XGDraw::XGDraw
 *
 *		Construct a drawing object for the printer. This is used to
 *	initiate drawing into a printer page.
 */

XGDraw::XGDraw(XGPrinter *print)
{
#if OPT_WINOS == 1
	if (!gCritSec) {
		gCritSec = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
		InitializeCriticalSection(gCritSec);
	}
	if (gCritSec)
		EnterCriticalSection(gCritSec);
#endif

#if OPT_MACOS == 1
	fGDCache = NULL;
#endif

	fLast = gDraw;
	gDraw = this;
	fView = NULL;
	fPrinter = print;
	fFontPtr = NULL;
	
	if (fLast) fLast->SaveContext();	// save old drawing context
	CreateContext(true);
}

/*$	XGDraw::~XGDraw
 *
 *		Destroy this, and restore drawing to the previous draw object.
 *	If you created this drawing object on the stack, this will automatically
 *	be removed when the routine returns or an execption is thrown.
 */

XGDraw::~XGDraw()
{
	DestroyContext();					// Release the current drawing context
	gDraw = fLast;
	if (gDraw) gDraw->RestoreContext();	// Restore last drawing context

#if OPT_WINOS == 1
	if (gCritSec)
		LeaveCriticalSection(gCritSec);
#endif
}

/************************************************************************/
/*																		*/
/*	Resolution Support													*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Resolution support
#endif

/*$	XGDraw::GetResolution
 *
 *		Returns the point resolution of the screen. This is only needed
 *	if you need an accurate point size value, in order to support drawing
 *	rulers or the like in a drawing program.
 */

XPoint XGDraw::GetResolution()
{
	XPoint pt;
	
#if OPT_MACOS
	/*
	 *	On the Macintosh this is fixed
	 */
	
	pt.h = 72;
	pt.v = 72;
#endif

#if OPT_XWIN
	/*
	 *	Calculate based on the width/height of the display in pixels and it's
	 *	width/height in millimeters. The results should be close enough.
	 */
	
	pt.h = (DisplayWidth(_GDisplay,0) * 254L) / (10L * DisplayWidthMM(_GDisplay,0));
	pt.v = (DisplayHeight(_GDisplay,0) * 254L) / (10L * DisplayHeightMM(_GDisplay,0));
#endif

#if OPT_WINOS
	pt.h = ::GetDeviceCaps(fDC,LOGPIXELSX);
	pt.v = ::GetDeviceCaps(fDC,LOGPIXELSY);
#endif

	return pt;
}

/************************************************************************/
/*																		*/
/*	Line Drawing Support												*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Line Drawing Support
#endif

/*$	XGDraw::MoveTo
 *
 *		Move the pen cursor to the specified location. If this is an
 *	operating system which does not maintain a "pen cursor" (such as
 *	X Windows), YAAF maintains a pen cursor internally.
 */

void XGDraw::MoveTo(long x, long y)
{
#if OPT_MACOS == 1
	::MoveTo(x,y);
#endif

#if OPT_WINOS == 1
	::MoveToEx(fDC,x,y,NULL);
#endif

#if OPT_XWIN == 1
	fXPen = x;
	fYPen = y;
#endif
}

/*$	XGDraw::LineTo
 *
 *		Draw line from the pen cursor to the specified location, and
 *	updates the pen location. The pen location was previously set by
 *	the MoveTo() or previous LineTo() call.
 */

void XGDraw::LineTo(long x, long y)
{
#if OPT_MACOS == 1
	::LineTo(x,y);
#endif

#if OPT_WINOS == 1
	HPEN pen;
	HGDIOBJ old;
	
	pen = ::CreatePen(PS_SOLID,fPenWidth,fForeColor);
	old = ::SelectObject(GetDC(),(HGDIOBJ)pen);
	::LineTo(fDC,x,y);
	::SelectObject(GetDC(),old);
	::DeleteObject((HGDIOBJ)pen);
#endif

#if OPT_XWIN == 1
	::XDrawLine(GetDisplay(),
				GetDrawable(),
				GetGC(),
				fXPen+fXOrigin,
				fYPen+fYOrigin,
				x+fXOrigin,
				y+fYOrigin);
	fXPen = x;
	fYPen = y;
#endif
}

void XGDraw::Move( long dx, long dy )
{
	XPoint pt = GetPenPos();
	MoveTo( pt.h + dx, pt.v + dy );
}

void XGDraw::Line( long dx, long dy )
{
	XPoint pt = GetPenPos();
	LineTo( pt.h + dx, pt.v + dy );
}


/*$	XGDraw::GetPenPos
 *
 *		Get the current pen location.
 */

XPoint XGDraw::GetPenPos(void)
{
	XPoint pt;
	
#if OPT_XWIN
	pt.h = fXPen;
	pt.v = fYPen;
#endif

#if OPT_WINOS
	POINT p;
	::MoveToEx(GetDC(),0,0,&p);
	::MoveToEx(GetDC(),p.x,p.y,NULL);
	pt.h = p.x;
	pt.v = p.y;
#endif

#if OPT_MACOS
	Point p;
	::GetPen(&p);
	pt.h = p.h;
	pt.v = p.v;
#endif

	return pt;
}

/************************************************************************/
/*																		*/
/*	Ovals/Circles														*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Oval Drawing
#endif

/*$	XGDraw::FrameOval
 *
 *		Frame this oval. Draws an oval outline that fits within the
 *	boundaries of the provided rectangle. This uses the current
 *	foreground color to draw the frame.
 */

void XGDraw::FrameOval(XRect r)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::FrameOval(&mr);
#endif

#if OPT_WINOS == 1
	HPEN pen;
	HGDIOBJ old;
	HGDIOBJ oldb;
	
	pen = ::CreatePen(PS_SOLID,fPenWidth,fForeColor);
	old = ::SelectObject(GetDC(),(HGDIOBJ)pen);
	oldb = ::SelectObject(GetDC(),GetStockObject(NULL_BRUSH));

	Ellipse(GetDC(),r.left,r.top,r.right-r.left,r.bottom-r.top);

	::SelectObject(GetDC(),oldb);
	::SelectObject(GetDC(),old);
	::DeleteObject((HGDIOBJ)pen);
#endif

#if OPT_XWIN == 1
	::XDrawArc(GetDisplay(),
			   GetDrawable(),
			   GetGC(),
			   r.left+fXOrigin,r.top+fYOrigin,
			   r.right-r.left,r.bottom-r.top,
			   0,23040);						// 23040 == 360 * 64
#endif
}

/*$	XGDraw::PaintOval
 *
 *		Paint this oval. Draws an filled oval that fits within the
 *	boundaries of the provided rectangle. This uses the current
 *	foreground color to draw the frame.
 */

void XGDraw::PaintOval(XRect r)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::PaintOval(&mr);
#endif

#if OPT_WINOS == 1
	HPEN pen;
	HBRUSH brush;
	HGDIOBJ old;
	HGDIOBJ oldb;
	
	pen = ::CreatePen(PS_NULL,1,fBackColor);
	old = ::SelectObject(GetDC(),(HGDIOBJ)pen);
	brush = ::CreateSolidBrush(fForeColor);
	oldb = ::SelectObject(GetDC(),brush);

	Ellipse(GetDC(),r.left,r.top,r.right-r.left,r.bottom-r.top);

	::SelectObject(GetDC(),oldb);
	::SelectObject(GetDC(),old);
	::DeleteObject((HGDIOBJ)pen);
	::DeleteObject((HGDIOBJ)brush);
#endif

#if OPT_XWIN == 1
	::XFillArc(GetDisplay(),
			   GetDrawable(),
			   GetGC(),
			   r.left+fXOrigin,r.top+fYOrigin,
			   r.right-r.left,r.bottom-r.top,
			   0,23040);						// 23040 == 360 * 64
#endif
}

/*$	XGDraw::EraseOval
 *
 *		Erase this oval. Erases a filled oval that fits within the
 *	boundaries of the provided rectangle.This uses the current
 *	background color to erase the frame.
 */

void XGDraw::EraseOval(XRect r)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::EraseOval(&mr);
#endif

#if OPT_WINOS == 1
	HPEN pen;
	HBRUSH brush;
	HGDIOBJ old;
	HGDIOBJ oldb;
	
	pen = ::CreatePen(PS_NULL,1,fBackColor);
	old = ::SelectObject(GetDC(),(HGDIOBJ)pen);
	brush = ::CreateSolidBrush(fBackColor);
	oldb = ::SelectObject(GetDC(),brush);

	Ellipse(GetDC(),r.left,r.top,r.right-r.left,r.bottom-r.top);

	::SelectObject(GetDC(),oldb);
	::SelectObject(GetDC(),old);
	::DeleteObject((HGDIOBJ)pen);
	::DeleteObject((HGDIOBJ)brush);
#endif

#if OPT_XWIN == 1
	Color oldfore;
	
	/* Kludge: Paint background */
	oldfore = fForeground;
	SetForeColor(fBackground);
	PaintOval(r);
	SetForeColor(oldfore);
#endif
}

/************************************************************************/
/*																		*/
/*	Arcs																*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Arc Drawing
#endif

/*$	XGDraw::FrameArc
 *
 *		Draw an arc. This draws an arc segment from the start angle to the
 *	end angle in the foreground color. Angles are specified as they are on
 *	the Macintosh: clockwise in degrees from "upright" or the Y axis.
 */

void XGDraw::FrameArc(XRect r, long start, long length)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::FrameArc(&mr,start,length);
#endif

#if OPT_WINOS == 1
	short end;
	short x1,y1,x2,y2;
	
	end = start + length;
	start = 90 - start;
	if (start < 0) start += 360;
	end = 90 - end;
	if (end < 0) end += 360;
	if (length > 0) {
		short tmp;
		tmp = start;
		start = end;
		end = tmp;
	}
	
	/*
	 *	I hate this...
	 */
	
	x1 = ((r.right - r.left) * sin(start * 3.1415926 / 180.0) + (r.right + r.left))/2;
	y1 = ((r.bottom - r.top) * sin(start * 3.1415926 / 180.0) + (r.bottom + r.top))/2;
	x2 = ((r.right - r.left) * sin(end * 3.1415926 / 180.0) + (r.right + r.left))/2;
	y2 = ((r.bottom - r.top) * sin(end * 3.1415926 / 180.0) + (r.bottom + r.top))/2;

	/*
	 *	Draw it
	 */
	
	HPEN pen;
	HGDIOBJ old;
	
	pen = ::CreatePen(PS_SOLID,fPenWidth,fForeColor);
	old = ::SelectObject(GetDC(),(HGDIOBJ)pen);
	::Arc(GetDC(),r.left,r.top,r.right,r.bottom,x1,y1,x2,y2);
	::SelectObject(GetDC(),old);
	::DeleteObject((HGDIOBJ)pen);
#endif

#if OPT_XWIN == 1
	start = 90 - start;
	if (start < 0) start += 360;
	start *= 64;
	length = -64 * length;

	::XDrawArc(GetDisplay(),
			   GetDrawable(),
			   GetGC(),
			   r.left+fXOrigin,r.top+fYOrigin,
			   r.right-r.left,r.bottom-r.top,
			   start,length);
#endif
}

/*$	XGDraw::PaintArc
 *
 *		Fill an arc. This draws an arc segment from the start angle to the
 *	end angle in the foreground color. Angles are specified as they are on
 *	the Macintosh: clockwise in degrees from "upright" or the Y axis.
 */

void XGDraw::PaintArc(XRect r, long start, long length)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::PaintArc(&mr,start,length);
#endif

#if OPT_WINOS == 1
	short end;
	short x1,y1,x2,y2;
	
	end = start + length;
	start = 90 - start;
	if (start < 0) start += 360;
	end = 90 - end;
	if (end < 0) end += 360;
	if (length > 0) {
		short tmp;
		tmp = start;
		start = end;
		end = tmp;
	}
	
	/*
	 *	I hate this...
	 */
	
	x1 = ((r.right - r.left) * sin(start * 3.1415926 / 180.0) + (r.right + r.left))/2;
	y1 = ((r.bottom - r.top) * sin(start * 3.1415926 / 180.0) + (r.bottom + r.top))/2;
	x2 = ((r.right - r.left) * sin(end * 3.1415926 / 180.0) + (r.right + r.left))/2;
	y2 = ((r.bottom - r.top) * sin(end * 3.1415926 / 180.0) + (r.bottom + r.top))/2;

	/*
	 *	Draw it
	 */
	
	HBRUSH brush;
	HGDIOBJ old;
	
	brush = ::CreateSolidBrush(fForeColor);
	old = ::SelectObject(GetDC(),(HGDIOBJ)brush);
	::Arc(GetDC(),r.left,r.top,r.right,r.bottom,x1,y1,x2,y2);
	::SelectObject(GetDC(),old);
	::DeleteObject((HGDIOBJ)brush);
#endif

#if OPT_XWIN == 1
	start = 90 - start;
	if (start < 0) start += 360;
	start *= 64;
	length = -64 * length;

	::XFillArc(GetDisplay(),
			   GetDrawable(),
			   GetGC(),
			   r.left+fXOrigin,r.top+fYOrigin,
			   r.right-r.left,r.bottom-r.top,
			   start,length);
#endif
}

/*$	XGDraw::EraseArc
 *
 *		Erase an arc. This erases an arc segment from the start angle to the
 *	end angle in the background color. Angles are specified as they are on
 *	the Macintosh: clockwise in degrees from "upright" or the Y axis.
 */

void XGDraw::EraseArc(XRect r, long start, long length)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::EraseArc(&mr,start,length);
#endif

#if OPT_WINOS == 1
	short end;
	short x1,y1,x2,y2;
	
	end = start + length;
	start = 90 - start;
	if (start < 0) start += 360;
	end = 90 - end;
	if (end < 0) end += 360;
	if (length > 0) {
		short tmp;
		tmp = start;
		start = end;
		end = tmp;
	}
	
	/*
	 *	I hate this...
	 */
	
	x1 = ((r.right - r.left) * sin(start * 3.1415926 / 180.0) + (r.right + r.left))/2;
	y1 = ((r.bottom - r.top) * sin(start * 3.1415926 / 180.0) + (r.bottom + r.top))/2;
	x2 = ((r.right - r.left) * sin(end * 3.1415926 / 180.0) + (r.right + r.left))/2;
	y2 = ((r.bottom - r.top) * sin(end * 3.1415926 / 180.0) + (r.bottom + r.top))/2;

	/*
	 *	Draw it
	 */
	
	HBRUSH brush;
	HGDIOBJ old;
	
	brush = ::CreateSolidBrush(fBackColor);
	old = ::SelectObject(GetDC(),(HGDIOBJ)brush);
	::Arc(GetDC(),r.left,r.top,r.right,r.bottom,x1,y1,x2,y2);
	::SelectObject(GetDC(),old);
	::DeleteObject((HGDIOBJ)brush);
#endif

#if OPT_XWIN == 1
	Color oldfore;
	
	/* Kludge: Paint background */
	oldfore = fForeground;
	SetForeColor(fBackground);
	PaintArc(r,start,length);
	SetForeColor(oldfore);
#endif
}


/************************************************************************/
/*																		*/
/*	Rectangle Support													*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Rectangle Drawing
#endif

/*$	XGDraw::FrameRect
 *
 *		This draws a framed rectangle. This uses the foreground color
 *	to frame the rectangle. If you are drawing a button, you should look
 *	into using the Draw3DRect routine instead.
 */

void XGDraw::FrameRect(XRect r)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::FrameRect(&mr);
#endif

#if OPT_WINOS == 1
	HBRUSH brush;
	
	brush = ::CreateSolidBrush(fForeColor);
	::FrameRect(fDC,&r,brush);
	::DeleteObject(brush);
#endif

#if OPT_XWIN == 1
	::XDrawRectangle(GetDisplay(),
					 GetDrawable(),
					 GetGC(),
					 r.left + fXOrigin,
					 r.top + fYOrigin,
					 r.right - r.left - 1,
					 r.bottom - r.top - 1);
#endif
}

/*$	XGDraw::PaintRect
 *
 *		Paint the specified rectangle. This fills the rectangle using
 *	the foreground color. If you are drawing a button, you should look
 *	into using the Draw3DRect routine instead. If you want to specify
 *	a particular color other than the foreground color, you may also
 *	want to look into the FillRect routine.
 */

void XGDraw::PaintRect(XRect r)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::PaintRect(&mr);
#endif

#if OPT_WINOS == 1
	FillRect(r,fForeColor);
#endif

#if OPT_XWIN == 1
	::XFillRectangle(GetDisplay(),
					 GetDrawable(),
					 GetGC(),
					 r.left + fXOrigin,
					 r.top + fYOrigin,
					 r.right - r.left,
					 r.bottom - r.top);
#endif
}

/*$	XGDraw::EraseRect
 *
 *		Erase the specified rectangle. This erases the rectangle using
 *	the background color. If you are drawing a button, you should look
 *	into using the Draw3DRect routine instead.
 */

void XGDraw::EraseRect(XRect r)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::EraseRect(&mr);
#endif

#if OPT_WINOS == 1
	::ExtTextOut(fDC,0,0,ETO_OPAQUE,&r,NULL,0,NULL);
#endif

#if OPT_XWIN == 1
	Color oldfore;
	
	/* Kludge: Paint background */
	oldfore = fForeground;
	SetForeColor(fBackground);
	PaintRect(r);
	SetForeColor(oldfore);
#endif
}

/*$	XGDraw::InvertRect
 *
 *		Invert the specified rectangle. This inverts the pixels in the
 *	current rectangle in such a manner that calling this routine again
 *	will put the pixels back.
 */

void XGDraw::InvertRect(XRect r)
{
#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::InvertRect(&mr);
#endif

#if OPT_WINOS == 1
	::InvertRect(fDC,&r);
#endif

#if OPT_XWIN == 1
	::XSetFunction(GetDisplay(),GetGC(),GXinvert);
	::XFillRectangle(GetDisplay(),
					 GetDrawable(),
					 GetGC(),
					 r.left + fXOrigin,
					 r.top + fYOrigin,
					 r.right - r.left,
					 r.bottom - r.top);
	::XSetFunction(GetDisplay(),GetGC(),GXcopy);
#endif
}

/*$	XGDraw::InvertFrameRect
 *
 *		Invert a one pixel frame around this rectangle. This is used
 *	for dragging a selection rectangle and anywhere else where a
 *	temporary inverted rectangle is needed
 */

void XGDraw::InvertFrameRect(XRect r)
{
	XRect s;
	
	if ((r.right - r.left <= 2) || (r.bottom - r.top <= 2)) {
		InvertRect(r);
	} else {	
		s = r;
		s.bottom = s.top + 1;
		InvertRect(s);
		s = r;
		s.top = s.bottom - 1;
		InvertRect(s);
		r.top++;
		r.bottom--;
		s = r;
		s.right = s.left + 1;
		InvertRect(s);
		s = r;
		s.left = s.right - 1;
		InvertRect(s);
	}
}

/*$	XGDraw::DrawFocusRect
 *
 *		Draws a focus rectangle. Used for indicating who has the
 *	focus. Note that this is an XOR drawing on Windows, but does 
 *  _not_ XOR draw on X or the Mac.
 *
 *		TODO: redo this to use the appearance manager on the Mac.
 *	Also note that for many controls, the border is drawn *outside*
 *	of the view; figure out how to handle that.
 */

void XGDraw::DrawFocusRect(XRect r)
{
#if OPT_MACOS || OPT_XWIN
	Draw3DRect(r,KXGEGrayFrame);
#endif

#if OPT_WINOS == 1
	::DrawFocusRect(GetDC(),&r);
#endif
}

/*$	XGDraw::FillRect
 *
 *		This quickly fills the rectangle with the specified color.
 *	This fills the rectangle with the specified color (including
 *	a palette color) without changing the foreground or background
 *	colors.
 */

void XGDraw::FillRect(XRect r, Color c)
{
#if OPT_MACOS == 1
	RGBColor oldc;
	Rect mr = r.MacRect();
	
	::GetForeColor(&oldc);
	SetForeColor(c);
	::PaintRect(&mr);
	::RGBForeColor(&oldc);
#endif

#if OPT_WINOS == 1
	COLORREF oldc;
	
	oldc = ::GetBkColor(fDC);
	::SetBkColor(fDC,ColorFromPalette(c));
	::ExtTextOut(fDC,0,0,ETO_OPAQUE,&r,NULL,0,NULL);
	::SetBkColor(fDC,oldc);
#endif

#if OPT_XWIN == 1
	Color oldfore;
	
	oldfore = fForeground;
	SetForeColor(c);
	PaintRect(r);
	SetForeColor(oldfore);
#endif
}

/*$	XGDraw::FillRect
 *
 *		This quickly fills the rectangle defined by the four parameters
 *	with the specified color. This is identical to the FillRect()
 *	routine that uses a XRect, but this specifies the border explicitly.
 */

void XGDraw::FillRect(long left, long top, long right, long bottom, Color c)
{
	
#if OPT_MACOS == 1
	RGBColor oldc;
	Rect r;
	
	r.left = left;
	r.top = top;
	r.right = right;
	r.bottom = bottom;
	
	::GetForeColor(&oldc);
	SetForeColor(c);
	::PaintRect(&r);
	::RGBForeColor(&oldc);
#endif

#if OPT_WINOS == 1
	COLORREF oldc;
	RECT r;
	
	r.left = left;
	r.top = top;
	r.right = right;
	r.bottom = bottom;
	
	oldc = ::GetBkColor(fDC);
	::SetBkColor(fDC,ColorFromPalette(c));
	::ExtTextOut(fDC,0,0,ETO_OPAQUE,&r,NULL,0,NULL);
	::SetBkColor(fDC,oldc);
#endif

#if OPT_XWIN == 1
	FillRect(r,c);
#endif
}

/*$	XGDraw::Draw3DRect
 *
 *		Draw the 3D rectangle using standard button colors instead
 *	of the standard foreground/background colors. This provides the
 *	heart of the button drawing code and border drawing code for
 *	the different platforms, and uses the default colors for the
 *	target operating system.
 */

void XGDraw::Draw3DRect(XRect r, XGERectType type)
{
	Color a,b;
	
	switch (type) {
		case KXGEFrame:
			/* Different from FrameRect() because this guarentees */
			/* the border is one pixel wide black.				  */
			a = ColorFromPalette(KXGColorBlack);
			b = a;
			break;
			
		case KXGEInset:
			a = ColorFromPalette(KXGColorBtnShadow);
			b = ColorFromPalette(KXGColorBtnHilite);
			break;

		case KXGEOutset:
			a = ColorFromPalette(KXGColorBtnHilite);
			b = ColorFromPalette(KXGColorBtnShadow);
			break;
			
		case KXGEButton:
			Draw3DRect(r,KXGEFrame);
			::InsetRect(&r,1,1);
			Draw3DRect(r,KXGEOutset);
			return;
			
		case KXGEInButton:
			Draw3DRect(r,KXGEFrame);
			::InsetRect(&r,1,1);
			Draw3DRect(r,KXGEInset);
			return;
			
		case KXGEGrayFrame:
			a = ColorFromPalette(KXGColorBtnShadow);
			b = a;
			break;

		case KXGEBackFrame:
			a = ColorFromPalette(KXGColorBtnFace);
			b = a;
			break;
			
		case KXGEHiliteFrame:
			a = ColorFromPalette(KXGColorBtnHilite);
			b = a;
			break;

		case KXGEBackground:
			a = ColorFromPalette(KXGColorBtnFace);
			FillRect(r,a);
			return;
	}

	/*
	 *	If we fall through to here, it's time to draw the
	 *	rectangle
	 */

	FillRect(r.left,r.top,r.right,r.top+1,a);
	FillRect(r.left,r.top+1,r.left+1,r.bottom,a);
	FillRect(r.left+1,r.bottom-1,r.right,r.bottom,b);
	FillRect(r.right-1,r.top+1,r.right,r.bottom-1,b);
}


/************************************************************************/
/*																		*/
/*	Character Drawing													*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Character Drawing
#endif

/*$	XGDraw::SetFont
 *
 *		This sets the current font for my drawing context. The #attach#
 *	parameter is normally set to false; it's setting determines if the
 *	font is 'attached' when this routine is called.
 *
 *		When false, the #attach# parameter causes the XGDraw object
 *	to assume ownership of the font. This has the advantage of making
 *	it easy to write:
 *
\begin{verbatim}
		draw.SetFont(XGFont::LoadFont(KMyFont));
\end{verbatim}
 *
 *		If you don't wish this XGDraw object to assume ownership, for
 *	example, because you have a palette of fonts you are passing to the
 *	draw object as you draw, you may wish to set the #attach# parameter
 *	explicitly to true.
 */

void XGDraw::SetFont(XGFont *font, bool attach)
{
	XGFont *old;
	
	/*
	 *	No font? Punt
	 */
	
	if (font == NULL) return;
	
	/*
	 *	Set the new font
	 */

	old = fFontPtr;
	fFontPtr = font;
	if (attach) font->Attach();
	
	/*
	 *	Now set the actual font specification
	 */
	
#if OPT_MACOS == 1
	font->SetFont();
#endif

#if OPT_WINOS == 1
	::SelectObject(fDC,font->GetFont(fDC));
#endif

#if OPT_XWIN == 1
	Font fh = font->GetFont();
	if (fh) ::XSetFont(GetDisplay(),GetGC(),fh);
#endif
	
	/*
	 *	And release the old font
	 */
	
	if (old != NULL) {
		old->Detach();
	}
}

/*$	XGDraw::GetFontAscent
 *
 *		Get the ascent of the currently selected font. This returns the
 *	height in pixels from the baseline of the font to the top of the font.
 */

long XGDraw::GetFontAscent()
{
#if OPT_MACOS == 1
	FontInfo finfo;
	
	::GetFontInfo(&finfo);
	return finfo.ascent;
#endif

#if OPT_WINOS == 1
	TEXTMETRIC tm;
	
	::GetTextMetrics(GetDC(),&tm);
	return tm.tmAscent;
#endif

#if OPT_XWIN == 1
	if (fFontPtr) {
		XFontStruct *fs;
		
		fs = fFontPtr->GetFontStruct();
		if (fs) {
			return fs->ascent;
		}
	}
	return 9;
#endif
}

/*$	XGDraw::GetFontDescent
 *
 *		Get the descent of this font. This returns the height in pixels
 *	from the bottom of the font to the baseline of the font.
 */

long XGDraw::GetFontDescent()
{
#if OPT_MACOS == 1
	FontInfo finfo;
	
	::GetFontInfo(&finfo);
	return finfo.descent;
#endif

#if OPT_WINOS == 1
	TEXTMETRIC tm;
	
	::GetTextMetrics(GetDC(),&tm);
	return tm.tmDescent;
#endif

#if OPT_XWIN == 1
	if (fFontPtr) {
		XFontStruct *fs;
		
		fs = fFontPtr->GetFontStruct();
		if (fs) {
			return fs->descent;
		}
	}
	return 3;
#endif
}

/*$	XGDraw::GetFontHeight
 *
 *		Get the total height of this font. This returns the total height
 *	of the font in pixels, excluding the "leading" of the font.
 */

long XGDraw::GetFontHeight()
{
#if OPT_MACOS == 1
	FontInfo finfo;
	
	::GetFontInfo(&finfo);
	return finfo.ascent + finfo.descent + finfo.leading;
#endif

#if OPT_WINOS == 1
	TEXTMETRIC tm;
	
	::GetTextMetrics(GetDC(),&tm);
	return tm.tmHeight; // + tm.tmExternalLeading + tm.tmInternalLeading;
#endif

#if OPT_XWIN == 1
	if (fFontPtr) {
		XFontStruct *fs;
		
		fs = fFontPtr->GetFontStruct();
		if (fs) {
			return fs->ascent + fs->descent + 1;
		}
	}
	return 13;
#endif
}

/*$	XGDraw::GetLineHeight
 *
 *		Get the total height of this font, including the internal
 *	and external leading values. This gives me the correct value when
 *	printing on older printers on Microsoft Windows.
 */

long XGDraw::GetLineHeight()
{
#if OPT_MACOS == 1
	FontInfo finfo;
	
	::GetFontInfo(&finfo);
	return finfo.ascent + finfo.descent + finfo.leading;
#endif

#if OPT_WINOS == 1
	TEXTMETRIC tm;
	
	::GetTextMetrics(GetDC(),&tm);
	return tm.tmHeight + tm.tmExternalLeading;
#endif

#if OPT_XWIN == 1
	if (fFontPtr) {
		XFontStruct *fs;
		
		fs = fFontPtr->GetFontStruct();
		if (fs) {
			return fs->ascent + fs->descent + 1;
		}
	}
	return 13;
#endif
}

/*$	XGDraw::StringWidth
 *
 *		Get the width of this string in pixels. If the #len# parameter
 *	is left to it's default of '-1', this will draw the string up to
 *	the C NULL terminator.
 */

long XGDraw::StringWidth(const char *string, long len)
{
	if (len == -1) {
		for (len = 0; string[len]; len++) ;
	}
	if (len == 0) return 0;

#if OPT_MACOS == 1
	return ::TextWidth((Ptr)string,0,len);
#endif

#if OPT_WINOS == 1
	SIZE size;
	
	::GetTextExtentPoint32(GetDC(),string,len,&size);
	return size.cx;
#endif

#if OPT_XWIN == 1
	if (fFontPtr) {
		XFontStruct *fs;
		
		fs = fFontPtr->GetFontStruct();
		if (fs) {
			return XTextWidth(fs,string,len);
		}
	}
	return 10;						// arbitrary width on failure
#endif
}

/*$	XGDraw::DrawString
 *
 *		Draws a string.  If the #len# parameter
 *	is left to it's default of '-1', this will draw the string up to
 *	the C NULL terminator.
 */

void XGDraw::DrawString(const char *string, long len)
{
	if (len == -1) {
		for (len = 0; string[len]; len++) ;
	}
	if (len == 0) return;

#if OPT_MACOS == 1
	::DrawText((Ptr)string,0,len);
#endif

#if OPT_WINOS == 1
	::TextOut(fDC,0,0,(LPCTSTR)string,len);
#endif

#if OPT_XWIN == 1
	::XDrawString(GetDisplay(),
				  GetDrawable(),
				  GetGC(),
				  fXPen + fXOrigin,
				  fYPen + fYOrigin,
				  string,
				  len);
	if (fFontPtr) {
		XFontStruct *fs;
		
		fs = fFontPtr->GetFontStruct();
		if (fs) {
			fXPen += XTextWidth(fs,string,len);
		}
	}
#endif
}

/*$	XGDraw::StringWidthA
 *
 *		This is the same as StringWidth, but also handles Windows
 *	style amperstand escapes.
 */

long XGDraw::StringWidthA(const char *string, long len)
{
	char buffer[256];
	int a;
	char *b;
	
	if (len == -1) {
		for (len = 0; string[len]; len++) ;
	}
	if (len == 0) return 0;

	b = buffer;
	a = 0;
	while (a < len) {
		if (string[a] == '&') {
			if (string[a+1] != 0) a++;
		}
		*b++ = string[a++];
	}
	buffer[a] = 0;
	return StringWidth(buffer);
}

/*$	XGDraw::DrawStringA
 *
 *		Draw with underscore. On the Macintosh this does not
 *	draw an underscore; this is not part of the Macintosh UI Guidelines.
 *	On Windows and X, this *is* a part of the guideline (as well as
 *	menu equivalents on windows and dialog boxes), so they are drawn.
 */

void XGDraw::DrawStringA(const char *string, long len)
{
	short x;
	short start;
	XPoint pt;
	short w;
	
	if (len == -1) {
		for (len = 0; string[len]; len++) ;
	}
	if (len == 0) return;

	start = 0;
	for (x = 0; x < len; x++) {
		if ((string[x] == '&') && (string[x+1] != 0)) {
			/*
			 *	Draw the string up until &
			 */
			 
			if (x > start) {
				DrawString(string+start,x-start);
			}
			x++;
			start = x;
			
			/*
			 *	Now draw the amperstand escape
			 */
			
			if (fAUnderscore && (string[x] != '&')) {
				/*
				 *	Underscore using the line drawing code.
				 */
				
				w = StringWidth(string+x,1);
				pt = GetPenPos();
				
#if OPT_XWIN || OPT_MACOS
				MoveTo(pt.h+w,pt.v+1);			// underscore
				LineTo(pt.h,pt.v+1);
#endif

#if OPT_WINOS
				/* There are subtle differences between these two platforms */
				/* on how lines are drawn. Usually this doesn't matter. Here */
				/* it does. */
				MoveTo(pt.h+w-2,pt.v+1);			// underscore
				LineTo(pt.h-1,pt.v+1);
#endif
				MoveTo(pt.h,pt.v);
			}
			
			/*
			 *	And continue parsing the string
			 */
			
		}
	}
	
	/*
	 *	Draw the rest of the string
	 */
	
	if (start < x) {
		DrawString(string+start,x-start);
	}
}

/*$	XGDraw::DrawWrap
 *
 *		Draw a line of text word wrapped. This wraps the string along
 *	word boundaries.
 */

void XGDraw::DrawWrap(XRect r, const char *line, long len)
{
	short h,ypos;
	short i,x,y;
	XPoint oldpos;
	
	oldpos = GetPenPos();
	
	if (len == -1) {
		for (len = 0; line[len]; len++) ;
	}
	if (len == 0) return;
	
	h = GetFontHeight();
	ypos = r.top + GetFontAscent();
	i = 0;
	while (i < len) {
		/*
		 *	Scan forward to see what I can draw
		 */
		
		y = -1;
		for (x = i; x < len; x++) {
			if (line[x] == '\n') break;
			if (StringWidth(line+i,x-i) > (r.right - r.left)) break;
			if ((line[x] == '\t') || (line[x] == ' ')) {
				y = x;
			}
		}
		if (x >= len) y = x;
		else if (y == -1) y = x-1;
		if (y <= i) y = i+1;
		MoveTo(r.left,ypos);
		DrawString(line+i,y-i);
		i = y;
		ypos += h;
		
		if (i < len) {
			if (line[i] == '\n') i++;
			while (i < len) {
				if ((line[i] == ' ') && (line[i] == '\t')) i++;
				else break;
			}
		}
	}
	
	MoveTo(oldpos.h,oldpos.v);
}

/*$	XGDraw::DrawWrapA
 *
 *		Draw a line of text word wrapped. This wraps the string along
 *	word boundaries.
 */

void XGDraw::DrawWrapA(XRect r, const char *line, long len)
{
	short h,ypos;
	short i,x,y;
	XPoint oldpos;
	
	oldpos = GetPenPos();
	
	if (len == -1) {
		for (len = 0; line[len]; len++) ;
	}
	if (len == 0) return;
	
	h = GetFontHeight();
	ypos = r.top + GetFontAscent();
	i = 0;
	while (i < len) {
		/*
		 *	Scan forward to see what I can draw
		 */
		
		y = -1;
		for (x = i; x < len; x++) {
			if (line[x] == '\n') break;
			if (StringWidthA(line+i,x-i) > (r.right - r.left)) break;
			if ((line[x] == '\t') || (line[x] == ' ')) {
				y = x;
			}
		}
		if (x >= len) y = x;
		else if (y == -1) y = x-1;
		if (y <= i) y = i+1;
		MoveTo(r.left,ypos);
		DrawStringA(line+i,y-i);
		i = y;
		ypos += h;
		
		if (i < len) {
			if (line[i] == '\n') i++;
			while (i < len) {
				if ((line[i] == ' ') || (line[i] == '\t')) i++;
				else break;
			}
		}
	}

	MoveTo(oldpos.h,oldpos.v);
}

/*$	XGDraw::TabDrawString
 *
 *		Draw a line of text with tabs.
 *
 *		This provides a mechanism for drawing a string that includes
 *	tab stops. The parameters are:
 *
 *		#line# gives a pointer to the beginning of the line of text
 *	to draw. This should point to the start of the logical line of
 *	text, and not the start of the text to draw.
 *
 *		#start# gives the offset into #line# where the first character
 *	should actually be drawn. This routine uses these two parameters to
 *	determine the actual pixel offset for tab stop calculations.
 *
 *		#len# is the number of pixels to draw. This must not be -1.
 *
 *		#tabs# is the width of a tab stop. If positive, this gives the
 *	tab stop width as a multiple of the width of the 'm' character. If 
 *	negative, the tab width is in pixels (and negated first). And if zero,
 *	this goes to the default of 8 'm' tab widths.
 *
 *		#password# indicates if this should replace the characters with
 *	a password character '*', and is used when dealing with password
 *	fields.
 */

void XGDraw::TabDrawString(const char *line, unsigned long start, 
						   unsigned long len, unsigned long tabs, bool password)
{
	unsigned long x;
	unsigned long pixel;
	unsigned long pixtab;
	unsigned long i = start;
	unsigned long newpix;
	unsigned long index;
	XPoint pos;

	/*
	 *	Quick abort
	 */
	
	if (len < 0) {
		Assert(len >= 0);
	}
	if (len == 0) return;
	if (start >= len) return;
	
	/*
	 *	Get the length of the text up to the start.
	 */

	if (tabs == 0) tabs = 8;
	if (tabs < 0) pixtab = -tabs;
	else pixtab = StringWidth("m") * tabs;
	
	pixel = TabStringWidth(line,start,tabs,password);
	
	/*
	 *	Scan the text and draw the non-tab stuff. This only draws
	 *	from the 'start' position.
	 */
	
	while (i < len) {
		/*
		 *	Find tab
		 */
		
		for (x = i; x < len; x++) {
			if (line[x] == '\t') break;
		}
		
		/*
		 *	Draw non-tab stuff
		 */
		
		if (x > i) {
			if (password) {
				for (index = 0; index < x-i; index++) {
					DrawString("*");
				}
				pixel += StringWidth("*") * (x - i);
			} else {
				DrawString(line+i,x-i);
				pixel += StringWidth(line+i,x-i);
			}
		}
		
		if (x >= len) break;
		i = x;
		
		/*
		 *	Now handle tab
		 */
		
		newpix = pixel;
		newpix += pixtab - newpix % pixtab;
		pos = GetPenPos();
		pos.h += (newpix - pixel);
		pixel = newpix;
		
		MoveTo(pos.h,pos.v);
		i++;
	}
}

/*$	XGDraw::TabStringWidth
 *
 *		Calculates the width of this tab-stopped field. This calculates
 *	the total width of a string field, including the width added by
 *	using tab stops.
 *
 *		The parameters are:
 *
 *		#line# is the pointer ot the start of the line of text
 *
 *		#len# is the total length of the line
 *
 *		#tabs# is the width of a tab stop, as used in TabDrawString
 *
 *		#password# is true if this should replace all the drawing
 *	characters with a password character '*'
 *
 *		#startpos# is the start position to use to calculate the
 *	tab stops, and is normally zero.
 */

long XGDraw::TabStringWidth(const char *line, unsigned long len, 
							 unsigned long tabs, bool password,
							 unsigned long startpos)
{
	unsigned long x;
	unsigned long pixel = startpos;
	unsigned long pixtab;
	unsigned long i = 0;
	
	/*
	 *	Quick abort
	 */
	
	if (len < 0) {
		Assert(len >= 0);
	}
	if (len == 0) return startpos;
	
	/*
	 *	Scan the text and draw the non-tab stuff
	 */
	
	if (tabs == 0) tabs = 8;
	if (tabs < 0) pixtab = -tabs;
	else pixtab = StringWidth("m") * tabs;
	
	while (i < len) {
		/*
		 *	Find tab
		 */
		
		for (x = i; x < len; x++) {
			if (line[x] == '\t') break;
		}
		
		/*
		 *	Draw non-tab stuff
		 */
		
		if (x > i) {
			if (password) {
				pixel += StringWidth("*") * (x - i);
			} else {
				pixel += StringWidth(line+i,x-i);
			}
		}
		
		if (x >= len) break;
		i = x;
		
		/*
		 *	Now handle tab
		 */
		
		pixel += pixtab - pixel % pixtab;
		i++;
	}
	
	return (long)pixel;
}

/*$	XGDraw::DrawNum
 *
 *		Note to self: rewrite routine.
 *
 *		This draws a number, right justifying it if the just flag is
 *	set. This probably doesn't belong here...
 */

void XGDraw::DrawNum (const long value, short just)
{
	char s[24];
	XPoint pt;
	short w;

	sprintf(s, "%d", value);
	if (just == 1) {
		w = StringWidth(s);
		pt = GetPenPos();
		MoveTo(pt.h - w,pt.v);
	}
	DrawString(s);
}


/************************************************************************/
/*																		*/
/*	Image Support														*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Image Support
#endif

/*$	XGDraw::DrawIcon
 *
 *		Draw an icon. This loads and draws the specified resource.
 *
 *		On the Macintosh, this loads and draws a 'cicn' resource.
 *	If a 'cicn' is not found, this tries the 'icon' resource.
 *
 *	On Windows, this loads and draws an ICON resource. And on X
 *	Windows, this loads and draws an 'icon' resource, which is stored
 *	as a Windows bitmap.
 */

void XGDraw::DrawIcon(short resID, long x, long y, bool enable)
{
#if OPT_MACOS == 1
	Rect r;
	CIconHandle cicn;
	Handle icon;
	short refNum;
	
	/*
	 *	System resource? If so, try to load from the system file first.
	 */
	
	if (resID < 128) {
		/*
		 *	Try to load the CIcon from the system file
		 */
		
		refNum = ::CurResFile();
		::UseResFile(0);
		cicn = ::GetCIcon(resID);
		::UseResFile(refNum);
		
		/*
		 *	See if it's in my own resource fork. The predefined resources
		 *	are offset by 120...
		 */
		
		if (cicn == NULL) {
			cicn = ::GetCIcon(resID);
		}
	} else {
		cicn = ::GetCIcon(resID);
	}
	
	/*
	 *	Did I load a cicn? If so, draw it. If not, get the icon
	 */
	
	if (cicn) {
		r = (**cicn).iconPMap.bounds;
		::OffsetRect(&r,x-r.left,y-r.top);
		if (enable) {
			::PlotCIcon(&r,cicn);
		} else {
			::PlotCIconHandle(&r,atNone,ttDisabled,cicn);
		}
		::DisposeCIcon(cicn);
	} else {
		icon = ::GetIcon(resID);
		if (icon) {
			r.left = x;
			r.top = y;
			r.right = x + 32;
			r.bottom = y + 32;
			PlotIcon(&r,icon);
			ReleaseResource(icon);
		}
	}
#endif

#if OPT_WINOS == 1
	HICON icon;
	bool destroy = false;
	
	/*
	 *	Map Macintosh resource IDs to Microsoft Windows resource IDs
	 */
	
	switch (resID) {
		case 0:
			icon = ::LoadIcon(NULL,IDI_ERROR);
			break;
		case 1:
			icon = ::LoadIcon(NULL,IDI_INFORMATION);
			break;
		case 2:
			icon = ::LoadIcon(NULL,IDI_WARNING);
			break;
		default:
			icon = (HICON)::LoadImage(_GInstance,
									  MAKEINTRESOURCE(resID),
									  IMAGE_ICON,
									  0,
									  0,
									  0);
			destroy = true;
			break;
	}
	
	if (icon) {
		if (enable) {
			::DrawIconEx(GetDC(),x,y,icon,0,0,0,NULL,DI_IMAGE | DI_MASK);
		} else {
			::DrawState(GetDC(),
						NULL,
						NULL,
						(LPARAM)icon,
						NULL,
						x,
						y,
						0,
						0,
						DST_ICON |	DSS_DISABLED);
		}
		if (destroy) ::DestroyIcon(icon);
	}
#endif

#if OPT_XWIN
	::DrawXIcon(GetDisplay(),
				GetDrawable(),
				GetGC(),
				resID,
				x+fXOrigin,
				y+fYOrigin,
				enable);
#endif
}

/*$	XGDraw::DrawPicture
 *
 *		Draw the picture. This is the same as above, except that
 *	this usually deals with pictures, stored as a separate
 *	data record.
 */

void XGDraw::DrawPicture(short resID, long x, long y)
{
#if OPT_MACOS == 1
	PicHandle p;
	Rect r;
	
	p = ::GetPicture(resID);
	if (!p) return;
	r = (**p).picFrame;
	::OffsetRect(&r,x-r.left,y-r.top);
	::DrawPicture(p,&r);
	::ReleaseResource((Handle)p);
#endif

#if OPT_WINOS == 1
	::DrawResBitmap(GetDC(),resID,x,y);
#endif

#if OPT_XWIN == 1
	::DrawXBitmap(GetDisplay(),GetDrawable(),GetGC(),resID,x+fXOrigin,y+fYOrigin);
#endif
}


#if OPT_MACOS == 1
void XGDraw::DrawBitmap( const XGBitmap *bmap, int x, int y, int width, int height )
{
	if (!bmap || !bmap->fGWorld) return;
	
	PixMapHandle pmap = GetGWorldPixMap( bmap->fGWorld );
	
	if (!pmap || !*pmap) return;
	
	Rect inBounds = pmap[0]->bounds;
	
	int inWidth = inBounds.right - inBounds.left;
	int inHeight = inBounds.bottom - inBounds.top;
	if (width <= 0) width = inWidth;
	if (height <= 0) height = inHeight;
	
	Rect outBounds = { y, x, y + height, x + width };
	
	LockPixels( pmap );
	GrafPtr port;
	GetPort( &port );
	CopyBits( &((GrafPtr)(bmap->fGWorld))->portBits, &port->portBits, &inBounds, &outBounds, srcCopy, nil );
	UnlockPixels( pmap );
}
#endif
#if OPT_WINOS == 1
void XGDraw::DrawBitmap( const XGBitmap* bmap, int x, int y, int width, int height)
{
	if(!bmap || !bmap->bitmap_buff) return;
	
	// draw onto device context.
	if(!fDC)
		return;
		
	if(width == -1)
		width = bmap->bitmap_info.bmiHeader.biWidth;
	if(height == -1)
		height = bmap->bitmap_info.bmiHeader.biHeight;
		
	StretchDIBits(fDC, x, y+height-1, width, -height, 0, 0, bmap->bitmap_info.bmiHeader.biWidth, bmap->bitmap_info.bmiHeader.biHeight, 
		bmap->bitmap_buff, &bmap->bitmap_info, DIB_RGB_COLORS, SRCCOPY);
}
#endif
/************************************************************************/
/*																		*/
/*	Color Support														*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Color Support
#endif

#if OPT_MACOS == 1

/*	ToWord
 *
 *		Take byte to word
 */

static unsigned short ToWord(unsigned char c)
{
	return (((unsigned short)c) << 8) | c;
}

#endif

/*$	XGDraw::SetForeColor
 *
 *		Set the foreground color. If this is a palette color, the color
 *	is converted to the proper RGB value first.
 */

void XGDraw::SetForeColor(Color c)
{
#if OPT_MACOS == 1
	RGBColor color;
	
	c = ColorFromPalette(c);
	color.red = ToWord(GetRValue(c));
	color.green = ToWord(GetGValue(c));
	color.blue = ToWord(GetBValue(c));
	
	::RGBForeColor(&color);
#endif

#if OPT_WINOS == 1
	c = ColorFromPalette(c);
	fForeColor = c;
	::SetTextColor(fDC,c);
#endif

#if OPT_XWIN == 1
	c = ColorFromPalette(c);
	fForeground = c;
	XSetForeground(GetDisplay(),GetGC(),AllocColor(c));
#endif
}

/*$	XGDraw::SetBackColor
 *
 *		Set background color. If this is a palette color, the color
 *	is converted to the proper RGB value first.
 */

void XGDraw::SetBackColor(Color c)
{

#if OPT_MACOS == 1
	RGBColor color;
	
	c = ColorFromPalette(c);
	color.red = ToWord(GetRValue(c));
	color.green = ToWord(GetGValue(c));
	color.blue = ToWord(GetBValue(c));
	
	::RGBBackColor(&color);
#endif

#if OPT_WINOS == 1
	c = ColorFromPalette(c);
	fBackColor = c;
	::SetBkColor(fDC,c);
#endif

#if OPT_XWIN == 1
	c = ColorFromPalette(c);
	fBackground = c;
	XSetBackground(GetDisplay(),GetGC(),AllocColor(c));
#endif
}

/*$	XGDraw::SetPenWidth
 *
 *		Set the width of the pen to use for drawing lines, rectangles,
 *	ovals and arcs.
 */

void XGDraw::SetPenWidth(long width)
{
	fPenWidth = width;
	
#if OPT_MACOS == 1
	::PenSize(width,width);
#endif

#if OPT_XWIN == 1
#endif
}


/************************************************************************/
/*																		*/
/*	Clipping															*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Clipping
#endif

/*$	XGDraw::ClipRect
 *
 *		Clip inside this rectangle. Allow the entire area if NULL.
 *	This routine currently does not work correctly if the XGDraw
 *	object was created with the 'lc' flag clear.
 */

void XGDraw::ClipRect(XRect *s)
{
	XRect r;
	
	/*
	 *	Find the local coordinate clipping region by intersecting
	 *	with 's
	 */
	
	if (!fView) return;
	r = fView->fGlobalRect;
	::OffsetRect(&r,-fView->fGlobalPos.h,-fView->fGlobalPos.v);
	if (s) ::SectRect(s,&r,&r);

#if OPT_MACOS == 1
	Rect mr = r.MacRect();
	::ClipRect(&mr);
#endif

#if OPT_WINOS == 1
	HRGN rgn;
	
	/*
	 *	Clip against my update bounding rectangle, if present
	 */

	::OffsetRect(&r,fView->fGlobalPos.h,fView->fGlobalPos.v);
	if (gUpdate) ::SectRect(gUpdate,&r,&r);
	rgn = ::CreateRectRgnIndirect(&r);
	::SelectClipRgn(fDC,rgn);
	::DeleteObject(rgn);
#endif

#if OPT_XWIN == 1
	Region a;
	XRectangle rr;
	
	/*
	 *	Clip against my update bounding rectangle and update
	 *	region, if present. This effectively converts the
	 *	rectangle clip area into a clipping region in my GC
	 */
	
	::OffsetRect(&r,fView->fGlobalPos.h,fView->fGlobalPos.v);
	if (gUpdate) ::SectRect(gUpdate,&r,&r);

	rr.x = r.left;
	rr.y = r.top;
	rr.width = r.right - r.left;
	rr.height = r.bottom - r.top;

	a = ::XCreateRegion();
	::XUnionRectWithRegion(&rr,a,a);
	if (gRegion) {
		::XIntersectRegion(a,gRegion,a);
	}
	
	::XSetRegion(_GDisplay,fGC,a);
	::XDestroyRegion(a);
#endif	
}

/************************************************************************/
/*																		*/
/*	Drawing Context	(Common)											*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Universal Internal Support
#endif

/*$	XGDraw::ColorFromPalette
 *
 *		If the palette color field is set, this gets the actual color.
 *
 *	TODO: On the Macintosh, this should determine if the Appearance manager
 *	is present. If it is, this should use the Appearance manager colors.
 */
 
Color XGDraw::ColorFromPalette(Color c)
{
#if OPT_MACOS
	RGBColor color;
#endif			

	/* If it's not a special color, just return the color */
	if (GetColorFlag(c) != 0x10) return c;
	
	/* Return the right color */
#if OPT_MACOS || OPT_XWIN
	if (PixelDepth() < 4) {
		/*
		 *	Less than 4 bits per pixel? Then go to black and white mode
		 */
		
		switch ((short)c) {
			default:
			case 0:	// black
			case 2:	// hilite
			case 5:	// shadow
			case 6:	// hilite
			case 8:	// Menu hilite
			case 11:// tool tip text
				return RGB(0,0,0);					// black
			case 1:	// white
			case 3:	// btn hilite
			case 4:	// btn face
			case 7:	// hilite text
			case 9: // Menu hilite text
			case 10:// Tool tip box
				return RGB(0xFF,0xFF,0xFF);			// white
		}
	} else {
		switch ((short)c) {
			default:
			case 0:	// black
			case 2:	// hilite
			case 11:// tool tip
				return RGB(0,0,0);					// black
			case 1:	// white
			case 3:	// hilite
				return RGB(0xFF,0xFF,0xFF);			// white
			case 4:	// face
				return RGB(0xDD,0xDD,0xDD);			// lgray
			case 5:	// shadow
				return RGB(0x80,0x80,0x80);			// dgray
#if OPT_XWIN
			case 6:	// hilite
				return RGB(0x80,0xFF,0x80);			// hilite (green)
			case 7:	// hilite text
				return RGB(0,0,0);					// hilite text (black)
#endif

#if OPT_MACOS
			case 6:	// hilite
				LMGetHiliteRGB(&color);
				return RGB((unsigned char)(color.red >> 8),
						   (unsigned char)(color.green >> 8), 
						   (unsigned char)(color.blue >> 8));
			case 7:	// hilite text
				LMGetHiliteRGB(&color);
				if ((color.red < 0x3000) && 
						(color.green < 0x3000) && 
						(color.blue < 0x3000)) {
					// dark color: return white
					return RGB(0xFF,0xFF,0xFF);
				} else {
					// light color: return black
					return RGB(0,0,0);
				}
#endif
			case 8:
				return RGB(0,0,0x80);				// menu hilite (dark blue)
			case 9:
				return RGB(0xDD,0xDD,0xDD);			// dark gray
			case 10:
				return RGB(0xFF,0xFF,0xCC);			// pale yellow
		}
	}
	return 0;
#endif // OPT_MACOS || OPT_XWIN

#if OPT_WINOS
	switch ((short)c) {
		default:
		case 0:										// black
			return RGB(0,0,0);
		case 1:
			return RGB(0xFF,0xFF,0xFF);
		case 2:
			return GetSysColor(COLOR_BTNTEXT);
		case 3:
			return GetSysColor(COLOR_BTNHILIGHT);	// white
		case 4:
			return GetSysColor(COLOR_BTNFACE);		// lgray
		case 5:
			return GetSysColor(COLOR_BTNSHADOW);	// dgray
		case 6:
		case 8:
			return GetSysColor(COLOR_HIGHLIGHT);	// hilite
		case 7:
		case 9:
			return GetSysColor(COLOR_HIGHLIGHTTEXT);
		case 10:
			return GetSysColor(COLOR_INFOBK);
		case 11:
			return GetSysColor(COLOR_INFOTEXT);
	}
	return 0;
#endif
}

/*$	XGDraw::IsColor
 *
 *		Is my current drawing context in color? This figures out if this
 *	this thing is in color. (On the Macintosh, this figures out the
 *	depth information for this thing on the monitor this thing intersects
 *	the most.)
 */

bool XGDraw::IsColor()
{
#if OPT_MACOS
	GDHandle gdh;
	
	gdh = GetGDHandle();
	if (gdh == NULL) return false;			/* No color quickdraw??? */
	if ((1 << gdDevType) & (**gdh).gdFlags) return true;
	return false;
#endif

#if OPT_WINOS || OPT_XWIN
	/*
	 *	No standard way to determine B&W only
	 */
	 
	return PixelDepth() > 1;
#endif
}

/*$	XGDraw::PixelDepth
 *
 *		Get the current pixel depth of this device. (On the Macintosh, this 
 *	figures out the depth information for this thing on the monitor this 
 *	thing intersects the most.)
 */

long XGDraw::PixelDepth()
{
#if OPT_MACOS
	GDHandle gdh;
	
	gdh = GetGDHandle();
	if (gdh == NULL) return 1;				/* No color quickdraw??? */
	return (**((**gdh).gdPMap)).pixelSize;
#endif

#if OPT_WINOS
	return GetDeviceCaps(fDC,BITSPIXEL);
#endif

#if OPT_XWIN
	XWindowAttributes wa;
	
	XGetWindowAttributes(_GDisplay,GetDrawable(),&wa);
	return wa.depth;
#endif
}

/************************************************************************/
/*																		*/
/*	Drawing Context	(Macintosh)											*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Internal Support (Macintosh)
#endif

#if OPT_MACOS == 1

/*	XGDraw::SaveContext
 *
 *		Save the drawing context
 */

void XGDraw::SaveContext()
{
	// Save drawing port
	::GetPort(&fPort);
	
	// Save origin
	fOffset.h = fPort->portRect.left;
	fOffset.v = fPort->portRect.top;
	
	// Save clipping rectangle
	fClip = ::NewRgn();
	::GetClip(fClip);
	
	// Save pen state and color information
	::GetPenState(&fPenState);
	fFont = fPort->txFont;
	fFace = fPort->txFace;
	fSize = fPort->txSize;
	fMode = fPort->txMode;
	fSpExtra = fPort->spExtra;	
	::GetForeColor(&fForeColor);
	::GetBackColor(&fBackColor);
}

/*	XGDraw::RestoreContext
 *
 *		Restore the drawing context
 */

void XGDraw::RestoreContext()
{
	// Restore drawing port
	::SetPort(fPort);
	
	// Restore origin
	::SetOrigin(fOffset.h,fOffset.v);
	
	// Restore clipping
	::SetClip(fClip);
	::DisposeRgn(fClip);
	
	// Restore pen state and color information
	::SetPenState(&fPenState);
	::TextFont(fFont);
	::TextFace(fFace);
	::TextSize(fSize);
	::TextMode(fMode);
	::SpaceExtra(fSpExtra);
	::RGBForeColor(&fForeColor);
	::RGBBackColor(&fBackColor);
}

/*	XGDraw::CreateContext
 *
 *		Create drawing context into the geometry specified. The 'local
 *	coordinate' flag indicates that we should clip and offset the viewport
 *	so that (0,0) is the upper left of the view. This is the default.
 *
 *		The only time when we don't want to do this is when we are
 *	dealing with standard Maintosh controls.
 */

void XGDraw::CreateContext(bool lc)
{
	GrafPtr port;
	RGBColor color;
	PenState state;
	Rect r;
	
	if (fView) {
		// Set the drawing port
		port = fView->fRootWindow;
		::SetPort(port);

		if (lc) {
			// Set the coordinate system so it's view local
			::SetOrigin(-fView->fGlobalPos.h,-fView->fGlobalPos.v);
			ClipRect();
		} else {
			// Set the coordinate system so it's global to the window.
			// useful for Macintosh controls
			::SetOrigin(0,0);
			r = fView->fGlobalRect.MacRect();
			::ClipRect(&r);
		}
	} else if (fPrinter) {
		port = (GrafPtr)(fPrinter->fPort);
		::SetPort(port);
	}
	
	// Now, set the font, text, color and other information
	color.red = 0;
	color.green = 0;
	color.blue = 0;
	::RGBForeColor(&color);
	if (lc) {
		/* Set this only for local views */
		color.red = 0xFFFF;
		color.green = 0xFFFF;
		color.blue = 0xFFFF;
	} else {
		color.red = 0xDDDD;
		color.green = 0xDDDD;
		color.blue = 0xDDDD;
	}
	::RGBBackColor(&color);
	
	::SpaceExtra(0);
	::TextFont(1);
	::TextSize(9);
	::TextFace(0);
	::TextMode(srcOr);
	
	state.pnLoc.h = 0;
	state.pnLoc.v = 0;
	state.pnSize.h = 1;
	state.pnSize.v = 1;
	state.pnMode = srcCopy;
	state.pnPat = qd.black;
	::SetPenState(&state);
	
	fPenWidth = 1;
	fAUnderscore = false;
}

/*	XGDraw::DestroyContext
 *
 *		Destroy this context.
 */

void XGDraw::DestroyContext()
{
	if (fFontPtr) fFontPtr->Detach();
}

/*	XGDraw::GetGDHandle
 *
 *		Get the GDHandle where this view resides. This figures out
 *	my view's global coordinates, and calculates the device which
 *	this view sits in.
 */

GDHandle XGDraw::GetGDHandle()
{
	XRect r,a;
	GrafPtr port;
	Point offset;
	GDHandle gdh;
	GDHandle dev;
	long area;
	long carea;
	
	/*
	 *	Did I already find this?
	 */
	
	if (fGDCache) return fGDCache;
	
	/*
	 *	Find global location
	 */
	
	r = fView->GetContentRect();
	
	::GetPort(&port);
	if (port->portRect.left || port->portRect.top) {
		/*
		 *	The port's origin has been set.
		 */
		
		offset.h = 0, offset.v = 0;
	} else {
		/*
		 *	Origin not set; get it from my location
		 */
		 
		offset = fView->fGlobalPos.MacPoint();
	}
	LocalToGlobal(&offset);
	
	OffsetRect(&r,offset.h,offset.v);
	
	/*
	 *	Now that I have the location of this thing in global
	 *	coordinates, figure out where this is living
	 */
	
	dev = NULL;
	for (gdh = GetDeviceList(); gdh != NULL; gdh = GetNextDevice(gdh)) {
		a = (**gdh).gdRect;
		SectRect(&a,&r,&a);
		if (!EmptyRect(&a)) {
			carea = a.bottom - a.top;
			carea *= a.right - a.left;
			if ((dev == NULL) || (carea > area)) {
				dev = gdh;
				area = carea;
			}
		}
	}
	if (dev == NULL) dev = GetMainDevice();
	
	fGDCache = dev;
	return dev;
}

#endif

/************************************************************************/
/*																		*/
/*	Drawing Context	(Windows)											*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Internal Support (Windows)
#endif

#if OPT_WINOS == 1

/*	XGDraw::SaveContext
 *
 *		Save the drawing context
 */

void XGDraw::SaveContext()
{
	fDCSave = ::SaveDC(fDC);
}

/*	XGDraw::RestoreContext
 *
 *		Restore the drawing context
 */

void XGDraw::RestoreContext()
{
	::RestoreDC(fDC,fDCSave);
}

/*	XGDraw::CreateContext
 *
 *		Create drawing context into the geometry specified.
 */

void XGDraw::CreateContext(bool)
{
	if (fView) {
		fDC = ::GetDC(fView->fRootWindow);

		/*
		 *	Set the local coordinates and clipping coordinates
		 */
		
		ClipRect();
		::SetViewportOrgEx(fDC,fView->fGlobalPos.h,fView->fGlobalPos.v,NULL);
	} else if (fPrinter) {
		fDC = fPrinter->fPrintDC;
	}
	
	
	/*
	 *	Set some parameters for me
	 */
	
	::SetTextColor(fDC,ColorFromPalette(KXGColorBlack));
	::SetBkColor(fDC,ColorFromPalette(KXGColorWhite));
	
	fForeColor = ColorFromPalette(KXGColorBlack);
	fBackColor = ColorFromPalette(KXGColorWhite);
	
	::SetTextAlign(fDC,TA_BASELINE | TA_LEFT | TA_UPDATECP);
	::SetBkMode(fDC,TRANSPARENT);					// sorta like mac
	
	/*
	 *	Set the font
	 */
	
	if (GDefaultFont == NULL) {
		GDefaultFont = ::CreateFont(-MulDiv(8,GetDeviceCaps(fDC,LOGPIXELSY),72),
									0,
									0,
									0,
									FW_NORMAL,
									false,
									false,
									false,
									ANSI_CHARSET,
									OUT_DEFAULT_PRECIS,
									CLIP_DEFAULT_PRECIS,
									PROOF_QUALITY,
									DEFAULT_PITCH | FF_SWISS,
									"Arial");
	}
	fDefFont = ::SelectObject(fDC,GDefaultFont);

	fPenWidth = 1;
	fAUnderscore = true;
}

/*	XGDraw::DestroyContext
 *
 *		Destroy this context. This doesn't do a thing
 */

void XGDraw::DestroyContext()
{
	::SelectObject(fDC,fDefFont);
	if (fFontPtr) fFontPtr->Detach();
	if (fView) {
		::ReleaseDC(fView->fRootWindow,fDC);
	} else if (fPrinter) {
		// On Windows, do nothing.
	}
}

#endif

/************************************************************************/
/*																		*/
/*	Drawing Context	(X Windows)											*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Internal Support (X)
#endif

#if OPT_XWIN == 1

/*	XGDraw::GetDrawable
 *
 *		Get the drawable of my view
 */

Drawable XGDraw::GetDrawable() const
{
	return fView->fRootWindow;
}

/*	XGDraw::SaveContext
 *
 *		Save the drawing context. This is ignored, as this is implicitly
 *	done by the act of creating a Graphics Context in X.
 */

void XGDraw::SaveContext()
{
}

/*$	XGDraw::RestoreContext
 *
 *		Restore the drawing context
 */

void XGDraw::RestoreContext()
{
}

/*	XGDraw::CreateContext
 *
 *		Create drawing context into the geometry specified. The 'local
 *	coordinate' parameter is ignored.
 */

void XGDraw::CreateContext(bool)
{
	fGC = ::XCreateGC(_GDisplay,fView->fRootWindow,0,NULL);
	
	/* Set clipping */
	ClipRect();
	
	/* Set other parameters */
	fXOrigin = fView->fGlobalPos.h;
	fYOrigin = fView->fGlobalPos.v;
	
	/* Set the GC parameters */
	SetForeColor(ColorFromPalette(KXGColorBlack));
	SetBackColor(ColorFromPalette(KXGColorWhite));
	
	/* Set the font */
	SetFont(XGFont::LoadFont(1));

	fPenWidth = 1;
	fAUnderscore = true;
}

/*	XGDraw::DestroyContext
 *
 *		Destroy this context. This doesn't do a thing
 */

void XGDraw::DestroyContext()
{
	if (fFontPtr) fFontPtr->Detach();
	::XFreeGC(_GDisplay,fGC);
}

#endif // OPT_XWIN
