/* +-------------------------------------------------------------------+ */
/* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)	       | */
/* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk)  | */
/* |								       | */
/* | 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.					       | */
/* |								       | */
/* +-------------------------------------------------------------------+ */

/* $Id: graphic.c,v 1.26 2000/10/02 20:55:46 torsten Exp $ */

#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/Xatom.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>
#include <xpm.h>
#ifdef HAVE_COLTOG
#include "ColToggle.h"
#endif
#include <stdio.h>
#include <math.h>
#include <limits.h>

#ifndef M_PI
#define M_PI		3.14159265358979323846
#endif

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

/*
 * Undefine this if you want the Lookup button to be
 * inside the palette area (not recommended)
 */
#define LOOKUP_OUTSIDE

#include "Paint.h"
#include "PaintP.h"

#include "xpaint.h"
#include "palette.h"
#include "misc.h"
#include "menu.h"
#include "messages.h"
#include "text.h"
#include "graphic.h"
#include "image.h"
#include "region.h"
#include "operation.h"
#include "rc.h"
#include "protocol.h"
#include "color.h"
#include "ops.h"

#include "rw/rwTable.h"

#include "bitmaps/xpm/eye.xpm"
#include "bitmaps/xpm/fill0.xpm"
#include "bitmaps/xpm/fill1.xpm"
#include "bitmaps/xpm/fill2.xpm"

extern Boolean * setDrawable(Widget w, Drawable d, XImage * s);
extern void fill(int x, int y, int width, int height);

enum {W_ICON_PALETTE=0,
      W_ICON_COLORS,
      W_ICON_COLPIXMAP,
      W_ICON_TOOL,
      W_ICON_BRUSH,
      W_ICON_FONT,
      W_INFO_DATA,
      W_FILE_REVERT,
      W_EDIT_PASTE,
      W_EDIT_RECALL,
      W_EDIT_REMOVE,
      W_REGION_LAST, 
      W_REGION_UNDO, 
      W_SELECTOR_GRID,
      W_SELECTOR_SNAP,
      W_LINE_WIDTHS, 
      W_FONT_DESCR = W_LINE_WIDTHS+7,
      W_TOPMENU = W_FONT_DESCR + 11 - W_FILE_REVERT,
      W_NWIDGETS = 2*W_TOPMENU + W_FILE_REVERT + 1
};

static Pixmap ImgProcessPix;
static Colormap ImgProcessCmap;
static int ImgProcessFlag;

static char *fontNames[] = {
"-*-times-medium-r-normal-*-*-80-*-*-p-*-*-*", /* Times 8 */
"-*-times-medium-r-normal-*-*-120-*-*-p-*-*-*", /* Times 12 */
"-*-times-medium-r-normal-*-*-180-*-*-p-*-*-*", /* Times 18 */
"-*-times-bold-r-normal-*-*-120-*-*-p-*-*-*", /* Times Bold 12 */
"-*-times-bold-i-normal-*-*-120-*-*-p-*-*-*", /* Times Bold Italic 12 */
"-*-lucida-medium-r-normal-*-*-120-*-*-p-*-*-*", /* Lucida 12 */
"-*-helvetica-medium-r-normal-*-*-120-*-*-p-*-*-*", /* Helvetica 12 */
"-*-helvetica-bold-r-normal-*-*-120-*-*-p-*-*-*", /* Helvetica Bold 12 */
"-*-fixed-medium-r-normal-*-*-120-*-*-m-*-*-*", /* Fixed 12 */
"-*-courier-medium-r-normal-*-*-120-*-*-m-*-*-*" /* Courier 12 */
};

static char dashStyleStr[36] = "=";

/* forward references */
static void fontSet(Widget w, void *ptr);

/* Global data */
struct imageprocessinfo ImgProcessInfo =
{
    7,				/* oilArea		*/
    20,				/* noiseDelta		*/
    2,				/* spreadDistance	*/
    4,				/* pixelizeXSize	*/
    4,				/* pixelizeYSize	*/
    3,				/* despeckleMask	*/
    3,				/* smoothMaskSize	*/
    20,				/* tilt			*/
    50,				/* solarizeThreshold	*/
    99,				/* contrastW		*/
    2,				/* contrastB		*/
    0,				/* redshift		*/
    0,				/* greenshift		*/
    0,				/* blueshift		*/
    16,				/* quantizeColors	*/
    0, 0, 0, 0,                 /* tilt values          */
    1.0, 0.0, 0.0, 1.0,         /* matrix for linear transform */
    NULL                        /* background color pointer */
};

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

typedef struct {
    Widget paint, viewport, form, paintbox, position, memory;
    Pixmap palette_pixmap, mem_pixmap;
    Palette *map;
    RCInfo *rcInfo;
    Pixel *pixels;
    Pixmap *patterns;
    int npatterns, npixels;
    int boolcount, channel, steps, boolpos, mem_index, mem_shift;
} LocalInfo;

static Boolean selectionOwner = False;

static Image *(*lastFilter) (Image *);

/*
 *  Begin menus
 */

static PaintMenuItem fileMenu[] =
{
#define FILE_SAVE 0
    MI_SIMPLE("save"),
#define FILE_SAVEAS 1
    MI_SIMPLE("saveas"),
#define FILE_SAVE_REGION 2
    MI_SIMPLE("saveregion"),
#define FILE_LOAD_MEMORY 3
    MI_SIMPLE("load-mem"),
#define FILE_REVERT 4
    MI_SIMPLE("revert"),
#define FILE_PRINT 5
    MI_SIMPLE("print"),
#define FILE_EXTERN 6
    MI_SIMPLE("extern"),
#define FILE_CLOSE 7
    MI_SIMPLE("close"),
};

static PaintMenuItem editMenu[] =
{
#define EDIT_UNDO	0
    MI_SIMPLE("undo"),
#define EDIT_REDO	1
    MI_SIMPLE("redo"),
#define EDIT_UNDO_SIZE	2
    MI_SIMPLE("undosize"),
    MI_SEPARATOR(),     /* 3 */
#define EDIT_REFRESH	4
    MI_SIMPLE("refresh"),
    MI_SEPARATOR(),     /* 5 */
#define EDIT_CUT	6
    MI_SIMPLE("cut"),
#define EDIT_COPY	7
    MI_SIMPLE("copy"),
#define EDIT_PASTE	8
    MI_SIMPLE("paste"),
#define EDIT_CLEAR	9
    MI_SIMPLE("clear"),
    MI_SEPARATOR(),     /* 10 */
#define EDIT_DUP	11
    MI_SIMPLE("dup"),
#define EDIT_SELECT_ALL	12
    MI_SIMPLE("all"),
#define EDIT_ERASE_ALL	13
    MI_SIMPLE("erase"),
    MI_SEPARATOR(),     /* 14 */
#define EDIT_SNAPSHOT	15
    MI_SIMPLE("snapshot"),
    MI_SEPARATOR(),     /* 16 */
#define EDIT_MEMORY	17
    MI_SIMPLE("memory"),
#define EDIT_RECALL	18
    MI_SIMPLE("recall"),
#define EDIT_REMOVE	19
    MI_SIMPLE("remove"),
};

static PaintMenuItem lineMenu[] =
{
    MI_FLAGCB("1", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("2", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("4", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("6", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("8", MF_CHECK | MF_GROUP3, lineWidth, NULL),
#define LINE_WIDTH_GENERAL 5
    MI_FLAGCB("select", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("dashstyle", changeDashStyleAction, NULL),
};

static PaintMenuItem fontMenu[] =
{
    MI_FLAGCB("Times 8", MF_CHECK | MF_GROUP1, fontSet, 1),
    MI_FLAGCB("Times 12", MF_CHECK | MF_GROUP1, fontSet, 2),
    MI_FLAGCB("Times 18", MF_CHECK | MF_GROUP1, fontSet, 3),
    MI_FLAGCB("Times Bold 12", MF_CHECK | MF_GROUP1, fontSet, 4),
    MI_FLAGCB("Times Italic 12", MF_CHECK | MF_GROUP1, fontSet, 5),
    MI_FLAGCB("Lucida 12", MF_CHECK | MF_GROUP1, fontSet, 6),
    MI_FLAGCB("Helvetica 12", MF_CHECK | MF_GROUP1, fontSet, 7),
    MI_FLAGCB("Helvetica Bold 12", MF_CHECK | MF_GROUP1, fontSet, 8),
    MI_FLAGCB("Fixed 12", MF_CHECK | MF_GROUP1, fontSet, 9),
    MI_FLAGCB("Courier 12", MF_CHECK | MF_GROUP1, fontSet, 10),
    MI_SEPARATOR(),
    MI_FLAGCB("select", MF_CHECK | MF_GROUP1, fontSet, NULL),
};

static PaintMenuItem rotateMenu[] =
{
    MI_SEPARATOR(),
    MI_SIMPLE("rotate1"),
    MI_SIMPLE("rotate2"),
    MI_SIMPLE("rotate3"),
    MI_SIMPLE("rotate4"),
    MI_SIMPLE("rotate5"),
};

static PaintMenuItem regionMenu[] =
{
#define REGION_FLIPX	0
    MI_SIMPLE("flipX"),
#define REGION_FLIPY	1
    MI_SIMPLE("flipY"),
#define REGION_ROTATETO	2
    MI_RIGHT("rotateTo", XtNumber(rotateMenu), rotateMenu),
#define REGION_ROTATE	3
    MI_SIMPLE("rotate"),
#define REGION_LINEAR	4
    MI_SIMPLE("linear"),
#define REGION_RESET	5
    MI_SIMPLE("reset"),
    MI_SEPARATOR(),	/* 6 */
#define REGION_CLONE    7
    MI_SIMPLE("clone"),
#define REGION_CROP	8
    MI_SIMPLE("crop"),
    MI_SEPARATOR(),	/* 9 */
#define	REGION_AUTOCROP	10
    MI_SIMPLE("autocrop"),
#define REGION_DELIMIT	11
    MI_SIMPLE("delimit"),
#define REGION_COMPLEMENT	12
    MI_SIMPLE("complement"),
    MI_SEPARATOR(),		/* 13 */
#define REGION_UNSELECT	14
    MI_SIMPLE("unselect"),
};

static PaintMenuItem filterMenu[] =
{
#define FILTER_INVERT	0
    MI_SIMPLE("invert"),
#define FILTER_SHARPEN	1
    MI_SIMPLE("sharpen"),
  /* remove noise */
    MI_SEPARATOR(),		/* 2 */
#define FILTER_SMOOTH	3
    MI_SIMPLE("smooth"),
#define FILTER_DIRFILT 4
    MI_SIMPLE("dirfilt"),
#define FILTER_DESPECKLE 5
    MI_SIMPLE("despeckle"),
    MI_SEPARATOR(),		/* 6 */
#define FILTER_EDGE	7
    MI_SIMPLE("edge"),
#define FILTER_EMBOSS	8
    MI_SIMPLE("emboss"),
#define FILTER_OIL	9
    MI_SIMPLE("oil"),
#define FILTER_NOISE	10
    MI_SIMPLE("noise"),
#define FILTER_SPREAD	11
    MI_SIMPLE("spread"),
#define FILTER_PIXELIZE  12
    MI_SIMPLE("pixelize"),
  /* special fx */
#define FILTER_TILT	13
    MI_SIMPLE("tilt"),
#define FILTER_BLEND	14
    MI_SIMPLE("blend"),
#define FILTER_SOLARIZE 15
    MI_SIMPLE("solarize"),
  /* colour manipulation */
    MI_SEPARATOR(),		/* 16 */
#define FILTER_TOGREY 17
    MI_SIMPLE("togrey"),
#define FILTER_CONTRAST 18
    MI_SIMPLE("contrast"),
#define FILTER_MODIFY_RGB 19
    MI_SIMPLE("modify_rgb"),
#define FILTER_QUANTIZE 20
    MI_SIMPLE("quantize"),
    MI_SEPARATOR(),		/* 21 */
#define FILTER_USERDEF 22
    MI_SIMPLE("userdef"),
    MI_SEPARATOR(),		/* 23 */
#define FILTER_LAST	24
    MI_SIMPLE("last"),
#define FILTER_UNDO	25
    MI_SIMPLE("undo"),
};

static PaintMenuItem selectorMenu[] =
{
#define	SELECTOR_FATBITS	0
    MI_SIMPLE("fatbits"),
#define	SELECTOR_PATTERNS	1
    MI_SIMPLE("patterns"),
#define	SELECTOR_CHROMA	2
    MI_SIMPLE("chroma"),
#define SELECTOR_BACKGROUND	3
    MI_SIMPLE("background"),
#define	SELECTOR_TOOLS		4
    MI_SIMPLE("tools"),
#define	SELECTOR_BRUSH		5
    MI_SIMPLE("brush"),
#define	SELECTOR_FONT		6
    MI_SIMPLE("font"),
#define	SELECTOR_SCRIPT		7
    MI_SIMPLE("c_script"),
    MI_SEPARATOR(),             /* 8 */
#define	SELECTOR_CHANGE_SIZE	9
    MI_SIMPLE("size"),
#define	SELECTOR_CHANGE_ZOOM		10
    MI_SIMPLE("zoom"),
#define	SELECTOR_SIZE_ZOOM_DEFS	11
    MI_SIMPLE("size_zoom_defs"),
#define	SELECTOR_CHANGE_SNAP		12
    MI_SIMPLE("snapSpacing"),
#define	SELECTOR_SNAP		13
    MI_FLAG("snap", MF_CHECK),
#define	SELECTOR_GRID		14
    MI_FLAG("grid", MF_CHECK),
#define	SELECTOR_SIMPLE		15
    MI_FLAG("simple", MF_CHECK),
#define	SELECTOR_HIDE_MENUBAR	16
    MI_FLAG("hide_menubar", MF_CHECK),
    MI_SEPARATOR(),		/* 17 */
#define	SELECTOR_HELP		18
    MI_SIMPLECB("help", HelpDialog, "introduction"),
};

static PaintMenuBar menuBar[] =
{
    {None, "file", XtNumber(fileMenu), fileMenu},
    {None, "edit", XtNumber(editMenu), editMenu},
    {None, "line", XtNumber(lineMenu), lineMenu},
    {None, "font", XtNumber(fontMenu), fontMenu},
    {None, "region", XtNumber(regionMenu), regionMenu},
    {None, "filter", XtNumber(filterMenu), filterMenu},
    {None, "selector", XtNumber(selectorMenu), selectorMenu},
};

/*
**  Standard popup menu
 */
/*
static PaintMenuItem popupCanvasMenu[] =
{
    MI_SEPARATOR(),
    MI_SIMPLECB("new", GraphicCreate, 0),
    MI_SIMPLECB("new-size", GraphicCreate, 2),
    MI_SIMPLECB("open", GraphicCreate, 1),
    MI_SIMPLECB("snapshot", takeSnapshot, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("quit", exitPaint, NULL),
};
*/

static PaintMenuItem popupFileMenu[] =
{
    MI_SEPARATOR(),
#define P_FILE_SAVE 1
    MI_SIMPLE("save"),
#define P_FILE_SAVEAS 2
    MI_SIMPLE("saveas"),
#define P_FILE_SAVE_REGION 3
    MI_SIMPLE("saveregion"),
#define P_FILE_LOAD_MEMORY 4
    MI_SIMPLE("load-mem"),
#define P_FILE_REVERT 5
    MI_SIMPLE("revert"),
#define P_FILE_PRINT 6
    MI_SIMPLE("print"),
#define P_FILE_EXTERN 7
    MI_SIMPLE("extern"),
#define P_FILE_CLOSE 8
    MI_SIMPLE("close"),
};

static PaintMenuItem popupEditMenu[] =
{
    MI_SEPARATOR(),     /* 0 */
#define P_EDIT_UNDO	1
    MI_SIMPLE("undo"),
#define P_EDIT_REDO	2
    MI_SIMPLE("redo"),
#define P_EDIT_UNDO_SIZE	        3
    MI_SIMPLE("undosize"),
    MI_SEPARATOR(),             /* 4 */
#define P_EDIT_REFRESH	5
    MI_SIMPLE("refresh"),
    MI_SEPARATOR(),  /* 6 */
#define P_EDIT_CUT	7
    MI_SIMPLE("cut"),
#define P_EDIT_COPY	8
    MI_SIMPLE("copy"),
#define P_EDIT_PASTE	9
    MI_SIMPLE("paste"),
#define P_EDIT_CLEAR	10
    MI_SIMPLE("clear"),
    MI_SEPARATOR(),  /* 11 */
#define P_EDIT_DUP	12
    MI_SIMPLE("dup"),
#define P_EDIT_SELECT_ALL 13
    MI_SIMPLE("all"),
#define P_EDIT_ERASE_ALL	14
    MI_SIMPLE("erase"),
    MI_SEPARATOR(),  /* 15 */
#define P_EDIT_SNAPSHOT	16
    MI_SIMPLE("snapshot"),
    MI_SEPARATOR(),  /* 17 */
#define P_EDIT_MEMORY	18
    MI_SIMPLE("memory"),
#define P_EDIT_RECALL	19
    MI_SIMPLE("recall"),
#define P_EDIT_REMOVE	20
    MI_SIMPLE("remove")
};

static PaintMenuItem popupLineMenu[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("1", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("2", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("4", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("6", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("8", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_FLAGCB("select", MF_CHECK | MF_GROUP3, lineWidth, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("dashstyle", changeDashStyleAction, NULL),
};

static PaintMenuItem popupFontMenu[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("Times 8", MF_CHECK | MF_GROUP1, fontSet, 1),
    MI_FLAGCB("Times 12", MF_CHECK | MF_GROUP1, fontSet, 2),
    MI_FLAGCB("Times 18", MF_CHECK | MF_GROUP1, fontSet, 3),
    MI_FLAGCB("Times Bold 12", MF_CHECK | MF_GROUP1, fontSet, 4),
    MI_FLAGCB("Times Italic 12", MF_CHECK | MF_GROUP1, fontSet, 5),
    MI_FLAGCB("Lucida 12", MF_CHECK | MF_GROUP1, fontSet, 6),
    MI_FLAGCB("Helvetica 12", MF_CHECK | MF_GROUP1, fontSet, 7),
    MI_FLAGCB("Helvetica Bold 12", MF_CHECK | MF_GROUP1, fontSet, 8),
    MI_FLAGCB("Fixed 12", MF_CHECK | MF_GROUP1, fontSet, 9),
    MI_FLAGCB("Courier 12", MF_CHECK | MF_GROUP1, fontSet, 10),
    MI_SEPARATOR(),
    MI_FLAGCB("select", MF_CHECK | MF_GROUP1, fontSet, NULL),
};

static PaintMenuItem popupRotateMenu[] =
{
    MI_SEPARATOR(),
    MI_SIMPLE("rotate1"),
    MI_SIMPLE("rotate2"),
    MI_SIMPLE("rotate3"),
    MI_SIMPLE("rotate4"),
    MI_SIMPLE("rotate5"),
};

static PaintMenuItem popupRegionMenu[] =
{
    MI_SEPARATOR(),	/* 0 */
#define P_REGION_FLIPX	1
    MI_SIMPLE("flipX"),
#define P_REGION_FLIPY	2
    MI_SIMPLE("flipY"),
#define P_REGION_ROTATETO	3
    MI_RIGHT("rotateTo", XtNumber(popupRotateMenu), popupRotateMenu),
#define P_REGION_ROTATE	4
    MI_SIMPLE("rotate"),
#define P_REGION_LINEAR	5
    MI_SIMPLE("linear"),
#define P_REGION_RESET	6
    MI_SIMPLE("reset"),
    MI_SEPARATOR(),		/* 7 */
#define P_REGION_CLONE		8
    MI_SIMPLE("clone"),
#define P_REGION_CROP		9
    MI_SIMPLE("crop"),
    MI_SEPARATOR(),		/* 10 */
#define	P_REGION_AUTOCROP	11
    MI_SIMPLE("autocrop"),
#define P_REGION_DELIMIT	12
    MI_SIMPLE("delimit"),
#define P_REGION_COMPLEMENT	13
    MI_SIMPLE("complement"),
    MI_SEPARATOR(),		/* 14 */
#define P_REGION_UNSELECT	15
    MI_SIMPLE("unselect"),
};

static PaintMenuItem popupFilterMenu[] =
{
    MI_SEPARATOR(),             /* 0 */
#define P_FILTER_INVERT	1
    MI_SIMPLE("invert"),
#define P_FILTER_SHARPEN	2
    MI_SIMPLE("sharpen"),
  /* remove noise */
    MI_SEPARATOR(),		/* 3 */
#define P_FILTER_SMOOTH	4
    MI_SIMPLE("smooth"),
#define P_FILTER_DIRFILT 5
    MI_SIMPLE("dirfilt"),
#define P_FILTER_DESPECKLE 6
    MI_SIMPLE("despeckle"),
    MI_SEPARATOR(),		/* 7 */
#define P_FILTER_EDGE	8
    MI_SIMPLE("edge"),
#define P_FILTER_EMBOSS	9
    MI_SIMPLE("emboss"),
#define P_FILTER_OIL	10
    MI_SIMPLE("oil"),
#define P_FILTER_NOISE	11
    MI_SIMPLE("noise"),
#define P_FILTER_SPREAD	12
    MI_SIMPLE("spread"),
#define P_FILTER_PIXELIZE  13
    MI_SIMPLE("pixelize"),
  /* special fx */
#define P_FILTER_TILT	14
    MI_SIMPLE("tilt"),
#define P_FILTER_BLEND	15
    MI_SIMPLE("blend"),
#define P_FILTER_SOLARIZE  16
    MI_SIMPLE("solarize"),
  /* colour manipulation */
    MI_SEPARATOR(),		/* 17 */
#define P_FILTER_TOGREY   18
    MI_SIMPLE("togrey"),
#define P_FILTER_CONTRAST 19
    MI_SIMPLE("contrast"),
#define P_FILTER_MODIFY_RGB   20
    MI_SIMPLE("modify_rgb"),
#define P_FILTER_QUANTIZE 21
    MI_SIMPLE("quantize"),
    MI_SEPARATOR(),		/* 22 */
#define P_FILTER_USERDEF 23
    MI_SIMPLE("userdef"),
    MI_SEPARATOR(),		/* 24 */
#define P_FILTER_LAST	25
    MI_SIMPLE("last"),
#define P_FILTER_UNDO	26
    MI_SIMPLE("undo"),
};

static PaintMenuItem popupSelectorMenu[] =
{
    MI_SEPARATOR(),             /* 0 */
#define	P_SELECTOR_FATBITS	1
    MI_SIMPLE("fatbits"),
#define	P_SELECTOR_PATTERNS	2
    MI_SIMPLE("patterns"),
#define	P_SELECTOR_CHROMA	3
    MI_SIMPLE("chroma"),
#define P_SELECTOR_BACKGROUND	4
    MI_SIMPLE("background"),
#define	P_SELECTOR_TOOLS	5
    MI_SIMPLE("tools"),
#define	P_SELECTOR_BRUSH	6
    MI_SIMPLE("brush"),
#define	P_SELECTOR_FONT		7
    MI_SIMPLE("font"),
#define	P_SELECTOR_SCRIPT	8
    MI_SIMPLE("c_script"),
    MI_SEPARATOR(),             /* 9 */
#define	P_SELECTOR_CHANGE_SIZE	10
    MI_SIMPLE("size"),
#define	P_SELECTOR_CHANGE_ZOOM	11
    MI_SIMPLE("zoom"),
#define	P_SELECTOR_SIZE_ZOOM_DEFS	12
    MI_SIMPLE("size_zoom_defs"),
#define	P_SELECTOR_CHANGE_SNAP	13
    MI_SIMPLE("snapSpacing"),
#define	P_SELECTOR_SNAP	14
    MI_FLAG("snap", MF_CHECK),
#define	P_SELECTOR_GRID		15
    MI_FLAG("grid", MF_CHECK),
    MI_SEPARATOR(),             /* 16 */
#define	P_SELECTOR_HIDE_MENUBAR	17
    MI_SIMPLE("hide_menubar"),
#define	P_SELECTOR_SHOW_MENUBAR	18
    MI_SIMPLE("show_menubar"),
    MI_SEPARATOR(),             /* 19 */
#define P_SELECTOR_HELP		20
    MI_SIMPLECB("help", HelpDialog, "introduction"),
};

static PaintMenuItem popupMenu[] =
{
    MI_SEPARATOR(),
    MI_RIGHT("File", XtNumber(popupFileMenu), popupFileMenu),
    MI_RIGHT("Edit", XtNumber(popupEditMenu), popupEditMenu),
    MI_RIGHT("Line", XtNumber(popupLineMenu), popupLineMenu),
    MI_RIGHT("Font", XtNumber(popupFontMenu), popupFontMenu),
    MI_RIGHT("Region", XtNumber(popupRegionMenu), popupRegionMenu),
    MI_RIGHT("Filters", XtNumber(popupFilterMenu), popupFilterMenu),
    MI_RIGHT("Selectors", XtNumber(popupSelectorMenu), popupSelectorMenu),
};

/*
**  This really should be a "local" or malloced variable
 */
typedef struct {
    Pixmap pixmap;
    int count;
    int width, height, depth;
} selectInfo;

/*
 *  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->remove(cur->paint, cur->ldata);
    XtFree((XtPointer) cur);
}

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->add != NULL)
	    cur->ldata = CurrentOp->add(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->add != NULL)
	    new->ldata = CurrentOp->add(paint);
	new->done = True;
    } else
	XtAddEventHandler(paint, StructureNotifyMask, False,
			  realize, (XtPointer) new);

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


void
GraphicAll(GraphicAllProc 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;
}

/*
 *  First level menu callbacks.
 */

static void
closeOkCallback(Widget shell, XtPointer paintArg, XtPointer junk2)
{
    LocalInfo * info = (LocalInfo *) paintArg;
    Display *dpy = XtDisplay(info->paint);
    Pixmap pix;
    WidgetList wlist;
    int i, rule;

    if (info->palette_pixmap) XFreePixmap(dpy, info->palette_pixmap);
    if (info->mem_pixmap) XFreePixmap(dpy, info->mem_pixmap);
    for (i=0; i<info->npatterns; i++) XFreePixmap(dpy, info->patterns[i]);
    free(info->pixels);
    free(info->patterns);
    XtVaGetValues(info->paint, XtNmenuwidgets, &wlist, NULL);
    if (wlist) {
        if (wlist[W_ICON_COLPIXMAP]) {
	    XFreePixmap(dpy, (Pixmap) wlist[W_ICON_COLPIXMAP]);
        wlist[W_ICON_COLPIXMAP] = 0;
	}
    }
    XtVaGetValues(info->paint, 
		  XtNfillRule, &rule, XtNpattern, &pix, NULL);
    if (rule==FillTiled && pix) XFreePixmap(dpy, pix);
    XtVaGetValues(info->paint, 
		  XtNlineFillRule, &rule, XtNlinePattern, &pix, NULL);
    if (rule==FillTiled && pix) XFreePixmap(dpy, pix);

    checkPatternLink(info->paint, 0);
    checkExternalLink(info->paint);
    if (Global.canvas == shell) {
	XtVaSetValues(Global.back, XtNsensitive, False, NULL);
        Global.canvas = None;
    }
    XtDestroyWidget(shell);
}

static void
genericCancelCallback(Widget shell, XtPointer junk, XtPointer junk2)
{
}

static void
closeCallback(Widget w, XtPointer wlArg, XtPointer junk2)
{
    LocalInfo *info = (LocalInfo *) wlArg;
    Widget shell = GetShell(info->paint);
    Boolean flg;
    XtVaGetValues(info->paint, XtNdirty, &flg, NULL);

    if (flg)
	AlertBox(shell, msgText[UNSAVED_CHANGES_WISH_TO_CLOSE],
		 closeOkCallback, genericCancelCallback, info);
    else
        closeOkCallback(shell, info, NULL);
}

/*
 * Reload the image from the last saved file.
 */

/*
 * We need to use a work procedure, otherwise X gets confused.
 * Simplify a bit by assuming that there will never be more than
 * one revert process going on at any one time.
 */
static XtWorkProcId workProcId;
static int workProcDone;

static Boolean
workProc(XtPointer w)
{
    if (workProcDone)
	return True;

    /* this is a kluge, but it is necessary */
    workProcDone = 1;

    XtRemoveWorkProc(workProcId);

    XtDestroyWidget(GetShell((Widget) w));
    return True;
}

static void
doRevert(Widget w)
{
    void *v;
    char *file;
    Widget paint, top;
    int zoom, snap;
    Boolean snapon;
    Pixel background;
    WidgetList wlist;

    XtVaGetValues(w,
		  XtNzoom, &zoom, XtNsnap, &snap, XtNsnapOn, &snapon,
		  XtNbackground, &background, XtNmenuwidgets, &wlist,
		  XtNfilename, &file,
		  NULL);
    if ((file == NULL) || (*file == 0))
	return;

    StateSetBusy(True);

    top = GetToplevel(w);
    workProcDone = 0;
    workProcId = XtAppAddWorkProc(Global.appContext, workProc, (XtPointer) w);

    if ((v = ReadMagic(file)) != NULL)
	paint = GraphicOpenFileZoom(top, file, v, zoom);
    else {
	StateSetBusy(False);
	Notice(top, msgText[UNABLE_TO_OPEN_INPUT_FILE], file, RWGetMsg());
	return;
    }

    XtVaSetValues(paint, XtNsnapOn, snapon, XtNsnap, snap,
		  XtNbackground, background, XtNdirty, False, NULL);

    StateSetBusy(False);
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_SELECTOR_SNAP], snapon);
    MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_SNAP], snapon);
}

static void
revertOkCallback(Widget shell, XtPointer arg, XtPointer junk2)
{
    doRevert((Widget) arg);
}

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

    XtVaGetValues(paint, XtNdirty, &flg, NULL);
    if (flg)
	AlertBox(GetShell(paint), msgText[UNSAVED_CHANGES_WISH_TO_REVERT],
		 revertOkCallback, genericCancelCallback, paint);
}

static void
printCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PrintPopup(GetToplevel(w), paintArg);
}

static void
externCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ExternPopup(GetToplevel(w), paintArg);
}

/*
 * Pop up the fatbits window.
 */
static void
fatCallback(Widget w, XtPointer paint, XtPointer junk2)
{
    FatbitsEdit((Widget) paint);
}

/*
 * Toggle the 'grid' menu item.
 */
static void 
setGridMenu(Widget paint, void *ptr)
{
    WidgetList wlist;
    Boolean v;
    v = (ptr)? True : False;
    XtVaSetValues(paint, XtNgrid, v, NULL);
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_SELECTOR_GRID], v);
    MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_GRID], v);
}
static void
gridCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    Boolean v;

    XtVaGetValues(paint, XtNgrid, &v, NULL);
    v = !v;
    GraphicAll(setGridMenu, (void *)((int) v));
}

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

static void 
setLineWidth(Widget w, void *width)
{
    XtVaSetValues(w, XtNlineWidth, (int) width, NULL);
    if (Global.patternshell) {
        setPatternLineWidth(Global.patterninfo, (int) width);
        checkPatternLink(w, 1);
    } else
        setCanvasColorsIcon(w);
}

static void 
setLineWidthMenu(Widget w, void *ptr)
{
    WidgetList wlist;
    XtVaGetValues(w, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_LINE_WIDTHS+(int)ptr], True);
    MenuCheckItem(wlist[W_TOPMENU+W_LINE_WIDTHS+(int)ptr], True);
}

void 
setToolIconPixmap(Widget paint, void *ptr) 
{
    WidgetList wlist;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    setToolIconOnWidget(wlist[W_ICON_TOOL]);
    XtVaSetValues(wlist[W_ICON_TOOL],
                  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT, NULL);
}

void
setCanvasColorsIcon(Widget paint)
{
    WidgetList wlist;
    static int *x0 = NULL, *x1;
    Pixel bg, fg, lfg;
    Pixmap pix, lpix, colpixmap;
    Display *dpy;
    GC gc;
    int v_fr, v_lfr, v_lw, x, y, width, height, depth;

    if (!paint) return;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist || !wlist[W_ICON_COLORS]) return;
    colpixmap = (Pixmap) wlist[W_ICON_COLPIXMAP];
    if (!colpixmap) return;
    dpy = XtDisplay(paint);
    gc = XtGetGC(paint, 0, 0);    
    depth = DefaultDepthOfScreen(XtScreen(paint));

    XtVaGetValues(paint, XtNbackground, &bg, NULL);
    XtVaGetValues(paint, XtNfillRule, &v_fr, NULL);
    XtVaGetValues(paint, XtNforeground, &fg, NULL);
    XtVaGetValues(paint, XtNpattern, &pix, NULL);
    XtVaGetValues(paint, XtNlineFillRule, &v_lfr, NULL);
    XtVaGetValues(paint, XtNlineForeground, &lfg, NULL);
    XtVaGetValues(paint, XtNlinePattern, &lpix, NULL);
    XtVaGetValues(paint, XtNlineWidth, &v_lw, NULL);

    if (v_lw>10) v_lw = 10;

    if (!x0) {
        x0 = (int *) xmalloc(ICONHEIGHT*sizeof(int));
        x1 = (int *) xmalloc(ICONHEIGHT*sizeof(int));
	for (y=0; y<ICONHEIGHT; y++) {
	    if (y<4 || y>ICONHEIGHT-5) {
                x0[y] = ICONWIDTH;
	    } else
                x0[y] = 11+abs(y-20)/3;
                x1[y] = ICONWIDTH-3-abs(y-20)/3;
	}
    }
    for (y=0; y<ICONHEIGHT; y++) {
        XSetForeground(dpy, gc, bg);
	if (y<4 || y>ICONHEIGHT-5) {
            for (x=0; x<x0[y]; x++)
                XDrawPoint(dpy, colpixmap, gc, x, y);
	    continue;
	}
        for (x=0; x<=2; x++)
                XDrawPoint(dpy, colpixmap, gc, x, y);
        for (x=10; x<x0[y]; x++)
                XDrawPoint(dpy, colpixmap, gc, x, y);

        for (x=x1[y]+1; x<ICONWIDTH; x++)
            XDrawPoint(dpy, colpixmap, gc, x, y);

	if (v_fr == FillSolid) {
	    XSetForeground(dpy, gc, fg);
            for (x=3; x<=9; x++)
                XDrawPoint(dpy, colpixmap, gc, x, y);
	} else {
            GetPixmapWHD(dpy, pix, &width, &height, &depth);
            for (x=3; x<=9; x++)
	        XCopyArea(dpy, pix, colpixmap, gc,
		          x%width, y%height, 1, 1, x, y);
	}

	if (y<=4+v_lw || y>=ICONHEIGHT-v_lw-5) {
	    if (v_fr == FillSolid) {
	        XSetForeground(dpy, gc, fg);
                for (x=x0[y]; x<=x1[y]; x++)
                    XDrawPoint(dpy, colpixmap, gc, x, y);
	    } else {
                GetPixmapWHD(dpy, pix, &width, &height, &depth);
                for (x=x0[y]; x<=x1[y]; x++)
	            XCopyArea(dpy, pix, colpixmap, gc,
		              x%width, y%height, 1, 1, x, y);
	    }
            continue;
	}
	if (v_fr == FillSolid) {
            XSetForeground(dpy, gc, fg);
            for (x=x0[y]; x<=x0[y]+v_lw; x++)
                XDrawPoint(dpy, colpixmap, gc, x, y);
            for (x=x1[y]-v_lw; x<=x1[y]; x++)
                XDrawPoint(dpy, colpixmap, gc, x, y);
	} else {
            GetPixmapWHD(dpy, pix, &width, &height, &depth);
            for (x=x0[y]; x<=x0[y]+v_lw; x++)
	        XCopyArea(dpy, pix, colpixmap, gc,
		          x%width, y%height, 1, 1, x, y);
            for (x=x1[y]-v_lw; x<=x1[y]; x++)
	        XCopyArea(dpy, pix, colpixmap, gc,
		          x%width, y%height, 1, 1, x, y);
	}
	if (v_lfr == FillSolid) {
            XSetForeground(dpy, gc, lfg);
            for (x=x0[y]+v_lw+1; x<=x1[y]-v_lw-1; x++)
                XDrawPoint(dpy, colpixmap, gc, x, y);
	} else {
            GetPixmapWHD(dpy, lpix, &width, &height, &depth);
            for (x=x0[y]+v_lw+1; x<=x1[y]-v_lw-1; x++)
	        XCopyArea(dpy, lpix, colpixmap, gc,
		          x%width, y%height, 1, 1, x, y);
	}
    }

    /* Trick to force refresh of background Pixmap,
       although pointer value colpixmap didn't change ... */
    XtVaSetValues(wlist[W_ICON_COLORS],
                  XtNbackgroundPixmap, XtUnspecifiedPixmap, NULL);
    XtVaSetValues(wlist[W_ICON_COLORS],
                  XtNbackgroundPixmap, colpixmap, NULL);
    XtReleaseGC(paint, gc);
}

void 
setBrushIconPixmap(Widget paint, void *ptr) 
{
    WidgetList wlist;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    setBrushIconOnWidget(wlist[W_ICON_BRUSH]);
    XtVaSetValues(wlist[W_ICON_BRUSH], 
                  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT, NULL);
}

extern void LoadRCInfo(RCInfo *rcInfo, Palette *map);

static void 
setPalettePixmap(Widget paint, LocalInfo *info, int mode)
{
    Display *dpy = XtDisplay(paint);
    Pixel black = BlackPixelOfScreen(XtScreen(paint)),
          white = WhitePixelOfScreen(XtScreen(paint));
    static Pixmap eye_pix;
    static Pixmap fill_pix[3];
    GC gc;
    int i, j, k, l, x, y;
    int width, height, depth;
    XpmAttributes attributes;
    WidgetList wlist;

    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    if (!info->rcInfo) {
        info->rcInfo = ReadDefaultRC();
        LoadRCInfo(info->rcInfo, info->map);
        info->npixels = 0;	
        for (i=0; i<info->rcInfo->ncolors; i++)
            if (info->rcInfo->colorFlags[i]) ++info->npixels;
        info->pixels = (Pixel *)xmalloc(info->npixels * sizeof(Pixel));
	j = 0;
        for (i=0; i<info->rcInfo->ncolors; i++) {
            if (info->rcInfo->colorFlags[i]) 
	        info->pixels[j++] = info->rcInfo->colorPixels[i];
	}
	info->npatterns = info->rcInfo->nimages;
	info->patterns = (Pixmap *) 
            xmalloc(info->npatterns * sizeof(Pixmap));
	for (i=0; i<info->npatterns; i++) {
	    info->patterns[i] = None;
            info->rcInfo->images[i]->refCount++;
	    ImageToPixmapCmap(info->rcInfo->images[i], 
                          info->paint, &info->patterns[i], info->map->cmap);
	}
    }

    if (!info->palette_pixmap) {
        info->palette_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
	                422, 10*info->steps-1, 
			DefaultDepthOfScreen(XtScreen(paint)));
	attributes.valuemask = XpmCloseness;
        attributes.closeness =  40000;
        XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy),
                                eye_xpm, &eye_pix, NULL, &attributes);
        XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy),
                                fill0_xpm, &fill_pix[0], NULL, &attributes);
        XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy),
                                fill1_xpm, &fill_pix[1], NULL, &attributes);
        XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy),
                                fill2_xpm, &fill_pix[2], NULL, &attributes);
    }
    gc = XCreateGC(dpy, XtWindow(paint), 0, 0);
    XCopyArea(dpy, fill_pix[info->channel % 3], 
                   info->palette_pixmap, gc, 0, 0, 16, 19, 16, 0);
    if (mode) goto finish;

    XCopyArea(dpy, eye_pix, info->palette_pixmap, gc, 0, 0, 16, 19, 0, 0);
    for (k=0; k<=1; k++)
    for (y = 19; y<10*info->steps-1; y++)
        XCopyArea(dpy, eye_pix, info->palette_pixmap, gc, 
		  0, 18, 16, 1, 16*k, y);

    XSetForeground(dpy, gc, white);
    for (x = 33; x<422; x++)
    for (y = 0; y<10*info->steps-1; y++)
        XDrawPoint(dpy, info->palette_pixmap, gc, x, y);

    i = -info->steps;
    j = -info->steps;
    for (x = 32; x<422; x++) {
        if ((x-32)%10 == 0) {
	    XSetForeground(dpy, gc, black);
	    for (y = 0; y<10*info->steps-1; y++) 
	        XDrawPoint(dpy, info->palette_pixmap, gc, x, y);
	    for (k = 1; k<info->steps; k++) 
	    for (l = 0; l<=9; l++) 
	        XDrawPoint(dpy, info->palette_pixmap, gc, x+l, 10*k-1);
	    ++x;
	    i += info->steps;
	    if (i >= info->npixels)
	        j += info->steps;
	}
        for (k = 0; k<info->steps; k++) {
	    if (i+k<info->npixels) {
                XSetForeground(dpy, gc, info->pixels[i+k]);
	        for (y = 0; y<=8; y++)
                    XDrawPoint(dpy, info->palette_pixmap, gc, x, y+10*k);
	    } else {
		if (j+k>=0 && j+k<info->npatterns) {
		    GetPixmapWHD(dpy, info->patterns[j+k], 
                                      &width, &height, &depth);
		    for (y = 0; y<=8; y++) {
                        XCopyArea(dpy, info->patterns[j+k], 
                            info->palette_pixmap, gc, 
                            ((x-32)%10)%width, y%height, 1, 1, x, y+10*k);
		    }
		}
	    }
	}
    }

 finish:
    XtVaSetValues(wlist[W_ICON_PALETTE], 
                      XtNbackgroundPixmap, XtUnspecifiedPixmap, NULL);
    XtVaSetValues(wlist[W_ICON_PALETTE], 
                      XtNbackgroundPixmap, info->palette_pixmap, NULL);
    XtReleaseGC(paint, gc);
}

void 
AddItemToCanvasPalette(Widget paint, Pixel p, Pixmap pix)
{
    Display *dpy = XtDisplay(paint);
    LocalInfo *info;
    WidgetList wlist;
    int i;

    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    info = (LocalInfo *)wlist[W_INFO_DATA];
    if (!info) return;
    
    if (p != None) {
         i = info->npixels;
         info->npixels = i+1;
	 info->pixels = realloc(info->pixels, info->npixels * sizeof(Pixel));
	 info->pixels[i] = p;
    }
    if (pix != None) {
         i = info->npatterns;
         info->npatterns = i+1;
	 info->patterns = 
               realloc(info->patterns, info->npixels * sizeof(Pixel));
         info->patterns[i] = dupPixmap(dpy, pix);
    }

    setPalettePixmap(paint, info, 0);
}

static void
paletteHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flg)
{
    Display *dpy = XtDisplay(w);
    Pixmap pix;
    Pixel p;
    int i, j, rule;

    if (!event) return;

    if (event->type == ButtonRelease) {
        if (event->xbutton.x<16) {
	    if (event->xbutton.y<18) {
	        DoGrabPixel(info->paint, &p, &info->map->cmap);
		j = 1;
		for (i=0; i<info->npixels; i++)
		    if (p == info->pixels[i]) {
		        j = 0;
			break;
		    }
		if (j) {
		    i = info->npixels;
                    info->npixels = i+1;
                    info->pixels = 
                    realloc(info->pixels, info->npixels * sizeof(Pixel));
                    info->pixels[i] = p;
                    setPalettePixmap(info->paint, info, 0);
		}
	        if (info->channel & 1) {
		    XtVaGetValues(info->paint, 
		       XtNfillRule, &rule, XtNpattern, &pix, NULL);
                    XtVaSetValues(info->paint, 
	                      XtNfillRule, FillSolid,
                              XtNforeground, p, NULL);
		    if (rule==FillTiled && pix) XFreePixmap(dpy, pix);
		}
                if (info->channel & 2) {
		    XtVaGetValues(info->paint, 
		       XtNlineFillRule, &rule, XtNlinePattern, &pix, NULL);
                    XtVaSetValues(info->paint, 
			      XtNlineFillRule, FillSolid,
			      XtNlineForeground, p, NULL);
		    if (rule==FillTiled && pix) XFreePixmap(dpy, pix);
		}
                setCanvasColorsIcon(info->paint);
	    }
	} else
        if (event->xbutton.x>=16 && event->xbutton.x<32) {
	    if (event->xbutton.y<18) {
	        info->channel = 1 + info->channel % 3;
                setPalettePixmap(info->paint, info, 1);
	    }
        } else {
            i = info->steps*((event->xbutton.x-32)/10) + 
                event->xbutton.y/10;
	    if (i<info->npixels) {
	        p = info->pixels[i];
	        if (info->channel & 1) {
		    XtVaGetValues(info->paint, 
		       XtNfillRule, &rule, XtNpattern, &pix, NULL);
                    XtVaSetValues(info->paint, 
			 	  XtNfillRule, FillSolid,
                                  XtNforeground, p, 
				  NULL);
		    if (rule==FillTiled && pix) XFreePixmap(dpy, pix);
		}
	        if (info->channel & 2) {
		    XtVaGetValues(info->paint, 
		       XtNlineFillRule, &rule, XtNlinePattern, &pix, NULL);
                    XtVaSetValues(info->paint, 
				  XtNlineFillRule, FillSolid,
                                  XtNlineForeground, p,
				  NULL);
		    if (rule==FillTiled && pix) XFreePixmap(dpy, pix);
		}
	    } else {
                i = i - ((info->npixels + info->steps-1)/info->steps)
		        * info->steps;
	        if (i>=0 && i<info->npatterns && (info->channel&1)) {
		   XtVaGetValues(info->paint, 
                       XtNfillRule, &rule, XtNpattern, &pix, NULL);
		   XtVaSetValues(info->paint, 
				 XtNfillRule, FillTiled,
                                 XtNpattern,
				 dupPixmap(dpy,info->patterns[i]), 
                                 NULL);
		   if (rule==FillTiled && pix) XFreePixmap(dpy, pix);
		}
	        if (i>=0 && i<info->npatterns && (info->channel&2)) {
		   XtVaGetValues(info->paint, 
                       XtNlineFillRule, &rule, XtNlinePattern, &pix, NULL);
		   XtVaSetValues(info->paint, 
				 XtNlineFillRule, FillTiled,
                                 XtNlinePattern, 
				 dupPixmap(dpy,info->patterns[i]), 
                                 NULL);
		   if (rule==FillTiled && pix) XFreePixmap(dpy, pix);
		}
	    }
            setCanvasColorsIcon(info->paint);
	}
    }
}

void
setMemoryIcon(Widget w, LocalInfo * info)
{
    Display *dpy = XtDisplay(w);
    WidgetList wlist;
    Widget paint;
    Pixel pixel, black, white;
    GC gc;
    int i, j, k, l;
    int memw, memh;
    char buf[40];

    if (!info) {
       XtVaGetValues(w, XtNmenuwidgets, &wlist, NULL);
       if (!wlist) return;
       info = (LocalInfo *)wlist[W_INFO_DATA];
    }
    paint = info->paint;
    black = BlackPixelOfScreen(XtScreen(paint));
    white = WhitePixelOfScreen(XtScreen(paint));
    gc = XCreateGC(dpy, XtWindow(paint), 0, 0);
    memw = 15;
    memh = 26 + 10*info->steps;
    XtVaGetValues(info->memory, XtNbackground, &pixel, NULL);

    if (info->mem_pixmap == None) {
         info->mem_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
                        memw, memh, DefaultDepthOfScreen(XtScreen(paint)));
       XSetForeground(dpy, gc, pixel);
       for (j=0; j<memh;j++)
          for (i=0; i<memw;i++)
	     XDrawPoint(dpy, info->mem_pixmap, gc, i, j);
       XSetForeground(dpy, gc, black);
       for (j=0; j<=memh-1; j+=memh-1)
          for (i=0; i<memw;i++) if ((i+j)&1)
	     XDrawPoint(dpy, info->mem_pixmap, gc, i, j);
       for (i=0; i<=memw-1; i+=memw-1)
          for (j=0; j<memh;j++) if ((i+j)&1)
	     XDrawPoint(dpy, info->mem_pixmap, gc, i, j);
    }
    
    if (7+6*((Global.numregions-1)/2)<memh)
       info->mem_shift = 0;

    if (info->mem_index == -1)
       l = -1;
    else
       l = (info->mem_index+info->mem_shift) % Global.numregions;

    if (l>=0) {
       if (info->mem_shift)
          sprintf(buf, "%d / %d (+%d)", 
             info->mem_index+1, Global.numregions, info->mem_shift);
       else
          sprintf(buf, "%d / %d", 
             info->mem_index+1, Global.numregions);
       XtVaSetValues(info->position, XtNlabel, buf, NULL);
       XtVaSetValues(info->position, XtNwidth, 62, NULL);
    }

    for (k=0; k<=Global.numregions; k++) {
       i = k%2;
       j = k/2;
       if (7+6*j >= memh) break;
       XSetForeground(dpy, gc, white);
       XFillRectangle(dpy, info->mem_pixmap, gc, 2+6*i, 2+6*j, 4, 4);
       XSetForeground(dpy, gc, (k==Global.numregions)?pixel:black);
       if (k == Global.numregions)
          XFillRectangle(dpy, info->mem_pixmap, gc, 2+6*i, 2+6*j, 5, 5);
       else
       if (k == l)
          XFillRectangle(dpy, info->mem_pixmap, gc, 2+6*i, 2+6*j, 4, 4);
       else
          XDrawRectangle(dpy, info->mem_pixmap, gc, 2+6*i, 2+6*j, 4, 4);
    }

    XtVaSetValues(info->memory, XtNbackgroundPixmap, 
                                XtUnspecifiedPixmap, NULL);
    XtVaSetValues(info->memory, XtNbackgroundPixmap, 
                                info->mem_pixmap, NULL);
    XFreeGC(dpy, gc);

    XtVaGetValues(info->paint, XtNmenuwidgets, &wlist, NULL);
    if (wlist) {
       l = (Global.numregions>0);
       if (wlist[W_EDIT_RECALL])
          XtVaSetValues(wlist[W_EDIT_RECALL], XtNsensitive, l, NULL);
       if (wlist[W_TOPMENU+W_EDIT_RECALL])
          XtVaSetValues(wlist[W_TOPMENU+W_EDIT_RECALL], XtNsensitive, l, NULL);
       if (wlist[W_EDIT_REMOVE])
          XtVaSetValues(wlist[W_EDIT_REMOVE], XtNsensitive, l, NULL);
       if (wlist[W_TOPMENU+W_EDIT_REMOVE])
          XtVaSetValues(wlist[W_TOPMENU+W_EDIT_REMOVE], XtNsensitive, l, NULL);
    }
}

static void
removeFromMemory(Display *dpy, int num)
{
    Pixmap pix, mask;
    int i;
    if (num<0 || num>=Global.numregions) return;

    pix = Global.regiondata[num].pix;
    mask = Global.regiondata[num].mask;
    if (pix!=None) XFreePixmap(dpy, pix);
    if (mask!=None) XFreePixmap(dpy, mask);
    --Global.numregions;
    for (i=num; i<Global.numregions; i++) {
       Global.regiondata[i].pix = Global.regiondata[i+1].pix;
       Global.regiondata[i].mask = Global.regiondata[i+1].mask;
    }
}

void 
recallCallback(Widget w, XtPointer paintArg,  XtPointer junk2)
{
    LocalInfo * info = (LocalInfo *) paintArg;
    Display * dpy = XtDisplay(info->paint);
    int width, height, depth;
    GC gc;
    XRectangle rect;
    Pixmap pix, mask;
    PaintWidget pw = (PaintWidget) info->paint;

    if (info->mem_index<0) return;
    if (info->mem_index>=Global.numregions) return;

    GetPixmapWHD(dpy, Global.regiondata[info->mem_index].pix,
		 &width, &height, &depth);
    rect.x = Global.regiondata[info->mem_index].x;
    rect.y = Global.regiondata[info->mem_index].y;

    rect.width = width;
    rect.height = height;
    pix = XCreatePixmap(dpy, DefaultRootWindow(dpy), width, height, depth);
    gc = XCreateGC(dpy, pix, 0, 0);
    XCopyArea(dpy, Global.regiondata[info->mem_index].pix, pix, gc, 0, 0, 
        width, height, 0, 0);
    XFreeGC(dpy, gc);

    mask = Global.regiondata[info->mem_index].mask;
    if (mask) {
        mask = XCreatePixmap(dpy, DefaultRootWindow(dpy), width, height, 1);
        gc = XCreateGC(dpy, mask, 0, 0);
        XCopyArea(dpy, Global.regiondata[info->mem_index].mask, 
		  mask, gc, 0, 0, 
                  width, height, 0, 0);
        XFreeGC(dpy, gc);
    }

    pw->paint.zoomX = 0;
    pw->paint.zoomY = 0;
    pw->paint.downX = 0;
    pw->paint.downY = 0;
    PwRegionSet(info->paint, &rect, pix, mask);
    RegionMove(pw, rect.x, rect.y);
}

static void
memoryHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flg)
{
    Display *dpy = XtDisplay(w);
    Pixmap pix;
    int i, j, x, y, num, no_rotation;

    num = 0;

    if (event->type == ButtonRelease &&
	event->xbutton.button == 2) {
       i = Global.numregions;
       StdMemoryCallback(w, info, NULL);
       if (Global.numregions>i) info->mem_index = i;
       setMemoryIcon(w, info);
       return;
    }

    no_rotation = (7+6*((Global.numregions-1)/2)<26 + 10*info->steps);

    if ((event->type == ButtonPress || event->type == ButtonRelease) &&
	(event->xbutton.button == 1 || event->xbutton.button == 3)) {
       num = -1;
       i = (event->xbutton.x-2)/6;
       j = (event->xbutton.y-2)/6;
       x = 2+6*i;
       y = 2+6*j;
       if (event->xbutton.x>=x && event->xbutton.x<=x+4 &&
	  event->xbutton.y>=y && event->xbutton.y<=y+4) {
          num = i+2*j;
          if (num>=0 && num<Global.numregions) {
             num = (num+Global.numregions-info->mem_shift) % Global.numregions;
             if (event->type == ButtonPress)
	        info->mem_index = num;
	     if (event->xbutton.button == 1 && event->type == ButtonRelease) {
	        if (num == info->mem_index || no_rotation) {
		   info->mem_index = num;
	           PwRegionOff(info->paint, True);
	           recallCallback(w, info, NULL);
		} else {
		   if (info->mem_index>=0) {
                      info->mem_shift += Global.numregions+num-info->mem_index;
                      info->mem_shift = info->mem_shift % Global.numregions;
		   }
		}
	     }
             if (event->xbutton.button == 3 && event->type == ButtonRelease) {
	        removeFromMemory(dpy, num);
		info->mem_index = -1;
	     }
             setMemoryIcon(w, info);
	     return;
	  }
       }
    }
}

/* This manages Window resize of main canvas */

static void
changeSize(Widget w, LocalInfo * l, XEvent * event, Boolean * flg)
{
    Dimension u, v, up, vp, us, vs;
    Boolean bool;

    if (event->type == ConfigureNotify) {
         u = WidthOfScreen(XtScreen(w));
         v = HeightOfScreen(XtScreen(w));
         XtVaGetValues(w, XtNwidth, &up, XtNheight, &vp, NULL);
	 if (up > u - 20) up = u - 20;
	 if (vp > v - 80) vp = v - 80;
	 if (up >= u-20 || vp >= v-80)
             XtVaSetValues(w, XtNwidth, up, XtNheight, vp, NULL);
         XtVaGetValues(l->paint, XtNwidth, &u, XtNheight, &v, NULL);
         XtVaGetValues(l->viewport, XtNwidth, &up, XtNheight, &vp, NULL);
	 if (up>=u+10) us = up-4; else us = u+10;
	 if (vp>=v+10) vs = vp-4; else vs = v+10;
         XtVaSetValues(l->paintbox, XtNwidth, us, XtNheight, vs, NULL);
	 XtVaGetValues(l->viewport, XtNallowVert, &bool, NULL);
#ifdef XAW95
	 if (up>=u+14 && vp>=v+14) {
#else
	 if (up>=u+10 && vp>=v+10) {
#endif
            XtVaSetValues(l->viewport, XtNallowVert, False,
                                       XtNallowHoriz, False, NULL);
	 } else {
            XtVaSetValues(l->viewport, XtNallowVert, True,
                                       XtNallowHoriz, True, NULL);
	    bool = !bool;
	 }
	 if (bool) {
	     ++l->boolcount;
             if (l->boolcount>=2) {
                 XtUnmanageChild(l->paint);
                 XtManageChild(l->paint);
	     }
	 }
    }
}

static int xorig = 0;
static int yorig = 0;

static void
buttonHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flg)
{
    xorig = event->xbutton.x;
    yorig = event->xbutton.y;
}

static void
motionHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flg)
{
    char buf[30];
    int zoom, h;
    if (info->boolpos) {
        XtVaGetValues(w, XtNzoom, &zoom, XtNheight, &h, NULL);
        if (((XButtonEvent *) event)->state & ControlMask) {
            sprintf(buf, "%4d %4d", 
	       (event->xbutton.x - xorig)/zoom, 
	       (event->xbutton.y - yorig)/zoom);
	} else
            sprintf(buf, "%4d %4d", 
	       event->xbutton.x/zoom, 
	       event->xbutton.y/zoom);
        XtVaSetValues(info->position, XtNlabel, buf, NULL);
        XtVaSetValues(info->position, XtNwidth, 62, NULL);
    }
}

void 
motionExtern(Widget paint, XEvent * event, int x, int y, int flag)
{
    LocalInfo *info;
    WidgetList wlist;
    char buf[30];
    int h;

    XtVaGetValues(paint, XtNmenuwidgets, &wlist, 
                         XtNdrawHeight, &h, NULL);
    if (!wlist) return;
    info = (LocalInfo *)wlist[W_INFO_DATA];
    if (flag) {
        xorig = x;
        yorig = y;
    }
    if (info->boolpos) {
        if (((XButtonEvent *) event)->state & ControlMask) {
            sprintf(buf, "%4d %4d", x - xorig, y - yorig);
	} else
            sprintf(buf, "%4d %4d", x, y);
        XtVaSetValues(info->position, XtNlabel, buf, NULL);
        XtVaSetValues(info->position, XtNwidth, 62, NULL);
    }
}

static void
positionHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flg)
{
    info->boolpos = !info->boolpos;
    XtVaSetValues(info->position, XtNlabel, 
	 (info->boolpos)? "???  ???" : msgText[POSITION], NULL);
    XtVaSetValues(info->position, XtNwidth, 62, NULL);
    xorig = 0;
    yorig = 0;
}

static void
formHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flg)
{
    Display *dpy = XtDisplay(w);
    Dimension v;
    WidgetList wlist;
    int i;

    XtVaGetValues(info->paint, XtNmenuwidgets, &wlist, NULL);
    XtVaGetValues(w, XtNheight, &v, NULL);
    i = (v - 32)/10;
    if (i<2) i = 2;

    if (wlist && i != info->steps) {
         info->steps = i;
	 XFreePixmap(XtDisplay(w), info->palette_pixmap);
	 info->palette_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
	                422, 10*info->steps-1, 
			DefaultDepthOfScreen(XtScreen(info->paint)));
	 if (info->mem_pixmap) {
            XFreePixmap(XtDisplay(w), info->mem_pixmap);
	    info->mem_pixmap = None;
	    setMemoryIcon(info->paint, info);
	    XtVaSetValues(info->memory, XtNheight, 26 + 10*info->steps, NULL);
	 }
	 XtVaSetValues(wlist[W_ICON_PALETTE], 
                       XtNheight, 10*info->steps-1, NULL);
	 setPalettePixmap(info->paint, info, 0);
	 changeSize(GetToplevel(info->paint), info, event, flg);
    }
}

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, msgText[INVALID_WIDTH_MUST_BE_GREATER_THAN_ZERO]);
	return;
    }
    sprintf(currentLineWidth, "%d", width);
    if (width == 1)
	width = 0;
    GraphicAll(setLineWidth, (void *) width);
    XtSetArg(arg, XtNlineWidth, width);
    OperationAddArg(arg);
}

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 = msgText[LINE_WIDTH];
	value[0].str = currentLineWidth;
	value[0].len = 4;
	info.prompts = value;
	info.title = msgText[ENTER_DESIRED_LINE_WIDTH];
	info.nprompt = 1;
        GraphicAll(setLineWidthMenu, (void *)LINE_WIDTH_GENERAL);
	TextPrompt(GetToplevel(w), 
            "linewidth", &info, okLineCallback, NULL, NULL);
    } else {
	Arg arg;

	if (width == 1)
	    width = 0;
	GraphicAll(setLineWidth, (void *) width);
	GraphicAll(setLineWidthMenu, (void *) (width/2));

	XtSetArg(arg, XtNlineWidth, width);
	OperationAddArg(arg);
    }
}

static void 
dashOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;

    int i, length, valid;

    length = strlen(info->prompts[0].rstr);
    if (length > 48) valid = 0; else {
        valid = 1;
        for (i = 0; i<length; i++)
	  if (info->prompts[0].rstr[i] != '-' &&
              info->prompts[0].rstr[i] != '=') {
	      valid = 0;
	      break;
	  }
    }

    if (!valid) {
	Notice(w, msgText[DASH_IS_GIVEN_BY_ALTERNATING_SUCH_CHARACTERS]);
	return;
    }
    strcpy(dashStyleStr, info->prompts[0].rstr);
    DashSetStyle(dashStyleStr);
}
static void 
dashMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[1];

    value[0].prompt = msgText[DASH_STYLE];
    
    value[0].str = dashStyleStr;
    value[0].len = 48;
    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_DASH_STYLE];
    info.nprompt = 1;

    TextPrompt(w, "dashselect", &info, dashOkCallback, NULL, NULL);
}
void 
changeDashStyleAction(Widget w, XEvent * event)
{
    dashMenuCallback(GetToplevel(w));
}

/*
**  Font menu callbacks.
 */
void
setFontIcon(Widget paint)
{
    WidgetList wlist;
    XFontStruct * info;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, XtNfont, &info, NULL);
    if (wlist && wlist[W_ICON_FONT]) {
        XtVaSetValues(wlist[W_ICON_FONT], XtNfont, info, NULL);
        XtVaSetValues(wlist[W_ICON_FONT], 
                      XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
                      NULL);
    }
}

static void 
fontSetCallback(Widget paint, void *info)
{
    XtVaSetValues(paint, XtNfont, (XFontStruct *) info, NULL);
    FontChanged(paint);
}

static void 
fontMenuCallback(Widget paint, void *ptr)
{
    WidgetList wlist;
    int i;
    if (ptr) i = (int)ptr - 1; else i = 10;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_FONT_DESCR+i], True);
    MenuCheckItem(wlist[W_TOPMENU+W_FONT_DESCR+i], True);
}
static void 
fontSet(Widget w, void *ptr)
{
    XFontStruct *info;
    Arg arg;

    if (ptr == NULL) {
        GraphicAll(fontMenuCallback, NULL);
	FontSelect(w, None);
    } else {
	if ((info = XLoadQueryFont(XtDisplay(GetShell(w)), 
                    fontNames[(int)ptr-1])) == NULL) {
	    XtVaSetValues(w, XtNsensitive, False, NULL);
	    Notice(w, msgText[UNABLE_TO_LOAD_REQUESTED_FONT]);
	    return;
	}
	GraphicAll(fontSetCallback, (void *) info);
        GraphicAll(fontMenuCallback, ptr);
	XtSetArg(arg, XtNfont, info);
	OperationAddArg(arg);
    }
}

/*
 * Toggle the 'snap' menu item.
 */
static void 
setSnapMenu(Widget paint, void *ptr)
{
    WidgetList wlist;
    Boolean v;

    v = (ptr)? True : False;
    XtVaSetValues(paint, XtNsnapOn, v, NULL);
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_SELECTOR_SNAP], v);
    MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_SNAP], v);
}
static void
snapCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    Boolean v;

    XtVaGetValues(paint, XtNsnapOn, &v, NULL);
    v = !v;
    GraphicAll(setSnapMenu, (void *)((int) v));
}

/*
 *  Callbacks for setting snap spacing.
 */
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, msgText[BAD_SNAP_SPACING]);
	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 = msgText[SPACING];
    value[0].str = buf;
    value[0].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_SNAP_SPACING];
    info.nprompt = 1;

    TextPrompt(paint, "linewidth", &info, changeSnapOkCallback, NULL, NULL);
}

/*
 *  Callback for changing the image size.
 */
static void
sizeCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    SizeSelect(GetShell(paint), paint, NULL);
}

static void
defaultsizeCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    SizeSelect(GetShell(paint), NULL, NULL);
}

static void
autocropOkCallback(Widget w, XtPointer argArg, XtPointer junk)
{
    AutoCrop((Widget) argArg);
}

static void
undosizeOkCallback(Widget paint, void * junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int n = atoi(info->prompts[0].rstr);

    if (n < 0 || n > 20) {
	Notice(paint, msgText[BAD_NUMBER_OF_UNDO_LEVELS]);
	return;
    }
    XtVaSetValues(paint, XtNundoSize, n, NULL);
}

static void
undosizeCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    static char buf[5];
    int undosize;

    XtVaGetValues(paint, XtNundoSize, &undosize, NULL);
    sprintf(buf, "%d", undosize);

    value[0].prompt = msgText[LEVELS];
    value[0].str = buf;
    value[0].len = 3;
    info.prompts = value;
    info.title = msgText[NUMBER_OF_UNDO_LEVELS];
    info.nprompt = 1;

    TextPrompt(paint, "undolevels", &info, undosizeOkCallback, NULL, NULL);
}


static void
autocropCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    AlertBox(GetShell(paintArg),
       msgText[AUTOCROP_WARNING_CANNOT_BE_UNDONE],
	     autocropOkCallback, genericCancelCallback, paintArg);
}

/*
 * Callback functions for changing zoom
 */
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);
    int oldzoom, dw, dh;
    Dimension u, v;

    if (zoom < 1 || zoom > 32) {
	Notice(paint, msgText[INVALID_ZOOM]);
    } else {
	XtVaGetValues(paint, XtNzoom, &oldzoom, 
		             XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);
	if (oldzoom == zoom) return;
	XtUnmanageChild(paint);
	XtVaSetValues(paint, XtNwidth, dw*zoom, XtNheight, dh*zoom,
                             XtNzoom, zoom, NULL);
	zoomAddChild(paint, zoom);
	if ((CurrentOp->add == BrushAdd) ||
	    (CurrentOp->add == EraseAdd) ||
	    (CurrentOp->add == SmearAdd)) {
	    if (zoom > 1) {
		SetCrossHairCursor(paint);
		FatCursorAddZoom(zoom, paint);
	    } else
		FatCursorRemoveZoom(paint);
	}
	FatbitsUpdate(paint, zoom);
	w = XtParent(XtParent(paint));
        XtVaGetValues(w, XtNwidth, &u, XtNheight, &v, NULL);
#ifdef XAW95
	if (u >= dw*zoom+14 && v >= dh*zoom+14)
#else
	if (u >= dw*zoom+10 && v >= dh*zoom+10)
#endif
	    XtVaSetValues(XtParent(XtParent(paint)), 
                XtNallowVert, False, XtNallowHoriz, False, NULL);
        else
	    XtVaSetValues(XtParent(XtParent(paint)), 
                XtNallowVert, True, XtNallowHoriz, True, NULL);
	XtManageChild(paint);
    }
}

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 = msgText[CHANGE_ZOOM_FACTOR_FOR_IMAGE];
    values[0].prompt = msgText[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);
}

/*
 * Callback functions for Region menu
 */
static void
rotateTo(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    float t;
    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 int rotateAngle = 0;

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);
    rotateAngle = (int) t;
}

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", rotateAngle);

    value[0].prompt = msgText[ANGLE_IN_DEGREES];
    value[0].str = buf;
    value[0].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_ROTATION];
    info.nprompt = 1;

    TextPrompt(paint, "rotation", &info, rotateOkCallback, NULL, NULL);
}

static void
resetMat(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PwRegionReset((Widget) paintArg, True);
}

static void
cropToRegionOkCallback(Widget w, PaintWidget paint, XtPointer infoArg)
{
    RegionCrop(paint);
}

static void
cropToRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    AlertBox(GetShell(paint),
    msgText[ARE_YOU_SURE_YOU_WANT_TO_CROP_THE_IMAGE_TO_THE_SIZE_OF_THE_REGION],
	     (XtCallbackProc) cropToRegionOkCallback,
	     genericCancelCallback, paint);
}

static void
linearRegionOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    pwMatrix m;

    ImgProcessInfo.linearA = atof(info->prompts[0].rstr);
    ImgProcessInfo.linearB= atof(info->prompts[1].rstr);
    ImgProcessInfo.linearC = atof(info->prompts[2].rstr);
    ImgProcessInfo.linearD = atof(info->prompts[3].rstr);
    m[0][0] = atof(info->prompts[0].rstr);
    m[0][1] = -atof(info->prompts[1].rstr);
    m[1][0] = -atof(info->prompts[2].rstr);
    m[1][1] = atof(info->prompts[3].rstr);
    PwRegionAppendMatrix(paint, m);
}

static void
linearRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[4];
    static char buf1[10], buf2[10], buf3[10], buf4[10];

    sprintf(buf1, "%g", ImgProcessInfo.linearA);
    sprintf(buf2, "%g", ImgProcessInfo.linearB);
    sprintf(buf3, "%g", ImgProcessInfo.linearC);
    sprintf(buf4, "%g", ImgProcessInfo.linearD);

    value[0].prompt = "a11 =";
    value[0].str = buf1;
    value[0].len = 4;
    value[1].prompt = "a12 =";
    value[1].str = buf2;
    value[1].len = 4;
    value[2].prompt = "a21 =";
    value[2].str = buf3;
    value[2].len = 4;
    value[3].prompt = "a22 =";
    value[3].str = buf4;
    value[3].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_TWOBYTWO_MATRIX_ENTRIES];
    info.nprompt = 4;

    TextPrompt(paint, "linear", &info, linearRegionOkCallback, NULL, NULL);
}

static void
tiltRegionOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;

    ImgProcessInfo.tiltX1 = atoi(info->prompts[0].rstr);
    ImgProcessInfo.tiltY1 = atoi(info->prompts[1].rstr);
    ImgProcessInfo.tiltX2 = atoi(info->prompts[2].rstr);
    ImgProcessInfo.tiltY2 = atoi(info->prompts[3].rstr);
    StdRegionTilt(paint, paint, NULL);
}

static void
tiltRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[4];
    static char buf1[10], buf2[10], buf3[10], buf4[10];

    sprintf(buf1, "%d", ImgProcessInfo.tiltX1);
    sprintf(buf2, "%d", ImgProcessInfo.tiltY1);
    sprintf(buf3, "%d", ImgProcessInfo.tiltX2);
    sprintf(buf4, "%d", ImgProcessInfo.tiltY2);

    value[0].prompt = "X1:";
    value[0].str = buf1;
    value[0].len = 4;
    value[1].prompt = "Y1:";
    value[1].str = buf2;
    value[1].len = 4;
    value[2].prompt = "X2:";
    value[2].str = buf3;
    value[2].len = 4;
    value[3].prompt = "Y2:";
    value[3].str = buf4;
    value[3].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_POINTS];
    info.nprompt = 4;

    TextPrompt(paint, "tilt", &info, tiltRegionOkCallback, NULL, NULL);
}

static void
unselectRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    PwRegionSet(paint, None, None, None);
}

/*
 * Callback functions for Filter menu
 */

static void
oilPaintOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t;

    t = atoi(info->prompts[0].rstr);
    if ((t < 3) || ((t & 1) == 0)) {
	Notice(paint, msgText[INVALID_MASK_SIZE]);
	return;
    }
    ImgProcessInfo.oilArea = t;
    StdRegionOilPaint(paint, paint, NULL);
}

static void
oilPaint(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", ImgProcessInfo.oilArea);

    info.prompts = value;
    info.title = msgText[ENTER_MASK_SIZE_FOR_OIL_PAINT_EFFECT];
    info.nprompt = 1;
    value[0].prompt = msgText[MUST_BE_ODD];
    value[0].str = buf;
    value[0].len = 3;

    TextPrompt(paint, "mask", &info, oilPaintOkCallback, NULL, NULL);
}

static void
SmoothOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t;

    t = atoi(info->prompts[0].rstr);
    if ((t < 3) || ((t & 1) == 0)) {
	Notice(paint, msgText[INVALID_MASK_SIZE]);
	return;
    }
    ImgProcessInfo.smoothMaskSize = t;
    StdRegionSmooth(paint, paint, NULL);
}

static void
doSmooth(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", ImgProcessInfo.smoothMaskSize);

    info.prompts = value;
    info.title = msgText[ENTER_MASK_SIZE_FOR_SMOOTHING_EFFECT];
    info.nprompt = 1;
    value[0].prompt = msgText[MUST_BE_ODD];
    value[0].str = buf;
    value[0].len = 3;

    TextPrompt(paint, "mask", &info, SmoothOkCallback, NULL, NULL);
}

static void
addNoiseOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t;

    t = atoi(info->prompts[0].rstr);
    if (t < 1) {
	Notice(paint, msgText[INVALID_NOISE_VARIANCE]);
	return;
    }
    ImgProcessInfo.noiseDelta = t;
    StdRegionAddNoise(paint, paint, NULL);
}

static void
addNoise(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", ImgProcessInfo.noiseDelta);

    value[0].prompt = "(0-255):";
    value[0].str = buf;
    value[0].len = 3;
    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_NOISE_VARIANCE];
    info.nprompt = 1;

    TextPrompt(paint, "delta", &info, addNoiseOkCallback, NULL, NULL);
}

static void
doSpreadOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t;

    t = atoi(info->prompts[0].rstr);
    if (t < 1) {
	Notice(paint, msgText[INVALID_SPREAD_DISTANCE]);
	return;
    }
    ImgProcessInfo.spreadDistance = t;
    StdRegionSpread(paint, paint, NULL);
}

static void
doSpread(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    static char buf1[10];


    sprintf(buf1, "%d", ImgProcessInfo.spreadDistance);

    value[0].prompt = msgText[DISTANCE_PIXELS];
    value[0].str = buf1;
    value[0].len = 3;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_SPREAD_DISTANCE];
    info.nprompt = 1;

    TextPrompt(paint, "distance", &info, doSpreadOkCallback, NULL, NULL);
}

static void
doPixelizeOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    char *s = info->prompts[0].rstr;
    int e = 0, tx, ty;


    if (strchr(s, 'x')) {
	if (sscanf(s, "%d x %d", &tx, &ty) != 2)
	    ++e;
    } else {
	if (sscanf(s, "%d", &tx) != 1)
	    ++e;
	ty = tx;
    }

    if (e || (tx < 1) || (ty < 1)) {
	Notice(paint, msgText[INVALID_PIXEL_SIZE]);
	return;
    }
    ImgProcessInfo.pixelizeXSize = tx;
    ImgProcessInfo.pixelizeYSize = ty;
    StdRegionPixelize(paint, paint, NULL);
}

static void
doPixelize(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    static char buf[10];


    if (ImgProcessInfo.pixelizeXSize != ImgProcessInfo.pixelizeYSize)
	sprintf(buf, "%dx%d", ImgProcessInfo.pixelizeXSize,
		ImgProcessInfo.pixelizeYSize);
    else
	sprintf(buf, "%d", ImgProcessInfo.pixelizeXSize);

    value[0].prompt = msgText[WIDTH_X_HEIGHT_OR_SINGLE_NUMBER];
    value[0].str = buf;
    value[0].len = 3;
    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_MEGAPIXEL_SIZE];
    info.nprompt = 1;

    TextPrompt(paint, "size", &info, doPixelizeOkCallback, NULL, NULL);
}

static void
despeckleOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t;

    t = atoi(info->prompts[0].rstr);
    if ((t < 3) || ((t & 1) == 0)) {
	Notice(paint, msgText[INVALID_MASK_SIZE]);
	return;
    }
    ImgProcessInfo.despeckleMask = t;
    StdRegionDespeckle(paint, paint, NULL);
}

static void
doDespeckle(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", ImgProcessInfo.despeckleMask);

    info.prompts = value;
    info.title = msgText[ENTER_MASK_SIZE_FOR_DESPECKLE_FILTER];
    info.nprompt = 1;
    value[0].prompt = msgText[MUST_BE_ODD];
    value[0].str = buf;
    value[0].len = 3;

    TextPrompt(paint, "despeckle", &info, despeckleOkCallback, NULL, NULL);
}

static void
contrastOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t1, t2;

    t1 = atoi(info->prompts[0].rstr);
    if ((t1 < 0) || (t1 > 100)) {
	Notice(paint, msgText[INVALID_WHITE_LEVEL]);
	return;
    }
    t2 = atoi(info->prompts[1].rstr);
    if ((t2 < 0) || (t2 > 100)) {
	Notice(paint, msgText[INVALID_BLACK_LEVEL]);
	return;
    }
    ImgProcessInfo.contrastB = t1;
    ImgProcessInfo.contrastW = t2;
    StdRegionNormContrast(paint, paint, NULL);
}


static void
doContrast(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[2];
    static char buf1[10], buf2[10];


    sprintf(buf1, "%d", ImgProcessInfo.contrastB);
    sprintf(buf2, "%d", ImgProcessInfo.contrastW);

    info.prompts = value;
    info.title = msgText[ENTER_LEVELS_FOR_CONTRAST_ADJUSTMENT];
    info.nprompt = 2;
    value[0].prompt = msgText[BLACK_LEVEL];
    value[0].str = buf1;
    value[0].len = 3;
    value[1].prompt = msgText[WHITE_LEVEL];
    value[1].str = buf2;
    value[1].len = 3;
    TextPrompt(paint, "contrast", &info, contrastOkCallback, NULL, NULL);
}

static void
solarizeOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t;

    t = atoi(info->prompts[0].rstr);
    if ((t < 1) || (t > 99)) {
	Notice(paint, msgText[INVALID_SOLARIZATION_THRESHOLD]);
	return;
    }
    ImgProcessInfo.solarizeThreshold = t;
    StdRegionSolarize(paint, paint, NULL);
}

static void
doSolarize(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", ImgProcessInfo.solarizeThreshold);

    info.prompts = value;
    info.title = msgText[ENTER_THRESHOLD_FOR_SOLARIZE_FILTER];
    info.nprompt = 1;
    value[0].prompt = "(%):";
    value[0].str = buf;
    value[0].len = 3;

    TextPrompt(paint, "mask", &info, solarizeOkCallback, NULL, NULL);
}

static void
quantizeOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int t;

    t = atoi(info->prompts[0].rstr);
    if ((t < 2) || (t > 256)) {
	Notice(paint, msgText[INVALID_NUMBER_OF_COLORS]);
	return;
    }
    ImgProcessInfo.quantizeColors = t;
    StdRegionQuantize(paint, paint, NULL);
}

static void
modifyRGBOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int r, g, b;

    r = atoi(info->prompts[0].rstr);
    g = atoi(info->prompts[1].rstr);
    b = atoi(info->prompts[2].rstr);

    if ((r < -100) || (r > 100) || 
        (g < -100) || (g > 100) ||
        (b < -100) || (b > 100)) {
	Notice(paint, msgText[INVALID_RGB_PERCENTAGES]);
	return;
    }
    ImgProcessInfo.redshift = r;
    ImgProcessInfo.greenshift = g;
    ImgProcessInfo.blueshift = b;
    StdRegionModifyRGB(paint, paint, NULL);
}

static void
doModifyRGB(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char buf_r[10], buf_g[10], buf_b[10];


    sprintf(buf_r, "%d", ImgProcessInfo.redshift);
    sprintf(buf_g, "%d", ImgProcessInfo.greenshift);
    sprintf(buf_b, "%d", ImgProcessInfo.blueshift);

    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_SHIFT_FOR_RGB_COMPONENTS];
    info.nprompt = 3;
    value[0].prompt = msgText[RED_SHIFT];
    value[0].str = buf_r;
    value[0].len = 4;
    value[1].prompt = msgText[GREEN_SHIFT];
    value[1].str = buf_g;
    value[1].len = 4;
    value[2].prompt = msgText[BLUE_SHIFT];
    value[2].str = buf_b;
    value[2].len = 4;

    TextPrompt(paint, "mask", &info, modifyRGBOkCallback, NULL, NULL);
}

static void
doQuantize(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", ImgProcessInfo.quantizeColors);

    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_NUMBER_OF_COLORS];
    info.nprompt = 1;
    value[0].prompt = "(2-256):";
    value[0].str = buf;
    value[0].len = 3;

    TextPrompt(paint, "mask", &info, quantizeOkCallback, NULL, NULL);
}

static void
doLast(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    StdLastImgProcess(paint);
}

static void
prCallback(Widget paint, Widget w, Boolean flag)
{
    XtVaSetValues(w, XtNsensitive, flag, NULL);
}

void
EnableRevert(Widget paint)
{
    WidgetList wlist;

    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    XtVaSetValues(wlist[W_FILE_REVERT], XtNsensitive, True, NULL);
    XtVaSetValues(wlist[W_TOPMENU+W_FILE_REVERT], 
                  XtNsensitive, True, NULL);
}

void
EnableLast(Widget paint)
{
    WidgetList wlist;

    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    XtVaSetValues(wlist[W_REGION_LAST], XtNsensitive, True, NULL);
    XtVaSetValues(wlist[W_REGION_UNDO], XtNsensitive, True, NULL);
    XtVaSetValues(wlist[W_TOPMENU+W_REGION_LAST], 
                  XtNsensitive, True, NULL);
    XtVaSetValues(wlist[W_TOPMENU+W_REGION_UNDO], 
                  XtNsensitive, True, 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);
	if (Global.patternshell)
            checkPatternLink(w, 1);
	else
	    setCanvasColorsIcon(w);
    }
}

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);
}

static void
selectColorRange(Widget w, XtPointer paintArg, XtPointer junk)
{
    Widget paint = (Widget) paintArg;
    Colormap cmap;
    Palette *map;

    /* StateShellBusy(paint, True); */

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

    ChromaDialog(paint, map);
}

/*
 *  Start of graphic window creation routines
 */

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

    XtSetArg(args[nargs], XtNtitle, DEFAULT_TITLE); nargs++;
    XtSetArg(args[nargs], XtNiconName, DEFAULT_TITLE); nargs++;
    XtSetArg(args[nargs], XtNdepth, Global.vis.depth); nargs++;
    XtSetArg(args[nargs], XtNvisual, Global.vis.visual); nargs++;
    
    shell = XtAppCreateShell("Canvas", "Canvas",
		   topLevelShellWidgetClass, XtDisplay(GetToplevel(wid)),
			     args, nargs);

    return shell;
}

#define ADDCALLBACK(menu, item, pw, func) \
  XtAddCallback(menu[item].widget, XtNcallback, (XtCallbackProc) func, \
		(XtPointer) pw);

static void
simpleMenuPopup(Widget w, XtPointer infoArg, XtPointer junk)
{
    SetFullMenuPopup(!IsFullPopup());
    MenuCheckItem(w, !IsFullPopup());
}

static void
hideMenuBar(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    SetMenuBarHidden(True);
    XtUnmanageChild(info->form);
}

static void
showMenuBar(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    XEvent event;
    SetMenuBarHidden(False);
    XtUnmanageChild(info->viewport);
    XtManageChild(info->form);
    XtManageChild(info->viewport);
    event.type = ConfigureNotify;
    changeSize(GetShell(info->paint), info, &event, NULL);
}

void 
setPasteItemMenu(Widget paint, void *ptr )
{
    WidgetList wlist;
    
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    if (Global.region.pix || Global.region.image) {
        XtVaSetValues(wlist[W_EDIT_PASTE], XtNsensitive, True, NULL);
        XtVaSetValues(wlist[W_TOPMENU+W_EDIT_PASTE], 
                      XtNsensitive, True, NULL);
    }
}

/*
**
 */

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

int 
ImageToMemory(Image * image)
{
    Pixmap pix, mask, source;
    Colormap cmap;
    Widget w = Global.toplevel;
    int i;

    if (!image) return;
    XtVaGetValues(w, XtNcolormap, &cmap, NULL);
    source = (Pixmap)image;
    mask = ImageMaskToPixmap(w, image);
    if (!ImageToPixmap(image, w, &pix, &cmap)) {
       XFreePixmap(XtDisplay(w), mask);
       return -1;
    }
    i = Global.numregions;
    ++Global.numregions;
    Global.regiondata = (RegionData *) 
        realloc(Global.regiondata, Global.numregions*sizeof(RegionData));
    Global.regiondata[i].x = 0;
    Global.regiondata[i].y = 0;
    Global.regiondata[i].sourcepix = source;
    Global.regiondata[i].pix = pix;
    Global.regiondata[i].mask = mask;
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
    return i;
}

static void 
loadMemoryImage(Widget w, char *file, Image *image)
{
    Pixmap pix, mask, source;
    Colormap cmap;
    int i;

    if (!image) return;
    XtVaGetValues(w, XtNcolormap, &cmap, NULL);
    source = (Pixmap)image;
    mask = ImageMaskToPixmap(w, image);
    if (!ImageToPixmap(image, w, &pix, &cmap)) {
       XFreePixmap(XtDisplay(w), mask);
       return;
    }
    i = Global.numregions;
    ++Global.numregions;
    Global.regiondata = (RegionData *) 
        realloc(Global.regiondata, Global.numregions*sizeof(RegionData));
    Global.regiondata[i].x = 0;
    Global.regiondata[i].y = 0;
    Global.regiondata[i].sourcepix = source;
    Global.regiondata[i].pix = pix;
    Global.regiondata[i].mask = mask;
}

static void 
loadMemoryCallback(LocalInfo *info, char *file, Image *image)
{
    loadMemoryImage(info->paint, file, image);
    setMemoryIcon(info->paint, info);
}

static void 
loadMemory(Widget w, XtPointer paintArg, XtPointer junk2)
{
    GetFileName(paintArg, 0, NULL, (XtCallbackProc) loadMemoryCallback, NULL);
}

void
BrushSelectCallback(Widget w, XtPointer junk, XtPointer junk2)
{
    BrushSelect(Global.toplevel);
}

void
ToolSelectCallback(Widget w, XtPointer shell, XtPointer junk2)
{
    Global.canvas = (Widget) shell;
    XtVaSetValues(Global.back, XtNsensitive, True, NULL);    
    XMapRaised(XtDisplay(Global.toplevel), XtWindow(Global.toplevel));
}

static void
PatternSelectCallback(Widget w, XtPointer wlArg, XtPointer junk)
{
    LocalInfo *info = (LocalInfo *) wlArg;
    PatternEdit(info->paint, 
                info->pixels, info->patterns, (void *)info->rcInfo->brushes,
		info->npixels, info->npatterns, info->rcInfo->nbrushes);
}

void 
checkPaintDimension(Widget shell)
{
    Dimension dw, dh;
    int u, v, up, vp, uset=0, vset=0;

    GetPaintWH(&u, &v);
    XtVaGetValues(shell, XtNwidth, &dw, XtNheight, &dh, NULL);
    if (u>50) {
        up = WidthOfScreen(XtScreen(shell));
	if (u > up-12) u = up-12;
#ifdef XAW95
        u -= 4;
#endif
        XtVaSetValues(shell, XtNwidth, u, NULL);
	uset = 1;
	
    }
    if (v>30) {
        vp = HeightOfScreen(XtScreen(shell));
	if (v > vp-40) v = vp-40;
#ifdef XAW95
        v -= 4;
#endif
        XtVaSetValues(shell, XtNheight, v, NULL);
	vset = 1;
    }
    if (!uset) u = dw;
    if (!vset) v = dh;
    if (uset || vset)
        XResizeWindow(XtDisplay(shell), XtWindow(shell), u, v);
}

/*
 * This assumes that you either
 *  - specify a pixmap, in which case width and height are taken from that, or
 *  - specify width and height, in which case the pixmap is not needed.
 * Returns the created PaintWidget.
 */

Widget
graphicCreate(Widget shell, int width, int height, int zoom,
	      Pixmap pix, Colormap cmap)
{
    Display *dpy = XtDisplay(shell);
    Widget form, viewport;
    Widget paint;
    Widget pane, paintbox;
    Widget bar, position, palette;
    Widget colors, tool, brush, font;
    WidgetList wlist;
    Palette *map;
    Dimension u, v;
    int i, j, k, l, m;
    int depth;
    LocalInfo *info = XtNew(LocalInfo);

    if (cmap == None) {
	map = PaletteCreate(shell);
	cmap = map->cmap;
    } else {
	map = PaletteFind(shell, cmap);
    }
    depth = map->depth;
    XtVaSetValues(shell, XtNcolormap, cmap, NULL);
    Global.canvas = shell;
    XtVaSetValues(Global.back, XtNsensitive, True, NULL);

    PaletteAddUser(map, shell);

    info->map = map;
    info->channel = 3;
    info->steps = 2;
    info->palette_pixmap = None;
    info->mem_pixmap = None;
    info->rcInfo = NULL;
    info->mem_index = -1;
    info->mem_shift = 0;

    pane = XtVaCreateManagedWidget("pane",
				   panedWidgetClass, shell,
				   NULL);
    /*
     *	Menu area
     */
    form = XtVaCreateManagedWidget("form",
				   formWidgetClass, pane, 
				   NULL);
    info -> form = form;

    /*
     *	Menu Bar
     */
    bar = MenuBarCreate(form, XtNumber(menuBar), menuBar);
    XtVaSetValues(bar, XtNhorizDistance, -1, XtNvertDistance, 0, NULL);
    XtVaSetValues(form, XtNshowGrip, True, NULL);

    position = XtVaCreateManagedWidget(msgText[POSITION],
				  labelWidgetClass, form,
				  XtNbackground, 
                                     WhitePixelOfScreen(XtScreen(shell)),
				  XtNborderWidth, 1,
                                  XtNwidth, 62,
				  XtNfromHoriz, bar,
				  XtNvertDistance, 4,
				  XtNhorizDistance, 0,
				  NULL);
    info->position = position;
    info->boolpos = True;

    palette = XtVaCreateManagedWidget("palette",
				  coreWidgetClass, form,
                                  XtNwidth, 422, 
				  XtNheight, 10*info->steps-1,
				  XtNfromVert, bar,
				  XtNvertDistance, 0,
				  XtNhorizDistance, 3,
				  NULL);

    info->memory = XtVaCreateManagedWidget("memory",
				  coreWidgetClass, form,
                                  XtNwidth, 15, 
				  XtNheight, 46,
				  XtNborderWidth, 0, 
				  XtNfromHoriz, palette,
				  XtNvertDistance, 4,
				  XtNhorizDistance, 3,
				  NULL);

    colors = XtVaCreateManagedWidget("",
				  commandWidgetClass, form,
                                  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
				  XtNfromHoriz, info->memory,
				  XtNhorizDistance, 3,
				  NULL);
    XtAddCallback(colors, XtNcallback, 
                   (XtCallbackProc) PatternSelectCallback, (XtPointer) info);

    tool = XtVaCreateManagedWidget("tool",
				  commandWidgetClass, form,
                                  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
				  XtNfromHoriz, colors,
				  XtNhorizDistance, 2,
				  NULL);
    XtAddCallback(tool, XtNcallback,
                   (XtCallbackProc) ToolSelectCallback, (XtPointer) shell);

    brush = XtVaCreateManagedWidget("brush",
 				  commandWidgetClass, form,
                                  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
  				  XtNfromHoriz, tool,
  				  XtNhorizDistance, 2,
				  NULL);
    XtAddCallback(brush, XtNcallback,
                   (XtCallbackProc) BrushSelectCallback, (XtPointer) NULL);

    font = XtVaCreateManagedWidget("Abc",
 				  commandWidgetClass, form,
                                  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
  				  XtNfromHoriz, brush,
  				  XtNhorizDistance, 2,
				  NULL);
    XtAddCallback(font, XtNcallback,
        	   (XtCallbackProc) FontSelect, (XtPointer) NULL);
    /*
     *	Drawing Area
     */
    viewport = XtVaCreateWidget("viewport",
				viewportWidgetClass, pane,
				XtNfromVert, form,
				XtNuseBottom, True,
				XtNuseRight, True,
				XtNtop, XtChainTop,
				NULL);
    info->viewport = viewport;

    /*
     *	Custom Drawing Widget here
     */
    paintbox = XtVaCreateWidget("paintBox",
			     boxWidgetClass, viewport,
		             XtNbackgroundPixmap,GetBackgroundPixmap(viewport),
			     NULL);
    info->paintbox = paintbox;
    info->boolcount = 0;

    /*
     *	Try and do something nice for the user
     */
    if (pix != None)
	GetPixmapWHD(dpy, pix, &width, &height, NULL);
    if (zoom == -1 && width <= 64 && height <= 64)
	zoom = 6;

    paint = XtVaCreateManagedWidget("paint",
				    paintWidgetClass, paintbox,
				    XtNdrawWidth, width,
				    XtNdrawHeight, height,
				    XtNzoom, zoom,
				    XtNcolormap, cmap,
				    XtNallowResize, True,
				    XtNundoSize, 4,
				    NULL);

    /* Hack to get a correct display of the menu bar */
    XtVaSetValues(viewport, XtNwidth, 650, XtNheight, 490, NULL);

    XtSetKeyboardFocus(pane, paint);
    zoomAddChild(paint, zoom);

    info->paint = paint;

    OperationSetPaint(paint);
    ccpAddStdPopup(paint, (void *)info);
    prCallback(paint, popupFileMenu[P_FILE_PRINT].widget, True);
    prCallback(paint, popupFileMenu[P_FILE_EXTERN].widget, True);
    prCallback(paint, popupEditMenu[P_EDIT_PASTE].widget, False);
    prCallback(paint, popupSelectorMenu[P_SELECTOR_FATBITS].widget, True);

    XtInsertRawEventHandler(paint, ButtonPressMask,
                      False, (XtEventHandler) buttonHandler, info, XtListHead);
    XtInsertRawEventHandler(paint, PointerMotionMask,
                      False, (XtEventHandler) motionHandler, info, XtListHead);
    XtAddEventHandler(GetToplevel(paint), StructureNotifyMask,
                      False, (XtEventHandler) changeSize, info);
    XtAddEventHandler(form, StructureNotifyMask,
                      False, (XtEventHandler) formHandler, info);
    XtAddEventHandler(position, ButtonPressMask,
                      False, (XtEventHandler) positionHandler, info);
    XtAddEventHandler(info->memory, ButtonPressMask | ButtonReleaseMask,
                      False, (XtEventHandler) memoryHandler, info);
    XtAddEventHandler(palette, ButtonPressMask | ButtonReleaseMask,
                      False, (XtEventHandler) paletteHandler, info);
    XtManageChild(paintbox);
    XtManageChild(viewport);

    ADDCALLBACK(fileMenu, FILE_SAVEAS, paint, StdSaveAsFile);
    ADDCALLBACK(fileMenu, FILE_SAVE, paint, StdSaveFile);
    ccpAddSaveRegion(fileMenu[FILE_SAVE_REGION].widget, paint);
    ADDCALLBACK(fileMenu, FILE_LOAD_MEMORY, info, loadMemory);
    ADDCALLBACK(fileMenu, FILE_REVERT, paint, revertCallback);
    prCallback(paint, fileMenu[FILE_REVERT].widget, False);
    ADDCALLBACK(fileMenu, FILE_PRINT, paint, printCallback);
    ADDCALLBACK(fileMenu, FILE_EXTERN, paint, externCallback);
    XtAddCallback(fileMenu[FILE_CLOSE].widget, XtNcallback, 
		     (XtCallbackProc) closeCallback, (XtPointer) info);

    ccpAddUndo(editMenu[EDIT_UNDO].widget, paint);
    ccpAddRedo(editMenu[EDIT_REDO].widget, paint);
    ADDCALLBACK(editMenu, EDIT_UNDO_SIZE, paint, undosizeCallback);
    ccpAddRefresh(editMenu[EDIT_REFRESH].widget, paint);
    ccpAddCut(editMenu[EDIT_CUT].widget, paint);
    ccpAddCopy(editMenu[EDIT_COPY].widget, paint);
    ccpAddPaste(editMenu[EDIT_PASTE].widget, paint);
    ccpAddClear(editMenu[EDIT_CLEAR].widget, paint);
    ccpAddDuplicate(editMenu[EDIT_DUP].widget, paint);
    ADDCALLBACK(editMenu, EDIT_SELECT_ALL, paint, StdSelectAllCallback);
    ADDCALLBACK(editMenu, EDIT_ERASE_ALL, paint, StdEraseAllCallback);
    ADDCALLBACK(editMenu, EDIT_SNAPSHOT, paint, StdSnapshotCallback);
    ADDCALLBACK(editMenu, EDIT_MEMORY, info, StdMemoryCallback);
    ADDCALLBACK(editMenu, EDIT_RECALL, info, StdRecallCallback);
    ADDCALLBACK(editMenu, EDIT_REMOVE, info, StdRemoveCallback);
    prCallback(paint, editMenu[EDIT_RECALL].widget, False);
    prCallback(paint, editMenu[EDIT_REMOVE].widget, False);

    ADDCALLBACK(regionMenu, REGION_FLIPX, paint, StdRegionFlipX);
    ADDCALLBACK(regionMenu, REGION_FLIPY, paint, StdRegionFlipY);
    for (i = 0; i < XtNumber(rotateMenu); i++) {
	if (rotateMenu[i].name[0] == '\0')
	    continue;

	ADDCALLBACK(rotateMenu, i, paint, rotateTo);
        XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		      (XtPointer) rotateMenu[i].widget);
	prCallback(paint, rotateMenu[i].widget, False);
    }

    ADDCALLBACK(regionMenu, REGION_ROTATE, paint, rotate);
    XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		  (XtPointer) regionMenu[REGION_ROTATE].widget);
    prCallback(paint, regionMenu[REGION_ROTATE].widget, False);

    ADDCALLBACK(regionMenu, REGION_LINEAR, paint, linearRegion);
    ADDCALLBACK(regionMenu, REGION_RESET, paint, resetMat);
    XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		  (XtPointer) regionMenu[REGION_RESET].widget);
    prCallback(paint, regionMenu[REGION_RESET].widget, False);

    ADDCALLBACK(regionMenu, REGION_CROP, paint, cropToRegion);
    ADDCALLBACK(regionMenu, REGION_DELIMIT, paint, StdRegionDelimit);
    ADDCALLBACK(regionMenu, REGION_COMPLEMENT, paint, StdRegionComplement);
    ccpAddCloneRegion(regionMenu[REGION_CLONE].widget, paint);
    XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		  (XtPointer) regionMenu[REGION_CROP].widget);
    prCallback(paint, regionMenu[REGION_CROP].widget, False);
    ADDCALLBACK(regionMenu, REGION_AUTOCROP, paint, autocropCallback);
    ADDCALLBACK(regionMenu, REGION_UNSELECT, paint, unselectRegion);

    ADDCALLBACK(filterMenu, FILTER_INVERT, paint, StdRegionInvert);
    ADDCALLBACK(filterMenu, FILTER_SHARPEN, paint, StdRegionSharpen);
    ADDCALLBACK(filterMenu, FILTER_SMOOTH, paint, doSmooth);
    ADDCALLBACK(filterMenu, FILTER_DIRFILT, paint, StdRegionDirFilt);
    ADDCALLBACK(filterMenu, FILTER_EDGE, paint, StdRegionEdge);
    ADDCALLBACK(filterMenu, FILTER_EMBOSS, paint, StdRegionEmboss);
    ADDCALLBACK(filterMenu, FILTER_OIL, paint, oilPaint);
    ADDCALLBACK(filterMenu, FILTER_NOISE, paint, addNoise);
    ADDCALLBACK(filterMenu, FILTER_SPREAD, paint, doSpread);
    ADDCALLBACK(filterMenu, FILTER_PIXELIZE, paint, doPixelize);
    ADDCALLBACK(filterMenu, FILTER_DESPECKLE, paint, doDespeckle);
    ADDCALLBACK(filterMenu, FILTER_TILT, paint, tiltRegion);
    ADDCALLBACK(filterMenu, FILTER_BLEND, paint, StdRegionBlend);
    ADDCALLBACK(filterMenu, FILTER_SOLARIZE, paint, doSolarize);
    ADDCALLBACK(filterMenu, FILTER_TOGREY, paint, StdRegionGrey);
    ADDCALLBACK(filterMenu, FILTER_CONTRAST, paint, doContrast);
    ADDCALLBACK(filterMenu, FILTER_MODIFY_RGB, paint, doModifyRGB);
    ADDCALLBACK(filterMenu, FILTER_QUANTIZE, paint, doQuantize);
    ADDCALLBACK(filterMenu, FILTER_USERDEF, paint, StdRegionUserDefined);
    ADDCALLBACK(filterMenu, FILTER_LAST, paint, doLast);
    prCallback(paint, filterMenu[FILTER_LAST].widget, False);
    ADDCALLBACK(filterMenu, FILTER_UNDO, paint, StdRegionUndo);
    prCallback(paint, filterMenu[FILTER_UNDO].widget, False);

    ADDCALLBACK(selectorMenu, SELECTOR_PATTERNS, info, PatternSelectCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_CHROMA, paint, selectColorRange);
    ADDCALLBACK(selectorMenu, SELECTOR_BACKGROUND, paint, changeBackground);
    ADDCALLBACK(selectorMenu, SELECTOR_FATBITS, paint, fatCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_TOOLS, GetShell(paint), ToolSelectCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_BRUSH, NULL, BrushSelectCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_FONT, NULL, FontSelect);
    ADDCALLBACK(selectorMenu, SELECTOR_SCRIPT, NULL, ScriptEditor);
    ADDCALLBACK(selectorMenu, SELECTOR_CHANGE_SIZE, paint, sizeCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_SIZE_ZOOM_DEFS, paint, defaultsizeCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_CHANGE_ZOOM, paint, zoomCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_SNAP, paint, snapCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_CHANGE_SNAP, paint, changeSnapCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_GRID, paint, gridCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_SIMPLE, paint, simpleMenuPopup);
    MenuCheckItem(selectorMenu[SELECTOR_SIMPLE].widget, !IsFullPopup());
    ADDCALLBACK(selectorMenu, SELECTOR_HIDE_MENUBAR, info, hideMenuBar);

    /* Store menu widgets for later reference */
    wlist = (WidgetList) XtMalloc(W_NWIDGETS * sizeof(Widget));
    for (i=0; i<W_NWIDGETS; i++) wlist[i] = None;
    wlist[W_FILE_REVERT] = popupFileMenu[P_FILE_REVERT].widget;
    wlist[W_EDIT_PASTE] = popupEditMenu[P_EDIT_PASTE].widget;
    wlist[W_EDIT_RECALL] = popupEditMenu[P_EDIT_RECALL].widget;
    wlist[W_EDIT_REMOVE] = popupEditMenu[P_EDIT_REMOVE].widget;
    wlist[W_REGION_LAST] = popupFilterMenu[P_FILTER_LAST].widget;
    wlist[W_REGION_UNDO] = popupFilterMenu[P_FILTER_UNDO].widget;
    wlist[W_SELECTOR_GRID] = popupSelectorMenu[P_SELECTOR_GRID].widget;
    wlist[W_SELECTOR_SNAP] = popupSelectorMenu[P_SELECTOR_SNAP].widget;

    wlist[W_TOPMENU+W_FILE_REVERT] = fileMenu[FILE_REVERT].widget;
    wlist[W_TOPMENU+W_EDIT_PASTE] = editMenu[EDIT_PASTE].widget;
    wlist[W_TOPMENU+W_EDIT_RECALL] = editMenu[EDIT_RECALL].widget;
    wlist[W_TOPMENU+W_EDIT_REMOVE] = editMenu[EDIT_REMOVE].widget;
    wlist[W_TOPMENU+W_REGION_LAST] = filterMenu[FILTER_LAST].widget;
    wlist[W_TOPMENU+W_REGION_UNDO] = filterMenu[FILTER_UNDO].widget;
    wlist[W_TOPMENU+W_SELECTOR_GRID] = selectorMenu[SELECTOR_GRID].widget;
    wlist[W_TOPMENU+W_SELECTOR_SNAP] = selectorMenu[SELECTOR_SNAP].widget;

    for (i=0; i<=LINE_WIDTH_GENERAL; i++) {
        wlist[W_LINE_WIDTHS+i] = popupLineMenu[i+1].widget;
        wlist[W_TOPMENU+W_LINE_WIDTHS+i] = lineMenu[i].widget;
    }
    for (i=0; i<=10; i++) {
        wlist[W_FONT_DESCR+i] = popupFontMenu[i+(i==10)+1].widget;
        wlist[W_TOPMENU+W_FONT_DESCR+i] = fontMenu[i+(i==10)].widget;
    }

    wlist[W_ICON_PALETTE] = palette;
    wlist[W_ICON_COLORS] = colors;
    wlist[W_ICON_TOOL] = tool;
    wlist[W_ICON_BRUSH] = brush;
    wlist[W_ICON_FONT] = font;
    wlist[W_ICON_COLPIXMAP] = (Widget)
      XCreatePixmap(dpy, DefaultRootWindow(dpy), ICONWIDTH, ICONHEIGHT, depth);
    XtVaSetValues(paint, XtNmenuwidgets, wlist, NULL);
    wlist[W_INFO_DATA] = (Widget) info;

    if (ThereIsNoMenuBar()) XtUnmanageChild(form);

    XtRealizeWidget(shell);
    AddDestroyCallback(shell,
		       (DestroyCallbackFunc) closeCallback, (void *) info);

    XtUnmanageChild(bar);
    XtUnmanageChild(position);
    XtUnmanageChild(palette);
    XtUnmanageChild(info->memory);
    XtUnmanageChild(colors);
    XtUnmanageChild(tool);
    XtUnmanageChild(brush);
    XtUnmanageChild(font);

    XMapWindow(dpy, XtWindow(bar)); 
    XMapWindow(dpy, XtWindow(position)); 
    XMapWindow(dpy, XtWindow(palette));
    XMapWindow(dpy, XtWindow(info->memory));  
    XMapWindow(dpy, XtWindow(colors)); 
    XMapWindow(dpy, XtWindow(tool)); 
    XMapWindow(dpy, XtWindow(brush)); 
    XMapWindow(dpy, XtWindow(font)); 

    XtVaSetValues(colors, XtNbackgroundPixmap, 
                          (Pixmap) wlist[W_ICON_COLPIXMAP], 
		          XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT, NULL);

    XtVaGetValues(shell, XtNwidth, &u, XtNheight, &v, NULL);
    if (u < width+10) {
        u = width+10;
    }
    if (u > WidthOfScreen(XtScreen(shell)) - 20) 
        u = WidthOfScreen(XtScreen(shell)) - 20;
    if (v > HeightOfScreen(XtScreen(shell)) - 80) 
        v = HeightOfScreen(XtScreen(shell)) - 80;
#ifdef XAW95
    XtVaSetValues(shell, XtNwidth, u+4, XtNheight, v+4, NULL);
#else
    XtVaSetValues(shell, XtNwidth, u, XtNheight, v, NULL);
#endif

    SetIconImage(shell);
    setPalettePixmap(paint, info, 0);
    setToolIconPixmap(paint, NULL);
    setCanvasColorsIcon(paint);
    setBrushIconPixmap(paint, NULL);
    setFontIcon(paint);
    setMemoryIcon(paint, info);

    if (pix) {
       XRectangle rect;
       rect.width = width;
       rect.height = height;
       PwRegionSet(paint, &rect, pix, None);
       PwRegionSet(paint, None, None, None);
    }

    if (head) {
        struct paintWindows *cur;
        wlist = NULL;
	cur = head;
	while (!wlist && cur) {
            XtVaGetValues(cur->paint, XtNmenuwidgets, &wlist, NULL);
	    cur = cur->next;
	}
    }
    if (head && wlist) {
        i = 0;
        XtVaGetValues(head->paint, XtNlineWidth, &i, NULL);
        for (j=0; j<=5; j++) 
	    if (IsItemChecked(wlist[W_LINE_WIDTHS+j])) break;
        for (k=0; k<=10; k++) 
	    if (IsItemChecked(wlist[W_FONT_DESCR+k])) break;
	l = IsItemChecked(wlist[W_SELECTOR_GRID]);
	m = IsItemChecked(wlist[W_SELECTOR_SNAP]);
    } else {
        i = 0;
	j = 0;
	k = 1;
	l = 0;
	m = 0;
    }

    GraphicAdd(paint);

    GraphicAll(setLineWidth, (void *)i);
    GraphicAll(setLineWidthMenu, (void *)j);
    GraphicAll(fontMenuCallback, (void *)(k+1));
    if (k==10) {
        XFontStruct * info;
        XtVaGetValues(head->paint, XtNfont, &info, NULL);
        XtVaSetValues(paint, XtNfont, info, NULL);
    }
    GraphicAll(setGridMenu, (void *)l);
    GraphicAll(setSnapMenu, (void *)m);
    GraphicAll(setPasteItemMenu, NULL);

/* Avoid unnecessary scrollbars */
    {
    Dimension w, h, wp, hp;
    XtVaGetValues(paint, XtNwidth, &w, XtNheight, &h, NULL);
    XtVaGetValues(viewport, XtNwidth, &wp, XtNheight, &hp, NULL);
#ifdef XAW95
    if (wp >= w+14 && hp >= h+14)
#else
    if (wp >= w+10 && hp >= h+10)
#endif
        XtVaSetValues(viewport, XtNallowHoriz, False,
                                XtNallowVert, False, NULL);
    else {
        XtVaSetValues(viewport, XtNallowHoriz, True,
                                XtNallowVert, True, NULL);
#ifndef XAW95
        XtUnmanageChild(paint);
        XtManageChild(paint);
#endif
    }
    }
    checkPaintDimension(shell);

    return paint;
}

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

static CanvasWriteInfo *cwiHead = NULL;

static 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;
}

Widget
GraphicOpenFileZoom(Widget w, char *file, XtPointer imageArg, int zoom)
{
    Image *image = (Image *) imageArg;
    Colormap cmap;
    Pixmap pix = None;
    Widget shell = makeGraphicShell(w);
    CanvasWriteInfo *ci = XtNew(CanvasWriteInfo);
    PaintWidget paint;

    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 ((paint = (PaintWidget)
	     graphicCreate(shell, 0, 0, zoom, pix, cmap)) != None) {
	    char *cp = strrchr(file, '/');
	    if (cp == NULL)
		cp = file;
	    else
		cp++;

	    XtVaSetValues(shell, XtNiconName, cp, XtNtitle, file, NULL);
	    cp = xmalloc(strlen(file) + 1);
	    strcpy(cp, file);
	    paint->paint.filename = cp;
	    EnableRevert((Widget) paint);
	    return (Widget) paint;
	} else {
	    XtDestroyWidget(shell);
	}
    } else {
	Notice(w, msgText[UNABLE_TO_CREATE_PAINT_WINDOW_WITH_IMAGE]);
	XtDestroyWidget(shell);
    }
    return NULL;
}

void
GraphicOpenFile(Widget w, XtPointer fileArg, XtPointer imageArg)
{
    int zoom;
    GetInitZoom(&zoom);
    GraphicOpenFileZoom(w, (char *) fileArg, imageArg, zoom);
}

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

/*
 * 0: Create new (blank) canvas
 * 1: Open a file
 * 2: Create new (blank) canvas, querying for size
 */
void
GraphicCreate(Widget wid, int value)
{
    int width, height, zoom;

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


/* This starts what used to be cutCopyPaste.c */

static void 
setToSelectOp(void)
{
    static String names[2] =
    {"selectBox", "selectArea"};
    OperationSet(names, 2);
}

static void 
selectionLost(Widget w, Atom * selection)
{
    selectionOwner = False;
}
static void 
selectionDone(Widget w, Atom * selection, Atom * target)
{
    /* empty */
}
static Boolean
selectionConvert(Widget w, Atom * selection, Atom * target,
		 Atom * type, XtPointer * value, unsigned long *len,
		 int *format)
{
    Pixmap src = Global.region.pix;
    Pixmap *pix, np;
    GC gc;
    int wth, hth, dth;
    static Atom A_TARGETS = 0;

    if (src == None)
	return False;

    if (A_TARGETS == 0)
      A_TARGETS = XInternAtom (XtDisplay(w), "TARGETS", False);

    if (*target == A_TARGETS)
      {
	Atom *targets;
	int num_targets = 3;

	targets = (Atom *) XtMalloc (sizeof (Atom) * num_targets);
	targets[0] = XA_PIXMAP;
	targets[1] = XA_BITMAP;
	targets[2] = A_TARGETS;
	*type = XA_ATOM;
	*value = (XtPointer) targets;
	*len = num_targets;
	*format = 32;
	return True;
      }
    

    if (*target != XA_PIXMAP && *target != XA_BITMAP)
	return False;

    pix = XtNew(Pixmap);

    GetPixmapWHD(XtDisplay(w), src, &wth, &hth, &dth);
    np = XCreatePixmap(XtDisplay(w), XtWindow(w), wth, hth, dth);
    gc = XCreateGC(XtDisplay(w), np, 0, 0);
    XCopyArea(XtDisplay(w), src, np, gc, 0, 0, wth, hth, 0, 0);
    XFreeGC(XtDisplay(w), gc);

    *pix = np;

    *type = XA_PIXMAP;
    *len = 1;
    *format = 32;
    *value = (XtPointer) pix;

    return True;
}

static void 
setMemoryRegion(Widget w, int x, int y,
                Pixmap source, Pixmap pix, Pixmap mask)
{
    Display *dpy = XtDisplay(w);
    GC gc;
    int i, width, height, depth;

    GetPixmapWHD(dpy, pix, &width, &height, &depth);
    i = Global.numregions;
    ++Global.numregions;
    Global.regiondata = (RegionData *) 
        realloc(Global.regiondata, Global.numregions*sizeof(RegionData));
    Global.regiondata[i].x = x;
    Global.regiondata[i].y = y;
    Global.regiondata[i].sourcepix = source;
    Global.regiondata[i].pix = XCreatePixmap(dpy, DefaultRootWindow(dpy),
                                             width, height, depth);
    gc = XCreateGC(dpy, pix, 0, 0);
    XCopyArea(dpy, pix, Global.regiondata[i].pix, gc, 0, 0,
	      width, height, 0, 0);
    XFreeGC(dpy, gc);
    Global.regiondata[i].mask = None;
    if (mask) {
        Global.regiondata[i].mask = 
            XCreatePixmap(dpy, DefaultRootWindow(dpy), width, height, 1);
        gc = XCreateGC(dpy, Global.regiondata[i].mask, 0, 0);
        XCopyArea(dpy, mask, Global.regiondata[i].mask, gc, 0, 0,
	          width, height, 0, 0);
        XFreeGC(dpy, gc);
    }
}

void 
StdMemoryCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    LocalInfo * info = (LocalInfo *) paintArg;
    Widget paint = info->paint;
    Display * dpy = XtDisplay(paint);
    Pixmap pix, mask;
    XRectangle rect;
    int i, width, height, depth, all = 0;
    PaintWidget pw = (PaintWidget) paint;

    if (!PwRegionGet(paint, &pix, None)) {
	XtVaGetValues(paint, XtNdrawWidth, &width, 
                             XtNdrawHeight, &height, NULL);
	rect.x = 0;
        rect.y = 0;
        rect.width = width;
        rect.height = height;
        PwRegionSet(paint, &rect, None, None);
	all = 1;
    }

    if (!PwRegionGet(paint, &pix, &mask)) return;

    /* bail out if region already in memory */
    for (i=0; i<Global.numregions; i++)
        if (Global.regiondata[i].sourcepix == pw->paint.region.source) return;

    setMemoryRegion(paint, pw->paint.region.rect.x, pw->paint.region.rect.y,
		    pw->paint.region.source, pix, mask);
    setMemoryIcon(paint, info);

    if (all) PwRegionSet(paint, None, None, None);
}

static void
recallOkCallback(Widget paint, XtPointer paintArg, XtPointer infoArg)
{
    LocalInfo *localinfo = (LocalInfo *) paintArg;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int n = atoi(info->prompts[0].rstr) - 1;

    if (n < 0 || n >= Global.numregions) {
	Notice(localinfo->paint, msgText[IMAGE_NUMBER_OUT_OF_RANGE]);
	return;
    }
    localinfo->mem_index = n;
    recallCallback(localinfo->paint, localinfo, NULL);
    setMemoryIcon(localinfo->paint, localinfo);
}

void
StdRecallCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    LocalInfo *localinfo = (LocalInfo *) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    static char buf[5];
    static char title[80];

    if (Global.numregions == 0) return;
    if (Global.numregions == 1) {
       localinfo->mem_index = 0;
       recallCallback(localinfo->paint, localinfo, NULL);
       setMemoryIcon(localinfo->paint, localinfo);
       return;   
    }

    if (localinfo->mem_index>=0)
       sprintf(buf, "%d", localinfo->mem_index+1);
    else
       *buf = '\0';

    value[0].prompt = msgText[IMAGE_NUMBER];
    value[0].str = buf;
    value[0].len = 3;
    info.prompts = value;
    sprintf(title, "%s (n = 1...%d)",
            msgText[RECALL_AN_IMAGE], Global.numregions);
    info.title = title;
    info.nprompt = 1;

    TextPrompt(localinfo->paint, "mem_recall", 
               &info, recallOkCallback, NULL, localinfo);
}

void 
removeOkCallback(Widget w, XtPointer paintArg,  XtPointer infoArg)
{
    LocalInfo *localinfo = (LocalInfo *) paintArg;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int n = atoi(info->prompts[0].rstr) - 1;

    if (n < 0 || n >= Global.numregions) {
	Notice(localinfo->paint, msgText[IMAGE_NUMBER_OUT_OF_RANGE]);
	return;
    }
    removeFromMemory(XtDisplay(localinfo->paint), n);
    localinfo->mem_index = -1;
    setMemoryIcon(localinfo->paint, localinfo);
}

void 
StdRemoveCallback(Widget w, XtPointer paintArg,  XtPointer junk2)
{
    LocalInfo *localinfo = (LocalInfo *) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    static char buf[5];
    static char title[80];

    if (!Global.numregions) return;

    if (localinfo->mem_index>=0)
       sprintf(buf, "%d", localinfo->mem_index+1);
    else
       *buf = '\0';

    value[0].prompt = msgText[IMAGE_NUMBER];
    value[0].str = buf;
    value[0].len = 3;
    info.prompts = value;
    sprintf(title, "%s (n = 1...%d)",
            msgText[REMOVE_AN_IMAGE], Global.numregions);
    info.title = title;
    info.nprompt = 1;

    TextPrompt(localinfo->paint, "mem_remove", 
               &info, removeOkCallback, NULL, localinfo);
}

void 
StdCopyCallback(Widget w, XtPointer paintArg, String * nm, XEvent * event)
{
    Widget paint = (Widget) paintArg;
    Pixmap pix, mask;
    int width, height;

    if (!PwRegionGet(paint, &pix, &mask))
	return;

    GetPixmapWHD(XtDisplay(paint), pix, &width, &height, NULL);

    if (Global.region.pix != None)
	XFreePixmap(XtDisplay(paint), Global.region.pix);
    if (Global.region.mask != None)
	XFreePixmap(XtDisplay(paint), Global.region.mask);

    Global.region.pix = pix;
    Global.region.mask = mask;
    Global.region.width = width;
    Global.region.height = height;

    XtVaGetValues(paint, XtNcolormap, &Global.region.cmap, NULL);

    selectionOwner = XtOwnSelection(paint, XA_PRIMARY, Global.currentTime,
			 selectionConvert, selectionLost, selectionDone);

    GraphicAll(setPasteItemMenu, NULL);
}

static void 
stdPasteCB(Widget paint, XtPointer infoArg, Atom * selection, Atom * type,
	   XtPointer value, unsigned long *len, int *format)
{
    selectInfo *info = (selectInfo *) infoArg;
    Display *dpy = XtDisplay(paint);
    XRectangle rect;
    Pixmap pix;
    Colormap cmap;
    Pixmap newMask = None;
    GC gc;

    if (type != NULL) {
	info->count--;
	if (*type == XA_BITMAP) {
	    int wth, hth, dth = 0;
	    Pixmap pix = *(Pixmap *) value;

            if (pix)
	        GetPixmapWHD(dpy, pix, &wth, &hth, &dth);
	    if (info->pixmap == None ||
		info->depth < dth) {
		info->pixmap = pix;
		info->width = wth;
		info->height = hth;
		info->depth = dth;
	    }
	} else if (*type == XA_PIXMAP) {
	    int wth, hth, dth = 0;
	    Pixmap pix = *(Pixmap *) value;

            if (pix)
	        GetPixmapWHD(dpy, pix, &wth, &hth, &dth);
	    if (info->pixmap == None ||
		info->depth < dth) {
		info->pixmap = pix;
		info->width = wth;
		info->height = hth;
		info->depth = dth;
	    }
	}
	/*
	**  Are there more possible selections coming?
	 */
	if (info->count != 0)
	    return;

	/*
	**  Now that we have gotten all of the selections
	**    use the best one.
	 */
	if (info->pixmap != None) {
	    Pixmap np;
	    GC gc;

	    if (Global.region.pix != None)
		XFreePixmap(dpy, Global.region.pix);
	    if (Global.region.mask != None)
		XFreePixmap(dpy, Global.region.mask);
	    if (Global.region.image != NULL)
		ImageDelete((Image *) Global.region.image);

	    Global.region.pix = None;
	    Global.region.mask = None;
	    Global.region.image = NULL;

	    np = XCreatePixmap(dpy, XtWindow(paint),
			       info->width, info->height, info->depth);
	    gc = XCreateGC(dpy, np, 0, 0);
	    XCopyArea(dpy, info->pixmap, np, gc,
		      0, 0,
		      info->width, info->height,
		      0, 0);
	    XFreeGC(dpy, gc);

	    Global.region.width = info->width;
	    Global.region.height = info->height;
	    Global.region.pix = np;

	    if (info->depth == 1)
		Global.region.cmap = -1;
	    else
		Global.region.cmap = DefaultColormapOfScreen(XtScreen(paint));
	}
	XtFree((XtPointer) info);
    }
    /*
    **	No valid selections anywhere or we own the selection.
     */
    if (Global.region.pix == None && Global.region.image == NULL)
	return;

    rect.x = 0;
    rect.y = 0;
    rect.width = Global.region.width;
    rect.height = Global.region.height;

    if (Global.region.mask != None) {
	newMask = XCreatePixmap(dpy, XtWindow(paint), rect.width, rect.height, 1);
	gc = XCreateGC(dpy, newMask, 0, 0);
	XCopyArea(dpy, Global.region.mask, newMask, gc,
		  0, 0, rect.width, rect.height, 0, 0);
	XFreeGC(dpy, gc);
    } else if (Global.region.image != NULL &&
	       ((Image *) Global.region.image)->maskData != NULL) {
	newMask = ImageMaskToPixmap(paint, (Image *) Global.region.image);
    }
    XtVaGetValues(paint, XtNcolormap, &cmap, NULL);
    if (cmap != Global.region.cmap) {
	Image *image;
	Pixmap npix = None;

	if (rect.width * rect.height > 1024)
	    StateSetBusy(True);

	if (Global.region.pix == None) {
	    image = (Image *) Global.region.image;
	    image->refCount++;
	} else {
	    image = PixmapToImage(paint, Global.region.pix, Global.region.cmap);
	}

	ImageToPixmapCmap(image, paint, &npix, cmap);

	PwRegionSet(paint, &rect, npix, newMask);

	if (rect.width * rect.height > 1024)
	    StateSetBusy(False);
    } else {
	int depth;

	XtVaGetValues(paint, XtNdepth, &depth, NULL);
	pix = XCreatePixmap(dpy, XtWindow(paint), rect.width, rect.height, depth);
	gc = XtGetGC(paint, 0, 0);
	XCopyArea(dpy, Global.region.pix, pix, gc,
		  0, 0, rect.width, rect.height, 0, 0);
	XtReleaseGC(paint, gc);

        PwRegionSet(paint, &rect, pix, newMask);
    }
    setToSelectOp();
}

void 
StdPasteCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
    Widget paint = (Widget) paintArg;

    if (!selectionOwner) {
	static Atom targets[2] =
	{XA_PIXMAP, XA_BITMAP};
	XtPointer data[2];
	selectInfo *info = XtNew(selectInfo);

	info->count = XtNumber(targets);
	info->pixmap = None;

	data[0] = (XtPointer) info;
	data[1] = (XtPointer) info;

	XtGetSelectionValues(paint, XA_PRIMARY, targets, 2,
			     stdPasteCB, data, Global.currentTime);
    } else {
	stdPasteCB(paint, NULL, NULL, NULL, 0, NULL, NULL);
    }
}

void 
StdSnapshotCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
     PaintWidget pw = (PaintWidget) paintArg;
     Widget paint = (Widget) paintArg;
     Display *dpy = XtDisplay(paint);
     int i, j, x, y, width, height, zoom, zoomp;
     GC gc;
     
     /* First unselect selected region, if any */
     PwRegionSet(paint, None, None, None);

     /* Ugly way of forcing window refresh under some X displays ... */
     gc = XtGetGC(paint, 0, 0);
     XtVaGetValues(paint, XtNwidth, &width, XtNheight, &height, 
		          XtNzoom, &zoom, NULL);
     if (zoom >= 6)
         zoomp = zoom-1;
     else
         zoomp = zoom;
     if (zoom == 1) 
         XCopyArea(dpy, pw->paint.current, XtWindow(paint), gc, 
             0, 0, width, height, 0, 0);
     else
       for (y=0; y<=205/zoom; y++)
       for (x=40/zoom; x<=165/zoom; x++)
       for (j=0; j<zoomp; j++)
       for (i=0; i<zoomp; i++)
         XCopyArea(dpy, pw->paint.current, XtWindow(paint), gc, 
             x, y, 1, 1, x*zoom+i, y*zoom+j);

     /* Now, really start snapshot ! */
     SnapshotImage(GetToplevel(w), paintArg, 1);

     /* Another ugly way of forcing window refresh */
     XtPopup(XtParent(editMenu[0].widget), XtGrabExclusive);
     XtPopdown(XtParent(editMenu[0].widget));
}

void 
StdClearCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    PwRegionClear(paint);
}

void 
StdCutCallback(Widget w, XtPointer paintArg, String * nm, XEvent * event)
{
    StdCopyCallback(w, paintArg, nm, event);
    StdClearCallback(w, paintArg, NULL);
}

void 
StdDuplicateCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    XRectangle rect;
    Widget paint = (Widget) paintArg;
    Pixmap pix, mask;
    int width, height;

    if (!PwRegionGet(paint, &pix, &mask))
	return;

    GetPixmapWHD(XtDisplay(paint), pix, &width, &height, NULL);

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

    PwRegionSet(paint, &rect, pix, mask);
}

void 
StdSelectAllCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    XRectangle rect;
    int dw, dh;

    XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);

    rect.x = 0;
    rect.y = 0;
    rect.width = dw;
    rect.height = dh;

    PwRegionSet(paint, &rect, None, None);
    setToSelectOp();
}

void 
StdEraseAllCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    XRectangle rect;
    int dw, dh;

    XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);

    rect.x = 0;
    rect.y = 0;
    rect.width = dw;
    rect.height = dh;

    PwRegionSet(paint, &rect, None, None);
    PwRegionClear(paint);
}

void 
StdUndoCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    int haveRegion = 0;
    XRectangle *o, *r;

    /* Only fiddle with the region if it has not been moved or resized */
    o = &pw->paint.region.orig;
    r = &pw->paint.region.rect;
    if ((o->x == r->x) && (o->y == r->y) &&
	(o->width == r->width) && (o->height == r->height))
	haveRegion = PwRegionOff((Widget) pw, True);

    Undo((Widget) pw);
    if (haveRegion)
	PwRegionSet((Widget) pw, &pw->paint.region.rect, None, None);
    XtUnmapWidget((Widget) pw);
    XtMapWidget((Widget) pw);
}

void 
StdRedoCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    int haveRegion = 0;
    XRectangle *o, *r;

    /* Only fiddle with the region if it has not been moved or resized */
    o = &pw->paint.region.orig;
    r = &pw->paint.region.rect;
    if ((o->x == r->x) && (o->y == r->y) &&
	(o->width == r->width) && (o->height == r->height))
	haveRegion = PwRegionOff((Widget) pw, True);

    Redo((Widget) pw);
    if (haveRegion)
	PwRegionSet((Widget) pw, &pw->paint.region.rect, None, None);
    XtUnmapWidget((Widget) pw);
    XtMapWidget((Widget) pw);
}

void 
StdRefreshCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;

    XtUnmapWidget((Widget) pw);
    XtMapWidget((Widget) pw);
}

void 
ClipboardSetImage(Widget w, Image * image)
{
    if (image==NULL) return;

    if (Global.region.pix != None)
	XFreePixmap(XtDisplay(Global.toplevel), Global.region.pix);
    if (Global.region.mask != None)
	XFreePixmap(XtDisplay(Global.toplevel), Global.region.mask);
    if (Global.region.image != NULL)
	ImageDelete(Global.region.image);

    Global.region.pix = None;
    Global.region.mask = None;
    Global.region.cmap = None;
    Global.region.image = (void *) image;
    Global.region.width = image->width;
    Global.region.height = image->height;
}

void 
MemorySetImage(Widget w, Image * image)
{
    if (image==NULL) return;
}

/*
**  End of "edit" menu function, start region menu functions.
**
 */

void 
StdRegionFlipX(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    float v = -1.0;

    PwRegionAddScale(paint, &v, NULL);
}

void 
StdRegionFlipY(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    float v = -1.0;

    PwRegionAddScale(paint, NULL, &v);
}

void 
StdRegionClone(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw  = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Display *dpy;
    Pixel p, bg;
    Pixmap pix, mask, newpix;
    XImage *maskImg = NULL;
    int x, y, width, height, depth, zoom;
    Colormap cmap;
    GC gc;

    dpy = XtDisplay(paint);
    if (PwRegionGet(paint, &pix, &mask)) {
         XtVaGetValues(paint, XtNcolormap, &cmap, XtNzoom, &zoom, NULL); 
         if (pw->paint.region.source) {
	    pix = pw->paint.region.source;
	    mask = pw->paint.region.mask;
	    width = pw->paint.region.rect.width/zoom;
	    height = pw->paint.region.rect.height/zoom;
	 } else {
	    width = pw->paint.region.orig.width/zoom;
	    height = pw->paint.region.orig.height/zoom;
	 }
         gc = XtGetGC(paint, 0, 0);
	 depth = DefaultDepthOfScreen(XtScreen(paint));
	 /* Create newpix, since original pixmap may be destroyed ... */
	 newpix = XCreatePixmap(dpy,
			XtWindow(Global.toplevel), width, height, depth);
	 XCopyArea(dpy, pix, newpix, gc, 0, 0, width, height, 0, 0);
	 if (mask) {
	     maskImg = NewXImage(dpy, NULL, pw->core.depth, width, height);
	     XGetSubImage(dpy, mask, 0, 0, width, height,
		 AllPlanes, ZPixmap, maskImg, 0, 0);
	     XtVaGetValues(paint, XtNbackground, &bg, NULL);
	     XSetForeground(dpy, gc, bg);
	     for (y=0; y<height; y++)
	       for (x=0; x<width; x++) {
		   p = XGetPixel(maskImg, x, y);
		   if (!p) XDrawPoint(dpy, newpix, gc, x, y);
	       }
	     XDestroyImage(maskImg);
	 }
	 
         graphicCreate(makeGraphicShell(Global.toplevel), 
                 0, 0, -1, newpix, cmap);
         XtReleaseGC(paint, gc);
    }
}

void
createRegionFromMaskData(XtPointer paintArg, Boolean *maskdata, Boolean target)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Display *dpy = XtDisplay(paint);
    XRectangle rect;
    Pixmap pix, mask;
    Pixel bg, black, white;
    GC gc = None, gcb = None, gcw = None;
    int u, x, y, dw, dh, depth, zoom;

    white = WhitePixelOfScreen(XtScreen(paint));
    black = BlackPixelOfScreen(XtScreen(paint));
    XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh,
		  XtNbackground, &bg, XtNzoom, &zoom, NULL);
    rect.x = dw;
    rect.y = dh;
    rect.width = 0;
    rect.height = 0;
    for (y=0; y<dh; y++)
      for (x=0; x<dw; x++) {
	u = y * dw + x;
	if (maskdata[u] == target) {
	    if (x<rect.x) rect.x = x;
	    if (y<rect.y) rect.y = y;
	    if (x>rect.width) rect.width = x;
	    if (y>rect.height) rect.height = y;
	}
      }
    if (rect.width<rect.x || rect.height<rect.y) return;
    rect.width = rect.width - rect.x + 1;
    rect.height = rect.height - rect.y + 1;

    depth = pw->core.depth;
    pix = XCreatePixmap(dpy, XtWindow(paint), rect.width, rect.height, depth);
    mask = XCreatePixmap(dpy, XtWindow(paint), rect.width, rect.height, 1);

    gc = XtGetGC(paint, 0, 0);
    XSetForeground(dpy, gc, bg);
    gcw = XCreateGC(dpy, mask, 0, NULL);
    XSetForeground(dpy, gcw, white);
    gcb = XCreateGC(dpy, mask, 0, NULL);
    XSetForeground(dpy, gcb, black);
    XCopyArea(dpy, pw->paint.current, pix, gc,
	           rect.x, rect.y, rect.width, rect.height, 0, 0);
    for (y = 0; y < rect.height; y++)
      for (x = 0; x < rect.width; x++) {
          u = (y + rect.y) * dw + x + rect.x;
	  if (maskdata[u] == target) {
	      XDrawPoint(dpy, mask, gcw, x, y);
              XDrawPoint(dpy, pw->paint.current, gc, x+rect.x, y+rect.y);
          } else {
	      XDrawPoint(dpy, mask, gcb, x, y);
	  }
      }

    PwRegionSet(paint, &rect, pix, mask);
    XtMoveWidget(pw->paint.region.child, rect.x*zoom, rect.y*zoom);
    pw->paint.region.rect.x = rect.x;
    pw->paint.region.rect.y = rect.y;
    XFreeGC(dpy, gc);
    XFreeGC(dpy, gcb);
    XFreeGC(dpy, gcw);
}

void 
StdRegionDelimit(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Display *dpy = XtDisplay(paint);
    XImage * source;
    Boolean *maskdata;
    Pixel corner[4];
    Pixmap pix, mask;
    int u, x0, y0, x, y, dw, dh, depth;

    if (PwRegionGet(paint, &pix, &mask)) return;

    StateSetBusy(True);
    XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);

    depth = pw->core.depth;

    source = NewXImage(dpy, NULL, depth, dw, dh);
    XGetSubImage(dpy, pw->paint.current, 0, 0, dw, dh,
		 AllPlanes, ZPixmap, source, 0, 0);
    for (y = 0, u = 0; y < dh; y += dh - 1)
	for (x = 0; x < dw; x += dw - 1)
	    corner[u++] = XGetPixel(source, x, y);

    if ((corner[0] == corner[1]) || (corner[0] == corner[2]) ||
	(corner[0] == corner[3])) {
	x0 = 0;
	y0 = 0;
    }
    else if ((corner[1] == corner[2]) || (corner[1] == corner[3])) {
	x0 = dw-1;
	y0 = 0;
    }
    else if (corner[2] == corner[3]) {
	x0 = 0;
	y0 = dh-1;
    }
    else {
	XDestroyImage(source);
	StateSetBusy(False);
	return;			/* No two corners have the same colour */
    }

    maskdata = setDrawable(paint, pw->paint.current, source);
    if (!maskdata) {
        XDestroyImage(source);
	return;
    }
    fill(x0, y0, dw, dh);
    XDestroyImage(source);
    createRegionFromMaskData(paint, maskdata, False);
    XtFree((char *)maskdata);
    StateSetBusy(False);
}

void 
StdRegionComplement(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Display *dpy = XtDisplay(paint);
    XImage * source;
    Boolean *maskdata;
    Pixmap pix, mask;
    Pixel bg;
    XRectangle rect;
    int x, y, dw, dh, depth;

    if (!PwRegionGet(paint, &pix, &mask)) return;    

    StateSetBusy(True);
    XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, 
                         XtNbackground, &bg, NULL);
    depth = pw->core.depth;

    rect = pw->paint.region.rect;
    maskdata = (Boolean *)xmalloc(dw * dh * sizeof(Boolean));
    memset(maskdata, False, dw * dh * sizeof(Boolean));

    if (mask) {
        source = NewXImage(dpy, NULL, 1, rect.width, rect.height);
        XGetSubImage(dpy, mask, 0, 0, rect.width, rect.height,
		 AllPlanes, ZPixmap, source, 0, 0);
        for (y=0; y<rect.height; y++)
            for (x=0; x<rect.width; x++) {
	        if (XGetPixel(source, x, y))
	            maskdata[(y+rect.y) * dw + x + rect.x] = True;
	    }
        XDestroyImage(source);
    } else
        for (y=0; y<rect.height; y++)
            for (x=0; x<rect.width; x++)
	        maskdata[(y+rect.y) * dw + x + rect.x] = True;

    PwRegionSet(paint, None, None, None);    
    createRegionFromMaskData(paint, maskdata, False);
    XtFree((char *)maskdata);
    StateSetBusy(False);
}

/*
** This is the function that calls the actual image processing functions.
 */
Image *
ImgProcessSetup(Widget paint)
{
    Display *dpy = XtDisplay(paint);
    GC gc;
    Image *in;
    Pixel bg;
    Palette *pal;
    extern struct imageprocessinfo ImgProcessInfo;
    PaintWidget pw = (PaintWidget) paint;
    int dw, dh, depth;

    StateSetBusy(True);

    ImgProcessFlag = 0;

    /* Select full canvas if no region has been selected */
    if (!PwRegionGet(paint, &ImgProcessPix, None)) {
        XRectangle rect;
	XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);
	rect.x = 0;
        rect.y = 0;
        rect.width = dw;
        rect.height = dh;
        PwRegionSet(paint, &rect, None, None);
    }

    if (!PwRegionGet(paint, &ImgProcessPix, None)) {
	StateSetBusy(False);
	return NULL;	 /* Region selection probably failed */
    }

    GetPixmapWHD(dpy, ImgProcessPix, &dw, &dh, &depth);
    /* free previous unfilterPixmap */
    if (pw->paint.region.unfilterPixmap != None)
       XFreePixmap(dpy, pw->paint.region.unfilterPixmap);
    /* create new unfilterPixmap and copy selected area to it */
    pw->paint.region.unfilterPixmap = XCreatePixmap(dpy, XtWindow(paint),
                       dw, dh, depth);
    gc = XCreateGC(dpy, ImgProcessPix, 0, 0);
    XCopyArea(dpy, ImgProcessPix, pw->paint.region.unfilterPixmap, gc,
	      0, 0, dw, dh, 0, 0);
    XFreeGC(dpy, gc);

    PwRegionTear(paint);
    XtVaGetValues(paint, XtNcolormap, &ImgProcessCmap, NULL);

    XtVaGetValues(paint, XtNbackground, &bg, NULL);
    pal = PaletteFind(paint, ImgProcessCmap);
    ImgProcessInfo.background = PaletteLookup(pal, bg);

    in = PixmapToImage(paint, ImgProcessPix, ImgProcessCmap);
    StateSetBusy(False);
    return in;
}

Image *
ImgProcessFinish(Widget paint, Image * in, Image * (*func) (Image *))
{
    Image *out;

    StateSetBusy(True);
    Global.curpaint = paint;
    out = func(in);
    if (out != in)		/* delete input unless done in place */
	ImageDelete(in);

    ImageToPixmapCmap(out, paint, &ImgProcessPix, ImgProcessCmap);

    if (!ImgProcessFlag)
	PwRegionSetRawPixmap(paint, ImgProcessPix);
    else {
	PwUpdate(paint, NULL, True);
	XtVaSetValues(paint, XtNdirty, True, NULL);
    }

    StateSetBusy(False);

    return out;
}

static void 
stdImgProcess(Widget paint, Image * (*func) (Image *))
{
    Image *in, *out;

    lastFilter = func;
    EnableLast(paint);

    if ((in = ImgProcessSetup(paint)) == NULL)
	return;
    out = ImgProcessFinish(paint, in, func);
    XtUnmapWidget(paint);
    XtMapWidget(paint);
}

void 
StdLastImgProcess(Widget paint)
{
    if (lastFilter != NULL)
	stdImgProcess(paint, lastFilter);
}

void 
StdRegionInvert(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    stdImgProcess(paint, ImageInvert);
}

void 
StdRegionSharpen(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    stdImgProcess(paint, ImageSharpen);
}

void 
StdRegionSmooth(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    stdImgProcess(paint, ImageSmooth);
}

void 
StdRegionEdge(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    stdImgProcess(paint, ImageEdge);
}

void 
StdRegionEmboss(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    stdImgProcess(paint, ImageEmboss);
}

void 
StdRegionOilPaint(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;

    stdImgProcess(paint, ImageOilPaint);
}

void 
StdRegionAddNoise(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageAddNoise);
}

void 
StdRegionSpread(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageSpread);
}

void 
StdRegionBlend(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageBlend);
}

void 
StdRegionPixelize(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImagePixelize);
}

void 
StdRegionDespeckle(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageDespeckle);
}

void 
StdRegionNormContrast(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageNormContrast);
}

void 
StdRegionSolarize(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageSolarize);
}

void 
StdRegionModifyRGB(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageModifyRGB);
}

void 
StdRegionGrey(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageGrey);
}

void 
StdRegionQuantize(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageQuantize);
}


void 
StdRegionUserDefined(Widget w, XtPointer paintArg, XtPointer junk2)
{
    if (!isFilterDefined()) {
       Notice(w, msgText[YOU_MUST_FIRST_COMPILE_A_C_SCRIPT]);
       return;
    }
    stdImgProcess((Widget) paintArg, ImageUserDefined);
}

void 
StdRegionDirFilt(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageDirectionalFilter);
}

void 
StdRegionUndo(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    PaintWidget pw = (PaintWidget) paintArg;
    Display * dpy = XtDisplay(paint);
    Pixmap np;
    GC gc;
    int width, height, depth;

    if (!pw->paint.region.unfilterPixmap) return;
    if (!PwRegionGet(paint, &ImgProcessPix, None)) return;
    GetPixmapWHD(dpy, pw->paint.region.unfilterPixmap, 
                 &width, &height, &depth);
    np = XCreatePixmap(dpy, XtWindow(paint), width, height, depth);
    gc = XCreateGC(dpy, np, 0, 0);
    XCopyArea(dpy, pw->paint.region.unfilterPixmap, np, gc,
                      0, 0, width, height, 0, 0);
    XFreeGC(dpy, gc);
    XFreePixmap(dpy, ImgProcessPix);
    XFreePixmap(dpy, pw->paint.region.unfilterPixmap);
    pw->paint.region.unfilterPixmap = None;
    PwRegionSetRawPixmap(paint, np);
}

void 
StdRegionTilt(Widget w, XtPointer paintArg, XtPointer junk2)
{
    stdImgProcess((Widget) paintArg, ImageTilt);
}

/*
**  Start callback functions
 */
static void 
addFunction(Widget item, Widget paint, XtCallbackProc func)
{
    XtAddCallback(item, XtNcallback, func, (XtPointer) paint);
    XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		  (XtPointer) item);
    XtVaSetValues(item, XtNsensitive, (XtPointer) False, NULL);
}

void 
ccpAddUndo(Widget w, Widget paint)
{
    XtAddCallback(w, XtNcallback, StdUndoCallback, (XtPointer) paint);
}

void 
ccpAddRedo(Widget w, Widget paint)
{
    XtAddCallback(w, XtNcallback, StdRedoCallback, (XtPointer) paint);
}

void 
ccpAddRefresh(Widget w, Widget paint)
{
    XtAddCallback(w, XtNcallback, StdRefreshCallback, (XtPointer) paint);
}

void 
ccpAddCut(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdCutCallback);
}

void 
ccpAddCopy(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdCopyCallback);
}

void 
ccpAddPaste(Widget w, Widget paint)
{
    XtVaSetValues(w, XtNsensitive, 
        (Global.region.pix || Global.region.image)?True:False, NULL);
    XtAddCallback(w, XtNcallback, 
                  (XtCallbackProc) StdPasteCallback, (XtPointer) paint);
}

void 
ccpAddClear(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdClearCallback);
}

void 
ccpAddDuplicate(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdDuplicateCallback);
}

void 
ccpAddSelectAll(Widget w, Widget paint)
{
    XtAddCallback(w, XtNcallback, (XtCallbackProc) StdSelectAllCallback, 
                  (XtPointer) paint);
}

void 
ccpAddEraseAll(Widget w, Widget paint)
{
    XtAddCallback(w, XtNcallback, (XtCallbackProc) StdEraseAllCallback, 
                  (XtPointer) paint);
}

/*
**  Region functions
 */
void 
ccpAddSaveRegion(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdSaveRegionFile);
}

void 
ccpAddCloneRegion(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdRegionClone);
}

#define ADDCALLBACK(menu, item, pw, func) \
  XtAddCallback(menu[item].widget, XtNcallback, (XtCallbackProc) func, \
		(XtPointer) pw);

void 
ccpAddStdPopup(Widget paint, void *info)
{
    int i;

    MenuPopupCreate(paint, "popup-menu", 
                    XtNumber(popupMenu), popupMenu);

    ADDCALLBACK(popupFileMenu, P_FILE_SAVE, paint, StdSaveFile);
    ADDCALLBACK(popupFileMenu, P_FILE_SAVEAS, paint, StdSaveAsFile);
    ccpAddSaveRegion(popupFileMenu[P_FILE_SAVE_REGION].widget, paint);
    ADDCALLBACK(popupFileMenu, P_FILE_LOAD_MEMORY, info, loadMemory);
    ADDCALLBACK(popupFileMenu, P_FILE_REVERT, paint, revertCallback);
    prCallback(paint, popupFileMenu[P_FILE_REVERT].widget, False);
    ADDCALLBACK(popupFileMenu, P_FILE_PRINT, paint, printCallback);
    ADDCALLBACK(popupFileMenu, P_FILE_EXTERN, paint, externCallback);
    if (info)
        XtAddCallback(popupFileMenu[P_FILE_CLOSE].widget, XtNcallback, 
		     (XtCallbackProc) closeCallback, (XtPointer) info);

    ccpAddUndo(popupEditMenu[P_EDIT_UNDO].widget, paint);
    ccpAddRedo(popupEditMenu[P_EDIT_REDO].widget, paint);
    ADDCALLBACK(popupEditMenu, P_EDIT_UNDO_SIZE, paint, undosizeCallback);
    ccpAddRefresh(popupEditMenu[P_EDIT_REFRESH].widget, paint);
    ccpAddCut(popupEditMenu[P_EDIT_CUT].widget, paint);
    ccpAddCopy(popupEditMenu[P_EDIT_COPY].widget, paint);
    ccpAddPaste(popupEditMenu[P_EDIT_PASTE].widget, paint);
    ccpAddClear(popupEditMenu[P_EDIT_CLEAR].widget, paint);
    ccpAddDuplicate(popupEditMenu[P_EDIT_DUP].widget, paint);
    ccpAddSelectAll(popupEditMenu[P_EDIT_SELECT_ALL].widget, paint);
    prCallback(paint, popupEditMenu[P_EDIT_SELECT_ALL].widget, True);
    ccpAddEraseAll(popupEditMenu[P_EDIT_ERASE_ALL].widget, paint);
    prCallback(paint, popupEditMenu[P_EDIT_ERASE_ALL].widget, True);
    ADDCALLBACK(popupEditMenu, P_EDIT_SNAPSHOT, paint, StdSnapshotCallback);
    ADDCALLBACK(popupEditMenu, P_EDIT_MEMORY, info, StdMemoryCallback);
    ADDCALLBACK(popupEditMenu, P_EDIT_RECALL, info, StdRecallCallback);
    ADDCALLBACK(popupEditMenu, P_EDIT_REMOVE, info, StdRemoveCallback);
    prCallback(paint, popupEditMenu[P_EDIT_RECALL].widget, False);
    prCallback(paint, popupEditMenu[P_EDIT_REMOVE].widget, False);

    ADDCALLBACK(popupRegionMenu, P_REGION_FLIPX, paint, StdRegionFlipX);
    ADDCALLBACK(popupRegionMenu, P_REGION_FLIPY, paint, StdRegionFlipY);
    ADDCALLBACK(popupRegionMenu, P_REGION_ROTATE, paint, rotate);
    for (i = 0; i < XtNumber(popupRotateMenu); i++) {
	if (popupRotateMenu[i].name[0] == '\0')
	    continue;

	ADDCALLBACK(popupRotateMenu, i, paint, rotateTo);
        XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		      (XtPointer) popupRotateMenu[i].widget);
	prCallback(paint, popupRotateMenu[i].widget, False);
    }

    XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		  (XtPointer) popupRegionMenu[P_REGION_ROTATE].widget);
    prCallback(paint, popupRegionMenu[P_REGION_ROTATE].widget, False);
    ADDCALLBACK(popupRegionMenu, P_REGION_LINEAR, paint, linearRegion);
    ADDCALLBACK(popupRegionMenu, P_REGION_RESET, paint, resetMat);
    XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		  (XtPointer) popupRegionMenu[P_REGION_RESET].widget);
    prCallback(paint, popupRegionMenu[P_REGION_RESET].widget, False);

    ADDCALLBACK(popupRegionMenu, P_REGION_CROP, paint, cropToRegion);
    XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		  (XtPointer) popupRegionMenu[P_REGION_CROP].widget);
    prCallback(paint, popupRegionMenu[P_REGION_CROP].widget, False);
    ADDCALLBACK(popupRegionMenu, P_REGION_DELIMIT, paint, StdRegionDelimit);
    ADDCALLBACK(popupRegionMenu, P_REGION_COMPLEMENT, paint, StdRegionComplement);
    ccpAddCloneRegion(popupRegionMenu[P_REGION_CLONE].widget, paint);

    ADDCALLBACK(popupRegionMenu, P_REGION_AUTOCROP, paint, autocropCallback);
    ADDCALLBACK(popupRegionMenu, P_REGION_UNSELECT, paint, unselectRegion);


    ADDCALLBACK(popupFilterMenu, P_FILTER_INVERT, paint, StdRegionInvert);
    ADDCALLBACK(popupFilterMenu, P_FILTER_SHARPEN, paint, StdRegionSharpen);
    ADDCALLBACK(popupFilterMenu, P_FILTER_SMOOTH, paint, doSmooth);
    ADDCALLBACK(popupFilterMenu, P_FILTER_DIRFILT, paint, StdRegionDirFilt);
    ADDCALLBACK(popupFilterMenu, P_FILTER_DESPECKLE, paint, doDespeckle);
    ADDCALLBACK(popupFilterMenu, P_FILTER_EDGE, paint, StdRegionEdge);
    ADDCALLBACK(popupFilterMenu, P_FILTER_EMBOSS, paint, StdRegionEmboss);
    ADDCALLBACK(popupFilterMenu, P_FILTER_OIL, paint, oilPaint);
    ADDCALLBACK(popupFilterMenu, P_FILTER_NOISE, paint, addNoise);
    ADDCALLBACK(popupFilterMenu, P_FILTER_SPREAD, paint, doSpread);
    ADDCALLBACK(popupFilterMenu, P_FILTER_PIXELIZE, paint, doPixelize);
    ADDCALLBACK(popupFilterMenu, P_FILTER_TILT, paint, StdRegionTilt);
    ADDCALLBACK(popupFilterMenu, P_FILTER_BLEND, paint, StdRegionBlend);
    ADDCALLBACK(popupFilterMenu, P_FILTER_SOLARIZE, paint, doSolarize);
    ADDCALLBACK(popupFilterMenu, P_FILTER_TOGREY, paint, StdRegionGrey);
    ADDCALLBACK(popupFilterMenu, P_FILTER_CONTRAST, paint, doContrast);
    ADDCALLBACK(popupFilterMenu, P_FILTER_MODIFY_RGB, paint, doModifyRGB);
    ADDCALLBACK(popupFilterMenu, P_FILTER_QUANTIZE, paint, doQuantize);
    ADDCALLBACK(popupFilterMenu, P_FILTER_USERDEF, paint, StdRegionUserDefined);
    ADDCALLBACK(popupFilterMenu, P_FILTER_LAST, paint, doLast);
    prCallback(paint, popupFilterMenu[P_FILTER_LAST].widget, False);
    ADDCALLBACK(filterMenu, FILTER_UNDO, paint, StdRegionUndo);
    prCallback(paint, filterMenu[FILTER_UNDO].widget, False);

    if (info)
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_PATTERNS, info, PatternSelectCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_CHROMA, paint, selectColorRange);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_BACKGROUND, paint, changeBackground);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_FATBITS, paint, fatCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_TOOLS, GetShell(paint), ToolSelectCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_BRUSH, NULL, BrushSelectCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_FONT, NULL, FontSelect);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SCRIPT, NULL, ScriptEditor);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_CHANGE_SIZE, paint, sizeCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SIZE_ZOOM_DEFS, paint, defaultsizeCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_CHANGE_ZOOM, paint, zoomCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SNAP, paint, snapCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_CHANGE_SNAP, paint, changeSnapCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_GRID, paint, gridCallback);
    if (info) {
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_HIDE_MENUBAR, info, hideMenuBar);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SHOW_MENUBAR, info, showMenuBar);
    }

    prCallback(paint, popupFileMenu[P_FILE_PRINT].widget, False);
    prCallback(paint, popupFileMenu[P_FILE_EXTERN].widget, False);
    prCallback(paint, popupEditMenu[P_EDIT_PASTE].widget, True);
    prCallback(paint, popupSelectorMenu[P_SELECTOR_FATBITS].widget, False);
}
