/* +-------------------------------------------------------------------+ */
/* | 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/StringDefs.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xaw/Viewport.h>
#include <X11/cursorfont.h>
#include <stdio.h>

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

#include "image.h"

#include "bitmaps/brushOp.xbm"
#include "bitmaps/brushOp.xpm"
#include "bitmaps/eraseOp.xbm"
#include "bitmaps/sprayOp.xbm"
#include "bitmaps/sprayOp.xpm"
#include "bitmaps/pencilOp.xbm"
#include "bitmaps/pencilOp.xpm"
#include "bitmaps/dotPenOp.xbm"
#include "bitmaps/dotPenOp.xpm"
#include "bitmaps/lineOp.xbm"
#include "bitmaps/arcOp.xbm"
#include "bitmaps/fillOp.xbm"
#include "bitmaps/fillOp.xpm"
#include "bitmaps/textOp.xbm"
#include "bitmaps/selectOp.xbm"
#include "bitmaps/boxOp.xbm"
#include "bitmaps/rayOp.xbm"
#include "bitmaps/fboxOp.xbm"
#include "bitmaps/ovalOp.xbm"
#include "bitmaps/fovalOp.xbm"
#include "bitmaps/lassoOp.xbm"
#include "bitmaps/clineOp.xbm"
#include "bitmaps/polyOp.xbm"
#include "bitmaps/fpolyOp.xbm"
#include "bitmaps/blobOp.xbm"
#include "bitmaps/fblobOp.xbm"
#include "bitmaps/selareaOp.xbm"

#include <xpm.h>

extern void AboutDialog(Widget);

#define	OP_BEGIN	0
#define	OP_END		1

#include "xpaint.h"
#include "misc.h"
#include "menu.h"
#include "Paint.h"
#include "text.h"
#include "ops.h"
static void changeLineAction(Widget, XEvent *);
static void changeSprayAction(Widget, XEvent *);
static void changeBrushAction(Widget, XEvent *);
static void changeChromaAction(Widget, XEvent *);
static void changeFontAction(Widget, XEvent *);

static void setOperation(Widget w, XtPointer opArg, XtPointer junk2) 
{
	Operation_t	*op = (Operation_t*)opArg;
	Operation_t	stop, start;

	if (op == CurrentOp)
		return;

	stop  = CurrentOp ? CurrentOp[OP_END]   : NULL;
	start = op        ? op       [OP_BEGIN] : NULL;

	GraphicSetOp((OperationRemoveProc)stop, (OperationAddProc)start);

	CurrentOp = op;
}

/*
**  The set line width callback pair
*/
static char	currentLineWidth[10] = "1";

static void setWidth(Widget w, void *width)
{
	XtVaSetValues(w, XtNlineWidth, (int)width, NULL);
}
static void okLineCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
	Arg		arg;
	TextPromptInfo	*info = (TextPromptInfo*)infoArg;
	int		width = atoi(info->prompts[0].rstr);

	if (width < 1 || width > 1000) {
		Notice(w, "Invalid width, must be greater than zero and less than 1000");
		return;
	}
	sprintf(currentLineWidth, "%d", width);

	MenuCheckItem(w, True);

	if (width == 1)
		width = 0;
	GraphicAll(setWidth, (void*)width);
	XtSetArg(arg, XtNlineWidth, width);
	OperationAddArg(arg);
}
static void lineWidth(Widget w, int width)
{
	String	lbl;

	XtVaGetValues(w, XtNlabel, &lbl, NULL);
	width = atoi(lbl);
	if (width <= 0) {
		static TextPromptInfo		info;
		static struct textPromptInfo	value[1];

		value[0].prompt = "Width:";
		value[0].str    = currentLineWidth;
		value[0].len    = 4;
		info.prompts = value;
		info.title   = "Enter desired line width";
		info.nprompt = 1;

		TextPrompt(w, "linewidth", &info, okLineCallback, NULL, NULL);
	} else {
		Arg	arg;

		if (width == 1)
			width = 0;
		MenuCheckItem(w, True);
		GraphicAll(setWidth, (void *)width);

		XtSetArg(arg, XtNlineWidth, width);
		OperationAddArg(arg);
	}
}
/*
**
*/
static char	sprayDensityStr[10] = "10";
static char	sprayRadiusStr[10] = "10";
static char	sprayRateStr[10] = "10";
static void sprayOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
	void		SpraySetParameters(int, int, int);
	TextPromptInfo	*info = (TextPromptInfo*)infoArg;
	int		radius  = atoi(info->prompts[0].rstr);
	int		density = atoi(info->prompts[1].rstr);
	int		rate    = atoi(info->prompts[2].rstr);

	if (radius < 1 || radius > 100) {
		Notice(w, "Radius should be between 1 and 100");
		return;
	}
	if (density < 1 || density > 500) {
		Notice(w, "Density should be between 1 and 500");
		return;
	}
	if (rate < 1 || rate > 500) {
		Notice(w, "Rate should be between 1 and 500");
		return;
	}

	SpraySetParameters(radius, density, rate);

	sprintf(sprayDensityStr,"%d",density);
	sprintf(sprayRadiusStr,"%d",radius);
	sprintf(sprayRateStr,"%d",rate);
}
static void sprayMenuCallback(Widget w)
{
	static TextPromptInfo		info;
	static struct textPromptInfo	value[3];

	value[0].prompt = "Radius:";
	value[0].str    = sprayRadiusStr;
	value[0].len    = 4;
	value[1].prompt = "Density:";
	value[1].str    = sprayDensityStr;
	value[1].len    = 4;
	value[2].prompt = "Rate:";
	value[2].str    = sprayRateStr;
	value[2].len    = 4;
	info.prompts = value;
	info.title   = "Enter the desired spray parameters";
	info.nprompt = 3;

	TextPrompt(w, "sprayparams", &info, sprayOkCallback, NULL, NULL);
}

/*
**  Font menu callbacks.
*/
static void fontSetCallback(Widget paint, void *info)
{
	XtVaSetValues(paint, XtNfont, (XFontStruct*)info, NULL);
	FontChanged(paint);
}
static void fontSet(Widget w, char *name)
{
	XFontStruct	*info;
	Arg		arg;

	if (name == NULL) {
		FontSelect(w, None);
	} else {
		if ((info = XLoadQueryFont(XtDisplay(GetShell(w)), name)) == NULL) {
			XtVaSetValues(w, XtNsensitive, False, NULL);
			Notice(w, "Unable to load requested font");
			return;
		}
		GraphicAll(fontSetCallback, (void*)info);
		XtSetArg(arg, XtNfont, info);
		OperationAddArg(arg);
	}
	MenuCheckItem(w, True);
}

/*
**  Exit callback (simple)
*/
static void exitOkCallback(Widget w, XtPointer junk, XtPointer junk2)
{
#if 0
	XtDestroyWidget(GetToplevel(w));
	XtDestroyApplicationContext(XtWidgetToApplicationContext(w));
	Global.timeToDie = True;
#else
	exit(0);
#endif
}
static void exitCancelCallback(Widget paint, XtPointer junk, XtPointer junk2)
{
}
static void exitPaintCheck(Widget paint, void *sumArg)
{
	int	*sum = (int *)sumArg;
	Boolean	flg;
	XtVaGetValues(paint, XtNdirty, &flg, NULL);
	*sum += flg ? 1 : 0;
}
static void exitPaint(Widget w, XtPointer junk, XtPointer junk2)
{
	int	total = 0;

	GraphicAll(exitPaintCheck, (void*)&total);

	if (total == 0) {
		exitOkCallback(w, NULL, NULL);
		return;
	}
	
	AlertBox(w, "There are unsaved changes,\nare you sure you wish to quit?",
				exitOkCallback, exitCancelCallback, NULL);
}

/*
**
*/
extern void ClipboardSetImage(Widget, void *);

static void loadClipboardCB(Widget w, char *file, void *image)
{
	ClipboardSetImage(w, image);
}
static void loadClipboard(Widget w, XtPointer junk, XtPointer junk2)
{
	GetFileName(GetShell(w), 0, NULL, (XtCallbackProc)loadClipboardCB, NULL);		
}


/*
**  Button popus 
*/
static void	erasePopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void	fillPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void	selectPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void	sprayPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void	ovalPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void	boxPopupCB(Widget w, XtPointer junk, XtPointer junk2);

#define GENERATE_HELP(name, hlpstr)				\
		static PaintMenuItem name [] = {		\
			MI_SEPERATOR(),				\
			MI_SIMPLECB("help", HelpDialog, hlpstr)	\
		};

static PaintMenuItem brushPopup[] = {
	MI_SEPERATOR(),
	MI_SIMPLECB("select", BrushSelect, NULL),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.brush"),
};
static PaintMenuItem erasePopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("original", MF_CHECKON, erasePopupCB, 0),
	MI_SIMPLECB("select", changeBrushAction, NULL),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.erase"),
};
static PaintMenuItem sprayPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("gauss", MF_CHECKON, sprayPopupCB, 0),
	MI_SIMPLECB("select", changeSprayAction, NULL),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.spray"),
};
GENERATE_HELP(pencilPopup, "toolbox.tools.pencil")
GENERATE_HELP(dotPencilPopup, "toolbox.tools.dotPencil")
GENERATE_HELP(linePopup, "toolbox.tools.line")
GENERATE_HELP(rayPopup, "toolbox.tools.ray")
GENERATE_HELP(arcPopup, "toolbox.tools.arc")
static PaintMenuItem fillPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("fill", MF_CHECKON|MF_GROUP1, fillPopupCB, 0),
	MI_FLAGCB("change", MF_CHECK|MF_GROUP1, fillPopupCB, 1),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.fill"),
};
static PaintMenuItem textPopup[] = {
	MI_SEPERATOR(),
	MI_SIMPLECB("select", changeFontAction, NULL),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.text"),
};
GENERATE_HELP(polyPopup, "toolbox.tools.poly")
GENERATE_HELP(freehandPopup, "toolbox.tools.blob")
GENERATE_HELP(clinePopup, "toolbox.tools.cline")
static PaintMenuItem selectBPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("shape", MF_CHECKON|MF_GROUP1, selectPopupCB, 0),
	MI_FLAGCB("not_color", MF_CHECK|MF_GROUP1, selectPopupCB, 1),
	MI_FLAGCB("only_color", MF_CHECK|MF_GROUP1, selectPopupCB, 2),
	MI_SIMPLECB("select", changeChromaAction, NULL),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.select"),
};
static PaintMenuItem selectAPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("shape", MF_CHECKON|MF_GROUP1, selectPopupCB, 0),
	MI_FLAGCB("not_color", MF_CHECK|MF_GROUP1, selectPopupCB, 1),
	MI_FLAGCB("only_color", MF_CHECK|MF_GROUP1, selectPopupCB, 2),
	MI_SIMPLECB("select", changeChromaAction, NULL),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.select"),
};
static PaintMenuItem boxPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("center", MF_CHECK, boxPopupCB, 0),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.box"),
};
static PaintMenuItem fboxPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("center", MF_CHECK, boxPopupCB, 0),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.box"),
};
static PaintMenuItem ovalPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("center", MF_CHECK, ovalPopupCB, 0),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.oval"),
};
static PaintMenuItem fovalPopup[] = {
	MI_SEPERATOR(),
	MI_FLAGCB("center", MF_CHECK, ovalPopupCB, 0),
	MI_SIMPLECB("help", HelpDialog, "toolbox.tools.oval"),
};
static void	ovalPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
	Boolean	m = !CircleGetStyle();

	CircleSetStyle(m);
	MenuCheckItem(ovalPopup[1].widget, m);
	MenuCheckItem(fovalPopup[1].widget, m);
}
static void	boxPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
	Boolean	m = !BoxGetStyle();

	BoxSetStyle(m);
	MenuCheckItem(boxPopup[1].widget, m);
	MenuCheckItem(fboxPopup[1].widget, m);
}
static void	fillPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
	int	nm = (int)junk;

	FillSetMode(nm);
	MenuCheckItem(fillPopup[nm + 1].widget, True);
}
static void	selectPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
	int	n = (int)junk;

	MenuCheckItem(selectAPopup[n + 1].widget, True);
	MenuCheckItem(selectBPopup[n + 1].widget, True);
	SelectSetCutMode(n);
}
void OperationSelectCallAcross(int n)
{
	MenuCheckItem(selectAPopup[n + 1].widget, True);
	MenuCheckItem(selectBPopup[n + 1].widget, True);
}
static void	sprayPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
	Boolean	m = !SprayGetStyle();

	SpraySetStyle(m);
	MenuCheckItem(w, m);
}
static void	erasePopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
	Boolean	m = !EraseGetMode();

	EraseSetMode(m);
	MenuCheckItem(w, m);
}

/*
**  Done with operation popup menus
*/

#define GENERATE_OP(name)				\
	extern void *CONCAT(name,Add)(Widget w,...),	\
		    *CONCAT(name,Remove)(Widget w,...);	\
	static Operation_t  CONCAT(name,Op)[] = {	\
		CONCAT(name,Add), CONCAT(name,Remove)	\
	};

GENERATE_OP(DotPencil)
GENERATE_OP(Pencil)
GENERATE_OP(Box)
GENERATE_OP(FBox)
GENERATE_OP(Line)
GENERATE_OP(Ray)
GENERATE_OP(Arc)
GENERATE_OP(Oval)
GENERATE_OP(FOval)
GENERATE_OP(Erase)
GENERATE_OP(Brush)
GENERATE_OP(Font)
GENERATE_OP(Smear)
GENERATE_OP(Spray)
GENERATE_OP(Poly)
GENERATE_OP(FPoly)
GENERATE_OP(Freehand)
GENERATE_OP(FFreehand)
GENERATE_OP(CLine)
GENERATE_OP(Fill)
GENERATE_OP(SelectBox)
GENERATE_OP(SelectArea)
#if 0
GENERATE_OP(SelectChroma)
#endif

#define	BITMAP(name)	\
	NULL, (char *)CONCAT(name,Op_bits) , CONCAT(name,Op_width), CONCAT(name,Op_height)
#define	XPMMAP(name)	\
	(char **)CONCAT(name,Op_xpm) , NULL , CONCAT(name,Op_width), CONCAT(name,Op_height)
#define MENU(name)	XtNumber(CONCAT(name,Popup)), CONCAT(name,Popup)

typedef struct {
	char		*name;
	char		**xpmmap;
	char		*bitmap;
	int		width, height;
	void		(*callback)();		/* By default setOperation() */
	void		*data;
	char		*translations;
	int		nitems;
	PaintMenuItem	*popupMenu;
	Pixmap		icon;
} IconListItem;

static Widget		iconListWidget;
static IconListItem	iconList[] = {
	{ "brush",	XPMMAP(brush), NULL, BrushOp, 
			"<BtnDown>(2): changeBrush()", MENU(brush), None },
	{ "erase",	BITMAP(erase), NULL, EraseOp, 
			"<BtnDown>(2): changeBrush()", MENU(erase), None },
	{ "selectBox",	BITMAP(select), NULL, SelectBoxOp, 
			"<BtnDown>(2): changeChroma()", MENU(selectB), None },
	{ "selectArea",	BITMAP(selarea), NULL, SelectAreaOp, 
			"<BtnDown>(2): changeChroma()", MENU(selectA), None },
	{ "pencil",	XPMMAP(pencil), NULL, PencilOp, 
			NULL, MENU(pencil), None },
	{ "dotPencil",	XPMMAP(dotPencil), NULL, DotPencilOp, 	
			NULL, MENU(dotPencil), None },
	{ "spray",	XPMMAP(spray), NULL, SprayOp, 
			"<BtnDown>(2): changeSpray()", MENU(spray), None },
	{ "fill",	XPMMAP(fill), NULL, FillOp, 
			NULL, MENU(fill), None },
	{ "text",	BITMAP(text), NULL, FontOp, 
			"<BtnDown>(2): changeFont()", MENU(text), None },
	{ "line",	BITMAP(line), NULL, LineOp, 
			NULL, MENU(line), None },
	{ "ray",	BITMAP(ray), NULL, RayOp, 
			NULL, MENU(ray), None },
	{ "arc",	BITMAP(arc), NULL, ArcOp, 
			NULL, MENU(arc), None },
	{ "box",	BITMAP(box), NULL, BoxOp, 
			NULL, MENU(box), None },
	{ "fBox",	BITMAP(fbox), NULL, FBoxOp, 
			NULL, MENU(fbox), None },
	{ "oval",	BITMAP(oval), NULL, OvalOp, 
			NULL, MENU(oval), None },
	{ "fOval",	BITMAP(foval), NULL, FOvalOp, 
			NULL, MENU(foval), None },
	{ "cLine",	BITMAP(cline), NULL, CLineOp, 
			NULL, MENU(cline), None },
	{ "polygon",	BITMAP(poly), NULL, PolyOp, 
			NULL, MENU(poly), None },
	{ "fPolygon",	BITMAP(fpoly), NULL, FPolyOp, 
			NULL, MENU(poly), None },
	{ "freehand",	BITMAP(freehand), NULL, FreehandOp, 
			NULL, MENU(freehand), None },
	{ "fFreehand",	BITMAP(ffreehand), NULL, FFreehandOp, 
			NULL, MENU(freehand), None },
};

void OperationSet(String names[], int num)
{
	IconListItem	*match = NULL;
	int		i, j;

	for (i = 0; i < XtNumber(iconList); i++) {
		for (j = 0; j < num; j++) {
			if (strcmp(names[j], iconList[i].name) == 0) {
				if (match == NULL)
					match = &iconList[i];
				if (CurrentOp == iconList[i].data)
					return;
			}
		}
	}
	if (match != NULL) 
		XawToggleSetCurrent(iconListWidget, (XtPointer)match->name);
}

static PaintMenuItem lineMenu[] = {
#if 0
	MI_FLAGCB("0", MF_CHECK|MF_GROUP1, lineWidth, NULL),
#endif
	MI_FLAGCB("1", MF_CHECK|MF_GROUP1, lineWidth, NULL),
	MI_FLAGCB("2", MF_CHECK|MF_GROUP1, lineWidth, NULL),
	MI_FLAGCB("4", MF_CHECK|MF_GROUP1, lineWidth, NULL),
	MI_FLAGCB("6", MF_CHECK|MF_GROUP1, lineWidth, NULL),
	MI_FLAGCB("8", MF_CHECK|MF_GROUP1, lineWidth, NULL),
#define LW_SELECT	5
	MI_FLAGCB("select", MF_CHECK|MF_GROUP1, lineWidth, NULL),
};

static PaintMenuItem	fontMenu[] = {
	MI_FLAGCB("Times 8", MF_CHECK|MF_GROUP1,
			fontSet, "-*-times-medium-r-normal-*-*-80-*-*-p-*-*-*"),
	MI_FLAGCB("Times 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-times-medium-r-normal-*-*-120-*-*-p-*-*-*"),
	MI_FLAGCB("Times 18", MF_CHECK|MF_GROUP1,
			fontSet, "-*-times-medium-r-normal-*-*-180-*-*-p-*-*-*"),
	MI_FLAGCB("Times Bold 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-times-bold-r-normal-*-*-120-*-*-p-*-*-*"),
	MI_FLAGCB("Times Italic 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-times-bold-i-normal-*-*-120-*-*-p-*-*-*"),
	MI_FLAGCB("Lucida 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-lucida-medium-r-normal-*-*-120-*-*-p-*-*-*"),
	MI_FLAGCB("Helvetica 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-helvetica-medium-r-normal-*-*-120-*-*-p-*-*-*"),
	MI_FLAGCB("Helvetica Bold 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-helvetica-bold-r-normal-*-*-120-*-*-p-*-*-*"),
	MI_FLAGCB("Fixed 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-fixed-medium-r-normal-*-*-120-*-*-m-*-*-*"),
	MI_FLAGCB("Courier 12", MF_CHECK|MF_GROUP1,
			fontSet, "-*-courier-medium-r-normal-*-*-120-*-*-m-*-*-*"),
	MI_SEPERATOR(),
#define FM_SELECT	11
	MI_FLAGCB("select", MF_CHECK|MF_GROUP1, fontSet, NULL),
};

static PaintMenuItem otherMenu[] = {
	MI_SIMPLECB("brushSelect", BrushSelect, NULL),
#define SP_SELECT	1
	MI_SIMPLECB("sprayEdit", sprayMenuCallback, NULL),
};

static PaintMenuItem fileMenu[] = {
	MI_SIMPLECB("new",       GraphicCreate, 0),
	MI_SIMPLECB("new-size",  GraphicCreate, 2),
	MI_SIMPLECB("open",      GraphicCreate, 1),
	MI_SIMPLECB("load-clip", loadClipboard, NULL),
	MI_SEPERATOR(),
	MI_SIMPLECB("quit", exitPaint, NULL),
};

static PaintMenuItem helpMenu[] = {
	MI_SIMPLECB("about", HelpDialog, "about"),
	MI_SEPERATOR(),
	MI_SIMPLECB("help", HelpDialog, "toolbox.toolbox"),
};

static PaintMenuBar   menuBar[] = {
	{ None, "file",  XtNumber(fileMenu), fileMenu },
#if 0
	{ None, "other",  XtNumber(otherMenu), otherMenu },
#endif
	{ None, "line",  XtNumber(lineMenu), lineMenu },
	{ None, "font",  XtNumber(fontMenu), fontMenu },
	{ None, "help",  XtNumber(helpMenu), helpMenu },
};
/*
**  Now for the callback functions
*/

static int	argListLen = 0;
static Arg	*argList;

void OperationSetPaint(Widget paint)
{
	XtSetValues(paint, argList, argListLen);
}

void OperationAddArg(Arg arg)
{
	int	i;

	for (i = 0; i < argListLen; i++) {
		if (strcmp(argList[i].name, arg.name) == 0) {
			argList[i].value = arg.value;
			return;
		}
	}

	if (argListLen == 0)
		argList = (Arg *)XtMalloc(sizeof(Arg) * 2);
	else
		argList = (Arg *)XtRealloc((XtPointer)argList, sizeof(Arg) * (argListLen + 2));

	argList[argListLen++] = arg;
}

/*
**  Double click action callback functions.
*/
static void changeLineAction(Widget w, XEvent *event)
{
	lineWidth(lineMenu[LW_SELECT].widget, (int)lineMenu[LW_SELECT].data);
}
static void changeSprayAction(Widget w, XEvent *event)
{
	sprayMenuCallback(w);
}
static void changeBrushAction(Widget w, XEvent *event)
{
	BrushSelect(w);
}
static void changeChromaAction(Widget w, XEvent *event)
{
	SelectChromaDialog(w);
}
static void changeFontAction(Widget w, XEvent *event)
{
	fontSet(fontMenu[FM_SELECT].widget, (char *)fontMenu[FM_SELECT].data);
}
	
/*
**  The real init function
*/
void OperationInit(Widget toplevel)
{
	static XtActionsRec	acts[] = {
					{ "changeLine", (XtActionProc)changeLineAction },
					{ "changeBrush", (XtActionProc)changeBrushAction },
					{ "changeFont", (XtActionProc)changeFontAction },
					{ "changeSpray", (XtActionProc)changeSprayAction },
					{ "changeChroma", (XtActionProc)changeChromaAction },
				};
	int		i;
	Pixmap		pix;
	Widget		vport, form, box, icon, bar, firstIcon = None;
	Pixel		fg, bg;
	char		*defTrans = "<BtnDown>,<BtnUp>: set() notify()\n";
	XtTranslations	trans = XtParseTranslationTable(defTrans);
	IconListItem	*cur;

	form = XtVaCreateManagedWidget("toolbox", 
			formWidgetClass, toplevel,
			XtNborderWidth, 0,
			NULL);
	XtAppAddActions(XtWidgetToApplicationContext(toplevel), acts, XtNumber(acts));
	/*
	**  Create the menu bar
	*/
	bar = MenuBarCreate(form, XtNumber(menuBar), menuBar);

	/*
	**  Create the operation icon list
	*/
	vport = XtVaCreateManagedWidget("vport", 
			viewportWidgetClass, form,
			XtNallowVert, True,
			XtNuseRight, True,
			XtNfromVert, bar,
			NULL);
	box = XtVaCreateManagedWidget("box", 
			boxWidgetClass, vport,
			XtNwidth, 32 * 8,
			XtNtop, XtChainTop,
			NULL);

	for (i = 0; i < XtNumber(iconList); i++) {
		cur = &iconList[i];

		icon = XtVaCreateManagedWidget(cur->name,
			toggleWidgetClass, box,
			XtNtranslations, trans,
			XtNradioGroup, firstIcon,
			XtNradioData, cur->name,
			NULL);

		if (cur->bitmap != NULL) {
			XtVaGetValues(icon, XtNforeground, &fg,
					    XtNbackground, &bg, 
					    NULL);

			bg = WhitePixelOfScreen(XtScreen(box));
			fg = BlackPixelOfScreen(XtScreen(box));

			pix = XCreatePixmapFromBitmapData(
					XtDisplay(box), 
					DefaultRootWindow(XtDisplay(box)),
					cur->bitmap,
					cur->width,
					cur->height,
					fg, bg,
					DefaultDepthOfScreen(XtScreen(box)));

		} else if (cur->xpmmap != NULL) {
			XpmCreatePixmapFromData(
					XtDisplay(box), 
					DefaultRootWindow(XtDisplay(box)),
					cur->xpmmap,
					&pix, NULL, NULL);
		} else {
			pix = None;
		}

		cur->icon = pix;

		if (pix != None)
			XtVaSetValues(icon, XtNbitmap, pix, NULL);
		if (cur->translations != NULL) {
			XtTranslations moreTrans;

			moreTrans = XtParseTranslationTable(cur->translations);
			XtAugmentTranslations(icon, moreTrans);
		}

		if (firstIcon == NULL) {
			XtVaSetValues(icon, XtNstate, True, NULL);
			setOperation(icon, cur->data, NULL);

			firstIcon = icon;
			iconListWidget = icon;
		}
		if (cur->callback != NULL)
			XtAddCallback(icon, XtNcallback, 
				cur->callback, cur->data);
		else
			XtAddCallback(icon, XtNcallback, 
				setOperation, cur->data);

#if 1
		if (cur->nitems != 0 && cur->popupMenu != NULL)
			MenuPopupCreate(icon, cur->nitems, cur->popupMenu);
#endif
	}

	lineWidth(lineMenu[0].widget, (int)lineMenu[0].data);
	fontSet(fontMenu[1].widget, fontMenu[1].data);

	AddDestroyCallback(toplevel, 
		(void (*)(Widget, void *, XEvent *))exitPaint, NULL);
}

Image *OperationIconImage(Widget w, char *name)
{
	Image	*img;
	int	i;

	for (i = 0; i < XtNumber(iconList); i++) 
		if (strcmp(name, iconList[i].name) == 0)
			break;
	if (i == XtNumber(iconList) || iconList[i].icon == None)
		return NULL;

	return PixmapToImage(w, iconList[i].icon, None);
}
