/* +-------------------------------------------------------------------+ */
/* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)            | */
/* |                                                                   | */
/* | Permission to use, copy, modify, and to distribute this software  | */
/* | and its documentation for any purpose is hereby granted without   | */
/* | fee, provided that the above copyright notice appear in all       | */
/* | copies and that both that copyright notice and this permission    | */
/* | notice appear in supporting documentation.  There is no           | */
/* | representations about the suitability of this software for        | */
/* | any purpose.  this software is provided "as is" without express   | */
/* | or implied warranty.                                              | */
/* |                                                                   | */
/* +-------------------------------------------------------------------+ */

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#include <X11/ShellP.h>
#include <X11/Xatom.h>
#include "PaintP.h"
#include "xpaint.h"
#include <stdio.h>

/*
**  Value at which zoom starts putting in "white" lines
**    between pixels
*/
#define ZOOM_THRESH 5

extern XRectangle *RectIntersect(XRectangle *, XRectangle *);

typedef	void		(*func_t)(Widget, void *, XEvent *, Boolean);
typedef struct data_s {
	func_t		func;
	void		*data;
	int		mask, flag;
	struct data_s	*next;
} data_t;

void PwAddChild(Widget, Widget);

static void pwSetPixmap(PaintWidget w, Pixmap pix, int flag);
static void fatCallback(Widget, XtPointer, XtPointer);
static void realExposeProc(PaintWidget, XExposeEvent *, Boolean);
static void drawVisibleGrid(PaintWidget, Widget, Boolean, int, int, int, int, int);

static XtResource resources[] = {
#define offset(field) XtOffset(PaintWidget, paint.field)
    /* {name, class, type, size, offset, default_type, default_addr}, */
    { XtNcompress, XtCCompress, XtRBoolean, sizeof(Boolean),
	  offset(compress), XtRImmediate, (XtPointer)False },
    { XtNreadOnly, XtCReadOnly, XtRBoolean, sizeof(Boolean),
	  offset(compress), XtRImmediate, (XtPointer)False },
    { XtNdrawWidth, XtCDrawWidth, XtRInt, sizeof(int),
	  offset(drawWidth), XtRImmediate, (XtPointer)512 },
    { XtNdrawHeight, XtCDrawHeight, XtRInt, sizeof(int),
	  offset(drawHeight), XtRImmediate, (XtPointer)512 },
    { XtNlineWidth, XtCLineWidth, XtRInt, sizeof(int),
	  offset(lineWidth), XtRImmediate, (XtPointer)0 },
    { XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct*),
	  offset(fontInfo), XtRString, XtDefaultFont },
    { XtNpattern, XtCPattern, XtRPixmap, sizeof(Pixmap),
	  offset(pattern), XtRPixmap, (XtPointer)None },
    { XtNlinePattern, XtCLinePattern, XtRPixmap, sizeof(Pixmap),
	  offset(linePattern), XtRPixmap, (XtPointer)None },
    { XtNpixmap, XtCPixmap, XtRPixmap, sizeof(Pixmap),
	  offset(sourcePixmap), XtRPixmap, (XtPointer)NULL },
    { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
	  offset(foreground), XtRString, XtDefaultForeground },
    { XtNlineForeground, XtCLineForeground, XtRPixel, sizeof(Pixel),
	  offset(lineForeground), XtRString, XtDefaultForeground },
    { XtNsnap, XtCSnap, XtRInt, sizeof(int),
	  offset(snap), XtRImmediate, (XtPointer)10 },
    { XtNregionCallback, XtCRegionCallback, XtRCallback, sizeof(XtPointer),
	  offset(regionCalls), XtRCallback, (XtPointer)None },
    { XtNsizeChanged, XtCSizeChanged, XtRCallback, sizeof(XtPointer),
	  offset(sizecalls), XtRCallback, (XtPointer)None },
    { XtNfatBack, XtCFatBack, XtRCallback, sizeof(XtPointer),
	  offset(fatcalls), XtRCallback, (XtPointer)None },
    { XtNundoSize, XtCUndoSize, XtRInt, sizeof(int),
	  offset(undoSize), XtRImmediate, (XtPointer)1 },
    { XtNzoom, XtCZoom, XtRInt, sizeof(int),
	  offset(zoom), XtRImmediate, (XtPointer)1 },
    { XtNzoomX, XtCZoomX, XtRInt, sizeof(int),
	  offset(zoomX), XtRImmediate, (XtPointer)0 },
    { XtNzoomY, XtCZoomY, XtRInt, sizeof(int),
	  offset(zoomY), XtRImmediate, (XtPointer)0 },
    { XtNgrid, XtCGrid, XtRBoolean, sizeof(Boolean),
	  offset(grid), XtRImmediate, (XtPointer)False },
    { XtNsnapOn, XtCSnapOn, XtRBoolean, sizeof(Boolean),
	  offset(snapOn), XtRImmediate, (XtPointer)False },
    { XtNpaint, XtCPaint, XtRWidget, sizeof(Widget),
	  offset(paint), XtRImmediate, (XtPointer)None },
    { XtNdirty, XtCDirty, XtRBoolean, sizeof(Boolean),
	  offset(dirty), XtRImmediate, (XtPointer)False },
    { XtNfillRule, XtCFillRule, XtRInt, sizeof(int),
	  offset(fillRule), XtRImmediate, (XtPointer)FillSolid },
    { XtNlineFillRule, XtCLineFillRule, XtRInt, sizeof(int),
	  offset(lineFillRule), XtRImmediate, (XtPointer)FillSolid },
    { XtNdownX, XtCDownX, XtRPosition, sizeof(Position),
	  offset(downX), XtRImmediate, (XtPointer)0 },
    { XtNdownY, XtCDownY, XtRPosition, sizeof(Position),
	  offset(downY), XtRImmediate, (XtPointer)0 },
    { XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
	  offset(cursor), XtRString, (XtPointer)"watch" },
#undef offset
};

/*
**  Some forward defines.
*/
static void InitializeProc(Widget, Widget);
static void DestroyProc(Widget);
static void ResizeProc(Widget);
static void ExposeProc(Widget, XExposeEvent *);
static void RealizeProc(Widget, Mask *, XSetWindowAttributes *);
static Boolean SetValuesProc(Widget, Widget, Widget);
static void GetValuesHook(Widget, ArgList, Cardinal *);
static int QueryGeometryProc(Widget w, XtWidgetGeometry *proposed,
				XtWidgetGeometry *reply);

static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply)
{
	*reply = *request;

	if ((request->request_mode & CWX) != 0)
		w->core.x = request->x;
	if ((request->request_mode & CWY) != 0)
		w->core.y = request->y;
	if ((request->request_mode & CWWidth) != 0)
		w->core.width = request->width;
	if ((request->request_mode & CWHeight) != 0)
		w->core.height = request->height;
	if ((request->request_mode & CWBorderWidth) != 0)
		w->core.border_width = request->border_width;

	return XtGeometryYes;
}


PaintClassRec paintClassRec = {
  { /* core fields */
    /* superclass		*/	(WidgetClass) &compositeClassRec,
    /* class_name		*/	"Paint",
    /* widget_size		*/	sizeof(PaintRec),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	(XtInitProc)InitializeProc,
    /* initialize_hook		*/	NULL,
    /* realize			*/	RealizeProc,
    /* actions			*/	NULL,
    /* num_actions		*/	0,
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	FALSE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	DestroyProc,
    /* resize			*/	ResizeProc,
    /* expose			*/	(XtExposeProc)ExposeProc,
    /* set_values		*/	(XtSetValuesFunc)SetValuesProc,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	GetValuesHook,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	NULL,
    /* query_geometry		*/	(XtGeometryHandler)QueryGeometryProc,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
  },
  { /* composite fields         */
    /* geometry_manager         */	GeometryManager,
    /* change_managed           */	XtInheritChangeManaged,
    /* insert_child             */	XtInheritInsertChild,
    /* delete_childion          */	XtInheritDeleteChild,
    /* extension                */	NULL,
  },
  { /* paint fields */		
				0
  }
};

WidgetClass paintWidgetClass = (WidgetClass)&paintClassRec;

static void undoBuffer(PaintWidget w) 
{
	UndoStack	*new = (UndoStack*)XtMalloc(sizeof(UndoStack));

	if (new == NULL)
		return;

	new->box.x      = 0;
	new->box.y      = 0;
	new->box.width  = 0;
	new->box.height = 0;
	new->pixmap	= None;
	new->next	= w->paint.head;
	new->prev       = NULL;
	new->valid      = False;
	w->paint.head	= new;

	if (w->paint.tail == NULL)
		w->paint.tail = new;

	if (new->next != NULL)
		new->next->prev = new;
}

static void InitializeProc(Widget w, Widget newArg)
{
	PaintWidget	new = (PaintWidget)newArg;
	int		i;
	int		zoom = new->paint.zoom;

	if (zoom == PwZoomParent && new->paint.paint == None)
		zoom = new->paint.zoom = 1;

	if (zoom == PwZoomParent)
		zoom = 1;
	else if (zoom <= 0)
		zoom = new->paint.zoom = 1;

	if (new->paint.sourcePixmap != None) {
		Window		root;
		int		x, y;
		unsigned int	width, height, bw, depth;

		XGetGeometry(XtDisplay(w), new->paint.sourcePixmap,
				&root, &x, &y, &width, &height, &bw, &depth);
		
		new->paint.drawWidth  = width;
		new->paint.drawHeight = height;
	} else if (new->paint.paint != None) {
		PaintWidget	parent = (PaintWidget)new->paint.paint;

		new->paint.drawWidth  = parent->paint.drawWidth;
		new->paint.drawHeight = parent->paint.drawHeight;
	}

	if (new->core.width == 0)
		new->core.width = new->paint.drawWidth * zoom;
	if (new->core.height == 0)
		new->core.height = new->paint.drawHeight * zoom;

	new->paint.foreground = BlackPixelOfScreen(XtScreen(new));
	new->paint.background = WhitePixelOfScreen(XtScreen(new));

	new->paint.gc  = None;
	new->paint.igc = None;
	new->paint.sgc = None;
	new->paint.fgc = None;
	new->paint.mgc = None;
	new->paint.xgc = None;
	new->paint.tgc = None;

	/*
	**  Init the undo stuff
	*/
	new->paint.tail = NULL;
	new->paint.undo = NULL;
	new->paint.head = NULL;


	/*
	**  The region information
	*/
	new->paint.region.isVisible = False;
	new->paint.region.mask   = None;
	new->paint.region.source = None;
	new->paint.region.notMask= None;
	new->paint.region.inited = False;
	new->paint.region.fg_gc  = None;
	new->paint.region.bg_gc  = None;
	new->paint.region.child  = None;
	new->paint.region.sourceImg = NULL;
	new->paint.region.maskImg   = NULL;
	new->paint.region.notMaskImg= NULL;
	new->paint.region.proc      = NULL;

	if (new->paint.paint == None || new->paint.readOnly)
		for (i = 0; i < new->paint.undoSize; i++)
			undoBuffer(new);

	for (i = 0; i < XtNumber(new->paint.region.grip); i++)
		new->paint.region.grip[i] = None;
	
	/*
	**  Now the backing store image
	*/
	new->paint.image = NULL;

	/*
	**
	*/
	new->paint.paintGone = False;
	new->paint.paintChildrenSize = 0;
	new->paint.paintChildren = NULL;
}

static XRectangle *GetRectangle(PaintWidget w)
{
	static XRectangle	rect;
	XRectangle		*rp = &rect, newRect;
	Widget			cur;
	Widget			prev = (Widget)w;
	int			dx, dy;

	rect.x      = dx = 0;
	rect.y      = dy = 0;
	rect.width  = w->core.width  - dx;
	rect.height = w->core.height - dy;

	/*
	**  Climb the tree to find the visible area.
	**
	*/
	for (cur = XtParent((Widget)w); !XtIsShell(cur); cur = XtParent(cur)) {
		dx += -prev->core.x;
		dy += -prev->core.y;
		newRect.x      = dx;
		newRect.y      = dy;
		newRect.width  = cur->core.width;
		newRect.height = cur->core.height;

		rp = RectIntersect(&newRect, rp);

		prev = cur;
	}

	if (rp != NULL)
		rect = *rp;
	else
		rect.x = rect.y = rect.width = rect.height = 0;

	return &rect;
}

static void RealizeProc(Widget w, Mask *valueMask, 
				XSetWindowAttributes *attributes)
{
	XWindowAttributes	wattr;
	PaintWidget		pw = (PaintWidget)w;
	PaintWidget		pp = (PaintWidget)pw->paint.paint;
	UndoStack		*cur;
        Window         		root;
        int            	 	x, y;
        unsigned int   		width, height, bw, depth;
	XGCValues		values;

	XtCreateWindow(w, InputOutput, CopyFromParent, *valueMask, attributes);

        XGetGeometry(XtDisplay(w), XtWindow(w),
                        &root, &x, &y, &width, &height, &bw, &depth);


	values.foreground = pw->paint.foreground;
	values.background = pw->core.background_pixel;
	pw->paint.gc  = XtGetGC(w, GCForeground|GCBackground, &values);

	if (pp == None) {
		/*
		**  Inverse of the base colors.
		*/
		values.foreground = pw->core.background_pixel;
		values.background = pw->paint.foreground;
		pw->paint.igc = XCreateGC(XtDisplay(w), XtWindow(w), 
					GCForeground|GCBackground, &values);
		pw->paint.tgc = XCreateGC(XtDisplay(w), XtWindow(w), 0, 0);

		values.function       = GXxor;
		values.foreground     = ~0;
		values.subwindow_mode = IncludeInferiors;
		pw->paint.xgc = XCreateGC(XtDisplay(w), XtWindow(w), 
			GCForeground|GCFunction|GCSubwindowMode, &values);
	} else {
		pw->paint.igc = pp->paint.igc;
		pw->paint.tgc = pp->paint.tgc;
		pw->paint.xgc = pp->paint.xgc;

		pw->paint.lineWidth = pp->paint.lineWidth;
	}

	/*
	**  Create the filled gc information
	*/
	if (pw->paint.paint == None || pw->paint.readOnly) {
		pw->paint.sgc = XCreateGC(XtDisplay(w), XtWindow(w), 0, 0);
		pw->paint.fgc = XCreateGC(XtDisplay(w), XtWindow(w), 0, 0);
		XSetForeground(XtDisplay(w), pw->paint.fgc, pw->paint.foreground);
		XSetForeground(XtDisplay(w), pw->paint.sgc, pw->paint.lineForeground);

		/*
		**  First the fill GC
		*/
		XSetFillStyle(XtDisplay(w), pw->paint.fgc, pw->paint.fillRule);
		XSetLineAttributes(XtDisplay(w), pw->paint.fgc, 
				pw->paint.lineWidth, LineSolid, 
				CapRound, JoinMiter);
		if (pw->paint.pattern != None)
			XSetTile(XtDisplay(w), pw->paint.fgc, pw->paint.pattern);
		if (pw->paint.fontInfo != NULL && pw->paint.fontInfo->fid != None)
			XSetFont(XtDisplay(w), pw->paint.fgc, pw->paint.fontInfo->fid);

		/*
		**  Then the line GC
		*/
		XSetFillStyle(XtDisplay(w), pw->paint.sgc, pw->paint.lineFillRule);
		XSetLineAttributes(XtDisplay(w), pw->paint.sgc, 
				pw->paint.lineWidth, LineSolid, 
				CapRound, JoinMiter);
		if (pw->paint.linePattern != None)
			XSetTile(XtDisplay(w), pw->paint.sgc, pw->paint.linePattern);
	} else if (pw->paint.paint != None) {
		pw->paint.sgc = pp->paint.sgc;
		pw->paint.fgc = pp->paint.fgc;
	}

	/*
	**  Parent realized after children, odd..
	*/
	if (pp == None && pw->paint.paintChildrenSize != 0) {
		int	i;
		for (i = 0; i < pw->paint.paintChildrenSize; i++) {
			PaintWidget	tp = (PaintWidget)pw->paint.paintChildren[i];
			tp->paint.sgc = pw->paint.sgc;
			tp->paint.fgc = pw->paint.fgc;
			tp->paint.igc = pw->paint.igc;
			tp->paint.xgc = pw->paint.xgc;
			tp->paint.tgc = pw->paint.tgc;
		}
	}

	/*
	**  Create the backing undo buffers
	*/
	for (cur = pw->paint.tail; cur != NULL; cur = cur->next) {
		cur->pixmap = XCreatePixmap(XtDisplay(w), XtWindow(w), 
				pw->paint.drawWidth, pw->paint.drawHeight,
				depth);
		cur->valid = False;
	}

	/*
	**  If this paint widgets is the child of another paint
	**   widget then, either creating our own unique information
	**   or just use the information from our parent.
	*/
	if (pw->paint.paint == None) {
		pw->paint.base = XCreatePixmap(XtDisplay(w), XtWindow(w), 
				pw->paint.drawWidth, pw->paint.drawHeight,
				depth);
		if (pw->paint.sourcePixmap != None)
			pwSetPixmap(pw, pw->paint.sourcePixmap, FALSE);
		else
			XFillRectangle(XtDisplay(w), pw->paint.base, 
				pw->paint.igc, 0,0, 
				pw->paint.drawWidth, pw->paint.drawHeight);
	} else {
		PwAddChild(pw->paint.paint, w);
	}

	XGetWindowAttributes(XtDisplay(w), XtWindow(w), &wattr);
	pw->paint.visual = wattr.visual;

	if (pw->paint.cursor != None)
		XDefineCursor(XtDisplay(pw), XtWindow(pw), pw->paint.cursor);
}

static void DestroyProc(Widget w)
{
	PaintWidget	pw = (PaintWidget)w;
	UndoStack	*cur, *nxt;
	int		i;

	if (pw->paint.mgc != None)
		XFreeGC(XtDisplay(pw), pw->paint.mgc);
	if (pw->paint.gc != None)
		XtReleaseGC(w, pw->paint.gc);

	if (pw->paint.region.source != None)
		XFreePixmap(XtDisplay(pw), pw->paint.region.source);
	if (pw->paint.region.mask != None)
		XFreePixmap(XtDisplay(pw), pw->paint.region.mask);
	if (pw->paint.region.notMask != None)
		XFreePixmap(XtDisplay(pw), pw->paint.region.notMask);
	if (pw->paint.region.sourceImg != NULL)
		XDestroyImage(pw->paint.region.sourceImg);
	if (pw->paint.region.maskImg != NULL)
		XDestroyImage(pw->paint.region.maskImg);
	if (pw->paint.region.notMaskImg != NULL)
		XDestroyImage(pw->paint.region.notMaskImg);

	if (pw->paint.paint != None)
		return;

	/*  WARNING: the rest of this callback doesn't execute for children */

	if (pw->paint.tgc != None)
		XFreeGC(XtDisplay(pw), pw->paint.tgc);
	if (pw->paint.xgc != None)
		XFreeGC(XtDisplay(pw), pw->paint.xgc);
	if (pw->paint.igc != None)
		XFreeGC(XtDisplay(pw), pw->paint.igc);
	if (pw->paint.fgc != None)
		XFreeGC(XtDisplay(pw), pw->paint.fgc);
	if (pw->paint.sgc != None)
		XFreeGC(XtDisplay(pw), pw->paint.sgc);
	
	if (pw->paint.base != None)
		XFreePixmap(XtDisplay(pw), pw->paint.base);

	cur = pw->paint.tail;
	while (cur != NULL) {
		nxt = cur->next;
		if (cur->pixmap != None)
			XFreePixmap(XtDisplay(pw), cur->pixmap);
		XtFree((XtPointer)cur);
		cur = nxt;
	}

	if (pw->paint.image != NULL) {
		XDestroyRegion(pw->paint.imageRegion);
		XDestroyImage(pw->paint.image);
	}

	for (i = 0; i < pw->paint.paintChildrenSize; i++) {
		PaintWidget	tpw = (PaintWidget)pw->paint.paintChildren[i];

		tpw->paint.paintGone = True;
	}

	if (pw->paint.paintChildren != NULL)
		XtFree((XtPointer)pw->paint.paintChildren);
}

static void ResizeProc(Widget w)
{
	/* printf("Resize called, this shouldn't happen!!\n"); */
}

#if 0
static XRectangle *overlap(XRectangle *a, XRectangle *b)
{
        static XRectangle       out;
        int                     w, h;

        out.x = MAX(a->x, b->x);
        out.y = MAX(a->y, b->y);
        w     = MIN(a->x + a->width,  b->x + b->width)  - out.x;
        h     = MIN(a->y + a->height, b->y + b->height) - out.y;

        if (w <= 0 || h <= 0)
                return NULL;

        out.width  = w;
        out.height = h;

        return &out;
}
#endif

static void drawVisibleGrid(PaintWidget pw, Widget w, Boolean flag, 
		int zoom, int sx, int sy, int ex, int ey)
{
	static 	GC	tgc = None;
	GC		gc;
	Display		*dpy = XtDisplay(w);
	Window		win  = XtWindow(w);
	int		i;

	if (zoom == 1)
		return;

	if (flag) {
		if (tgc == None) {
			XGCValues	values;
			
			values.foreground = BlackPixelOfScreen(XtScreen(w));
			tgc = XtGetGC(w, GCForeground, &values);
		}
		gc = tgc;
	} else {
		/*
		**  Turning off visible grid, and we need to clear
		**    to no space between pixels... 
		*/
		if (zoom <= ZOOM_THRESH) {
			XClearArea(dpy, win, sx, sy, ex - sx, ey - sy, True);
			return;
		}
		gc = pw->paint.igc;
	}

	for (i = sx; i < ex; i += zoom)
		XDrawLine(dpy, win, gc, i, sy, i, ey);
	for (i = sy; i < ey; i += zoom) 
		XDrawLine(dpy, win, gc, sx, i, ex, i);
}

/*
**  Update the zoomed region as specified by rect.
**    rect is in screen coordinates.
**
**  There are two drawers, the first draws using rectangles
**   the second draws using XImages.
**
*/

static void zoomDrawRects(PaintWidget pw, Widget w, GC gc, XImage *xim, XImage *mask, Boolean isExpose,
			int xstart, int ystart, int zoom, XRectangle *rect)
{
	static Boolean	*flags = NULL;
	static int	flagsSize = 0;
	Display		*dpy   = XtDisplay(w);
	Window		win    = XtWindow(w);
	int		width  = rect->width;
	int		count  = rect->width * rect->height;
	XRectangle	rects[256];
	int		index, i;

	if (flagsSize < count) {
		int	allocSize = (count + 4) * sizeof(Boolean);
		if (flags == NULL)
			flags = (Boolean *)XtMalloc(allocSize);
		else
			flags = (Boolean *)XtRealloc((XtPointer)flags, allocSize);
		flagsSize = count;
	}

	memset(flags, 0, flagsSize * sizeof(Boolean));
	if (mask != NULL) {
		int	x, y;

		i = 0;
		for (y = rect->y; y < rect->height + rect->y; y++) {
			for (x = rect->x; x < rect->width + rect->x; x++, i++) {
				if (!XGetPixel(mask, x, y))
					flags[i] = True;
			}
		}
	}
	for (i = 0; i < XtNumber(rects) && i < count; i++)
		if (zoom > ZOOM_THRESH)
			rects[i].width = rects[i].height = zoom - 1;
		else
			rects[i].width = rects[i].height = zoom;

	for (index = 0; index != count; index++) {
		if (!flags[index]) {
			int	xindex = index % width + rect->x;
			int	yindex = index / width + rect->y;
			int	xindexEnd = rect->x + rect->width;
			Pixel	p = xxGetPixel(xim, xindex, yindex);
			int	xpos = (index % width + xstart) * zoom;
			int	ypos = (index / width + ystart) * zoom;
			int	pos, flag;

			flag = (isExpose && p == w->core.background_pixel);

			XSetForeground(dpy, gc, p);
			for (pos = index, i = 0; pos != count; pos++, xindex++, xpos += zoom) {
				if (xindex == xindexEnd) {
					xindex = rect->x;
					xpos   = xstart * zoom;
					yindex++;
					ypos  += zoom;
				}
				if (flags[pos] || xxGetPixel(xim, xindex, yindex) != p)
					continue;
				flags[pos] = True;

				if (flag)
					continue;

				rects[i].x = xpos;
				rects[i].y = ypos;
				i++;

				if (i == XtNumber(rects)) {
					XFillRectangles(dpy, win, gc, rects, i);
					i = 0;
				}
			}
			if (i > 0) 
				XFillRectangles(dpy, win, gc, rects, i);
		}
	}
}

static void zoomDrawImage(PaintWidget pw, Widget w, GC gc, XImage *xim, XImage *mask, Boolean isExpose,
			int xstart, int ystart, int zoom, XRectangle *rect)
{
	Display		*dpy   = XtDisplay(w);
	int		width  = rect->width;
	int		height = rect->height;
	int		size   = width*zoom * height*zoom * sizeof(long);
	XImage		*dst;
	XImage		*dstMsk = NULL;
	int		x, y, x1, y1, dx, dy;
	int		numDraw = zoom - ((zoom > ZOOM_THRESH) ? 1 : 0);
	int		endX = rect->x + rect->width;
	int		endY = rect->y + rect->height;

	dst = XCreateImage(dpy, pw->paint.visual, xim->depth, xim->format, 
					0, NULL, width * zoom, height * zoom, 32, 0);
	dst->data = (char*)XtMalloc(height * zoom * dst->bytes_per_line);

	if (mask != NULL) {
		dstMsk = XCreateImage(dpy, pw->paint.visual, 
					mask->depth, mask->format, 
					0, NULL, width*zoom, height*zoom, 8, 0);
		dstMsk->data = (char*)XtMalloc(height * zoom * dstMsk->bytes_per_line);
		memset(dstMsk->data, 0, height * zoom * dstMsk->bytes_per_line);
	}

	if (mask == NULL) {
		for (dy = 0, y = rect->y; y < endY; y++, dy += zoom) {
			for (dx = 0, x = rect->x; x < endX; x++, dx += zoom) {
				Pixel	p;

				p = xxGetPixel(xim, x, y);
		
				for (y1 = 0; y1 < numDraw; y1++) {
					for (x1 = 0; x1 < numDraw; x1++) {
						xxPutPixel(dst, dx + x1, dy + y1, p);
					}
					if (numDraw != zoom)
						xxPutPixel(dst, dx + x1, dy + y1, w->core.background_pixel);
				}
				if (numDraw != zoom)
					for (x1 = 0; x1 < zoom; x1++)
						xxPutPixel(dst, dx + x1, dy + y1, w->core.background_pixel);
			}
		}
	} else {
		for (dy = 0, y = rect->y; y < endY; y++, dy += zoom) {
			for (dx = 0, x = rect->x; x < endX; x++, dx += zoom) {
				Pixel	p;

				if (!xxGetPixel(mask, x, y))
					continue;

				p = xxGetPixel(xim, x, y);

				for (y1 = 0; y1 < numDraw; y1++) {
					for (x1 = 0; x1 < numDraw; x1++) {
						xxPutPixel(dst, dx + x1, dy + y1, p);
						xxPutPixel(dstMsk, dx + x1, dy + y1, True); 
					}
				}
			}
		}
	}

	if (mask != NULL) {
		Pixmap	pix = XCreatePixmap(XtDisplay(w), XtWindow(w), width * zoom, height * zoom, 1);

		XPutImage(XtDisplay(w), pix, pw->paint.mgc, dstMsk, 
				0, 0, 0, 0, width * zoom, height * zoom);
			
		XSetClipMask(XtDisplay(w), gc, pix);
		XSetClipOrigin(XtDisplay(w), gc, xstart * zoom, ystart * zoom);
		XPutImage(XtDisplay(w), XtWindow(w), gc, dst, 0, 0, 
				xstart * zoom, ystart * zoom, 
				width * zoom, height * zoom);
		XSetClipMask(XtDisplay(w), gc, None);

		XFreePixmap(XtDisplay(w), pix);
	} else {
		XPutImage(XtDisplay(w), XtWindow(w), gc, dst, 0, 0, 
				xstart * zoom, ystart * zoom, 
				width * zoom, height * zoom);
	}

	XDestroyImage(dst);
	if (dstMsk != NULL)
		XDestroyImage(dstMsk);
}

static void zoomUpdate(PaintWidget pw, Boolean isExpose, XRectangle *rect)
{
	int		zoom = GET_ZOOM(pw);
	int		sx, sy, w, h, zsx, zsy;
	XImage		*xim;
	XRectangle	*isec, *tmp;
	XRectangle	real, grab;

	if ((isec = RectIntersect(rect, GetRectangle(pw))) == NULL)
		return;

	zsx = sx = isec->x / zoom;
	zsy = sy = isec->y / zoom;
	w  = (isec->width  + (isec->x % zoom) + zoom - 1) / zoom;
	h  = (isec->height + (isec->y % zoom) + zoom - 1) / zoom;

	grab.x      = sx + pw->paint.zoomX;
	grab.y      = sy + pw->paint.zoomY;
	grab.width  = w;
	grab.height = h;
	real.x      = 0;
	real.y      = 0;
	real.width  = pw->paint.drawWidth;
	real.height = pw->paint.drawHeight;

	if ((tmp = RectIntersect(&grab, &real)) == NULL)
		return;

	xim = PwGetImage((Widget)pw, tmp);

	real.x      = tmp->x;
	real.y      = tmp->y;
	real.width  = tmp->width;
	real.height = tmp->height;

	_PwZoomDraw(pw, (Widget)pw, pw->paint.tgc, xim, NULL, isExpose, sx, sy, zoom, &real);
}
/*
**  pw  = parent paint widget
**  w   = widget who's window will be updated
**  gc  = gc to use (read/written)
**  src = source XImage to use
**  mask = mask XImage to use
**  flag = is this an expose event, so we don't have to update pixels == window background
**  sx,sy = start x, y for window drawing (non-zoomed)
**  zoom = zoom value to apply
**  rect = region of the input ximages to use to draw a start x,y with the specified width,height
*/
void _PwZoomDraw(PaintWidget pw, Widget w, GC gc, XImage *src, XImage *mask, Boolean flag,
			int sx, int sy, int zoom, XRectangle *rect)
{
	if (rect->width * rect->height < 1024 + 256)
		zoomDrawRects(pw, w, gc, src, mask, flag, sx, sy, zoom, rect);
	else
		zoomDrawImage(pw, w, gc, src, mask, flag, sx, sy, zoom, rect);

	if (pw->paint.grid) {
		int		xstart, xend, ystart, yend;

		xstart = sx * zoom - 1;
		ystart = sy * zoom - 1;
		xend   = xstart + rect->width * zoom + zoom - 1;
		yend   = ystart + rect->height * zoom + zoom - 1;

		drawVisibleGrid(pw, w, True, zoom, xstart, ystart, xend, yend);
	}
}


void PwUpdateDrawable(Widget w, Drawable draw, XRectangle *rect)
{
	PaintWidget	pw = (PaintWidget)w;
	XRectangle	all;
	Pixmap		pix = GET_PIXMAP(pw);

	if (rect == NULL || rect->width == 0 || rect->height == 0) {
		rect = &all;
		all.x = all.y = 0;
		all.width  = pw->core.width;
		all.height = pw->core.height;
	}
	XCopyArea(XtDisplay(w), pix, draw, pw->paint.gc,
			rect->x + pw->paint.zoomX, 
			rect->y + pw->paint.zoomY,
			rect->width, rect->height,
			rect->x, rect->y);
}

static void realExposeProc(PaintWidget pw, XExposeEvent *event, Boolean flag)
{
	int		x, y, width, height;
	XRectangle	rect;

	if (!XtIsRealized(((Widget)pw)))
		return;

	/*
	**  Clean up the event, since I do simulate them
	*/
	x      = event->x;
	y      = event->y;
	width  = event->width;
	height = event->height;

	if (x < 0) {
		width += x;
		x = 0;
	}
	if (y < 0) {
		height += y;
		y = 0;
	}
	if (width < 0 || height < 0)
		return;
	if (x > pw->core.width || y > pw->core.height)
		return;
	if (x + width > pw->core.width)
		width = pw->core.width - x;
	if (y + height > pw->core.height)
		height = pw->core.height - y;

	rect.x      = x;
	rect.y      = y;
	rect.width  = width;
	rect.height = height;

	if (GET_ZOOM(pw) <= 1)
		PwUpdateDrawable((Widget)pw, XtWindow((Widget)pw), &rect);
	else
		zoomUpdate(pw, flag, &rect);
}

static void ExposeProc(Widget w, XExposeEvent *event)
{
	realExposeProc((PaintWidget)w, event, True);
}

static void resizePixmap(PaintWidget w, Pixmap *pixmap)
{
	Pixmap	n;

	if (pixmap == NULL || *pixmap == None)
		return;

	n = XCreatePixmap(XtDisplay(w), XtWindow(w), 
				w->paint.drawWidth, w->paint.drawHeight,
				w->core.depth);
	XFillRectangle(XtDisplay(w), n, 
				w->paint.igc, 0,0, 
				w->paint.drawWidth, w->paint.drawHeight);
	XCopyArea(XtDisplay(w), *pixmap, n,
				w->paint.gc, 0, 0, 
				w->paint.drawWidth, w->paint.drawHeight, 0, 0);
	XFreePixmap(XtDisplay(w), *pixmap);
	*pixmap = n;
}

static Boolean SetValuesProc(Widget curArg, Widget request, 
				Widget newArg)
{
	PaintWidget	cur = (PaintWidget)curArg;
	PaintWidget	new = (PaintWidget)newArg;
	int		ret = False;
	int		sizeChanged = False;
	int		i;

	if (cur->paint.sourcePixmap != new->paint.sourcePixmap) 
		pwSetPixmap(new, new->paint.sourcePixmap, True);

	if (cur->paint.compress != new->paint.compress) 
		new->core.widget_class->core_class.compress_motion = new->paint.compress;

	if (!XtIsRealized((Widget)new))
		return ret;

	if (cur->paint.fillRule != new->paint.fillRule) 
		XSetFillStyle(XtDisplay(new), new->paint.fgc, new->paint.fillRule);
	if (cur->paint.foreground != new->paint.foreground) 
		XSetForeground(XtDisplay(new), new->paint.fgc, new->paint.foreground);
	if (cur->paint.pattern != new->paint.pattern) 
		XSetTile(XtDisplay(new), new->paint.fgc, new->paint.pattern);

	if (cur->paint.lineFillRule != new->paint.lineFillRule) 
		XSetFillStyle(XtDisplay(new), new->paint.sgc, new->paint.lineFillRule);
	if (cur->paint.lineForeground != new->paint.lineForeground) 
		XSetForeground(XtDisplay(new), new->paint.sgc, new->paint.lineForeground);
	if (cur->paint.linePattern != new->paint.linePattern) 
		XSetTile(XtDisplay(new), new->paint.sgc, new->paint.linePattern);

	if (cur->paint.lineWidth != new->paint.lineWidth) {
		XSetLineAttributes(XtDisplay(new), new->paint.fgc, 
				new->paint.lineWidth, LineSolid, 
				CapRound, JoinMiter);
		XSetLineAttributes(XtDisplay(new), new->paint.sgc, 
				new->paint.lineWidth, LineSolid, 
				CapRound, JoinMiter);
	}
	if (cur->paint.fontInfo->fid != new->paint.fontInfo->fid) {
		XSetFont(XtDisplay(new), new->paint.fgc, 
				new->paint.fontInfo->fid);
	}
	if (cur->paint.drawWidth  != new->paint.drawWidth || 
	    cur->paint.drawHeight != new->paint.drawHeight) {
		UndoStack	*cur;

		/*
		**  Resize the undo buffers
		*/
		if (new->paint.paint == None) {
			resizePixmap(new, &new->paint.base);
			for (cur = new->paint.tail; cur != NULL; cur = cur->next) 
				resizePixmap(new, &cur->pixmap);
		}
		
		/*
		**  Destroy the XImage that is cached
		*/
		if (new->paint.image != NULL) {
			XDestroyImage(new->paint.image);
			XDestroyRegion(new->paint.imageRegion);
			new->paint.image = NULL;
		}

		if (XtMakeResizeRequest((Widget)new, 
				new->paint.drawWidth * new->paint.zoom,
				new->paint.drawHeight * new->paint.zoom,
				NULL, NULL) == XtGeometryDone) {
			ret = True;
			sizeChanged = True;
		}
		for (i = 0; i < new->paint.paintChildrenSize; i++) {
			XtVaSetValues(new->paint.paintChildren[i], 
					XtNdrawWidth, new->paint.drawWidth,
					XtNdrawHeight, new->paint.drawHeight,
					NULL);
		}
	}
	if (cur->paint.zoom != new->paint.zoom) {
		ret = True;

		if (XtMakeResizeRequest((Widget)new, 
				new->paint.drawWidth * new->paint.zoom,
				new->paint.drawHeight * new->paint.zoom,
				NULL, NULL) == XtGeometryDone) {
			/*
			**   XXX  -- resize children  ???
			*/
		}
		sizeChanged = True;
	}
	if (cur->paint.zoomX != new->paint.zoomX || 
	    cur->paint.zoomY != new->paint.zoomY) {
		XExposeEvent	event;

		event.x      = 0;
		event.y      = 0;
		event.width  = new->core.width;
		event.height = new->core.height;

		realExposeProc(new, &event, False);
		pwRegionZoomPosChanged(new);
	}
#if 0
	if (cur->core.width != new->core.width ||
	    cur->core.height != new->core.height) {
		int	rc =
		XtMakeResizeRequest((Widget)new, 
				new->core.width, new->core.height, 
				NULL, NULL);
		printf("rc = %d\n",rc);
	}
#endif
	if (cur->paint.grid != new->paint.grid) {
		XRectangle	*rect = GetRectangle(new);

		rect->x -= rect->x % new->paint.zoom + 1;
		rect->y -= rect->y % new->paint.zoom + 1;

		drawVisibleGrid(new, (Widget)new, 
				new->paint.grid, GET_ZOOM(new),
				rect->x, rect->y, 
				rect->x + rect->width, rect->y + rect->height);

		for (i = 0; i < new->paint.paintChildrenSize; i++) {
			XtVaSetValues(new->paint.paintChildren[i], 
						XtNgrid, new->paint.grid, 
						NULL);
		}
	}

	if (cur->core.background_pixel != new->core.background_pixel)
		XSetForeground(XtDisplay(new), new->paint.igc, new->core.background_pixel);

	if (cur->paint.cursor != new->paint.cursor)
		XDefineCursor(XtDisplay(new), XtWindow(new), new->paint.cursor);
	
	if (sizeChanged)
		XtCallCallbackList((Widget)new, new->paint.sizecalls, NULL);

	return ret;
}
static void GetValuesHook(Widget w, ArgList args, Cardinal *nargs)
{
	PaintWidget	pw = (PaintWidget)w;
	PaintWidget	pp = (PaintWidget)pw->paint.paint;
	Arg		a;
	int		i;

	if (pp == None)
		return;
	
	for (i = 0; i < *nargs; i++) {
		char	*nm = args[i].name;

		if (strcmp(nm, XtNlineWidth) == 0)
			XtSetArg(a, XtNlineWidth, args[i].value);
		else if (strcmp(nm, XtNlineFillRule) == 0)
			XtSetArg(a, XtNlineFillRule, args[i].value);
		else if (strcmp(nm, XtNfillRule) == 0)
			XtSetArg(a, XtNfillRule, args[i].value);
		else if (strcmp(nm, XtNdrawWidth) == 0)
			XtSetArg(a, XtNdrawWidth, args[i].value);
		else if (strcmp(nm, XtNdrawHeight) == 0)
			XtSetArg(a, XtNdrawHeight, args[i].value);
		else
			continue;
		XtGetValues((Widget)pp, &a, 1);
	}
}

static int QueryGeometryProc(Widget w, XtWidgetGeometry *proposed,
				XtWidgetGeometry *reply)
{

/*
**  Use the fact that the return values are ordered by "importance"
*/
#define SETRET(ret,x)	ret = (x > ret) ? x : ret
	PaintWidget	pw = (PaintWidget)w;
	int		width  = pw->paint.drawWidth  * pw->paint.zoom;
	int		height = pw->paint.drawHeight * pw->paint.zoom;

	reply->request_mode = CWWidth | CWHeight;
	reply->width  = width;
	reply->height = height;

#if 0
	printf("In %d,%d   Out %d,%d\n",
			proposed->width, proposed->height,
			reply->width, reply->height);
#endif

	if (proposed->request_mode & (CWWidth | CWHeight) == (CWWidth | CWHeight) &&
	    proposed->width == reply->width && proposed->height == reply->height)
		return XtGeometryNo;
#if 0
	else if (reply->width == w->core.width && reply->height == w->core.height)
		return XtGeometryNo;
#endif
	else
		return XtGeometryAlmost;
}

XImage *PwGetImage(Widget w, XRectangle *rect)
{
	PaintWidget	pw = (PaintWidget)w;
	Pixmap		pix = GET_PIXMAP(pw);
	Region		r;
	XRectangle	clean;

	if (pw->paint.paint != None)
		pw = (PaintWidget)pw->paint.paint;


	if (rect != NULL) {
		if (rect->x < 0) {
			clean.x = 0;
			clean.width = rect->width + rect->x;
		} else {
			clean.x = rect->x;
			clean.width = rect->width;
		}
		if (rect->y < 0) {
			clean.y = 0;
			clean.height = rect->height + rect->y;
		} else {
			clean.y = rect->y;
			clean.height = rect->height;
		}
		if (clean.width > pw->paint.drawWidth)	
			clean.width = pw->paint.drawWidth;
		if (clean.height > pw->paint.drawHeight)	
			clean.height = pw->paint.drawHeight;
	} else {
		clean.x = 0;
		clean.y = 0;
		clean.width = pw->paint.drawWidth;
		clean.height = pw->paint.drawHeight;
	}

	if (pw->paint.image == NULL) {
		pw->paint.image = XCreateImage(XtDisplay(w), pw->paint.visual,
					pw->core.depth, ZPixmap, 0, NULL, 
					pw->paint.drawWidth, pw->paint.drawHeight,
					32, 0);
		pw->paint.image->data = (char*)XtMalloc(pw->paint.drawHeight * pw->paint.image->bytes_per_line);
		pw->paint.imageRegion = XCreateRegion();
		pw->paint.invalidateRegion = False;
	}

	if (pw->paint.invalidateRegion) {
		XDestroyRegion(pw->paint.imageRegion);
		pw->paint.imageRegion = XCreateRegion();
		pw->paint.invalidateRegion = False;
	}

	if (XRectInRegion(pw->paint.imageRegion, clean.x, clean.y,
				clean.width, clean.height) == RectangleIn)
		return pw->paint.image;

	r = XCreateRegion();
	XUnionRectWithRegion(&clean, r, r);
	XSubtractRegion(r, pw->paint.imageRegion, r);

	XClipBox(r, &clean);

	/*
	**  Since this is "caching" grow the image sligtly over the
	**    requested size.
	*/
	if ((clean.x -= 8) < 0) 
		clean.x = 0;
	if ((clean.y -= 8) < 0) 
		clean.y = 0;
	if ((clean.width += 16) > pw->paint.drawWidth - clean.x) 
		clean.width = pw->paint.drawWidth - clean.x;
	if ((clean.height += 16) > pw->paint.drawHeight - clean.y) 
		clean.height = pw->paint.drawHeight - clean.y;

	XGetSubImage(XtDisplay(pw), pix, clean.x, clean.y,
			clean.width, clean.height, AllPlanes, ZPixmap,
			pw->paint.image, clean.x, clean.y);
	XUnionRectWithRegion(&clean, pw->paint.imageRegion, 
					pw->paint.imageRegion);

	XDestroyRegion(r);
#if 0
printf("num rects = %d\n", ((long*)pw->paint.imageRegion)[1]);
#endif
	return pw->paint.image;
}

void PwGetPixmap(Widget w, Pixmap *pix, int *width, int *height)
{
	PaintWidget	pw = (PaintWidget)w;
	*pix = None;

	if (pw->paint.paint != None) {
		PwGetPixmap((Widget)pw->paint.paint, pix, width, height);
		return;
	}

	if (width != NULL)
		*width  = pw->paint.drawWidth;
	if (height != NULL)
		*height = pw->paint.drawHeight;

	*pix= XCreatePixmap(XtDisplay(w), XtWindow(w), 
				pw->paint.drawWidth, pw->paint.drawHeight,
				pw->core.depth);
	PwUpdateDrawable(w, *pix, NULL);
}
Pixmap PwGetRawPixmap(Widget w)
{
	PaintWidget	pw = (PaintWidget)w;

	if (pw->paint.paint != None)
		return PwGetRawPixmap((Widget)pw->paint.paint);

	return GET_PIXMAP(pw);
}

static void pwSetPixmap(PaintWidget w, Pixmap pix, int flag)
{
	Window		root;
	int		x, y;
	unsigned int	width, height, bw, depth;
	
	XGetGeometry(XtDisplay(w), pix,
			&root, &x, &y, &width, &height, &bw, &depth);

depth = 2;
	if (depth == 1) {
		XCopyPlane(XtDisplay(w), pix, GET_PIXMAP(w),
			w->paint.gc, 0, 0, width, height, 0, 0, 1);
	} else {
		XCopyArea(XtDisplay(w), pix, GET_PIXMAP(w),
			w->paint.gc, 0, 0, width, height, 0, 0);
	}
}

static void fatCallback(Widget parent, XtPointer w, XtPointer rectArg)
{
	XRectangle	*rect = (XRectangle*)rectArg;
	PaintWidget	pw = (PaintWidget)w;
	XExposeEvent	event;
	int		zoom = GET_ZOOM(pw);

	/*
	**  Make this look like an expose event on the fatbits region
	*/
	event.x      = (rect->x - pw->paint.zoomX) * zoom;
	event.y      = (rect->y - pw->paint.zoomY) * zoom;
	event.width  = rect->width * zoom;
	event.height = rect->height * zoom;

	realExposeProc(pw, &event, False);
}

void PwUpdate(Widget w, XRectangle *rect, Boolean force)
{
	PaintWidget	pw = (PaintWidget)w;
	PaintWidget	parent = (PaintWidget)pw->paint.paint;
	PaintWidget	usePW = (parent == None) ? pw : parent;
	XRectangle	all;

	if (rect == NULL) {
		if (pw->paint.undo == NULL) {
			all.x      = 0;
			all.y      = 0;
			all.width  = pw->core.width;
			all.height = pw->core.height;
			rect = &all;
		} else {
			rect = &pw->paint.undo->box;
		}
	} else {
		all.x = rect->x - pw->paint.lineWidth;
		all.y = rect->y - pw->paint.lineWidth;
		all.width = rect->width + pw->paint.lineWidth * 2;
		all.height = rect->height + pw->paint.lineWidth * 2;
		rect = &all;
	}

	/*
	**  If we have a caching image
	*/
	if (usePW->paint.image != NULL && !usePW->paint.invalidateRegion) {
#if 0
		if (hadRect) {
			/* XXX - There should be some better way */
			if (((long*)usePW->paint.imageRegion)[1] > 100) {
				usePW->paint.invalidateRegion = True;
			} else {
				Region	r = XCreateRegion();
				XUnionRectWithRegion(rect, r, r);
				XSubtractRegion(usePW->paint.imageRegion, r, 
						usePW->paint.imageRegion);
				XDestroyRegion(r);
			}
		} else {
			usePW->paint.invalidateRegion = True;
		}
#else
		usePW->paint.invalidateRegion = True;
#endif
	}

	XtCallCallbackList((Widget)usePW, usePW->paint.fatcalls, 
					(XtPointer)rect);

	if (force || pw->paint.zoom > 1 || parent != None) 
		fatCallback((Widget)usePW, (XtPointer)usePW, (XtPointer)rect);
}

/*
**  Update the current visual buffer with the contents
**   of the undo area, thus a pseudo-real time undo.
**   (it's not undo at all)
*/
void PwUpdateFromLast(Widget w, XRectangle *rect) 
{
	Pixmap		pix;
	PaintWidget	pw = (PaintWidget)w;
	Pixmap		draw = GET_PIXMAP(pw);
	
	if (pw->paint.paint != None)
		pw = (PaintWidget)pw->paint.paint;

	/*
	**  If there is undoing enabled
	*/
	if (pw->paint.undo == None) {
		pix = pw->paint.base;
	} else {
		if (pix = pw->paint.undo->next != NULL)
			pix = pw->paint.undo->next->pixmap;
		else
			pix = pw->paint.base;
	}

	XCopyArea(XtDisplay(w), pix, draw, pw->paint.gc,
			rect->x, rect->y, rect->width, rect->height,
			rect->x, rect->y);
	PwUpdate(w, rect, True);
}

/*
**  Bogus XXX
*/
XRectangle *PwScaleRectangle(Widget w, XRectangle *src)
{
	static XRectangle	dst;
	PaintWidget		pw = (PaintWidget)w;

	return src;
}

void PwPutPixmap(Widget w, Pixmap pix)
{
	PaintWidget	pw = (PaintWidget)w;
	GC		gc = pw->paint.gc;
	Pixmap		dst;
	Window		root;
	int		x, y;
	unsigned int	width, height, bw, depth;
	XRectangle	rect;

	XGetGeometry(XtDisplay(w), pix, &root, &x, &y, &width, &height, &bw, &depth);
	rect.x = 0;
	rect.y = 0;
	rect.width = width;
	rect.height = height;
	dst = PwUndoStart(w, &rect);
	XCopyArea(XtDisplay(pw), pix, dst, gc, 0, 0,
			width, height, 0, 0);
	PwUpdate(w, &rect, True);
}

static void removeChild(Widget child, Widget w)
{
	PaintWidget	pw = (PaintWidget)w;
	int		i;

	for (i = 0; i < pw->paint.paintChildrenSize; i++)
		if (pw->paint.paintChildren[i] == child)
			break;
	for (i++; i < pw->paint.paintChildrenSize; i++)
		pw->paint.paintChildren[i-1] = pw->paint.paintChildren[i];

	pw->paint.paintChildrenSize--;
	
	XtRemoveCallback((Widget)w, XtNfatBack, fatCallback, (XtPointer)child);
}

void PwAddChild(Widget w, Widget child)
{
	PaintWidget	pw = (PaintWidget)w;

	if (pw->paint.paintChildren == NULL)
		pw->paint.paintChildren = (Widget*)XtMalloc(sizeof(Widget) * (pw->paint.paintChildrenSize + 2));
	else
		pw->paint.paintChildren = (Widget*)XtRealloc((XtPointer)pw->paint.paintChildren, sizeof(Widget) * (pw->paint.paintChildrenSize + 2));
	pw->paint.paintChildren[pw->paint.paintChildrenSize++] = child;
	XtAddCallback(child, XtNdestroyCallback, (XtCallbackProc)removeChild, (XtPointer)w);
	XtAddCallback(w, XtNfatBack, fatCallback, (XtPointer)child);
}
