/* +-------------------------------------------------------------------+ */
/* | 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/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/Paned.h>
#ifdef HAVE_COLTOG
#include "ColToggle.h"
#endif
#include <stdio.h>
#include <math.h>

#ifndef M_PI
#define M_PI            3.14159265358979323846
#endif

#ifndef NOSTDHDRS
#include <stdlib.h>
#include <unistd.h>
#endif

#define LOOKUP_OUTSIDE

#include "Paint.h"

#include "xpaint.h"
#include "palette.h"
#include "misc.h"
#include "menu.h"
#include "text.h"
#include "image.h"
#include "cutCopyPaste.h"
#include "rc.h"

#define DEFAULT_TITLE	"Untitled"

#define PATTERN(name)      \
	(char *)CONCAT(name,_bits), CONCAT(name,_width), CONCAT(name,_height)

struct paintWindows {
	Widget			paint;
	struct paintWindows	*next;
	void			*ldata;
	Boolean			done;
};

typedef struct {
	Widget			paint;
	Palette			*map;
	Widget			primaryBox,  secondaryBox;
	Widget			primaryList, secondaryList;
	Widget			first, second;
} LocalInfo;

/*
**  Forward references
*/
static void 	makePaletteArea(LocalInfo*, RCInfo*);
static void	editPatternCB(Widget, Widget);
static void	deletePatternCB(Widget, Widget);

/*
**  Begin menus
*/

static PaintMenuItem	fileMenu[] = {
#define SAVE_ITEM 0
	MI_SIMPLE("save"),
#define SAVEAS_ITEM 1
	MI_SIMPLE("saveas"),
#define SAVER_ITEM 2
	MI_SIMPLE("saveregion"),
#define SAVE_CONFIG 3
	MI_SIMPLE("saveconfig"),
#define LOAD_CONFIG 4
	MI_SIMPLE("loadconfig"),
#define CLOSE_ITEM 5
	MI_SIMPLE("close"),
};

static PaintMenuItem	editMenu[] = {
#define UNDO_ITEM	0
	MI_SIMPLE("undo"),
	MI_SEPERATOR(),
#define CUT_ITEM	2
	MI_SIMPLE("cut"),
#define COPY_ITEM	3
	MI_SIMPLE("copy"),
#define PASTE_ITEM	4
	MI_SIMPLE("paste"),
#define CLEAR_ITEM	5
	MI_SIMPLE("clear"),
	MI_SEPERATOR(),
#define DUP_ITEM	7
	MI_SIMPLE("dup"),
#define SELECT_ALL_ITEM	8
	MI_SIMPLE("all"),
#if 0
	{ "lock",   NULL, NULL, MF_NONE, 0, NULL },
	{ "unlock", NULL, NULL, MF_NONE, 0, NULL },
#endif
};
static PaintMenuItem	rotateMenu[] = {
	MI_SEPERATOR(),
	MI_SIMPLE("rotate1"),
	MI_SIMPLE("rotate2"),
	MI_SIMPLE("rotate3"),
	MI_SIMPLE("rotate4"),
	MI_SIMPLE("rotate5"),
};

static PaintMenuItem	regionMenu[] = {
#define RG_FLIPX_ITEM	0
	MI_SIMPLE("flipX"),
#define RG_FLIPY_ITEM	1
	MI_SIMPLE("flipY"),
	MI_SEPERATOR(),
#define RG_ROTATETO	3
	MI_RIGHT("rotateTo", XtNumber(rotateMenu), rotateMenu),
#define RG_ROTATE	4
	MI_SIMPLE("rotate"),
	MI_SEPERATOR(),
#define RG_INVERT_ITEM	6
	MI_SIMPLE("invert"),
#define RG_SHARPEN_ITEM	7
	MI_SIMPLE("sharpen"),
#define RG_SMOOTH_ITEM	8
	MI_SIMPLE("smooth"),
#define RG_EDGE_ITEM	9
	MI_SIMPLE("edge"),
#define RG_EMBOSE_ITEM	10
	MI_SIMPLE("emboss"),
#define RG_OIL_ITEM	11
	MI_SIMPLE("oil"),
	MI_SEPERATOR(),
#if 0
#define RG_BOX	13
	MI_SIMPLE("nudge"),
	MI_SEPERATOR(),
#define RG_RESET	15
#endif
#define RG_RESET	13
	MI_SIMPLE("reset"),
};

static PaintMenuItem	otherMenu[] = {
#define	FAT_ITEM	0
	MI_SIMPLE("fatbits"),
#define	GRID_ITEM	1
	MI_FLAG("grid", MF_CHECK),
#define	SNAP_ITEM	2
	MI_FLAG("snap", MF_CHECK),
#define	CHSNAP_ITEM	3
	MI_SIMPLE("snapSpacing"),
	MI_SEPERATOR(),
#define EDIT_BACKGROUND	5
	MI_SIMPLE("background"),
	MI_SEPERATOR(),
#define	CHSIZE_ITEM	7
	MI_SIMPLE("size"),
#define	CHZOOM_ITEM	8
	MI_SIMPLE("zoom"),
};

static PaintMenuItem	helpMenu[] = {
	MI_SIMPLECB("help", HelpDialog, "canvas"),
};

static PaintMenuBar	menuBar[] = {
	{ None, "file",   XtNumber(fileMenu),   fileMenu },
	{ None, "edit",   XtNumber(editMenu),   editMenu },
	{ None, "region", XtNumber(regionMenu), regionMenu },
	{ None, "other",  XtNumber(otherMenu),  otherMenu },
	{ None, "help",   XtNumber(helpMenu),   helpMenu },
};

static PaintMenuItem	paletteMenu[] = {
	MI_SEPERATOR(),
	MI_SIMPLECB("edit", (PaintMenuCallback)editPatternCB, NULL),
	MI_SIMPLECB("remove", (PaintMenuCallback)deletePatternCB, NULL),
	MI_SEPERATOR(),
	MI_SIMPLECB("help", HelpDialog, "canvas.patBox"),
};

/*
**  End of menus
*/

static struct paintWindows	*head = NULL;

void GraphicRemove(Widget paint, XtPointer junk, XtPointer junk2)
{
	struct paintWindows	*cur = head, **prev = &head;

	while (cur != NULL && cur->paint != paint) {
		prev = &cur->next;
		cur = cur->next;
	}

	if (cur == NULL)
		return;

	*prev = cur->next;

	if (cur->done)
		CurrentOp[1](cur->paint, cur->ldata);
	XtFree((XtPointer)cur);
}

static void destroyCallback(Widget paint, XtPointer data, XtPointer junk2)
{
	LocalInfo		*info = (LocalInfo *)data;
	Colormap		cmap;
	Palette			*map;

	XtVaGetValues(paint, XtNcolormap, &cmap, NULL);

	if ((map = PaletteFind(paint, cmap)) != NULL) 
		PaletteDelete(map);
	XtFree((XtPointer)info);
}

static void realize(Widget paint, XtPointer ldataArg, XEvent *event, Boolean *junk)
{
	struct paintWindows	*cur = (struct paintWindows*)ldataArg;

	if (event->type == MapNotify) {
		XtRemoveEventHandler(paint, StructureNotifyMask, False, realize, ldataArg);
		if (CurrentOp != NULL && CurrentOp[0] != NULL)
			cur->ldata = CurrentOp[0](paint);
		cur->done = True;
	}
}
void GraphicAdd(Widget paint)
{
 	struct paintWindows	*new = XtNew(struct paintWindows);

	new->next  = head;
	head       = new;
	new->paint = paint;
	new->ldata = NULL;
	new->done  = False;

	if (XtIsRealized(paint)) {
		if (CurrentOp != NULL && CurrentOp[0] != NULL)
			new->ldata = CurrentOp[0](paint);
		new->done = True;
	} else {
		XtAddEventHandler(paint, StructureNotifyMask, False, realize, (XtPointer)new);
	}

	XtAddCallback(paint, XtNdestroyCallback, GraphicRemove, NULL);
}


void GraphicAll(void (*func)(), void *data)
{
	struct paintWindows	*cur;

	for (cur = head; cur != NULL; cur=cur->next) {
		if (!cur->done)
			continue;
		func(cur->paint, data);
	}
}
void GraphicSetOp(void (*stop)(Widget, void *), void *(*start)(Widget))
{
	struct paintWindows	*cur;

	for (cur = head; cur != NULL; cur=cur->next) {
		if (stop != NULL)
			stop(cur->paint, cur->ldata);
		if (start != NULL)
			cur->ldata = start(cur->paint);
	}
}
void *GraphicGetData(Widget w)
{
	struct paintWindows	*cur;

	for (cur = head; cur != NULL; cur=cur->next)
		if (cur->paint == w)
			return cur->ldata;
	return NULL;
}

/*
**  Pattern creation and chaning, utilitys.
*/
#define PAT_MIN_WH	24

typedef struct {
	Widget		paint;
	Widget		widget, widget2;
	int		width, height;
	Pixmap		pixmap, shownPixmap;
	Pixel		pixel;
	LocalInfo	*li;
} PatternInfo;

static void	patternUpdate(PatternInfo *info, Boolean isFirst)
{
	char		*w_fr, *w_fg, *w_pat;

	if (isFirst) {
		w_fr  = XtNfillRule;
		w_fg  = XtNforeground;
		w_pat = XtNpattern;
	} else {
		w_fr  = XtNlineFillRule;
		w_fg  = XtNlineForeground;
		w_pat = XtNlinePattern;
	}

	if (info->pixmap == None) {
		XtVaSetValues(info->paint, w_fg, info->pixel, 
					   w_fr, FillSolid, 
					   NULL);
	} else {
		XtVaSetValues(info->paint, w_pat, info->pixmap, 
					   w_fr, FillTiled, 
					   NULL);
	}
}

static void	setPatternCallback(Widget icon, PatternInfo *info, XtPointer junk2)
{
	Widget	rg;
	Boolean	state;

	XtVaGetValues(icon, XtNstate, &state, XtNradioGroup, &rg, NULL);

	if (state == False)
		return;

	if (rg == info->li->primaryList || info->li->primaryList == icon)
		patternUpdate(info, True);
	else
		patternUpdate(info, False);
}
static void 	freePatternInfo(Widget icon, PatternInfo *info)
{
	if (info->pixmap != info->shownPixmap)
		XFreePixmap(XtDisplay(icon), info->shownPixmap);
	if (info->pixmap != None)
		XFreePixmap(XtDisplay(icon), info->pixmap);

	XtFree((XtPointer)info);
}

static void installPatternPixmap(PatternInfo *info, Pixmap pix)
{
	XtPointer	curPrimary, curSecondary;

	curPrimary   = XawToggleGetCurrent(info->li->primaryList);
	curSecondary = XawToggleGetCurrent(info->li->secondaryList);

	if (curPrimary == (XtPointer)info)
		XawToggleUnsetCurrent(info->li->primaryList);
	if (curSecondary == (XtPointer)info)
		XawToggleUnsetCurrent(info->li->secondaryList);

	XtVaSetValues(info->widget, XtNbitmap, pix, NULL);
	XtVaSetValues(info->widget2, XtNbitmap, pix, NULL);

	if (curPrimary == (XtPointer)info)
		XawToggleSetCurrent(info->li->primaryList, curPrimary);
	if (curSecondary == (XtPointer)info)
		XawToggleSetCurrent(info->li->secondaryList, curSecondary);
}

static void editSolidOk(Widget paint, PatternInfo *info, XColor *col)
{
	LocalInfo	*l = info->li;

	StateShellBusy(paint, False);

	if (col == NULL)
		return;

	if (!PaletteSetPixel(l->map, info->pixel, col)) {
		/*
		**  This will fail for because we are on a static screen
		**   if it is a TrueColor screen change the "icon"
		*/
		if (!l->map->isMapped) {
			/*
			**  TrueColor
			*/
			Pixel	p = PaletteAlloc(l->map, col);
			Display	*dpy = XtDisplay(paint);
			GC	gc = XCreateGC(dpy, XtWindow(paint), 0, 0);
			Pixmap	pix;
			int	depth;

			XtVaGetValues(info->widget, XtNdepth, &depth, NULL);

			pix = XCreatePixmap(dpy, DefaultRootWindow(dpy), PAT_MIN_WH, PAT_MIN_WH, depth);
			XSetForeground(dpy, gc, p);
			XFillRectangle(dpy, pix, gc, 0, 0, PAT_MIN_WH, PAT_MIN_WH);

			info->pixel       = p;
			installPatternPixmap(info, pix);

			XFreePixmap(dpy, info->shownPixmap);
			info->shownPixmap = pix;

			XFreeGC(dpy, gc);
		}
	}
}

static void	editPatternAction(Widget w, XEvent *event)
{
	PatternInfo	*info;

	XtVaGetValues(w, XtNradioData, &info, NULL);

	if (info->pixmap == None) {
		LocalInfo	*l = (LocalInfo *)info->li;

		if (!l->map->readonly || !l->map->isMapped) {
			StateShellBusy(l->paint, True);

			ColorEditor(l->paint, info->pixel, l->map, True,
				(XtCallbackProc)editSolidOk, (XtPointer)info);
		}
	} else {
		void	PatternEdit(Widget, Pixmap, Widget);

		PatternEdit(info->paint, info->pixmap, w);
	}
}

static void	deletePatternCB(Widget mb, Widget w)
{
	LocalInfo	*li;
	PatternInfo	*info, *ainfo;
	Widget		parent;
	WidgetList	children;
	int		nchild;
	Boolean		setFirst = False;
	Boolean		actPrimary = False, actSecondary = False;

	XtVaGetValues(w, XtNradioData, &info, NULL);

	/*
	**  Check to see if it is active
	*/
	ainfo = (PatternInfo*)XawToggleGetCurrent(info->widget);
	if (info == ainfo) {
		XawToggleUnsetCurrent(info->widget);
		actPrimary = True;
	}
	ainfo = (PatternInfo*)XawToggleGetCurrent(info->widget2);
	if (info == ainfo) {
		XawToggleUnsetCurrent(info->widget2);
		actSecondary = True;
	}

	/*  
	**  Make sure there is enough widgets
	*/
	XtVaGetValues(parent = XtParent(info->widget), 
				XtNnumChildren, &nchild, 
				XtNchildren, &children, 
				NULL);
	if (nchild == 1) {
		Notice(w, "You must have at least one entry");
		return;
	}

	li = info->li;
	if (info->widget  == li->primaryList)
		setFirst = True;

	XtDestroyWidget(info->widget);
	XtDestroyWidget(info->widget2);

	if (setFirst) {
		XtVaGetValues(parent, XtNchildren, &children, NULL);
		if (children[0] == info->widget)
			li->primaryList = children[1];
		else
			li->primaryList = children[0];
		XtVaGetValues(li->primaryList, XtNradioData, &info, NULL);
		li->secondaryList = info->widget2;
	}
	if (actPrimary) {
		XtVaGetValues(li->primaryList, XtNradioData, &info, NULL);
		XawToggleSetCurrent(li->primaryList, (XtPointer)info);
	}
	if (actSecondary) {
		XtVaGetValues(li->secondaryList, XtNradioData, &info, NULL);
		XawToggleSetCurrent(li->secondaryList, (XtPointer)info);
	}
}

static void	editPatternCB(Widget mb, Widget w)
{
	editPatternAction(w, NULL);
}


/*
**   Special trick so that during creationg of the pattern box
**    initialization can happen
*/
static LocalInfo	*hiddenLocalInfo;

Widget AddPattern(Widget group, Widget paint, Pixmap pix, Pixel pxl)
{
	static XtTranslations	trans = None;
	static GC		gc = None;
	static Boolean		added = False;
	Widget			parent, iconA, iconB, box;
	PatternInfo		*info = XtNew(PatternInfo);
	Display			*dpy = XtDisplay(group);
	int			depth;
	char			*nm = "pattern";

	if (!added) {
		static XtActionsRec v = { "editPattern", (XtActionProc)editPatternAction };
		XtAppAddActions(XtWidgetToApplicationContext(paint), &v, 1);
		added = True;
	}

	if (trans == None)
		trans = XtParseTranslationTable("<BtnDown>,<BtnUp>: set() notify()\n<BtnDown>(2): editPattern()");

	if (XtClass(group) == toggleWidgetClass) {
		PatternInfo	*tpi;

		parent = XtParent(group);
		XtVaGetValues(group, XtNradioData, &tpi, NULL);
		info->li = tpi->li;
#ifdef HAVE_COLTOG
	} else if (XtClass(group) == colToggleWidgetClass) {
		PatternInfo	*tpi;

		parent = XtParent(group);
		XtVaGetValues(group, XtNradioData, &tpi, NULL);
		info->li = tpi->li;
#endif
	} else {
		parent = group;
		group  = None;
		info->li = hiddenLocalInfo;
	}

	box = parent;

	info->paint = paint;

	if (pix != None) {
		GetPixmapWHD(dpy, pix, &info->width, &info->height, &depth);
		info->pixmap    = pix;
		if (info->width == 1 && info->height == 1) {
			XImage	*xim = XGetImage(XtDisplay(paint), pix, 0, 0, 1, 1, AllPlanes, ZPixmap);
			info->pixel  = XGetPixel(xim, 0, 0);
			XDestroyImage(xim);

			info->pixmap = None;
			pix = None;
		}
	} else {
		info->pixel  = pxl;
		info->pixmap = None;
		XtVaGetValues(parent, XtNdepth, &depth, NULL);
	}
	if (info->pixmap == None)
		nm = "solid";

	if (info->pixmap == None) {
		/* XXX Getting a read only GC, and writing to it */
		if (gc == None) gc = XtGetGC(paint, 0, 0);

		pix = XCreatePixmap(dpy, DefaultRootWindow(dpy), PAT_MIN_WH, PAT_MIN_WH, depth);
		XSetForeground(dpy, gc, info->pixel);
		XFillRectangle(dpy, pix, gc, 0, 0, PAT_MIN_WH, PAT_MIN_WH);
	} else if (info->width < PAT_MIN_WH || info->height < PAT_MIN_WH) {
		int	nw = (info->width < PAT_MIN_WH)  ? PAT_MIN_WH : info->width;
		int	nh = (info->height < PAT_MIN_WH) ? PAT_MIN_WH : info->height;
		int	x, y;

		if (gc == None) gc = XtGetGC(paint, 0, 0);

		pix = XCreatePixmap(dpy, DefaultRootWindow(dpy), nw, nh, depth);

		for (y = 0; y < nh; y += info->height)
			for (x = 0; x < nw; x += info->width)
				XCopyArea(dpy, info->pixmap, pix, gc,
					0, 0, info->width, info->height, x, y);
	}

#ifdef HAVE_COLTOG
	if (info->pixmap == None) {
		iconA = XtVaCreateManagedWidget(nm, colToggleWidgetClass, 
					info->li->primaryBox,
					XtNforeground, info->pixel,
					XtNradioGroup, info->li->primaryList,
					XtNtranslations, trans,
					XtNradioData, info,
					XtNisSolid, True,
					NULL);
		iconB = XtVaCreateManagedWidget(nm, colToggleWidgetClass, 
					info->li->secondaryBox,
					XtNforeground, info->pixel,
					XtNradioGroup, info->li->secondaryList,
					XtNtranslations, trans,
					XtNradioData, info,
					XtNisSolid, True,
					NULL);
	} else {
#endif
		iconA = XtVaCreateManagedWidget(nm, toggleWidgetClass, 
					info->li->primaryBox,
					XtNbitmap, pix,
					XtNradioGroup, info->li->primaryList,
					XtNtranslations, trans,
					XtNradioData, info,
					NULL);
		iconB = XtVaCreateManagedWidget(nm, toggleWidgetClass, 
					info->li->secondaryBox,
					XtNbitmap, pix,
					XtNradioGroup, info->li->secondaryList,
					XtNtranslations, trans,
					XtNradioData, info,
					NULL);
#ifdef HAVE_COLTOG
	}
#endif

	if (info->li->primaryList == None)
		info->li->primaryList = iconA;
	if (info->li->secondaryList == None)
		info->li->secondaryList = iconB;
	
	XtAddCallback(iconA, XtNcallback, (XtCallbackProc)setPatternCallback, (XtPointer)info);
	XtAddCallback(iconB, XtNcallback, (XtCallbackProc)setPatternCallback, (XtPointer)info);

	paletteMenu[1].data = (void*)iconA;
	paletteMenu[2].data = (void*)iconA;
	MenuPopupCreate(iconA, XtNumber(paletteMenu), paletteMenu);
	paletteMenu[1].data = (void*)iconB;
	paletteMenu[2].data = (void*)iconB;
	MenuPopupCreate(iconB, XtNumber(paletteMenu), paletteMenu);

	info->widget  = iconA;
	info->widget2 = iconB;

	info->shownPixmap = pix;
	XtAddCallback(iconA, XtNdestroyCallback, (XtCallbackProc)freePatternInfo, (XtPointer)info);

	return iconA;
}
Widget	AddPatternInfo(void *piArg, Pixmap pix, Pixel pxl)
{
	PatternInfo	*pi = (PatternInfo *)piArg;

	return AddPattern(pi->widget, pi->paint, pix, pxl);
}
void	ChangePattern(void *iArg, Pixmap pix)
{
	PatternInfo	*info = (PatternInfo *)iArg;
	int		depth;
	Display		*dpy = XtDisplay(info->paint);
	Pixmap		pfree1 = None, pfree2 = None;

	GetPixmapWHD(dpy, pix, &info->width, &info->height, &depth);

	if (info->pixmap != info->shownPixmap)
		pfree1 = info->shownPixmap;
	if (info->pixmap != None)
		pfree2 = info->pixmap;
	
	info->pixmap = pix;

	if (info->width < PAT_MIN_WH || info->height < PAT_MIN_WH) {
		int	nw = (info->width < PAT_MIN_WH)  ? PAT_MIN_WH : info->width;
		int	nh = (info->height < PAT_MIN_WH) ? PAT_MIN_WH : info->height;
		int	x, y;
		GC	gc = XtGetGC(info->paint, 0, 0);

		pix = XCreatePixmap(dpy, DefaultRootWindow(dpy), nw, nh, depth);

		for (y = 0; y < nh; y += info->height)
			for (x = 0; x < nw; x += info->width)
				XCopyArea(dpy, info->pixmap, pix, gc,
					0, 0, info->width, info->height, x, y);
		XtReleaseGC(info->paint, gc);	
	}

	installPatternPixmap(info, pix);

	info->shownPixmap = pix;

	if (pfree1 != None)
		XFreePixmap(dpy, pfree1);
	if (pfree2 != None)
		XFreePixmap(dpy, pfree2);
}

/*
**  Save configuration 
*/
static void saveConfigOkCallback(Widget il, LocalInfo *info, char *file)
{
	Widget		parent = XtParent(info->primaryList);
	WidgetList	children;
	int		nchildren;
	int		i;
	FILE		*fd;
	Colormap	cmap;

	XtVaGetValues(parent, XtNchildren, &children, XtNnumChildren, &nchildren, NULL);
	XtVaGetValues(GetShell(parent), XtNcolormap, &cmap, NULL);

	if (nchildren == 0) {
		Notice(parent, "No information to save");
		return;
	}
	
	StateSetBusyWatch(True);

	if ((fd = fopen(file, "w")) == NULL) {
		Notice(parent, "Unable to open file '%s' for writing", file);
		return;
	}
	fprintf(fd, "reset\n\n");

	for (i = 0; i < nchildren; i++) {
		PatternInfo	*pi;

		pi = NULL;
		XtVaGetValues(children[i], XtNradioData, &pi, NULL);

		if (pi == NULL)
			continue;

		if (pi->pixmap == None) {
			XColor	col;
			int	r,g,b;

			col.pixel = pi->pixel;
			col.flags = DoRed|DoGreen|DoBlue;
			XQueryColor(XtDisplay(parent), cmap, &col);
			r = (col.red >> 8) & 0xff;
			g = (col.green >> 8) & 0xff;
			b = (col.blue >> 8) & 0xff;
			fprintf(fd, "solid #%02x%02x%02x\n",r,g,b);
		} else {
			extern	void	WriteAsciiPNMfd(FILE *, Image *);

			Image	*image;
			fprintf(fd, "pattern BeginData\n");
			image = PixmapToImage(info->paint, pi->pixmap, cmap);
			WriteAsciiPNMfd(fd, image);
			ImageDelete(image);
			fprintf(fd, "\nEndData\n");
		}
	}

	fclose(fd);

	StateSetBusyWatch(False);
}

static void saveConfigCallback(Widget w, LocalInfo *info, XtPointer junk)
{
	GetFileName(info->paint, 2, ".XPaintrc", 
			(XtCallbackProc)saveConfigOkCallback, (XtPointer)info);
}

static void loadConfigOkCallback(Widget il, LocalInfo *info, char *file)
{
	RCInfo		*rcInfo = ReadRC(file);

	if (rcInfo == NULL) {
		Notice(info->paint, "Unable to open file %s", file);
		return;
	}

	makePaletteArea(info, rcInfo);

	FreeRC(rcInfo);
}

static void loadConfigCallback(Widget w, LocalInfo *info, XtPointer junk)
{
	GetFileName(info->paint, 3, ".XPaintrc", 
			(XtCallbackProc)loadConfigOkCallback, (XtPointer)info);
}

/*
**  Pattern edit/add callback
*/
static void addSolidOk(Widget w, LocalInfo *info, XColor *col)
{
	StateShellBusy(w, False);

	if (col != NULL) {
		Pixel	pix   = PaletteAlloc(info->map, col);

		AddPattern(info->primaryList, info->paint, None, pix);
	}
}

static void addSolidCallback(Widget w, XtPointer wlArg, XtPointer junk2)
{
	LocalInfo	*info = (LocalInfo *)wlArg;
	Widget		paint = info->paint;
	Pixel		bg;

	StateShellBusy(paint, True);

	XtVaGetValues(paint, XtNbackground, &bg, NULL);

	ColorEditor(paint, bg, info->map, False, 
			(XtCallbackProc)addSolidOk, (XtPointer)info);
}

static void addPatternCallback(Widget w, XtPointer wlArg, XtPointer junk2)
{
	void	PatternEdit(Widget, Pixmap, Widget);
	LocalInfo	*info = (LocalInfo *)wlArg;

	PatternEdit(info->paint, None, info->primaryList);
}
static void lookupPatternCallback(Widget w, XtPointer infoArg, XtPointer junk2)
{
	LocalInfo	*info = (LocalInfo *)infoArg;
	int		nchildren, i;
	WidgetList	children;
	Widget		icon, list;
	Colormap	cmap;
	Pixel		p;
	PatternInfo	*pi;

	DoGrabPixel(w, &p, &cmap);

	if (cmap != info->map->cmap) {
		XColor	col;

		col.pixel = p;
		col.flags = DoRed|DoGreen|DoBlue;
		XQueryColor(XtDisplay(w), cmap, &col);
		if (!PaletteLookupColor(info->map, &col, &p)) 
			p = PaletteAlloc(info->map, &col);
	}

#ifndef LOOKUP_OUTSIDE
	if (XtParent(w) == info->primaryBox)
		list = info->primaryList;
	else 
		list = info->secondaryList;
#else
	if (XtNameToWidget(XtParent(w), "viewport2.patternRack") == info->primaryBox)
		list = info->primaryList;
	else 
		list = info->secondaryList;
#endif

	XtVaGetValues(XtParent(list), 
			XtNnumChildren, &nchildren,
			XtNchildren, &children,
			NULL);

	for (i = 0; i < nchildren; i++) {
		pi = NULL;
		XtVaGetValues(children[i], XtNradioData, &pi, NULL);
		if (pi == NULL || pi->pixmap != None)
			continue;

		if (pi->pixel == p) {
			XawToggleSetCurrent(list, (XtPointer)pi);
			return;
		}
	}

	icon = AddPattern(info->primaryList, info->paint, None, p);
	XtVaGetValues(icon, XtNradioData, &pi, NULL);
	XawToggleSetCurrent(list, (XtPointer)pi);
}

/*
**  Convert RC file information into the pattern box info
*/
static void makePaletteArea(LocalInfo *info, RCInfo *rcInfo)
{
	Widget		firstIcon, icon;
	Widget		pattern = info->secondaryBox;
	XColor		col, rgb;
	int		i, j;

	/*
	**  Allocate the color entries
	*/
	rcInfo->colorFlags  = (Boolean *)XtCalloc(sizeof(Boolean), 
							rcInfo->ncolors);
	rcInfo->colorPixels = (Pixel   *)XtCalloc(sizeof(Pixel),   
							rcInfo->ncolors);
	for (i = 0; i < rcInfo->ncolors; i++)
		rcInfo->colorFlags[i] = False;

	for (i = 0; i < rcInfo->ncolors; i++) {
		if (XLookupColor(XtDisplay(info->paint), info->map->cmap, 
				rcInfo->colors[i], &col, &rgb) || 
		    XParseColor(XtDisplay(info->paint), info->map->cmap, 
				rcInfo->colors[i], &col)) {
			rcInfo->colorPixels[i] = PaletteAlloc(info->map, &col);
			rcInfo->colorFlags[i]  = True;
			for (j = 0; j < i; j++)
				if (rcInfo->colorPixels[i] == rcInfo->colorPixels[j])
					rcInfo->colorFlags[i]  = False;
		}
	}

	/*
	**  Sneeky pass to AddPattern()
	*/
	hiddenLocalInfo = info;

	firstIcon = pattern;

	for (i = 0; i < rcInfo->ncolors; i++) {
		if (!rcInfo->colorFlags[i])
			continue;

		icon = AddPattern(firstIcon, info->paint, None, rcInfo->colorPixels[i]);

		if (firstIcon == pattern) 
			firstIcon = icon;
	}

	for (i = 0; i < rcInfo->nimages; i++) {
		Pixmap			pix;

		rcInfo->images[i]->refCount++;
		pix = None;
		ImageToPixmapCmap(rcInfo->images[i], info->paint, &pix, info->map->cmap);

		icon = AddPattern(firstIcon, info->paint, pix, 0);

		if (firstIcon == pattern) 
			firstIcon = icon;
	}
}

/*
**  First level menu callbacks.
*/

static void closeOkCallback(Widget shell, XtPointer junk, XtPointer junk2)
{
	XtDestroyWidget(shell);
}
static void closeCancelCallback(Widget shell, XtPointer junk, XtPointer junk2)
{
}

static void closeCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget	paint = (Widget)paintArg;
	Boolean	flg;

	XtVaGetValues(paint, XtNdirty, &flg, NULL);
	if (flg)
		AlertBox(GetShell(paint), "Unsaved changes!\nDo you really wish to close?", 
				closeOkCallback, closeCancelCallback, NULL);
	else
		XtDestroyWidget(GetShell(paint));
}

/*
**
*/
static void fatCallback(Widget w, XtPointer paint, XtPointer junk2)
{
	void	FatbitsEdit(Widget);

	FatbitsEdit((Widget)paint);
}
static void gridCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget	paint = (Widget)paintArg;
	Boolean	v;

	XtVaGetValues(paint, XtNgrid, &v, NULL);
	v = !v;
	XtVaSetValues(paint, XtNgrid, v, NULL);

	MenuCheckItem(w, v);
}
static void snapCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget	paint = (Widget)paintArg;
	Boolean	v;

	XtVaGetValues(paint, XtNsnapOn, &v, NULL);
	v = !v;
	XtVaSetValues(paint, XtNsnapOn, v, NULL);

	MenuCheckItem(w, v);
}
/*
**  
*/
static int  snapSpacing = 10;
static void changeSnapOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
	TextPromptInfo	*info = (TextPromptInfo*)infoArg;
	int		v = atoi(info->prompts[0].rstr);

	if (v < 1 || v > 100) {
		Notice(paint,"Bad snap spacing.\nShould be between 2 and 100");
		return;
	}
		
	snapSpacing = v;
	XtVaSetValues(paint, XtNsnap, v, NULL);
}
static void changeSnapCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget paint = (Widget)paintArg;
	static TextPromptInfo		info;
	static struct textPromptInfo	value[1];
	static char	buf[10];

	sprintf(buf, "%d", snapSpacing);

	value[0].prompt = "Spacing:";
	value[0].str    = buf;
	value[0].len    = 4;
	info.prompts = value;
	info.title   = "Enter desired snap spacing";
	info.nprompt = 1;

	TextPrompt(paint, "linewidth", &info, changeSnapOkCallback, NULL, NULL);
}
static void sizeCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget	paint = (Widget)paintArg;

	SizeSelect(GetShell(paint), paint, NULL);
}
/*
**
*/
static void zoomAddChild(Widget paint, int zoom)
{
	Cardinal	nc;
	Widget		t, box = XtParent(paint);
	WidgetList	children;
	int		dw, dh;

	/*
	**  1 child == just paint widget
	**  2 children paint widget + normal size view
	*/
	XtVaGetValues(box, XtNchildren, &children, XtNnumChildren, &nc, NULL);
	XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);
	if (nc == 1 && zoom > 1 && dw < 256 && dh < 256) {
		/*
		** Add child
		*/
		t = XtVaCreateManagedWidget("norm", paintWidgetClass, box,
				XtNpaint, paint,
				XtNzoom, 1,
				NULL);
		GraphicAdd(t);
	} else if (nc != 1 && zoom <= 1) {
		/*
		** Remove child
		*/
		t = children[(children[0] == paint) ? 1 : 0];
		XtDestroyWidget(t);
	}
}

static void zoomOkCallback(Widget w, XtPointer paintArg, XtPointer infoArg)
{
        Widget          paint = (Widget)paintArg;
        TextPromptInfo  *info = (TextPromptInfo*)infoArg;
        int             zoom = atoi(info->prompts[0].rstr);

        if (zoom <= 0) {
                Notice(paint, "Invalid zoom");
        } else {
                XtVaSetValues(paint, XtNzoom, zoom, NULL);
		zoomAddChild(paint, zoom);
	}
}
void zoomCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
        static TextPromptInfo           info;
        static struct textPromptInfo    values[2];
        char	                  	buf[80];
        int				zoom;
	Widget				paint = (Widget)paintArg;

        info.nprompt = 1;
        info.prompts = values;
        info.title = "Change zoom factor for image";
        values[0].prompt = "Zoom: ";
        values[0].len    = 4;
        values[0].str    = buf;

        XtVaGetValues(paint, XtNzoom, &zoom, NULL);
        sprintf(buf, "%d", (int)zoom);

        TextPrompt(GetShell(paint), "zoomselect", &info, zoomOkCallback, NULL, paint);
}

/*
**
*/
static void rotateTo(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;
	float		t = M_PI / 4.0;
	pwMatrix	m;
	String		lbl;

	XtVaGetValues(w, XtNlabel, &lbl, NULL);
	t = atof(lbl);
	if (t == 0.0)
		return;

	t *= M_PI / 180.0;

	m[0][0] =  cos(t);
	m[0][1] = -sin(t);
	m[1][0] =  sin(t);
	m[1][1] =  cos(t);
	PwRegionAppendMatrix(paint, m);
}
static void rotateOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
	TextPromptInfo	*info = (TextPromptInfo*)infoArg;
	float		t = atof(info->prompts[0].rstr) * M_PI / 180.0;
	pwMatrix	m;

	m[0][0] =  cos(t);
	m[0][1] = -sin(t);
	m[1][0] =  sin(t);
	m[1][1] =  cos(t);
	PwRegionAppendMatrix(paint, m);
}
static void rotate(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget paint = (Widget)paintArg;
	static TextPromptInfo		info;
	static struct textPromptInfo	value[1];
	static char	buf[10];

	sprintf(buf, "%d", snapSpacing);

	value[0].prompt = "Angle (in Degrees):";
	value[0].str    = buf;
	value[0].len    = 4;
	info.prompts = value;
	info.title   = "Enter desired rotation";
	info.nprompt = 1;

	TextPrompt(paint, "linewidth", &info, rotateOkCallback, NULL, NULL);
}
static void resetMat(Widget w, XtPointer paintArg, XtPointer junk2)
{
	pwMatrix	m;

	m[0][0] = m[1][1] = 1;
	m[1][0] = m[0][1] = 0;

	PwRegionReset((Widget)paintArg, True);
}
static void nudgeBox(Widget w, XtPointer paintArg, XtPointer junk2)
{
	/* RegionBox((Widget)paintArg); */
}
static void prCallback(Widget paint, Widget w, Boolean flag)
{
	XtVaSetValues(w, XtNsensitive, flag, NULL);
}

/*
**  Background changer
*/
static void changeBgOk(Widget w, Palette *map, XColor *col)
{
	StateShellBusy(w, False);

	if (col != NULL) {
		Pixel	pix   = PaletteAlloc(map, col);
		XtVaSetValues(w, XtNbackground, pix, NULL);
	}
}

static void changeBackground(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;
	Colormap	cmap;
	Pixel		bg;
	Palette		*map;

	StateShellBusy(paint, True);

	XtVaGetValues(GetShell(paint), XtNcolormap, &cmap, NULL);
	XtVaGetValues(paint, XtNbackground, &bg, NULL);
	map = PaletteFind(paint, cmap);

	ColorEditor(paint, bg, map, 
			False, (XtCallbackProc)changeBgOk, (XtPointer)map);
}

/*
**  Start of graphic window creation routines
*/

Widget makeGraphicShell(Widget wid)
{
	Arg		args[20];
	int		nargs = 0;
	Widget		shell;

	XtSetArg(args[nargs], XtNtitle, DEFAULT_TITLE);		nargs++;
	XtSetArg(args[nargs], XtNiconName, DEFAULT_TITLE);	nargs++;

	shell = XtAppCreateShell("Canvas", "Canvas", 
			topLevelShellWidgetClass, XtDisplay(GetToplevel(wid)),
			args, nargs);

	return shell;
}

static Widget mkPatternArea(Widget parent, Widget left, char *name, Widget *pform, LocalInfo *info)
{
	Widget	form, label, pattern, vport, lookup;

	form = XtVaCreateManagedWidget("patternRackForm", 
			formWidgetClass, parent,
			XtNborderWidth, 0,
			XtNfromHoriz, left,
			XtNleft, XtChainLeft,
			XtNright, XtChainLeft,
			NULL);
	if (pform != NULL)
		*pform = form;
	label = XtVaCreateManagedWidget(name, labelWidgetClass, form,
			XtNborderWidth, 0,
			NULL);
#ifdef LOOKUP_OUTSIDE
	lookup = XtVaCreateManagedWidget("lookup",
				commandWidgetClass, form,
				XtNfromHoriz, label,
				NULL);
#else
	lookup = label;
#endif

	vport = XtVaCreateManagedWidget("viewport2",
			viewportWidgetClass, form,
			XtNallowVert, True,
			XtNuseBottom, True,
			XtNuseRight, True,
			XtNfromVert, lookup,
			NULL);
	pattern = XtVaCreateWidget("patternRack",
			boxWidgetClass, vport,
			NULL);
#ifndef LOOKUP_OUTSIDE
	lookup = XtVaCreateManagedWidget("lookup",
				commandWidgetClass, pattern,
				NULL);
#endif

	XtAddCallback(lookup, XtNcallback, lookupPatternCallback, (XtPointer)info);

	return pattern;
}

Widget graphicCreate(Widget shell, int width, int height, int zoom, Pixmap pix, Colormap cmap)
{
	Widget			form, viewport, pattern, bar;
	Widget			pane, pane1, pform;
	Widget			paint;
	Widget			icon, firstIcon;
	Widget			add, edit;
	WidgetList		child;
	int			nchild;
	RCInfo			*rcInfo;
	Palette			*map;
	int			i, j;
	int			depth;
	LocalInfo		*info = XtNew(LocalInfo);
	PatternInfo		*pData;

	/*
	**  Menu Bar
	*/

	if (cmap == None) {
		map = PaletteCreate(shell);
		cmap = map->cmap;
	} else {
		map = PaletteFind(shell, cmap);
	}
	depth = map->depth;
	XtVaSetValues(shell, XtNcolormap, cmap, NULL);

	PaletteAddUser(map, shell);

	info->map = map;

	pane = XtVaCreateManagedWidget("pane", 
			panedWidgetClass, shell,
			NULL);

	rcInfo = ReadDefaultRC();

	/*
	**  Menu area
	*/
	form = XtVaCreateManagedWidget("form1", 
			formWidgetClass, pane,
			NULL);
	bar = MenuBarCreate(form, XtNumber(menuBar), menuBar);
	XtVaSetValues(form, XtNshowGrip, False, NULL);

	/*
	**  Drawing Area
	*/
	viewport = XtVaCreateWidget("viewport",
			viewportWidgetClass, pane,
			XtNfromVert, bar,
			XtNallowVert, True,
			XtNallowHoriz, True,
			XtNuseBottom, True,
			XtNuseRight, True,
			XtNtop, XtChainTop,
			NULL);

	/*
	**  Custom Drawing Widget here
	**   First read the file and convert to a bitmap
	*/
	pane1 = XtVaCreateWidget("paintBox", 
			boxWidgetClass, viewport,
			XtNbackgroundPixmap, GetBackgroundPixmap(viewport),
			NULL);

	/*
	**  Try and do something nice for the user
	*/
	if (zoom == -1 && width <= 64 && height <= 64) {
		int	wth = width;
		int	hth = height;
		if (pix != None) 
			GetPixmapWHD(XtDisplay(pane1), pix, &wth, &hth, NULL);
		if (wth <= 64 && hth <= 64 && wth != 0 && hth != 0)
			zoom = 6;
	}

	paint = XtVaCreateManagedWidget("paint",
			paintWidgetClass, pane1,
			XtNdrawWidth, width,
			XtNdrawHeight, height,
			XtNzoom, zoom,
			XtNpixmap, pix,
			XtNcolormap, cmap,
			XtNallowResize, True,
			XtNshowGrip, False,
			NULL);
	XtSetKeyboardFocus(pane, paint);
	zoomAddChild(paint, zoom);
	info->paint = paint;
	OperationSetPaint(paint);
	ccpAddStdPopup(paint);

	XtManageChild(pane1);
	XtManageChild(viewport);

	XtAddCallback(fileMenu[SAVE_CONFIG].widget,
			XtNcallback, (XtCallbackProc)saveConfigCallback, (XtPointer)info);
	XtAddCallback(fileMenu[LOAD_CONFIG].widget,
			XtNcallback, (XtCallbackProc)loadConfigCallback, (XtPointer)info);

	ccpAddUndo(editMenu[UNDO_ITEM].widget, paint);
	ccpAddCut(editMenu[CUT_ITEM].widget, paint);
	ccpAddCopy(editMenu[COPY_ITEM].widget, paint);
	ccpAddPaste(editMenu[PASTE_ITEM].widget, paint);
	ccpAddClear(editMenu[CLEAR_ITEM].widget, paint);
	ccpAddDuplicate(editMenu[DUP_ITEM].widget, paint);

	XtAddCallback(editMenu[SELECT_ALL_ITEM].widget,
			XtNcallback, StdSelectAllCallback, (XtPointer)paint);
	XtAddCallback(fileMenu[SAVEAS_ITEM].widget,
			XtNcallback, StdSaveAsFile, (XtPointer)paint);
	XtAddCallback(fileMenu[SAVE_ITEM].widget,
			XtNcallback, StdSaveFile, (XtPointer)paint);
	
	ccpAddSaveRegion(fileMenu[SAVER_ITEM].widget, paint);

	XtAddCallback(fileMenu[CLOSE_ITEM].widget,
			XtNcallback, closeCallback, (XtPointer)paint);
	XtAddCallback(otherMenu[GRID_ITEM].widget,
			XtNcallback, gridCallback, (XtPointer)paint);
	XtAddCallback(otherMenu[SNAP_ITEM].widget,
			XtNcallback, snapCallback, (XtPointer)paint);
	XtAddCallback(otherMenu[CHSNAP_ITEM].widget,
			XtNcallback, changeSnapCallback, (XtPointer)paint);
	XtAddCallback(otherMenu[FAT_ITEM].widget,
			XtNcallback, fatCallback, (XtPointer)paint);
	XtAddCallback(otherMenu[CHSIZE_ITEM].widget,
			XtNcallback, sizeCallback, (XtPointer)paint);
	XtAddCallback(otherMenu[CHZOOM_ITEM].widget,
			XtNcallback, zoomCallback, (XtPointer)paint);
	XtAddCallback(otherMenu[EDIT_BACKGROUND].widget,
			XtNcallback, changeBackground, (XtPointer)paint);

	ccpAddRegionFlipX(regionMenu[RG_FLIPX_ITEM].widget, paint);
	ccpAddRegionFlipY(regionMenu[RG_FLIPY_ITEM].widget, paint);
	ccpAddRegionInvert(regionMenu[RG_INVERT_ITEM].widget, paint);
	ccpAddRegionSharpen(regionMenu[RG_SHARPEN_ITEM].widget, paint);
	ccpAddRegionSmooth(regionMenu[RG_SMOOTH_ITEM].widget, paint);
	ccpAddRegionEdge(regionMenu[RG_EDGE_ITEM].widget, paint);
	ccpAddRegionEmbose(regionMenu[RG_EMBOSE_ITEM].widget, paint);
	ccpAddRegionOilPaint(regionMenu[RG_OIL_ITEM].widget, paint);

	for (i = 0; i < XtNumber(rotateMenu); i++) {
		if (rotateMenu[i].name[0] == '\0')
			continue;

		XtAddCallback(rotateMenu[i].widget, XtNcallback, 
					(XtCallbackProc)rotateTo, (XtPointer)paint);
		XtAddCallback(paint, XtNregionCallback, (XtCallbackProc)prCallback, 
				(XtPointer)rotateMenu[i].widget);
		prCallback(paint, rotateMenu[i].widget, False);
	}
	
	XtAddCallback(regionMenu[RG_ROTATE].widget, 
			XtNcallback, (XtCallbackProc)rotate, (XtPointer)paint);
	XtAddCallback(paint, XtNregionCallback, (XtCallbackProc)prCallback, 
			(XtPointer)regionMenu[RG_ROTATE].widget);
	prCallback(paint, regionMenu[RG_ROTATE].widget, False);
	XtAddCallback(regionMenu[RG_RESET].widget, 
			XtNcallback, (XtCallbackProc)resetMat, (XtPointer)paint);

#ifdef RG_BOX
	XtAddCallback(regionMenu[RG_BOX].widget, 
			XtNcallback, (XtCallbackProc)nudgeBox, (XtPointer)paint);
#endif
	XtAddCallback(paint, XtNregionCallback, (XtCallbackProc)prCallback, 
			(XtPointer)regionMenu[RG_RESET].widget);
	prCallback(paint, regionMenu[RG_RESET].widget, False);


	form = XtVaCreateManagedWidget("form2", 
			formWidgetClass, pane,
			XtNskipAdjust, True,
			NULL);

	pattern = mkPatternArea(form, None, "primary", &pform, info);
	info->primaryBox = pattern;

	pattern = mkPatternArea(form, pform, "secondary", &pform, info);
	info->secondaryBox = pattern;

	info->primaryList   = None;
	info->secondaryList = None;

	/*
	**  Now construct the paletter area and set the
	**    primary and secondary selections correctly
	*/

	makePaletteArea(info, rcInfo);

	XtVaGetValues(info->primaryBox, XtNchildren, &child, 
					XtNnumChildren, &nchild,
					NULL);
	XtVaGetValues(child[0], XtNradioData, &pData, NULL);
	XawToggleSetCurrent(info->primaryList, (XtPointer)pData);

	XtVaGetValues(info->secondaryBox, XtNchildren, &child, 
					XtNnumChildren, &nchild,
					NULL);
	if (nchild == 1)
		XtVaGetValues(child[0], XtNradioData, &pData, NULL);
	else
		XtVaGetValues(child[1], XtNradioData, &pData, NULL);
	XawToggleSetCurrent(info->secondaryList, (XtPointer)pData);

	XtManageChild(info->primaryBox);
	XtManageChild(info->secondaryBox);

	/*
	**  A few buttons for help...
	*/

	add  = XtVaCreateManagedWidget("addPattern",
			commandWidgetClass, form,
			XtNfromHoriz, pform,
			XtNfromVert,  None,
			XtNleft, XtChainRight,
			XtNright, XtChainRight,
			XtNtop, XtChainTop,
			XtNbottom, XtChainTop,
			NULL);
	/*
	**  If we are on a small static colormap, we shouldn't be able to do an add. XXX
	*/
	edit = XtVaCreateManagedWidget("addSolid",
			commandWidgetClass, form,
			XtNfromHoriz, pform,
			XtNfromVert,  add,
			XtNleft, XtChainRight,
			XtNright, XtChainRight,
			XtNtop, XtChainTop,
			XtNbottom, XtChainTop,
			NULL);

	if (edit != None)
		XtAddCallback(edit, XtNcallback, addSolidCallback, (XtPointer)info);
	XtAddCallback(add , XtNcallback, addPatternCallback, (XtPointer)info);
	XtAddCallback(paint, XtNdestroyCallback, destroyCallback, (XtPointer)info);

	AddDestroyCallback(shell, 
		(void (*)(Widget, void *, XEvent *))closeCallback,(void*)paint);

	SetIconImage(shell, False);

	XtRealizeWidget(shell);

	GraphicAdd(paint);

	return shell;
}

typedef struct cwi_s {
	Widget	paint;	
	void	*id;
	struct cwi_s	*next;
} CanvasWriteInfo;

static CanvasWriteInfo	*cwiHead = NULL;

void removeCWI(Widget w, CanvasWriteInfo *ci, XtPointer junk)
{
	CanvasWriteInfo	*cur = cwiHead, **pp = &cwiHead;

	while (cur != NULL && cur != ci) {
		pp = &cur->next;
		cur = cur->next;
	}	

	if (cur == NULL)
		return;
	*pp = cur->next;
	XtFree((XtPointer)ci);
}

void *GraphicGetReaderId(Widget paint)
{
	CanvasWriteInfo	*cur;

	paint = GetShell(paint);

	for (cur = cwiHead; cur != NULL && cur->paint != paint; cur = cur->next)
		;

	if (cur == NULL)
		return NULL;

	return cur->id;
}

void GraphicOpenFile(Widget w, XtPointer fileArg, XtPointer imageArg)
{
	char		*file = (char *)fileArg;
	Image		*image = (Image *)imageArg;
	Colormap	cmap;
	Pixmap		pix;
	Widget		shell = makeGraphicShell(w);
	CanvasWriteInfo	*ci = XtNew(CanvasWriteInfo);

	ci->next  = cwiHead;
	cwiHead   = ci;
	ci->paint = shell;
	ci->id    = GetFileNameGetLastId();

	XtAddCallback(shell, XtNdestroyCallback, (XtCallbackProc)removeCWI, (XtPointer)ci);

	if (ImageToPixmap(image, shell, &pix, &cmap)) {
		/*
		**   If mask != None, set the mask region color to the BG color of the Canvas
		*/
		if ((shell = graphicCreate(shell, 0, 0, -1, pix, cmap)) != None) {
			char	*cp = strrchr(file, '/');
			if (cp == NULL)
				cp = file;
			else
				cp++;

			XtVaSetValues(shell, XtNiconName, cp, XtNtitle, file, NULL);
		} else {
			XtDestroyWidget(shell);
		}
	} else {
		Notice(w, "Unable to create paint window with image");
		XtDestroyWidget(shell);
	}
}

static void doCreate(Widget wid, int width, int height, int zoom)
{
	graphicCreate(makeGraphicShell(wid), width, height, zoom, None, None);
}

void GraphicCreate(Widget wid, int value)
{
	int	width, height;
	
	switch (value) {
	case 0:
		GetDefaultWH(&width, &height);
		graphicCreate(makeGraphicShell(wid), width, height, -1, None, None);
		break;
	case 1:
		GetFileName(GetToplevel(wid), 0, NULL, GraphicOpenFile, NULL);
		break;
	case 2:
		SizeSelect(wid, None, doCreate);
		break;
	}
}
