/* +-------------------------------------------------------------------+ */
/* | 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.17 2005/03/20 20:15:32 demailly Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <limits.h>

#include <X11/Intrinsic.h>
#include <X11/Xft/Xft.h>
#ifdef XAW3D
#include <X11/IntrinsicP.h>
#endif
#include <X11/Shell.h>
#include <X11/Xatom.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/xpm.h>

#include "xaw_incdir/Form.h"
#include "xaw_incdir/Viewport.h"
#if !defined(NEXTAW) && !defined(XAW95)
#include "xaw_incdir/Tip.h"
#endif
#ifdef XAW3D
#include "xaw_incdir/ViewportP.h"
#endif
#include "xaw_incdir/Box.h"
#include "xaw_incdir/Label.h"
#include "xaw_incdir/Toggle.h"
#include "xaw_incdir/SmeBSB.h"
#include "xaw_incdir/SmeLine.h"
#include "xaw_incdir/Paned.h"
#include "xaw_incdir/MenuButton.h"
#include "xaw_incdir/SimpleMenu.h"


#ifdef HAVE_COLTOG
#include "ColToggle.h"
#endif

#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 "crc32.h"

#include "xpaintrw/rwTable.h"

static int LINESTYLE_WIDTH = 180;

extern int file_isSpecialImage;
extern int file_transparent;
extern int file_numpages;
extern char *filter_so_file;

extern int WritePNM(char *file, Image * image);
extern void fill(int x, int y, int width, int height);

#define NUMBER_LINEWIDTHS 5 /* 6 items in Line selector */

#define NUMBER_PREDEF_FONTS 12
char *fontNames[NUMBER_PREDEF_FONTS] = {
  "times-12",
  "times-18",
  "times-24",
  "times-18:bold",
  "times-18:italic",
  "times-18:bold:italic",
  "dejavu.sans.mono-12",
  "dejavu.sans.mono-18",
  "dejavu.sans.mono-24",
  "dejavu.sans.mono-18:bold",
  "dejavu.sans.mono-18:italic",
  "dejavu.sans.mono-18:bold:italic"
};

enum {W_ICON_PALETTE=0,
      W_ICON_COLORS,
      W_ICON_COLPIXMAP,
      W_ICON_TOOL,
      W_ICON_BRUSH,
      W_ICON_FONT,
      W_ICON_LINESTYLE,
      W_ICON_LINEPIXMAP,
      W_INFO_DATA,
      W_FILE_REVERT,
      W_EDIT_PASTE,
      W_FILTER_LAST, 
      W_FILTER_UNDO, 
      W_SELECTOR_TRANSPARENT,
      W_SELECTOR_INTERPOLATION,
      W_SELECTOR_GRID,
      W_SELECTOR_SNAP,
      W_SELECTOR_LEFT_SCROLLBAR,
      W_SELECTOR_TOP_SCROLLBAR,
      W_ALPHA_MODES,
      W_MEMORY_STACK = W_ALPHA_MODES + 13,
      W_MEMORY_RECALL,
      W_MEMORY_EDIT,
      W_MEMORY_DISCARD,
      W_MEMORY_SCROLL,
      W_MEMORY_ERASE,
      W_MEMORY_ORIGIN,
      W_MEMORY_TO_ALPHA,
      W_MEMORY_TO_MASK,
      W_MEMORY_TO_RGBA,
      W_MEMORY_MERGE_RGBA,
      W_LINE_WIDTHS, 
      W_FONT_DESCR = W_LINE_WIDTHS + NUMBER_LINEWIDTHS + 1, 
      W_TOPMENU = W_FONT_DESCR + NUMBER_PREDEF_FONTS - W_FILE_REVERT + 1,
      W_NWIDGETS = 2*W_TOPMENU + W_FILE_REVERT + 1
};

int w_edit_paste = W_EDIT_PASTE;

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

static char dashStyleStr[72] = "=";

/* forward references */
static void fontSet(Widget w, void *ptr);
static void StdCloneCanvasCallback(Widget w, XtPointer paintArg, XtPointer junk2);

/* Global data */
imageprocessinfo ImgProcessInfo =
{
    0,				/* mergeType		*/
    5000,			/* mergePercent*100	*/
    0,				/* shiftX		*/
    0,				/* shiftY		*/
    128,                        /* embossBG		*/
    100,                        /* embossRatio		*/
    7,				/* oilArea		*/
    20,				/* noiseDelta		*/
    2,				/* spreadDistance	*/
    4,				/* pixelizeXSize	*/
    4,				/* pixelizeYSize	*/
    3,				/* despeckleMask	*/
    3,				/* smoothMaskSize	*/
    50,				/* solarizeThreshold	*/
    99,				/* contrastW		*/
    2,				/* contrastB		*/
    16,				/* quantizeColors	*/
    1,                          /* interpolate          */
    40, -60,			/* distortion values	*/
    "0",			/* RGB_correction	*/
    0, 255,                     /* RGB_min, RGB_max     */
    2,				/* FSsteps		*/
    128,			/* FSmidrange		*/
    0.0,			/* FSgamma		*/
    0.0,			/* r_gamma		*/
    0.0,			/* g_gamma		*/
    0.0,			/* b_gamma		*/
    { 100, 0, 0, 0,   0, 100, 0, 0,   0, 0, 100, 0 },
    				/* rgb_mat[12]		*/
    4.0,                        /* rescale_x            */
    4.0,                        /* rescale_y            */
    "",				/* projective transform source points */
    "rect  0,0  1,1",		/* projective transform image points */
    "0",                        /* projective transform: inverse ? */
    "1",                        /* perform interpolation ? */    
    1.0, 1.0, 0.0, 0.0,         /* matrix for linear transform */
    NULL,                       /* background color pointer */
    NULL			/* Image* : underlying canvas */
};

static struct paintWindows *head = NULL;

typedef struct {
    Widget shell, pane, form, bar, viewport, paint, paintbox;
    Widget zoom, alpha, resize, position, linestyle, 
           palette, memory, mempopup, colors, tool, brush, font;
    Pixmap palette_pixmap, mem_pixmap;
    Palette *map;
    RCInfo *rcInfo;
    Pixel *pixels;
    Pixmap *patterns;
    Dimension barheight; /* height of menu bar */
    int npatterns, npixels;
    int boolcount, channel, vsteps, boolpos, mem_index;
    int resize_state, key_handler, left_sb, top_sb;
} LocalInfo;

static Boolean selectionOwner = False;
static Widget pwlast;

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

/*
 *  Begin menus
 */

static PaintMenuItem fileMenu[] =
{
#define FILE_OPEN 0
    MI_SIMPLE("open"),
#define FILE_SAVE 1
    MI_SIMPLE("save"),
#define FILE_SAVEAS 2
    MI_SIMPLE("saveas"),
#define FILE_SAVE_REGION 3
    MI_SIMPLE("saveregion"),
#define FILE_LOAD_MEMORY 4
    MI_SIMPLE("load-mem"),
#define FILE_REVERT 5
    MI_SIMPLE("revert"),
#define FILE_LOADED 6
    MI_SIMPLE("loaded"),
#define FILE_PRINT 7
    MI_SIMPLE("print"),
#define FILE_EXTERN 8
    MI_SIMPLE("extern"),
#define FILE_CLOSE 9
    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_SELECT_ALL	11
    MI_SIMPLE("all"),
#define EDIT_UNSELECT	12
    MI_SIMPLE("unselect"),
    MI_SEPARATOR(),     /* 13 */
#define EDIT_DUP	14
    MI_SIMPLE("dup"),
#define EDIT_ERASE_ALL	15
    MI_SIMPLE("erase"),
    MI_SEPARATOR(),     /* 16 */
#define EDIT_CLONE_CANVAS       17
    MI_SIMPLE("clone_canvas"),
#define EDIT_CLONE_CANVAS1	18
    MI_SIMPLE("clonezoom_canvas"),
    MI_SEPARATOR(),     /* 19 */
#define EDIT_SNAPSHOT	20
    MI_SIMPLE("screenshot"),
};

static PaintMenuItem lineMenu[] =
{
    MI_FLAGCB("1", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("2", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("4", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("6", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("8", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_SEPARATOR(),
    MI_FLAGCB("linestyle", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
};

static PaintMenuItem textMenu[] =
{
    MI_FLAGCB("times 12", MF_CHECK | MF_GROUP1, fontSet, 0),
    MI_FLAGCB("times 18", MF_CHECK | MF_GROUP1, fontSet, 1),
    MI_FLAGCB("times 24", MF_CHECK | MF_GROUP1, fontSet, 2),
    MI_FLAGCB("times 18 bold", MF_CHECK | MF_GROUP1, fontSet, 3),
    MI_FLAGCB("times 18 italic", MF_CHECK | MF_GROUP1, fontSet, 4),
    MI_FLAGCB("times 18 bold italic", MF_CHECK | MF_GROUP1, fontSet, 5),
    MI_FLAGCB("dejavu sans mono 12", MF_CHECK | MF_GROUP1, fontSet, 6),
    MI_FLAGCB("dejavu sans mono 18", MF_CHECK | MF_GROUP1, fontSet, 7),
    MI_FLAGCB("dejavu sans mono 24", MF_CHECK | MF_GROUP1, fontSet, 8),
    MI_FLAGCB("dejavu sans mono 18 bold", MF_CHECK | MF_GROUP1, fontSet, 9),
    MI_FLAGCB("dejavu sans mono 18 italic", MF_CHECK | MF_GROUP1, fontSet, 10),
    MI_FLAGCB("dejavu sans mono 18 bold italic", MF_CHECK | MF_GROUP1, fontSet, 11),
    MI_SEPARATOR(),
#define FONT_SELECT	13
    MI_SIMPLE("select"),
    MI_SEPARATOR(),
#define FONT_WRITE	15
    MI_SIMPLE("write"),
#define FONT_TEXINPUT	16
    MI_SIMPLE("texinput"),
#define FONT_TEXOUTPUT	17
    MI_SIMPLE("texoutput"),
};

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

static PaintMenuItem delimitMenu[] =
{
    MI_SEPARATOR(),
    MI_SIMPLE("delimit1"),
    MI_SIMPLE("delimit2"),
    MI_SIMPLE("delimit3"),
    MI_SIMPLE("delimit4"),
    MI_SIMPLE("delimit5"),
    MI_SIMPLE("delimit6"),
    MI_SIMPLE("delimit7"),
};

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_EXPAND 7
    MI_SIMPLE("expand"),
#define REGION_DOWNSCALE 8
    MI_SIMPLE("downscale"),
    MI_SEPARATOR(),	/* 9 */
#define REGION_EDIT    10
    MI_SIMPLE("edit"),
#define REGION_CROP	11
    MI_SIMPLE("crop"),
    MI_SEPARATOR(),	/* 12 */
#define REGION_MASK	13
    MI_SIMPLE("mask"),
#define REGION_MERGE	14
    MI_SIMPLE("merge"),    
    MI_SEPARATOR(),	/* 15 */    
#define	REGION_AUTOCROP	16
    MI_SIMPLE("autocrop"),
#define REGION_COMPLEMENT	17
    MI_SIMPLE("complement"),
#define REGION_DELIMIT	18
    MI_RIGHT("delimit", XtNumber(delimitMenu), delimitMenu),
    MI_SEPARATOR(),	/* 19 */
#define REGION_OCR	20
    MI_SIMPLE("ocr"),
};

static PaintMenuItem filterMenu[] =
{
  /* color manipulation */
#define FILTER_INVERT	0
    MI_SIMPLE("invert"),
#define FILTER_TO_GRAY 1
    MI_SIMPLE("to_grey"),
#define FILTER_CONTRAST 2
    MI_SIMPLE("contrast"),
#define FILTER_GAMMA 3
    MI_SIMPLE("gamma"),
#define FILTER_FLOYD_STEINBERG 4
    MI_SIMPLE("floyd_steinberg"),
#define FILTER_QUANTIZE 5
    MI_SIMPLE("quantize"),
#define FILTER_SOLARIZE 6
    MI_SIMPLE("solarize"),
    MI_SEPARATOR(),		/* 7 */
#define FILTER_SHARPEN	8
    MI_SIMPLE("sharpen"),
#define FILTER_MERGE	9
    MI_SIMPLE("merge"),
  /* remove noise */
#define FILTER_SMOOTH	10
    MI_SIMPLE("smooth"),
#define FILTER_DIRFILT 11
    MI_SIMPLE("dirfilt"),
#define FILTER_DESPECKLE 12
    MI_SIMPLE("despeckle"),
    MI_SEPARATOR(),		/* 13 */
#define FILTER_EDGE	14
    MI_SIMPLE("edge"),
#define FILTER_EMBOSS	15
    MI_SIMPLE("emboss"),
#define FILTER_OIL	16
    MI_SIMPLE("oil"),
#define FILTER_NOISE	17
    MI_SIMPLE("noise"),
#define FILTER_SPREAD	18
    MI_SIMPLE("spread"),
#define FILTER_PIXELIZE  19
    MI_SIMPLE("pixelize"),
  /* special fx */
#define FILTER_DISTORT	20
    MI_SIMPLE("distort"),
#define FILTER_PERSPECTIVE	21
    MI_SIMPLE("perspective"),
#define FILTER_BLEND	22
    MI_SIMPLE("blend"),
    MI_SEPARATOR(),		/* 23 */
#define FILTER_USERDEF 24
    MI_SIMPLE("userdef"),
    MI_SEPARATOR(),		/* 25 */
#define FILTER_LAST	26
    MI_SIMPLE("last"),
#define FILTER_UNDO	27
    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_LUPE		7    
    MI_SIMPLE("magnifier"),
#define	SELECTOR_TABLET		8
    MI_SIMPLE("tablet"),    
#define	SELECTOR_SCRIPT		9
    MI_SIMPLE("c_script"),
#define	SELECTOR_VXPDATA       10
    MI_SIMPLE("vxpdata"),
    MI_SEPARATOR(),             /* 11 */
#define	SELECTOR_CHANGE_SIZE	12
    MI_SIMPLE("size"),
#define	SELECTOR_CHANGE_ZOOM		13
    MI_SIMPLE("zoom"),
#define	SELECTOR_SIZE_ZOOM_DEFS	14
    MI_SIMPLE("size_zoom_defs"),
    MI_SEPARATOR(),             /* 15 */
#define	SELECTOR_TRANSPARENT  16
    MI_FLAG("transparent", MF_CHECK),
#define	SELECTOR_INTERPOLATION  17
    MI_FLAG("interpolation", MF_CHECK),
    MI_SEPARATOR(),             /* 18 */
#define	SELECTOR_SNAP		19
    MI_FLAG("snap", MF_CHECK),
#define	SELECTOR_SNAP_SPACING		20
    MI_SIMPLE("snap_spacing"),
    MI_SEPARATOR(),             /* 21 */
#define	SELECTOR_GRID		22
    MI_FLAG("grid", MF_CHECK),
#define	SELECTOR_GRID_PARAM		23
    MI_SIMPLE("grid_param"),
    MI_SEPARATOR(),             /* 24 */
#define	SELECTOR_SIMPLE		25
    MI_FLAG("simple", MF_CHECK),
#define	SELECTOR_HIDE_MENUBAR	26
    MI_FLAG("hide_menubar", MF_CHECK),
    MI_SEPARATOR(),		/* 27 */
#define	SELECTOR_LEFT_SCROLLBAR 28
    MI_FLAG("left_sb", MF_CHECK),
#define	SELECTOR_TOP_SCROLLBAR 29
    MI_FLAG("top_sb", MF_CHECK),
    MI_SEPARATOR(),		/* 30 */
#define	SELECTOR_HELP		31
    MI_SIMPLECB("help", HelpDialog, "introduction"),
};

#define ZOOMMENU_VALUES 25
static PaintMenuItem zoomMenu[ZOOMMENU_VALUES];

static void
alphaModeCallback(Widget w, XtPointer wlArg, XtPointer junk);

static PaintMenuItem alphaMenu[] =
{
#define ALPHA_MODES	0
    MI_FLAG("mode0", MF_CHECK | MF_GROUP5),
    MI_FLAG("mode1", MF_CHECK | MF_GROUP5),
    MI_FLAG("mode2", MF_CHECK | MF_GROUP5),
    MI_FLAG("mode3", MF_CHECK | MF_GROUP5),
    MI_SEPARATOR(),	/* 4 */
#define ALPHA_RGBEDIT	5
    MI_SIMPLE("rgbedit"),
    MI_SEPARATOR(),	/* 6 */
#define ALPHA_PARAMS	7
    MI_SIMPLE("params"),
    MI_SEPARATOR(),	/* 8 */
#define ALPHA_CREATE	9
    MI_SIMPLE("create"),
#define ALPHA_SET	10
    MI_SIMPLE("set"),
#define ALPHA_EDIT	11
    MI_SIMPLE("edit"),
#define ALPHA_MEMORY	12
    MI_SIMPLE("memory"),
#define ALPHA_DELETE 13
    MI_SIMPLE("delete"),
};

static PaintMenuBar menuBar[] =
{
    {None, "file", XtNumber(fileMenu), fileMenu},
    {None, "edit", XtNumber(editMenu), editMenu},
    {None, "line", XtNumber(lineMenu), lineMenu},
    {None, "text", XtNumber(textMenu), textMenu},
    {None, "region", XtNumber(regionMenu), regionMenu},
    {None, "filter", XtNumber(filterMenu), filterMenu},
    {None, "selector", XtNumber(selectorMenu), selectorMenu},
    {None, "zoom", 0, zoomMenu},
    {None, "alpha", XtNumber(alphaMenu), alphaMenu}
};

static PaintMenuItem memoryMenu[] =
{
#define	MEMORY_NOOP	0
    MI_SEPARATOR(),
#define	MEMORY_STACK	1
    MI_SIMPLE("stack"),
#define	MEMORY_RECALL	2
    MI_SIMPLE("recall"),
#define	MEMORY_EDIT	3
    MI_SIMPLE("edit"),
#define	MEMORY_DISCARD	4
    MI_SIMPLE("discard"),
#define	MEMORY_SCROLL	5
    MI_SIMPLE("scroll"),
    MI_SEPARATOR(),  /* 6 */
#define	MEMORY_ERASE	7
    MI_SIMPLE("erase"),
    MI_SEPARATOR(),	/* 8 */
#define	MEMORY_ORIGIN	9
    MI_SIMPLE("origin"),
    MI_SEPARATOR(),	/* 10 */
#define MEMORY_TO_ALPHA   11
    MI_SIMPLE("to_alpha"),
#define MEMORY_TO_MASK 12
    MI_SIMPLE("to_mask"),
#define MEMORY_TO_RGBA   13
    MI_SIMPLE("to_rgba"),
#define MEMORY_MERGE_RGBA 14
    MI_SIMPLE("merge_rgba"),
};

static PaintMenuItem popupFileMenu[] =
{
    MI_SEPARATOR(),
#define P_FILE_OPEN 1
    MI_SIMPLE("open"),
#define P_FILE_SAVE 2
    MI_SIMPLE("save"),
#define P_FILE_SAVEAS 3
    MI_SIMPLE("saveas"),
#define P_FILE_SAVE_REGION 4
    MI_SIMPLE("saveregion"),
#define P_FILE_LOAD_MEMORY 5
    MI_SIMPLE("load-mem"),
#define P_FILE_REVERT 6
    MI_SIMPLE("revert"),
#define P_FILE_LOADED 7
    MI_SIMPLE("loaded"),
#define P_FILE_PRINT 8
    MI_SIMPLE("print"),
#define P_FILE_EXTERN 9
    MI_SIMPLE("extern"),
#define P_FILE_CLOSE 10
    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_SELECT_ALL 12
    MI_SIMPLE("all"),
#define P_EDIT_UNSELECT	13
    MI_SIMPLE("unselect"),
    MI_SEPARATOR(),		/* 14 */
#define P_EDIT_DUP	15
    MI_SIMPLE("dup"),
#define P_EDIT_ERASE_ALL	16
    MI_SIMPLE("erase"),
    MI_SEPARATOR(),     /* 17 */
#define P_EDIT_CLONE_CANVAS	18
    MI_SIMPLE("clone_canvas"),
#define P_EDIT_CLONE_CANVAS1	19
    MI_SIMPLE("clonezoom_canvas"),
    MI_SEPARATOR(),  /* 20 */
#define P_EDIT_SNAPSHOT	21
    MI_SIMPLE("screenshot"),
};

static PaintMenuItem popupLineMenu[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("1", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("2", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("4", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("6", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_FLAGCB("8", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
    MI_SEPARATOR(),
    MI_FLAGCB("linestyle", MF_CHECK | MF_GROUP3, lineStyleCallback, NULL),
};

static PaintMenuItem popupTextMenu[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("times 12", MF_CHECK | MF_GROUP1, fontSet, 0),
    MI_FLAGCB("times 18", MF_CHECK | MF_GROUP1, fontSet, 1),
    MI_FLAGCB("times 24", MF_CHECK | MF_GROUP1, fontSet, 2),
    MI_FLAGCB("times 18 bold", MF_CHECK | MF_GROUP1, fontSet, 3),
    MI_FLAGCB("times 18 italic", MF_CHECK | MF_GROUP1, fontSet, 4),
    MI_FLAGCB("times 18 bold italic", MF_CHECK | MF_GROUP1, fontSet, 5),
    MI_FLAGCB("dejavu sans mono 12", MF_CHECK | MF_GROUP1, fontSet, 6),
    MI_FLAGCB("dejavu sans mono 18", MF_CHECK | MF_GROUP1, fontSet, 7),
    MI_FLAGCB("dejavu sans mono 24", MF_CHECK | MF_GROUP1, fontSet, 8),
    MI_FLAGCB("dejavu sans mono 18 bold", MF_CHECK | MF_GROUP1, fontSet, 9),
    MI_FLAGCB("dejavu sans mono 18 italic", MF_CHECK | MF_GROUP1, fontSet, 10),
    MI_FLAGCB("dejavu sans mono 18 bold italic", MF_CHECK | MF_GROUP1, fontSet, 11),
    MI_SEPARATOR(),
#define P_FONT_SELECT	14
    MI_SIMPLE("select"),
    MI_SEPARATOR(),
#define P_FONT_WRITE	16
    MI_SIMPLE("write"),
#define P_FONT_TEXINPUT	17
    MI_SIMPLE("texinput"),
#define P_FONT_TEXOUTPUT 18
    MI_SIMPLE("texoutput"),        
};

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

static PaintMenuItem popupDelimitMenu[] =
{
    MI_SEPARATOR(),
    MI_SIMPLE("delimit1"),
    MI_SIMPLE("delimit2"),
    MI_SIMPLE("delimit3"),
    MI_SIMPLE("delimit4"),
    MI_SIMPLE("delimit5"),
    MI_SIMPLE("delimit6"),
    MI_SIMPLE("delimit7"),
};

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_EXPAND 8
    MI_SIMPLE("expand"),
#define P_REGION_DOWNSCALE 9
    MI_SIMPLE("downscale"),
    MI_SEPARATOR(),		/* 10 */
#define P_REGION_EDIT		11
    MI_SIMPLE("edit"),
#define P_REGION_CROP		12
    MI_SIMPLE("crop"),
    MI_SEPARATOR(),		/* 13 */
#define P_REGION_MASK	14
    MI_SIMPLE("mask"),
#define P_REGION_MERGE	15
    MI_SIMPLE("merge"),    
    MI_SEPARATOR(),	/* 16 */  
#define	P_REGION_AUTOCROP	17
    MI_SIMPLE("autocrop"),
#define P_REGION_COMPLEMENT	18
    MI_SIMPLE("complement"),
#define P_REGION_DELIMIT	19
    MI_RIGHT("delimit", XtNumber(popupDelimitMenu), popupDelimitMenu),
    MI_SEPARATOR(),		/* 20 */
#define P_REGION_OCR	21
    MI_SIMPLE("ocr"),
};

static PaintMenuItem popupFilterMenu[] =
{
    MI_SEPARATOR(),             /* 0 */
  /* color manipulation */
#define P_FILTER_INVERT	1
    MI_SIMPLE("invert"),
#define P_FILTER_TO_GRAY   2
    MI_SIMPLE("to_grey"),
#define P_FILTER_CONTRAST 3
    MI_SIMPLE("contrast"),
#define P_FILTER_GAMMA 4
    MI_SIMPLE("gamma"),
#define P_FILTER_FLOYD_STEINBERG 5
    MI_SIMPLE("floyd_steinberg"),
#define P_FILTER_QUANTIZE 6
    MI_SIMPLE("quantize"),
#define P_FILTER_SOLARIZE  7
    MI_SIMPLE("solarize"),
    MI_SEPARATOR(),		/* 8 */
#define P_FILTER_SHARPEN	9
    MI_SIMPLE("sharpen"),
#define P_FILTER_MERGE	10
    MI_SIMPLE("merge"),
  /* remove noise */
#define P_FILTER_SMOOTH	11
    MI_SIMPLE("smooth"),
#define P_FILTER_DIRFILT 12
    MI_SIMPLE("dirfilt"),
#define P_FILTER_DESPECKLE 13
    MI_SIMPLE("despeckle"),
    MI_SEPARATOR(),		/* 14 */
#define P_FILTER_EDGE	15
    MI_SIMPLE("edge"),
#define P_FILTER_EMBOSS	16
    MI_SIMPLE("emboss"),
#define P_FILTER_OIL	17
    MI_SIMPLE("oil"),
#define P_FILTER_NOISE	18
    MI_SIMPLE("noise"),
#define P_FILTER_SPREAD	19
    MI_SIMPLE("spread"),
#define P_FILTER_PIXELIZE  20
    MI_SIMPLE("pixelize"),
  /* special fx */
#define P_FILTER_DISTORT	21
    MI_SIMPLE("distort"),
#define P_FILTER_PERSPECTIVE	22
    MI_SIMPLE("perspective"),
#define P_FILTER_BLEND	23
    MI_SIMPLE("blend"),
    MI_SEPARATOR(),		/* 24 */
#define P_FILTER_USERDEF 25
    MI_SIMPLE("userdef"),
    MI_SEPARATOR(),		/* 26 */
#define P_FILTER_LAST	27
    MI_SIMPLE("last"),
#define P_FILTER_UNDO	28
    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_LUPE		8
    MI_SIMPLE("magnifier"),
#define	P_SELECTOR_TABLET	9
    MI_SIMPLE("magnifier"),
#define	P_SELECTOR_SCRIPT      10
    MI_SIMPLE("c_script"),
#define	P_SELECTOR_VXPDATA     11
    MI_SIMPLE("vxpdata"),
    MI_SEPARATOR(),             /* 12 */
#define	P_SELECTOR_CHANGE_SIZE	13
    MI_SIMPLE("size"),
#define	P_SELECTOR_CHANGE_ZOOM	14
    MI_SIMPLE("zoom"),
#define	P_SELECTOR_SIZE_ZOOM_DEFS	15
    MI_SIMPLE("size_zoom_defs"),
    MI_SEPARATOR(),             /* 16 */
#define	P_SELECTOR_TRANSPARENT        17
    MI_FLAG("transparent", MF_CHECK),
#define	P_SELECTOR_INTERPOLATION        18
    MI_FLAG("interpolation", MF_CHECK),
    MI_SEPARATOR(),             /* 19 */
#define	P_SELECTOR_SNAP	20
    MI_FLAG("snap", MF_CHECK),
#define	P_SELECTOR_SNAP_SPACING	21
    MI_SIMPLE("snap_spacing"),
    MI_SEPARATOR(),             /* 22 */
#define	P_SELECTOR_GRID		23
    MI_FLAG("grid", MF_CHECK),
#define	P_SELECTOR_GRID_PARAM		24
    MI_SIMPLE("grid_param"),
    MI_SEPARATOR(),             /* 25 */
#define	P_SELECTOR_HIDE_MENUBAR	26
    MI_SIMPLE("hide_menubar"),
#define	P_SELECTOR_SHOW_MENUBAR	27
    MI_SIMPLE("show_menubar"),
    MI_SEPARATOR(),             /* 28 */
#define	P_SELECTOR_LEFT_SCROLLBAR 29
    MI_FLAG("left_sb", MF_CHECK),
#define	P_SELECTOR_TOP_SCROLLBAR 30
    MI_FLAG("top_sb", MF_CHECK),
#define P_SELECTOR_HELP		31
    MI_SIMPLECB("help", HelpDialog, "introduction"),
};

static PaintMenuItem popupAlphaMenu[] =
{
    MI_SEPARATOR(),	/* 0 */
#define P_ALPHA_MODES 1
    MI_FLAG("mode0", MF_CHECK | MF_GROUP5),
    MI_FLAG("mode1", MF_CHECK | MF_GROUP5),
    MI_FLAG("mode2", MF_CHECK | MF_GROUP5),
    MI_FLAG("mode3", MF_CHECK | MF_GROUP5),
    MI_SEPARATOR(),	/* 5 */
#define P_ALPHA_RGBEDIT	6
    MI_SIMPLE("rgbedit"),
    MI_SEPARATOR(),	/* 7 */
#define P_ALPHA_PARAMS	8
    MI_SIMPLE("params"),
    MI_SEPARATOR(),	/* 9 */
#define P_ALPHA_CREATE	10
    MI_SIMPLE("create"),
#define P_ALPHA_SET	11
    MI_SIMPLE("set"),
#define P_ALPHA_EDIT	12
    MI_SIMPLE("edit"),
#define P_ALPHA_MEMORY	13
    MI_SIMPLE("memory"),
#define P_ALPHA_DELETE 14
    MI_SIMPLE("delete"),
};

static PaintMenuItem popupMemoryMenu[] =
{
    MI_SEPARATOR(),  /* 0 */
#define P_MEMORY_STACK	1
    MI_SIMPLE("stack"),
#define P_MEMORY_RECALL	2
    MI_SIMPLE("recall"),
#define P_MEMORY_EDIT	3
    MI_SIMPLE("edit"),
#define P_MEMORY_DISCARD	4
    MI_SIMPLE("discard"),
    MI_SEPARATOR(),  /* 5 */
#define P_MEMORY_ERASE	6
    MI_SIMPLE("erase"),
    MI_SEPARATOR(),	/* 7 */
#define P_MEMORY_ORIGIN	8
    MI_SIMPLE("origin"),
    MI_SEPARATOR(),	/* 9 */
#define P_MEMORY_TO_ALPHA   10
    MI_SIMPLE("to_alpha"),
#define P_MEMORY_TO_MASK   11
    MI_SIMPLE("to_mask"),
#define P_MEMORY_TO_RGBA   12
    MI_SIMPLE("to_rgba"),
#define P_MEMORY_MERGE_RGBA 13
    MI_SIMPLE("merge_rgba"),
};

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("Text", XtNumber(popupTextMenu), popupTextMenu),
    MI_RIGHT("Region", XtNumber(popupRegionMenu), popupRegionMenu),
    MI_RIGHT("Filters", XtNumber(popupFilterMenu), popupFilterMenu),
    MI_RIGHT("Selectors", XtNumber(popupSelectorMenu), popupSelectorMenu),
    MI_RIGHT("Alpha", XtNumber(popupAlphaMenu), popupAlphaMenu),
    MI_RIGHT("Memory", XtNumber(popupMemoryMenu), popupMemoryMenu)
};

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

/*
 *  End of menus
 */

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

int
CheckPaintWidget(Widget paint)
{
    struct paintWindows *cur = head;  
    for (cur = head; cur != NULL; cur = cur->next) {
        if (paint == cur->paint) return 1;
    }
    return 0;  
}

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

struct paintWindows *getPaintHead()
{
    return head;
}

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

Widget
GetNonDirtyCanvas()
{
    struct paintWindows *cur;
    PaintWidget pw;
    Widget paint = None;
    WidgetList wlist;
    Boolean flag;

    for (cur = head; cur != NULL; cur = cur->next) {
        pw = (PaintWidget) cur->paint;
        if (pw->paint.paint) continue; /* maybe fatbits ... */
        XtVaGetValues(cur->paint, XtNdirty,&flag, XtNmenuwidgets,&wlist, NULL);
	if (!flag && wlist) {
	    paint = cur->paint;
	    break;
	}
    }
    return paint;
}

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;

    PopdownMenusGlobal();
    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;
	}
        if (wlist[W_ICON_LINEPIXMAP]) {
	    XFreePixmap(dpy, (Pixmap) wlist[W_ICON_LINEPIXMAP]);
        wlist[W_ICON_LINEPIXMAP] = 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.delimitation) {
        if (Global.delimitation->paint == info->paint) {
            free(Global.delimitation);
            Global.delimitation = NULL;
        }
    }
    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 flag;
    XtVaGetValues(info->paint, XtNdirty, &flag, NULL);

    if (flag)
	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.
 */

#if 0 /* remove this clumsy stuff !! */

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;
}
#endif

static void
revertOkCallback(Widget shell, XtPointer arg, XtPointer junk2)
{
    Widget paint = (Widget) arg;
    char *file;
    XtVaGetValues(paint, XtNfilename, &file, NULL);
    loadPrescribedFile(paint, file);
}

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

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

static void
loadedCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    GetFileName(Global.toplevel, BROWSER_LOADED, NULL, GraphicOpenFile, NULL);
}

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

void RefreshWidget(Widget w)
{
    XtUnmapWidget(w);
    XtMapWidget(w);
    XFlush(XtDisplay(w));
}

void RedrawPaintWidget(Widget w)
{
    XRectangle rect;
    PaintWidget pw = (PaintWidget) w;
    rect.x = 0;
    rect.y = 0;
    rect.width = pw->paint.drawWidth;
    rect.height = pw->paint.drawHeight;
    zoomUpdate(pw, True, &rect);
}

void RefreshPaintWidget(Widget w)
{
    RefreshWidget(w);
    RedrawPaintWidget(w);
}

void 
StdRefreshCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    RefreshPaintWidget((Widget) paintArg);
}

/*
 * Toggle the 'transparent' and 'interpolation' menu item.
 */

static void
transparencyCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
    Widget paint = (Widget) paintArg;
    WidgetList wlist;
    int v;

    Global.transparent = !Global.transparent;
    v = (Global.transparent)?1:2;
    XtVaSetValues(paint, XtNtransparent, v, NULL);
    MenuCheckItem(w, Global.transparent);
    RefreshPaintWidget((Widget) paintArg);
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_SELECTOR_TRANSPARENT], Global.transparent);
    MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_TRANSPARENT], Global.transparent);
}

static void
interpolationCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
    Widget paint = (Widget) paintArg;
    WidgetList wlist;

    int v;
    XtVaGetValues(paint, XtNinterpolation, &v, NULL);
    v = !v;
    XtVaSetValues(paint, XtNinterpolation, v, NULL);
    MenuCheckItem(w, v);

    RefreshPaintWidget((Widget) paintArg);

    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_SELECTOR_INTERPOLATION], v);
    MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_INTERPOLATION], v);
}

/*
 * Toggle the 'grid' menu item.
 */

static void 
gridParamOkCallback(Widget w, XtPointer paintArg, XtPointer infoArg)
{
    PaintWidget pw = (PaintWidget) w;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    Display *dpy = XtDisplay(w);
    Colormap cmap = None;
    int gridmode;
    Pixel gridcolor = 0;
    XColor color;

    gridmode = (atoi(info->prompts[0].rstr)&3)|
               ((atoi(info->prompts[1].rstr)>0)<<31);
    XtVaSetValues(w, XtNgridmode, gridmode, NULL);

    if (DefaultDepthOfScreen(XtScreen(w)) > 8) {
        cmap = DefaultColormapOfScreen(XtScreen(w));
        if (XAllocNamedColor(dpy, cmap, info->prompts[2].rstr,
                             &color, &color)!=0)
            gridcolor = color.pixel;
        XtVaSetValues(w, XtNgridcolor, gridcolor, NULL);
    }
    RefreshPaintWidget((Widget)pw);
}

static void 
gridParamCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char buf[10];
    char *num[4] = { "0", "1", "2", "3" };
    int gridmode;
    Display *dpy = XtDisplay(paint);
    Colormap cmap = None;
    Pixel gridcolor;
    XColor color;

    XtVaGetValues(paint, 
                  XtNgridmode, &gridmode, XtNgridcolor, &gridcolor, NULL);

    info.prompts = value;
    info.title = msgText[GRID_DRAWING_MODE];
    info.nprompt = 3;
    value[0].prompt = msgText[GRID_LINES_OR_OTHER];
    value[0].str = num[gridmode&3];
    value[1].prompt = msgText[GRID_USES_SNAP];
    value[1].str = num[(gridmode>>31)&1];
    value[2].prompt = msgText[GRID_COLOR];

    if (DefaultDepthOfScreen(XtScreen(paint)) > 8) {
        cmap = DefaultColormapOfScreen(XtScreen(paint));
        XtVaGetValues(GetShell(paint), XtNcolormap, &cmap, NULL);
        color.pixel = gridcolor;
        XQueryColor(dpy, cmap, &color);
        color.red = color.green = color.blue = 0;
        sprintf(buf, "#%02x%02x%02x", 
                color.red>>8, color.green>>8, color.blue>>8);
    } else
        sprintf(buf, "#000000");
    value[2].str = buf;
    value[0].len = 4;
    value[1].len = 4;
    value[2].len = 10;

    TextPrompt(paint, msgText[GRID_PARAMETERS],
               &info, gridParamOkCallback, NULL, NULL);
}

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);
    RefreshPaintWidget(paint);
}

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

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

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

static void 
setLineWidth(Widget w, void *width)
{
    int i = (int)(long)width;
    XtVaSetValues(w, XtNlineWidth, i, NULL);
    if (Global.patternshell) {
        setPatternLineWidth(Global.patterninfo, i);
        checkPatternLink(w, 1);
    } else
      setCanvasColorsIcon(w, NULL);
    if (i==0) i = 1;
    sprintf(currentLineWidth, "%d", i);
}

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

void
setZoomButtonLabel(Widget paint, int zoom)
{
    WidgetList wlist;
    LocalInfo *info;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    if (!wlist[W_ICON_TOOL]) return;
    info = (LocalInfo *)wlist[W_INFO_DATA];
    if (!info) return;  
    XtVaSetValues(info->zoom, XtNlabel, ZoomToStr(zoom), NULL);
    XtVaSetValues(info->zoom, XtNwidth, Global.btn_height-1, XtNheight, Global.btn_height, NULL);
}

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

void
setCanvasColorsIcon(Widget paint, void *data)
{
    WidgetList wlist;
    static int *x0 = NULL, *x1;
    Pixel bg, fg, lfg;
    Pixmap pix, lpix, colpixmap, linepixmap;
    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-ICONHEIGHT/2)/3;
                x1[y] = ICONWIDTH-3-abs(y-ICONHEIGHT/2)/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 if (pix) {
            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 if (lpix) {
            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);

    if (!wlist[W_ICON_LINESTYLE]) return;
    linepixmap = (Pixmap) wlist[W_ICON_LINEPIXMAP];
    if (linepixmap) {
        gc = XCreateGC(dpy, XtWindow(paint), 0, 0);
        XSetForeground(dpy, gc, bg);
        XFillRectangle(dpy, linepixmap, gc, 0, 0, LINESTYLE_WIDTH, LINESTYLE_HEIGHT);
        if (v_fr == FillSolid)
	    XSetForeground(dpy, gc, fg);
        else {
	    XSetFillStyle(dpy, gc, v_fr);
	    XSetTile(dpy, gc, pix);
	}
        SetCapAndJoin(paint, gc, CapButt, JoinBevel);
        y = LINESTYLE_HEIGHT/2;
        XDrawLine(dpy, linepixmap, gc, 10, y, LINESTYLE_WIDTH-10, y);
        XtVaSetValues(wlist[W_ICON_LINESTYLE],
                      XtNbackgroundPixmap, linepixmap, NULL);
        XCopyArea(dpy, linepixmap, XtWindow(wlist[W_ICON_LINESTYLE]), gc,
                  0, 0, LINESTYLE_WIDTH, LINESTYLE_HEIGHT, 0, 0);
        XFreeGC(dpy, gc);
    }
}

void 
setBrushIconPixmap(Widget paint, void *ptr) 
{
    WidgetList wlist;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    if (!wlist[W_ICON_BRUSH]) return;
    setBrushIconOnWidget(wlist[W_ICON_BRUSH]);
    XtResizeWidget(wlist[W_ICON_BRUSH], ICONWIDTH, ICONHEIGHT,     
#ifdef XAWPLAIN
    1);
#else
    0);
#endif
}

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)),
          pixel;
    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 (!wlist[W_ICON_PALETTE]) 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);
	}
    }
     
    gc = XCreateGC(dpy, XtWindow(paint), 0, 0);
    
    if (!info->palette_pixmap) {
	info->palette_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
			PALETTE_SIZE*CELL_WIDTH+2*EYE_WIDTH,
			CELL_WIDTH*info->vsteps-1, 
			DefaultDepthOfScreen(XtScreen(paint)));
	attributes.valuemask = XpmCloseness;
	attributes.closeness =  40000;
	XtVaGetValues(info->memory, XtNbackground, &pixel, NULL);
	XSetForeground(dpy, gc, pixel);
	XFillRectangle(dpy, info->palette_pixmap, gc, 
		       0, 0, 2*EYE_WIDTH, CELL_WIDTH*info->vsteps-1);
	XSetForeground(dpy, gc, info->pixels[2]);
	x = EYE_WIDTH/12;
	y = -2.44 * EYE_WIDTH;
	XDrawArc(dpy, info->palette_pixmap, gc,
		 -EYE_WIDTH/2, x-1, 3*EYE_WIDTH, 3*EYE_WIDTH, 55*64, 70*64);
	XDrawArc(dpy, info->palette_pixmap, gc,
    		 -EYE_WIDTH/2, x, 3*EYE_WIDTH, 3*EYE_WIDTH, 55*64, 70*64);    
	XDrawArc(dpy, info->palette_pixmap, gc,
    		 -EYE_WIDTH/2, y, 3*EYE_WIDTH, 3*EYE_WIDTH, 235*64, 70*64);
	XDrawArc(dpy, info->palette_pixmap, gc,
    		 -EYE_WIDTH/2, y+1, 3*EYE_WIDTH, 3*EYE_WIDTH, 235*64, 70*64);
	y = 0.52*EYE_WIDTH;
	XDrawArc(dpy, info->palette_pixmap, gc,
    		 EYE_WIDTH-y/2-1, x, y, y, 0, 360*64);
	XDrawArc(dpy, info->palette_pixmap, gc,
    		 EYE_WIDTH-y/2, x+1, y-2, y-2, 0, 360*64);
    }

    XSetForeground(dpy, gc, info->pixels[8]);	    
    x = EYE_WIDTH/5;
    y = 7*EYE_WIDTH/8;
    for (i=1; i<= EYE_WIDTH/12; i++) {
       XDrawLine(dpy, info->palette_pixmap, gc,
	   EYE_WIDTH-4*x, y-x+i, EYE_WIDTH+4*x, y+x+i);
       XDrawLine(dpy, info->palette_pixmap, gc,
	   EYE_WIDTH-4*x, y+x+i, EYE_WIDTH+4*x, y-x+i);       
    }
    
    x = EYE_WIDTH/6;
    y = 0.2*EYE_WIDTH + 1;
    XSetForeground(dpy, gc, info->pixels[(mode==2)? 8 : 3]);
    XFillArc(dpy, info->palette_pixmap, gc,
    	     EYE_WIDTH-y/2-1, x+0.075*EYE_WIDTH, y, y, 0, 360*64);    
    y = 2 * EYE_WIDTH / 3;
    XSetForeground(dpy, gc, info->pixels[(info->channel & 1)? 8 : 5]);
    XDrawArc(dpy, info->palette_pixmap, gc,
    	     x, x+11*EYE_WIDTH/10, y, y, 0, 360*64);
    XDrawArc(dpy, info->palette_pixmap, gc,
    	     x+1, x+11*EYE_WIDTH/10+1, y-2, y-2, 0, 360*64);
    XSetForeground(dpy, gc, info->pixels[(info->channel & 2)? 36  : 2]);
    XFillArc(dpy, info->palette_pixmap, gc,
	     x+EYE_WIDTH, x+11*EYE_WIDTH/10, y, y, 0, 360*64);	

    if (mode) goto finish;

    XSetForeground(dpy, gc, white);
    for (x = 2*EYE_WIDTH+1; x<PALETTE_SIZE*CELL_WIDTH+2*EYE_WIDTH; x++)
    for (y = 0; y<CELL_WIDTH*info->vsteps-1; y++)
	XDrawPoint(dpy, info->palette_pixmap, gc, x, y);

    i = -1;
    for (x = 2*EYE_WIDTH; x<PALETTE_SIZE*CELL_WIDTH+2*EYE_WIDTH; x++) {
	if ((x-2*EYE_WIDTH)%CELL_WIDTH == 0) {
	    XSetForeground(dpy, gc, black);
	    for (y = 0; y<CELL_WIDTH*info->vsteps-1; y++) 
		XDrawPoint(dpy, info->palette_pixmap, gc, x, y);
	    for (k = 1; k<info->vsteps; k++) 
	    for (l = 0; l<CELL_WIDTH; l++) 
		XDrawPoint(dpy, info->palette_pixmap, gc, x+l, CELL_WIDTH*k-1);
	    ++x;
	    ++i;
	}
	for (k = 0; k<info->vsteps; k++) {
	    j = i+k*PALETTE_SIZE;
	    if (j<info->npixels) {
		XSetForeground(dpy, gc, info->pixels[j]);
		for (y = 0; y<=CELL_WIDTH-2; y++)
		    XDrawPoint(dpy, info->palette_pixmap, gc, x, y+CELL_WIDTH*k);
	    } else {
		/* number of rows of pixels */
		y = 1 + (info->npixels-1)/PALETTE_SIZE; 
		if (info->npatterns>(info->vsteps-y)*PALETTE_SIZE)
		    j -= info->npixels;
		else {
		    j -= y*PALETTE_SIZE;
		}
		if (j>=0 && j<info->npatterns) {
		    GetPixmapWHD(dpy, info->patterns[j], 
				      &width, &height, &depth);
		    for (y = 0; y<=CELL_WIDTH-2; y++) {
			XCopyArea(dpy, info->patterns[j], 
			    info->palette_pixmap, gc, 
			    ((x-2*EYE_WIDTH)%CELL_WIDTH)%width, y%height, 
			    1, 1, x, y+CELL_WIDTH*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 SwitchPrimarySecondary(Widget paint)
{
    Pixmap pix = None, pixl = None;
    Pixel p, pl;
    int r, rl; 
		    
    XtVaGetValues(paint, 
	XtNforeground, &p,
	XtNfillRule, &r, XtNpattern, &pix, NULL);
    XtVaGetValues(paint, 
	XtNlineForeground, &pl,
	XtNlineFillRule, &rl, XtNlinePattern, &pixl, NULL);
    XtVaSetValues(paint, 
	XtNlineForeground, p,
	XtNlineFillRule, r, NULL);
    XtVaSetValues(paint, 
	XtNforeground, pl,
	XtNfillRule, rl, NULL);
    if (r == FillTiled)
	XtVaSetValues(paint, XtNlinePattern, pix, NULL);
    if (rl == FillTiled)
	XtVaSetValues(paint, XtNpattern, pixl, NULL);
    setCanvasColorsIcon(paint, NULL);
    if (DoVxp(paint)) {
        strcpy(Global.vxpinput, "\n*switchpatterns");
        RecordVxp(paint);
    }
}

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

    if (!event) return;

    if (event->type == ButtonRelease) {
	if (Global.popped_up) {
	   PopdownMenusGlobal();
	   event->type = None;
	   return;
	}
	Global.popped_parent = None;
	SetWMInputHint(dpy, XtWindow(info->shell));

	if (event->xbutton.x>=0 &&
	    event->xbutton.x<2*EYE_WIDTH &&
	    event->xbutton.y>=0 &&	    
	    event->xbutton.y<2*EYE_WIDTH) {
	    i = event->xbutton.x / EYE_WIDTH;
	    j = 3 * event->xbutton.y / (2 * EYE_WIDTH);	    
	    if (j == 1) {
	        SwitchPrimarySecondary(info->paint);
	    } else
	    if (j == 2) {
	        if (i) {
		   if (info->channel == 2)
		       info->channel = 3;
		   else
		       info->channel = 2;		     
		} else {
		   if (info->channel == 1)
		       info->channel = 3;
		   else
		       info->channel = 1;		     
		}
		setPalettePixmap(info->paint, info, 1);
	    }	    
	    if (j == 0) {
		setPalettePixmap(info->paint, info, 2);
		DoGrabPixel(info->paint, &p, &info->map->cmap);
		j = 1;
		for (i=0; i<info->npixels; i++)
		    if (p == info->pixels[i]) {
			j = 0;
			setPalettePixmap(info->paint, info, 1);
			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 (DoVxp(info->paint)) {
		        sprintf(Global.vxpinput, "\n*color1 %06lx", p);
			RecordVxp(info->paint);
		    }
		}
		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);
		    if (DoVxp(info->paint)) {
		        sprintf(Global.vxpinput, "\n*color2 %06lx", p);
			RecordVxp(info->paint);
		    }		    
		}
		setCanvasColorsIcon(info->paint, NULL);
	    }
	} else
	if (event->xbutton.x>=2*EYE_WIDTH) {
	    i = (event->xbutton.x-2*EYE_WIDTH)/CELL_WIDTH +
		(event->xbutton.y/CELL_WIDTH)*PALETTE_SIZE;
	    if (i<info->npixels) {
		p = info->pixels[i];
		setcolor:
		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 (DoVxp(info->paint)) {
		        sprintf(Global.vxpinput, "\n*color1 %06lx", p);
			RecordVxp(info->paint);
		    }		    		    
		}
		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);
		    if (DoVxp(info->paint)) {
		        sprintf(Global.vxpinput, "\n*color2 %06lx", p);
			RecordVxp(info->paint);
		    }		    		    
		}
	    } else {
		j = 1 + (info->npixels-1)/PALETTE_SIZE; 
		if (info->npatterns>(info->vsteps-j)*PALETTE_SIZE)
		    i -= info->npixels;
		else {
		    i -= j*PALETTE_SIZE;
		}
		if (i<0 || i>=info->npatterns) {
		    p = WhitePixelOfScreen(XtScreen(info->paint));
		    goto setcolor;
		} else
		if (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 (DoVxp(info->paint)) {
		        sprintf(Global.vxpinput, "\n*pattern1 %d", i);
			RecordVxp(info->paint);
		    }		    		   
		}
		if (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);
		    if (DoVxp(info->paint)) {
		        sprintf(Global.vxpinput, "\n*pattern2 %d", i);
			RecordVxp(info->paint);
		    }		    
		}
	    }
	    setCanvasColorsIcon(info->paint, NULL);
	}
    }
}

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, width, vsteps;
    char buf[40];

    if (!info) {
       XtVaGetValues(w, XtNmenuwidgets, &wlist, NULL);
       if (!wlist) return;
       info = (LocalInfo *)wlist[W_INFO_DATA];
    }
    if (!info) return;
    paint = info->paint;
    black = BlackPixelOfScreen(XtScreen(paint));
    white = WhitePixelOfScreen(XtScreen(paint));
    gc = XCreateGC(dpy, XtWindow(paint), 0, 0);
    width = 2+MEMSLOT_SIZE;
    memw = width*MEMORY_HSTEPS+3;
    memh = CELL_WIDTH*info->vsteps+1;
    vsteps = (memh-1)/width;
    
    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 (info->mem_index == -1 || Global.numregions == 0)
       l = -1;
    else
       l = info->mem_index;

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

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

    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);
       for (i = W_MEMORY_RECALL; i<= W_MEMORY_MERGE_RGBA; i++) {
	  if (wlist[i])
	     XtVaSetValues(wlist[i], XtNsensitive, l, NULL);
	  if (wlist[W_TOPMENU+i])
	     XtVaSetValues(wlist[W_TOPMENU+i], XtNsensitive, l, NULL);
       }
       if (wlist[W_TOPMENU+W_MEMORY_SCROLL])
	  XtVaSetValues(wlist[W_TOPMENU+W_MEMORY_SCROLL], XtNsensitive, 
			(Global.numregions>vsteps*MEMORY_HSTEPS), NULL);
    }
}

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

    pix = Global.regiondata[num].pix;
    mask = Global.regiondata[num].mask;
    alpha = Global.regiondata[num].alpha;
    if (pix!=None) XFreePixmap(dpy, pix);
    if (mask!=None) XFreePixmap(dpy, mask);
    if (alpha!=NULL) XtFree((char *)alpha);
    --Global.numregions;
    for (i=num; i<Global.numregions; i++)
	memcpy(&Global.regiondata[i], &Global.regiondata[i+1],
	       sizeof(RegionData));
}

void 
memoryRecallCallback(Widget w, XtPointer paintArg,  XtPointer junk2)
{
    LocalInfo * info = (LocalInfo *) paintArg;
    Display *dpy = XtDisplay(info->paint);
    unsigned char *alpha;
    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);
    alpha = Global.regiondata[info->mem_index].alpha;
    if (alpha) {
	/* ?? ??
	   if (pw->paint.region.alpha) XtFree(pw->paint.region.alpha); */
	pw->paint.region.alpha = (unsigned char *)
	    XtMalloc(rect.width*rect.height);
	memcpy(pw->paint.region.alpha, alpha, rect.width*rect.height);
    }
}

void 
memoryEditCallback(Widget w, XtPointer paintArg,  XtPointer junk2)
{
    LocalInfo * info = (LocalInfo *) paintArg;
    Display * dpy = XtDisplay(info->paint);
    unsigned char *alpha = NULL;
    int width, height, depth, zoom;
    GC gc;
    Pixmap pix;
    Colormap cmap;

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

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

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

    if (Global.regiondata[info->mem_index].alpha) {
	alpha = (unsigned char *) xmalloc(width*height);
	memcpy(alpha, Global.regiondata[info->mem_index].alpha, width*height);
    }

    XtVaGetValues(info->paint, XtNzoom, &zoom, XtNcolormap, &cmap, NULL);
    graphicCreate(makeGraphicShell(Global.toplevel), 
		  width, height, zoom, pix, cmap, alpha);
}

void 
memoryStackCallback(Widget w, XtPointer infoArg,  XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    StdMemorySetCallback(info->paint, info, NULL);
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
}

void 
memoryDiscardCallback(Widget w, XtPointer infoArg,  XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    removeFromMemory(XtDisplay(info->paint), info->mem_index);
    info->mem_index = Global.numregions-1;
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
}

void 
memoryScrollCallback(Widget w, XtPointer infoArg,  XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    RegionData r;
    int i, j;
    int vsteps = (CELL_WIDTH*info->vsteps)/(2+MEMSLOT_SIZE);
    int shift = ((vsteps+1)/2)*MEMORY_HSTEPS;

    if (Global.numregions<=vsteps*MEMORY_HSTEPS) return;

    for (j=1; j<=shift; j++) {
       r = Global.regiondata[0];
       for (i=0; i<=Global.numregions-2; i++) {
	   Global.regiondata[i] = Global.regiondata[i+1];
       Global.regiondata[Global.numregions-1] = r;
       }
    }
    if (info->mem_index >= 0)
       info->mem_index = 
	  (info->mem_index+Global.numregions-shift) % Global.numregions;
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
}

void 
memoryEraseCallback(Widget w, XtPointer infoArg,  XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    int i;

    if (!info) return;
    for (i=Global.numregions-1; i>=0; i--) {
	removeFromMemory(XtDisplay(info->paint), i);
	GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
    }
}

void 
memoryOriginCallback(Widget w, XtPointer infoArg,  XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    int i;

    if (!info) return; 
    i = info->mem_index;
    if (i<0) return;
    Global.regiondata[i].x = 0;
    Global.regiondata[i].y = 0;
}

static void
memoryHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flag)
{
    int i, j, x, y, num, width;

    if (Global.popped_up) {
	if (event->type == ButtonPress)
	    PopdownMenusGlobal();
	return;
    }

    SetWMInputHint(XtDisplay(info->shell), XtWindow(info->shell));
    width = MEMSLOT_SIZE+2;
    num = -1;

    if (event->xbutton.button == 1 &&
	(event->type == ButtonPress || event->type == ButtonRelease)) {
       i = (event->xbutton.x-2)/width;
       j = (event->xbutton.y-2)/width;
       x = 2+width*i;
       y = 2+width*j;
       if (event->xbutton.x>=x && event->xbutton.x<=x+MEMSLOT_SIZE &&
	  event->xbutton.y>=y && event->xbutton.y<=y+MEMSLOT_SIZE) {
	  num = i+MEMORY_HSTEPS*j;
	  if (num>=0 && num<Global.numregions) {
	     if (event->xbutton.button == 1 && event->type == ButtonRelease) {
		info->mem_index = num;
		PwRegionOff(info->paint, True);
		memoryRecallCallback(w, info, NULL);
		GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
	     }
	  }
       } 
    }

    if ( event->xbutton.button == 2 && event->type == ButtonRelease)
       StdMemorySetCallback(w, info, NULL);

    if (event->xbutton.button == 3) {
       if (event->type == ButtonPress)
	   XtPopup(info->mempopup, XtGrabNone);
       else
       if (event->type == ButtonRelease)
	   Global.popped_up = info->mempopup;
    }
}

/* This manages Window resize of main canvas */

static void
shellHandler(Widget w, LocalInfo * l, XEvent * event, Boolean * flag)
{
    Dimension u, v, up, vp, ub, vb;
    static int i = 0;

    if (event->type == ConfigureNotify) {

	XtVaGetValues(l->paint, XtNwidth, &u, XtNheight, &v, NULL);
	XtVaGetValues(l->viewport, XtNwidth, &up, XtNheight, &vp, NULL);

#if defined(XAW3D) || defined(XAW95)
	if (up>=u+14) ub = up-4; else ub = u+10;
	if (vp>=v+14) vb = vp-4; else vb = v+10;
#else
	if (up>=u+14) ub = up; else ub = u+14;
	if (vp>=v+14) vb = vp; else vb = v+14;
#endif
	XtVaSetValues(l->paintbox, XtNwidth, ub, XtNheight, vb, NULL);

#if defined(XAW3D) || defined(XAW95)
	if (up>=u+14 && vp>=v+14)
	    XtVaSetValues(l->viewport, XtNallowVert, False,
				       XtNallowHoriz, False, NULL);
	else {
	  if (vp>=v+30)
	      XtVaSetValues(l->viewport, XtNallowVert, False,
					 XtNallowHoriz, True, NULL);
	  else
	  if (up>=u+30)
	      XtVaSetValues(l->viewport, XtNallowVert, True,
					 XtNallowHoriz, False, NULL);
	  else
	      XtVaSetValues(l->viewport, XtNallowVert, True,
					 XtNallowHoriz, True, NULL);

	  /* For unknown reasons, we need to cycle twice in this case !!! */

	  XtVaSetValues(l->viewport, "sbShiftX2", 
			l->top_sb? (l->left_sb ? 20:4) : 0, NULL);
	  XtVaSetValues(l->viewport, "sbShiftY1", l->left_sb? 0 : 4, NULL);

	  i = (i+1)&1;
	  if (i) {
 	      XtVaSetValues(l->viewport, XtNuseRight, !l->left_sb, NULL);
	      XtVaSetValues(l->viewport, XtNuseBottom, !l->top_sb, NULL);
	      if (flag)
		  XPutBackEvent(XtDisplay(l->viewport), event);
	  }
	}
#else
	if (up>=u+10 && vp>=v+10) {
	    XtVaSetValues(l->viewport, XtNallowVert, False,
				       XtNallowHoriz, False, NULL);
	} else 
	if (up>=u+26) {
	    XtVaSetValues(l->viewport, XtNallowVert, True,
				       XtNallowHoriz, False, NULL);
	} else
	if (vp>=v+26) {
	    XtVaSetValues(l->viewport, XtNallowVert, False,
				       XtNallowHoriz, True, NULL);
	} else {
	    XtVaSetValues(l->viewport, XtNallowVert, True,
				       XtNallowHoriz, True, NULL);
	}
#endif
	/*
	sb = XtNameToWidget(l->viewport, "vertical");
	if (sb && XtIsRealized(sb)) {
	    XtVaSetValues(sb, XtNborderWidth, 0, NULL);
	}
	*/
    }
}

static Pixel
GetPixelByName(Widget w, char *name)
{
    XColor color;
    Colormap cmap;
    cmap = DefaultColormapOfScreen(XtScreen(w));
    if (XAllocNamedColor(XtDisplay(w), cmap, name, &color, &color)!=0)
       return color.pixel;
    else
       return BlackPixelOfScreen(XtScreen(w));
}

void StoreName(Widget w, char *name)
{
    Display *dpy = XtDisplay(w);
    char *cp;

    XStoreName(dpy, XtWindow(w), name);

    if ((cp = strrchr(name, '/')) == NULL)
	cp = name;
    else
	cp++;
    XtVaSetValues(w, XtNtitle, name, XtNiconName, cp, NULL);

    XChangeProperty(dpy, XtWindow(w),
	XInternAtom(dpy, "_NET_WM_NAME", False),
	XInternAtom(dpy, "UTF8_STRING", False),
	8, PropModeReplace, (unsigned char *) name,
	strlen(name));

    SetWMInputHint(dpy, XtWindow(w));
}

void 
SetAlphaMode(Widget paint, int mode)
{
    PaintWidget pw = (PaintWidget) paint;
    WidgetList wlist;
    int i;

    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
   
    if (pw->paint.current.alpha) {
	if (mode == -1) mode = 2;
    } else
	mode = -1;

    if (mode>=0) {
	pw->paint.alpha_mode = mode%4;
	for (i=1; i<=ALPHA_DELETE; i++) {
	    prCallback(paint, wlist[W_ALPHA_MODES+i], True);
	    prCallback(paint, wlist[W_TOPMENU+W_ALPHA_MODES+i], True);
	}
	XtVaSetValues(paint, XtNborderColor, 
		      GetPixelByName(paint, "blue"), NULL);
    } else {
	pw->paint.alpha_mode = 0;
	for (i=1; i<=ALPHA_DELETE; i++) 
	if (i!=ALPHA_PARAMS && i!=ALPHA_CREATE && i!=ALPHA_SET) {
	    prCallback(paint, wlist[W_ALPHA_MODES+i], False);
	    prCallback(paint, wlist[W_TOPMENU+W_ALPHA_MODES+i], False);
	}
	XtVaSetValues(paint, XtNborderColor,
		      BlackPixelOfScreen(XtScreen(paint)), NULL);
    }
    MenuCheckItem(wlist[W_ALPHA_MODES+pw->paint.alpha_mode], True);
    MenuCheckItem(wlist[W_TOPMENU+W_ALPHA_MODES+pw->paint.alpha_mode], True);
}

void
loadPrescribedFile(Widget w, char *file)
{
    Image * image;
    WidgetList wlist = NULL;
    LocalInfo * info = NULL;
    unsigned char *alpha;
    XRectangle rect;
    Colormap  cmap;
    Pixmap pix;
    PaintWidget pw = (PaintWidget) w;
    XEvent event;
    int width, height, widthp, heightp, oldw, oldh, zoom;

    if ((file == NULL) || (*file == 0)) return;

    if ((image = (Image *) ReadMagic(file)) != NULL) {
        if ((char *)image == filter_so_file) return;
        StateSetBusy(True);
        XtVaGetValues(w, XtNzoom, &zoom, XtNcolormap, &cmap, 
		      XtNmenuwidgets, &wlist, NULL);
        if (wlist)
	    info = (LocalInfo *)wlist[W_INFO_DATA];
	
	width = image->width;
	height = image->height;
	XtVaSetValues(w, XtNdrawWidth, width, XtNdrawHeight, height, NULL);
	if (zoom>0) {
	    widthp = width * zoom;
	    heightp = height * zoom;
	} else {
	    widthp = (width-zoom-1) / (-zoom);
	    heightp = (height-zoom-1) / (-zoom);
	}
	XtVaGetValues(w, XtNwidth, &oldw, XtNheight, &oldh, NULL);
	XtVaSetValues(w, XtNwidth, widthp, XtNheight, heightp, NULL);
	rect.x = 0;
	rect.y = 0;
	rect.width = width;
	rect.height = height;
	PwRegionSet(w, NULL, None, None);
	alpha = image->alpha;
	image->alpha = NULL;
	ImageToPixmap(image, w, &pix, &cmap);
	pw->paint.zoomX = 0;
	pw->paint.zoomY = 0;
	pw->paint.downX = 0;
	pw->paint.downY = 0;
	PwRegionSet(w, &rect, pix, None);
	PwRegionSet(w, NULL, None, None);
	XtVaSetValues(w, XtNdirty, False, NULL);
	/* ?? ??
	   if (pw->paint.current.alpha) XtFree(pw->paint.current.alpha); */
	pw->paint.current.alpha = alpha;
	event.type = ConfigureNotify;
	if (info && (widthp!=oldw || heightp != oldh)) {
	    XtResizeWidget(w, widthp, heightp, 1);
	    shellHandler(info->shell, info, &event, NULL);
	    StoreName(info->shell, file);
	}
	SetAlphaMode(w, -1);
	StateSetBusy(False);
    } else {
	Notice(Global.toplevel, msgText[UNABLE_TO_OPEN_INPUT_FILE], file, RWGetMsg());
    }
}

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

static void 
resizeCanvas(LocalInfo *info, int w, int h, int mode)
{
    Dimension u, v;
    Widget parent;
    int wp, hp, w1, h1, w2, h2, d, z;
    static int n=-1;
    char buf[16];

    XtVaGetValues(info->paint, XtNzoom, &z, 
		  XtNdrawWidth, &w1, XtNdrawHeight, &h1, NULL);
    parent = XtParent(XtParent(info->paint));
    XtVaGetValues(parent, XtNwidth, &u, XtNheight, &v, NULL);

    XtUnmanageChild(info->paint);
    if (mode == 1) {
       if (w < w1 ||  h < h1) {
           if (w - w1 > h - h1) h = h1 ; else w = w1;
       }
    }
    if (w<=0) w = 1;
    if (h<=0) h = 1;
    
    if (info->resize_state != 1 && (w != w1 || h != h1)) {
	n = Global.numregions;
	StdMemorySetCallback(info->paint, info, NULL);
	if (Global.numregions == n) n = -1;
    }

    /* Popdown fatbit window, if any, to avoid problems */
    /* FatbitsEditDestroy(info->paint); */

    if (z==0 || z==-1) z = 1;
    if (z>0) {
	wp = w*z; hp = h*z; 
    } else {
	z = -z;
	wp = (w+z-1)/z; hp = (h+z-1)/z; 
    }

    /* This has the effect of resizing pixmap and alpha channel buffer */
    XtVaSetValues(info->paint, XtNdrawWidth, w, XtNdrawHeight, h,
		  XtNwidth, wp, XtNheight, hp, XtNdirty, True, NULL);

#ifdef XAW95
    if (u >= wp+14 && v >= hp+14)
#else
    if (u >= wp+10 && v >= hp+10)
#endif
	XtVaSetValues(parent, XtNallowVert, False, XtNallowHoriz, False, NULL);
    else
	XtVaSetValues(parent, XtNallowVert, True, XtNallowHoriz, True, NULL);
    FatbitsUpdate(info->paint, 0);
    XtManageChild(info->paint);

    if (info->resize_state == 1 && Global.numregions && 
	(n==Global.numregions-1) && (w>w1 || h>h1)) {
	Pixmap pix = Global.regiondata[n].pix;
	w2 = h2 = 0;
	if (pix) GetPixmapWHD(XtDisplay(info->paint), pix, &w2, &h2, &d);
	if (w2>w1 || h2>h1) {
	    d = info->mem_index;
	    info->mem_index = n;
	    memoryRecallCallback(info->paint, info, NULL);
	    PwRegionSet(info->paint, NULL, None, None);
	    info->mem_index = d;
	}
    }
    if (info->resize_state == -1) info->resize_state = 1;

    sprintf(buf, "%d %d", w, h);
    XtVaSetValues(info->position, XtNlabel, buf, NULL);
    XtVaSetValues(info->position, XtNwidth, POS_WIDTH, NULL);
}

static void
popupHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flag)
{
    if (Global.popped_up) {
	if (event->type == ButtonRelease || event->type == ButtonPress)
	    PopdownMenusGlobal();
	event->type = None;       
    } 
    SetWMInputHint(XtDisplay(info->shell), XtWindow(info->shell));
}

static void
canvasHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flag)
{
    static int x, y, z;	
    
    if (w==info->paint) {
	xorig = event->xbutton.x;
	yorig = event->xbutton.y;
    } else
    if (Global.popped_up) {
	if (event->type == ButtonPress || event->type == ButtonRelease)
	    PopdownMenusGlobal();
	event->type = None;
	return;
    } 

    if (info->resize_state) {
	if (event->xbutton.button>3) return;
	if (event->type == ButtonPress) {
	   if (w==info->paint) {
	      x = event->xbutton.x; 
	      y = event->xbutton.y;
	   } else
	   if (w==info->paintbox) {
 	      x = event->xbutton.x-4; 
	      y = event->xbutton.y-4;
	   }
	} else
	if (event->type == MotionNotify) {
	   if (w==info->paint) {
	      x = event->xmotion.x; 
	      y = event->xmotion.y;
	   } else
	   if (w==info->paintbox) {
 	      x = event->xmotion.x-4; 
	      y = event->xmotion.y-4;
	   }
	} else
	   return;
	XtVaGetValues(info->paint, XtNzoom, &z, NULL);
	if (z>0)
	    resizeCanvas(info, x/z, y/z, 1+(event->xbutton.button>1));
	else
	    resizeCanvas(info, -x*z, -y*z, 1+(event->xbutton.button>1));
    }
}

static void
motionHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flag)
{
    char buf[30];
    int zoom, h;

    if (info->boolpos) {
	XtVaGetValues(w, XtNzoom, &zoom, XtNheight, &h, NULL);
	if (((XButtonEvent *) event)->state & ControlMask) {
	    if (zoom>0)
		sprintf(buf, "%d %d", 
		    (event->xbutton.x - xorig)/zoom, 
		    (event->xbutton.y - yorig)/zoom);
	    else
		sprintf(buf, "%d %d", 
		    -(event->xbutton.x - xorig)*zoom, 
		    -(event->xbutton.y - yorig)*zoom);
	} else
	    if (zoom>0)
		sprintf(buf, "%d %d", 
		    event->xbutton.x/zoom, event->xbutton.y/zoom);
	    else
		sprintf(buf, "%d %d", 
		    -event->xbutton.x*zoom, -event->xbutton.y*zoom);
	XtVaSetValues(info->position, XtNlabel, buf, NULL);
	XtVaSetValues(info->position, 
		      XtNwidth, POS_WIDTH, XtNheight, POS_HEIGHT, 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, NULL);
    if (!wlist) return;
    info = (LocalInfo *)wlist[W_INFO_DATA];
    if (!info) return;

    if (flag) {
	xorig = x;
	yorig = y;
    }
    if (info->boolpos) {
	XtUnmanageChild(info->position);
	if (((XButtonEvent *) event)->state & ControlMask) {
	    sprintf(buf, "%4d %4d", x - xorig, y - yorig);
	} else
	    sprintf(buf, "%4d %4d", x, y);
	XtVaGetValues(info->position, XtNheight, &h, NULL);
	XtVaSetValues(info->position, XtNlabel, buf, NULL);
	XtVaSetValues(info->position, XtNwidth, POS_WIDTH,
		      XtNheight, h, NULL);
    }
}

static void
positionHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flag)
{
    if (Global.popped_up) {
	if (event->type == ButtonRelease)
	    PopdownMenusGlobal();
	event->type = None;
	return;
    }

    info->boolpos = !info->boolpos;
    XtVaSetValues(info->position, XtNlabel, 
	 (info->boolpos)? "???  ???" : msgText[POSITION], NULL);
    XtVaSetValues(info->position, XtNwidth, POS_WIDTH, NULL);
    xorig = 0;
    yorig = 0;
}

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

    if (!IsMenuBarGlobal()) return;    
    
    XtVaGetValues(info->paint, XtNmenuwidgets, &wlist, NULL);
    XtVaGetValues(w, XtNheight, &v, NULL);
    i = (v - Global.btn_height - 10)/CELL_WIDTH;
    if (i<3) i = 3;

    if (wlist && i != info->vsteps) {
	 info->vsteps = i;
	 XFreePixmap(XtDisplay(w), info->palette_pixmap);
	 info->palette_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
			PALETTE_SIZE*CELL_WIDTH+2*EYE_WIDTH,
			CELL_WIDTH*info->vsteps-1, 
			DefaultDepthOfScreen(XtScreen(info->paint)));
	 if (info->mem_pixmap) {
	    XFreePixmap(XtDisplay(w), info->mem_pixmap);
	    info->mem_pixmap = None;
	    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
	    XtVaSetValues(info->memory, XtNheight, CELL_WIDTH*info->vsteps+1, NULL);
	 }
	 if (wlist[W_ICON_PALETTE])
	     XtVaSetValues(wlist[W_ICON_PALETTE], 
			   XtNheight, CELL_WIDTH*info->vsteps-1, NULL);
	 setPalettePixmap(info->paint, info, 0);
	 shellHandler(GetToplevel(info->paint), info, event, flag);
    }
}

static void 
lineStyleOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    Arg arg;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int i, width, length, valid, cap, join;
    struct paintWindows *cur;
    
#define MAXLENGTH 64

    width = atoi(info->prompts[0].rstr);

    if (width < 1 || width > 1000) {
	Notice(w, msgText[INVALID_WIDTH_MUST_BE_GREATER_THAN_ZERO]);
	return;
    }

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

    if (!valid) {
	Notice(w, msgText[DASH_IS_GIVEN_BY_ALTERNATING_THAT_MANY_CHARACTERS], 
	       MAXLENGTH);
	return;
    }

    cap = atoi(info->prompts[2].rstr);
    if (cap<0 || cap>4) {
	Notice(w, msgText[DASH_IS_GIVEN_BY_ALTERNATING_THAT_MANY_CHARACTERS], 
	       0);
	return;
    }

    join = atoi(info->prompts[3].rstr);
    if (join<0 || join>3) {
	Notice(w, msgText[DASH_IS_GIVEN_BY_ALTERNATING_THAT_MANY_CHARACTERS], 
	       0);
	return;
    }

    sprintf(currentLineWidth, "%d", width);
    if (width == 1) width = 0;
    GraphicAll(setLineWidth, (void *)(long)width);
    XtSetArg(arg, XtNlineWidth, width);
    OperationAddArg(arg);
    strncpy(dashStyleStr, info->prompts[1].rstr, 64);
    DashSetStyle(dashStyleStr);
    Global.cap = cap;
    Global.join = join;
    GraphicAll(setCanvasColorsIcon, NULL);
    for (cur = head; cur != NULL; cur = cur->next) {
	if (!cur->done)
	    continue;    
        if (DoVxp(cur->paint)) {
            sprintf(Global.vxpinput, "\n*setlinestyle %d %d %d %s",
	            width, cap, join, dashStyleStr);
            RecordVxp(cur->paint);
	}
    }
}

void 
lineStyleAction(Widget w, int width)
{
    if (width <= 0) {
	static TextPromptInfo info;
	static struct textPromptInfo value[4];
	static char cap_buf[80];
	static char join_buf[80];
	Dimension h;

	info.prompts = value;
	info.title = msgText[WIDTH_AND_DESIRED_LINE_STYLE];
	info.nprompt = 4;
	value[0].prompt = msgText[LINE_WIDTH];
	value[0].str = currentLineWidth;
	value[0].len = 5;
	value[1].prompt = msgText[DASH_STYLE];    
	value[1].str = dashStyleStr;
	value[1].len = 66;
	value[2].prompt = msgText[CAP_STYLE];    
	sprintf(cap_buf, "%d  (%s)", Global.cap, msgText[CAP_HINT]);
	value[2].str = cap_buf;
	value[2].len = 5;
	value[3].prompt = msgText[JOIN_STYLE];    
	sprintf(join_buf, "%d  (%s)", Global.join, msgText[JOIN_HINT]);
	value[3].str = join_buf;
	value[3].len = 5;

	GraphicAll(setLineWidthMenu, (void *)NUMBER_LINEWIDTHS);
	w = TextPrompt(GetToplevel(w), msgText[LINE_STYLE_PARAMETERS], 
	    &info, lineStyleOkCallback, NULL, NULL);
	if (w) {
	    XtVaGetValues(w, XtNheight, &h, NULL);
	    XtResizeWidget(w, 28*Global.btn_height, h, 0);
	}
    } else {
	Arg arg;
	if (width == 1) width = 0;
	GraphicAll(setLineWidth, (void *)(long)width);
	GraphicAll(setLineWidthMenu, (void *)(long)(width/2));
	XtSetArg(arg, XtNlineWidth, width);
	OperationAddArg(arg);
    }
}

void 
lineStyleCallback(Widget w, XEvent * event)
{
    String lbl;
    int width;
    struct paintWindows *cur;

    XtVaGetValues(w, XtNlabel, &lbl, NULL);
    width = atoi(lbl);
    lineStyleAction(GetToplevel(w), width);

    for (cur = head; cur != NULL; cur = cur->next) {
	if (!cur->done)
	    continue;    
        if (DoVxp(cur->paint)) {
	    sprintf(Global.vxpinput, "\n*linewidth %d", width);
            RecordVxp(cur->paint);
	}
    }    
}

static void
lineStyleHandler(Widget w, LocalInfo * info, XEvent * event, Boolean * flag)
{
    lineStyleAction(GetToplevel(w), 0);
}

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

    proc = junk;
    if (proc) proc(info);
}

void *
PopupMenu(char * title, int n, void *values, void * proc)
{
    static TextPromptInfo info;
    Widget w = (Global.curpaint)? Global.curpaint : Global.toplevel;

    PopdownMenusGlobal();

    if (!values || n<=0) return NULL;
    info.prompts = (struct textPromptInfo *) values;
    info.title = title;
    info.nprompt = n;

    TextPrompt(w, msgText[PARAMETERS], &info, PopupMenuOkCallback, NULL, proc);

#if DEBUG
    fprintf(stderr, "graphic.c/PopupMenu : %ld (%ld, %ld, %ld):\n",
		    PopupMenuOkCallback, w, proc, &info);
#endif
    return &info;
}

/*
**  Font menu callbacks.
 */
void
setFontIcon(Widget paint)
{
    WidgetList wlist;
    char buf[10];
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    sprintf(buf, "Abc\n:%g", Global.xft_size);
    if (wlist && wlist[W_ICON_FONT]) {
	XtVaSetValues(wlist[W_ICON_FONT], XtNlabel, buf, NULL);
	XtVaSetValues(wlist[W_ICON_FONT], 
		      XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
		      NULL);
	/* XFlush(XtDisplay(paint)); */
	XtResizeWidget(wlist[W_ICON_FONT], ICONWIDTH, ICONHEIGHT, 
#ifdef XAWPLAIN
	1);
#else 
	0);
#endif
    }
}

static void 
fontSetCallback(Widget paint, void *junk)
{
    FontChanged(paint);
}

static void 
fontMenuCallback(Widget paint, void *ptr)
{
    WidgetList wlist;
    static int i[2] = {-1 , -1};
    int k;

    i[1] = (int)(long)ptr;
    XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    for (k=0; k<=1; k++)
    if (i[k]>=0 && i[k]<NUMBER_PREDEF_FONTS) {
	MenuCheckItem(wlist[W_FONT_DESCR+i[k]], k);
	MenuCheckItem(wlist[W_TOPMENU+W_FONT_DESCR+i[k]], k);
    }
    i[0] = i[1];
}

static void 
fontSet(Widget w, void *ptr)
{
    Display * dpy;
    struct paintWindows * cur;

    if (ptr == (void *)NUMBER_PREDEF_FONTS) {
	GraphicAll(fontMenuCallback, ptr);
	FontSelect(Global.toplevel, None);
    } else {
	dpy = XtDisplay(GetShell(w));
	if (setDefaultGlobalFont(dpy, fontNames[(long)ptr])) {
	    GraphicAll(fontSetCallback, NULL);
	    GraphicAll(fontMenuCallback, ptr);
	} else
  	    XtVaSetValues(w, XtNsensitive, False, NULL);
    }
    for (cur = head; cur != NULL; cur = cur->next) {
	if (!cur->done)
	    continue;    
        if (DoVxp(cur->paint)) {
	    sprintf(Global.vxpinput, "\n*font %s", fontNames[(long)ptr]);
            RecordVxp(cur->paint);
	}
    }    
}

static void 
StdFontSet(Widget w, XtPointer infoArg, XtPointer junk)
{
    fontSet(w, (void *)NUMBER_PREDEF_FONTS);
}

/*
 * 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);
    RefreshPaintWidget(paint);
    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;
    setSnapMenu(paint, (void *)((long) v));
}

/*
 *  Callbacks for setting snap spacing.
 */
static void
snapSpacingOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int snap_x = atoi(info->prompts[0].rstr);
    int snap_y = atoi(info->prompts[1].rstr);

    if (snap_x < 1 || snap_x > 100 || snap_y < 1 || snap_y > 100) {
	Notice(paint, msgText[BAD_SNAP_SPACING]);
	return;
    }
    XtVaSetValues(paint, XtNsnapX, snap_x, NULL);
    XtVaSetValues(paint, XtNsnapY, snap_y, NULL);
    RefreshPaintWidget(paint);
}

static void
snapSpacingCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[2];
    static char bufX[10];
    static char bufY[10];
    int snap_x, snap_y;

    XtVaGetValues(paint, XtNsnapX, &snap_x, NULL);
    XtVaGetValues(paint, XtNsnapY, &snap_y, NULL);
    sprintf(bufX, "%4d", snap_x);
    sprintf(bufY, "%4d", snap_y);

    info.prompts = value;
    info.title = msgText[ENTER_SNAP_SPACING_PARAMETERS];
    info.nprompt = 2;
    value[0].prompt = msgText[SNAP_SPACING_X];
    value[0].str = bufX;
    value[0].len = 4;
    value[1].prompt = msgText[SNAP_SPACING_Y];
    value[1].str = bufY;
    value[1].len = 4;

    TextPrompt(paint, msgText[SNAP_SPACING_PARAMETERS], 
	       &info, snapSpacingOkCallback, NULL, NULL);
}

/*
 *  Routines for changing the image size.
 */
void
cancelWHZSizeCallback(Widget w, XtPointer arg, XtPointer junk)
{
    /* XtFree((XtPointer) arg); */
}

static void 
okWHZSizeCallback(Widget w, XtPointer argArg, XtPointer infoArg)
{
    LocalInfo *localinfo = (LocalInfo *) argArg;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int width, height, w1, h1, z1, canvas;
    char *buf;
    FILE *out;
    w1 = atoi(info->prompts[0].rstr);
    buf = info->prompts[1].rstr;
    if (isalpha(*buf)) {
	buf = malloc(128);
	sprintf(buf, "%s -aspect", info->prompts[1].rstr);
        out = popen(buf, "r");
	if (out) {
	  free(Global.tablet);
	  Global.tablet = strdup(info->prompts[1].rstr);
	  if (fgets(buf, 120, out));
	  pclose(out);
	  h1 = (int)(atof(buf) * w1 + 0.5);
	} else
	  h1 = 0;
	free(buf);
      } else
        h1 = atoi(info->prompts[1].rstr);
    z1 = Global.default_zoom;
    canvas = info->prompts[3].len;
    width = w1;
    height = h1;

    if (localinfo == NULL || localinfo->paint == None)
	z1 = StrToZoom(info->prompts[2].rstr);
    else
	XtVaGetValues(localinfo->paint, XtNdrawWidth, &width,
		      XtNdrawHeight, &height, XtNzoom, &z1, NULL);

    if (w1 <= 0) {
	Notice(w, msgText[INVALID_WIDTH]);
	return;
    }
    if (h1 <= 0) {
	Notice(w, msgText[INVALID_HEIGHT]);
	return;
    }
    if (z1==0 || z1==-1 || z1<-16 || z1>32) {
	Notice(w, msgText[INVALID_ZOOM]);
	return;
    }

    if (localinfo && (w1 != width || h1 != height)) {
	resizeCanvas(localinfo, w1, h1, 0);
	return;
    }
    if (!localinfo) {
        if (canvas) {
	    /* Call from toolbox. Create new canvas */
	    CreateCanvas(w, w1, h1, z1);
	} else
	    SetDefaultWHZ(w1, h1, z1);
	return;
    }
}

void 
WHZSizeSelect(Widget w, XtPointer arg, int canvas)
{
    static TextPromptInfo info;
    static struct textPromptInfo values[4];
    LocalInfo * localinfo = (LocalInfo *) arg;
    int width, height, zoom;
    char bufA[16], bufB[16], bufC[16];

    info.prompts = values;
    info.nprompt = (localinfo) ? 2 : 3;
    info.title = msgText[ENTER_DESIRED_IMAGE_SIZE];

    values[0].prompt = msgText[SIZE_WIDTH];
    values[0].str = bufA;
    values[0].len = 5;
    values[1].prompt = msgText[SIZE_HEIGHT];
    values[1].str = bufB;
    values[1].len = 5;
    values[2].prompt = msgText[SIZE_ZOOM];
    values[2].str = bufC;
    values[2].len = 5;
    /* ugly hack : pass argument 'new' through  values[3].len */
    values[3].len = canvas;

    if (localinfo) {
	XtVaGetValues(localinfo->paint, XtNdrawWidth, &width,
		      XtNdrawHeight, &height,
		      XtNzoom, &zoom,
		      NULL);
    } else {
	width = Global.default_width;
	height = Global.default_height;
	zoom = Global.default_zoom;
    }

    sprintf(bufA, "%d", width);
    sprintf(bufB, "%d", height);
    strcpy(bufC, ZoomToStr(zoom));
    TextPrompt(w, msgText[WHZ_PARAMETERS], 
	       &info, okWHZSizeCallback, cancelWHZSizeCallback, 
	       localinfo);
}

static void
WHZSizeCallback(Widget w, XtPointer arg, XtPointer junk2)
{
    LocalInfo * info = (LocalInfo *) arg;
    Widget paint = (Widget) info->paint;

    WHZSizeSelect(GetShell(paint), info, 0);
}

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

    WHZSizeSelect(GetShell(paint), NULL, 0);
}

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, msgText[NUMBER_OF_UNDO_LEVELS],
	       &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;
    WidgetList children;
    int dw, dh;

    if (!paint) return;
    box = XtParent(paint);
    if (!box) return;

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

void setZoom(Widget paint, int zoom)
{
    Widget w;
    int oldzoom, dw, dh, wp, hp;
    Dimension u, v;

    XtVaGetValues(paint, XtNzoom, &oldzoom, 
		     XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);
    if (oldzoom == zoom) return;

    StateSetBusy(True);
    XtUnmanageChild(paint);
    if (zoom>0) {
       wp = dw*zoom;
       hp = dh*zoom;
    } else {
       wp = (dw-zoom-1)/(-zoom);
       hp = (dh-zoom-1)/(-zoom);
    }

    XtVaSetValues(paint, XtNwidth, wp, XtNheight, hp, XtNzoom, zoom, NULL);
    zoomAddChild(paint, zoom);
    setStandardCursor(paint);
    FatbitsUpdate(paint, zoom);
    RegionMove((PaintWidget)paint, 0, 0);
  
    w = XtParent(paint);
    if (w) w = XtParent(w);
    if (w) {
	XtVaGetValues(w, XtNwidth, &u, XtNheight, &v, NULL);
#if defined(XAW3D) || defined(XAW95)
	if (u >= wp+14 && v >= hp+14)
#else
	if (u >= wp+10 && v >= hp+10)
#endif
	XtVaSetValues(w, 
	       XtNallowVert, False, XtNallowHoriz, False, NULL);
	else
	XtVaSetValues(w, 
	       XtNallowVert, True, XtNallowHoriz, True, NULL);
    }

    XtManageChild(paint);
    RefreshWidget(paint);
    StateSetBusy(False);
}

static void
zoomOkCallback(Widget w, XtPointer arg, XtPointer data)
{
    LocalInfo * localinfo = (LocalInfo *) arg;
    TextPromptInfo *info = (TextPromptInfo *) data;
    int zoom;

    zoom = StrToZoom(info->prompts[0].rstr);

    if (zoom == 0 || zoom < -9 || zoom > 32) {
	Notice(localinfo->paint, msgText[INVALID_ZOOM]);
    } else {
	setZoom(localinfo->paint, zoom);
	XtVaSetValues(localinfo->zoom, XtNlabel, ZoomToStr(zoom), NULL);
	XtVaSetValues(localinfo->zoom, XtNwidth, Global.btn_height-1, XtNheight, Global.btn_height, NULL);
    }
}

static void
zoomCallback(Widget w, XtPointer arg, XtPointer junk2)
{
    static TextPromptInfo info;
    static struct textPromptInfo values[2];
    static char buf[80];
    LocalInfo * localinfo = (LocalInfo *) arg;
    int zoom;

    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(localinfo->paint, XtNzoom, &zoom, NULL);
    strcpy(buf, ZoomToStr(zoom));

    TextPrompt(GetShell(localinfo->paint), msgText[ZOOM_FACTOR],
	       &info, zoomOkCallback, NULL, localinfo);
}

/*
 * Callback functions for Region menu
 */
static void
rotateTo(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    double 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;
    double 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_ANGLE];
    info.nprompt = 1;

    TextPrompt(paint, msgText[ROTATION_ANGLE],
	       &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
maskOfRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    RegionCreateMask((PaintWidget) paintArg);
}

static void
mergeRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    PwRegionMergeOption(1);
    PwRegionSet(paint, NULL, None, None);
    PwRegionMergeOption(0);    
}

static void
linearRegionOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    pwMatrix m;
    double a, u, v, x, y, z;
    ImgProcessInfo.linearDX = atof(info->prompts[0].rstr);
    ImgProcessInfo.linearDY = atof(info->prompts[1].rstr);
    ImgProcessInfo.linearIncli = atof(info->prompts[2].rstr);
    ImgProcessInfo.linearAngle = atof(info->prompts[3].rstr);
    a = ImgProcessInfo.linearAngle * M_PI / 180.0;
    u = cos(a);
    v = sin(a);
    x = ImgProcessInfo.linearDX;
    y = ImgProcessInfo.linearDY;
    z = ImgProcessInfo.linearIncli * y;
    m[0][0] = u * x;
    m[0][1] = v * y - u * z;
    m[1][0] = -v * x;
    m[1][1] = u * y + v * z;
    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 buf0[10], buf1[10], buf2[10], buf3[10];

    sprintf(buf0, "%g", ImgProcessInfo.linearDX);
    sprintf(buf1, "%g", ImgProcessInfo.linearDY);
    sprintf(buf2, "%g", ImgProcessInfo.linearIncli);
    sprintf(buf3, "%g", ImgProcessInfo.linearAngle);

    value[0].prompt = msgText[LINEAR_TRANSFORM_DX];
    value[0].str = buf0;
    value[0].len = 4;
    value[1].prompt = msgText[LINEAR_TRANSFORM_DY];
    value[1].str = buf1;
    value[1].len = 4;
    value[2].prompt = msgText[LINEAR_TRANSFORM_INCLINATION];
    value[2].str = buf2;
    value[2].len = 4;
    value[3].prompt = msgText[LINEAR_TRANSFORM_ANGLE];
    value[3].str = buf3;
    value[3].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_LINEAR_TRANSFORM_PARAMETERS];
    info.nprompt = 4;

    TextPrompt(paint, msgText[LINEAR_TRANSFORM], 
	       &info, linearRegionOkCallback, NULL, NULL);
}

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

    ImgProcessInfo.distortX = atoi(info->prompts[0].rstr);
    ImgProcessInfo.distortY = atoi(info->prompts[1].rstr);
    StdRegionDistort(paint, paint, NULL);
}

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

    strcpy(ImgProcessInfo.proj0, info->prompts[0].rstr);
    strcpy(ImgProcessInfo.proj1, info->prompts[1].rstr);
    strcpy(ImgProcessInfo.proj2, info->prompts[2].rstr);
    strcpy(ImgProcessInfo.proj3, info->prompts[3].rstr);    
    StdRegionProjTransform(paint, paint, NULL);
}

static void
distortRegion(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.distortX);
    sprintf(buf2, "%d", ImgProcessInfo.distortY);

    value[0].prompt = "X (-200 .. 200):";
    value[0].str = buf1;
    value[0].len = 6;
    value[1].prompt = "Y (-200 .. 200):";
    value[1].str = buf2;
    value[1].len = 6;
    info.prompts = value;
    info.title = msgText[DISTORTION_PARAMETERS];
    info.nprompt = 2;

    TextPrompt(paint, msgText[DISTORTION_PARAMETERS],
	       &info, distortRegionOkCallback, NULL, NULL);
}

static void
projTransformRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[4];
    static char buf0[64], buf1[64], buf2[16], buf3[16];

    strcpy(buf0, ImgProcessInfo.proj0);
    strcpy(buf1, ImgProcessInfo.proj1);
    strcpy(buf2, ImgProcessInfo.proj2);
    strcpy(buf3, ImgProcessInfo.proj3);

    value[0].prompt = msgText[FIRST_QUADRUPLE_OF_POINTS];
    value[0].str = buf0;
    value[0].rstr =  NULL;
    value[0].len = 54;
    value[1].prompt = msgText[SECOND_QUADRUPLE_OF_POINTS];
    value[1].str = buf1;
    value[1].rstr =  NULL;
    value[1].len = 54;
    value[2].prompt = msgText[TAKE_INVERSE_MAPPING];
    value[2].str = buf2;
    value[2].rstr =  NULL;
    value[2].len = 6;
    value[3].prompt = msgText[DO_INTERPOLATION_OF_PIXELS];
    value[3].str = buf3;
    value[3].rstr =  NULL;
    value[3].len = 6;

    info.prompts = value;
    info.title = msgText[SELECT_FOUR_POINTS_AND_TRANSFORMS];
    info.nprompt = 4;

    TextPrompt(paint, msgText[PERSPECTIVE_TRANSFORMATION],
	       &info, projRegionOkCallback, NULL, NULL);
}

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

void 
delimit(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    Pixmap pix, mask;
    char *ptr;
    int mode;

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

    ptr = XtName(w);
    mode = (int)(ptr[strlen(ptr)-1] - '0');

    if (mode<=0 || mode>=7) {
	if (Global.delimitation) {
	    free(Global.delimitation);
	    Global.delimitation = NULL;
	}
	return;
    }

    if (!Global.delimitation)
	Global.delimitation = (Delimitation *)xmalloc(sizeof(Delimitation));
    Global.delimitation->paint = paint;
    Global.delimitation->mode = mode;
}

/*
 * Callback functions for Filter menu
 */

static void
embossOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int a, b;

    a = atoi(info->prompts[0].rstr);
    b = atoi(info->prompts[1].rstr);
    if ((a < 0) || (a > 255) ||
	(b < -500) || (b > 500)) {
	Notice(paint, "Invalid emboss parameters");
	return;
    }
    ImgProcessInfo.embossBG = a;
    ImgProcessInfo.embossRatio = b;
    StdRegionEmboss(paint, paint, NULL);
}

static void
doEmboss(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[2];
    static char bufa[20], bufb[20];

    sprintf(bufa, "%d", ImgProcessInfo.embossBG);
    sprintf(bufb, "%d", ImgProcessInfo.embossRatio);

    info.prompts = value;
    info.title = msgText[EMBOSS_PARAMETERS];
    info.nprompt = 2;
    value[0].prompt = msgText[BACKGROUND_GRAY_VALUE];
    value[0].str = bufa;
    value[0].len = 5;
    value[1].prompt = msgText[SCALING_FACTOR];
    value[1].str = bufb;
    value[1].len = 5;

    TextPrompt(paint, msgText[EMBOSS_PARAMETERS], 
	       &info, embossOkCallback, NULL, NULL);
}

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, msgText[OIL_PAINT_EFFECT], 
	       &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, msgText[SMOOTHING_EFFECT], 
	       &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, msgText[NOISE_EFFECT], 
	       &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, msgText[SPREAD_EFFECT],
	       &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, msgText[PIXELIZATION],
	       &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, msgText[DESPECKLE_FILTER],
	       &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;
    StdRegionNormalizeContrast(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, msgText[CONTRAST_ADJUSTMENT], 
	       &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, msgText[SOLARIZE_EFFECT], 
	       &info, solarizeOkCallback, NULL, NULL);
}

static void
GammaCorrectionOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    char RGB_correction[8];
    char *ptr;
    double r_gamma, g_gamma, b_gamma;
    double mat[12];
    int i, n, lmin, lmax;

    strncpy(RGB_correction, info->prompts[0].rstr, 7);
    RGB_correction[7] = '\0';
    
    if (!RGB_correction[0]) {
      error:
	Notice(paint, msgText[INVALID_RGB_PARAMETERS]);
	return;
    }
    for (i=0; i<strlen(RGB_correction); i++)
        if (!isdigit(RGB_correction[i])) goto error;
    
    r_gamma = atof(info->prompts[1].rstr);
    g_gamma = atof(info->prompts[2].rstr);
    b_gamma = atof(info->prompts[3].rstr);
 
    n = sscanf(info->prompts[4].rstr, "%d %d", &lmin, &lmax);
    if (n<2 || lmin>=lmax) goto error;

    for (i = 0; i<=2; i++) {
	n = sscanf(info->prompts[5+i].rstr, "%lf%% %lf%% %lf%% %lf",
		   &mat[4*i], &mat[4*i+1], &mat[4*i+2], &mat[4*i+3]);
	if (n<4) goto error;
    }

    strcpy(ImgProcessInfo.RGB_correction, RGB_correction);
    ImgProcessInfo.r_gamma = r_gamma;
    ImgProcessInfo.g_gamma = g_gamma;
    ImgProcessInfo.b_gamma = b_gamma;
    ImgProcessInfo.RGB_min = lmin;
    ImgProcessInfo.RGB_max = lmax;
    for (i=0; i<12; i++) ImgProcessInfo.rgb_mat[i] = mat[i];

    StdRegionGammaCorrection(paint, paint, NULL);
}

static void
FloydSteinbergOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int c, p;
    double gamma;

    c = atoi(info->prompts[0].rstr);
    p = atoi(info->prompts[1].rstr);
    gamma = atof(info->prompts[2].rstr);
    if ((c < 2) || (c > 256) ||
	(p < 1) || (p > 255) ||
	(gamma<-12.0 || gamma>12.0)) {
	Notice(paint, msgText[INVALID_FLOYD_STEINBERG_PARAMETERS]);
	return;
    }
    ImgProcessInfo.FSsteps = c;
    ImgProcessInfo.FSmidrange = p;
    ImgProcessInfo.FSgamma = gamma;
    StdRegionFloydSteinberg(paint, paint, 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);
}

int
expandImage(Display *dpy, GC gc,
	    Pixmap pix, unsigned char *alpha,
	    int depth, 
	    int width, int height,
	    int widthp, int heightp, int interp, int expand,
	    double xf, double yf,
	    Pixmap *pixp, unsigned char **alphap)
{
    int i=0, j, x, y, u, v, up, vp, r, g ,b, gray, m;
    Pixel p = 0;
    XImage *xim, *ximp = NULL;
    unsigned char col[3], colp[3];
    unsigned char *c_buffer, *col0, *col1;
    unsigned char *beta;

    if (widthp<=0 || heightp<=0) return -2; /* invalid request */
    if (widthp<=0 || heightp<=0) return -2;

    *pixp = XCreatePixmap(dpy, DefaultRootWindow(dpy), widthp, heightp, depth);
    if (!*pixp) return -1; /* failure */

    x = (int) (xf-0.000001);
    y = (int) (yf-0.000001);

    xim = NewXImage(dpy, NULL, depth, width, height);
    if (!xim) {
    cancel:
	XFreePixmap(dpy, *pixp);
	XDestroyImage(xim);
	return -1;
    }

    XGetSubImage(dpy, pix, 0, 0, width, height,
		 AllPlanes, ZPixmap, xim, 0, 0);

    if (alpha)
	beta = (unsigned char *) XtMalloc(widthp*heightp);
    else
	beta = NULL;

    if (interp==0 && expand==0) {
      no_interp_expand:
	ximp = NewXImage(dpy, NULL, depth, widthp, heightp);
	if (!ximp) goto cancel;
	for (v=0; v<height; v++)
	for (u=0; u<width; u++) {
	    p = XGetPixel(xim, u, v);
	    for (vp=0; vp<=y; vp++) {
		j = (int)(yf*v+vp);
		for (up=0; up<=x; up++) {
		    i = (int)(xf*u+up);
		    XPutPixel(ximp, i, j, p);
		    if (beta) 
			beta[i+j*widthp] = alpha[u+v*width];
		}
	    }
	}
	XDestroyImage(xim);
    }

    if (interp==1 && expand==0) {
	c_buffer = (unsigned char *)xmalloc(3*(width+1)*(height+1));
	if (!c_buffer) {
	    interp = 0;
	    goto no_interp_expand;
	}
	for (v=0; v<height; v++) {
	    for (u=0; u<width; u++) {
		p = XGetPixel(xim, u, v);
		get_color_components(p, c_buffer + 3*(v*(width+1)+u));
	    }
	    get_color_components(p, c_buffer + 3*(v*(width+1)+width));
	}
	memcpy(c_buffer+3*height*(width+1), c_buffer+3*(height-1)*(width+1),
	       3*(width+1));
	XDestroyImage(xim);
	ximp = NewXImage(dpy, NULL, depth, widthp, heightp);
	if (!ximp) goto cancel;
	for (v=0; v<height; v++)
	for (u=0; u<width; u++) {
	    for (vp=0; vp<=y; vp++)
	    for (up=0; up<=x; up++) {
		col0 = c_buffer + 3*(v*(width+1)+u);
		col1 = c_buffer + 3*((v+1)*(width+1)+u);
		col[0] = ((col0[0]*(xf-up)+col0[3]*up)/xf);
		col[1] = ((col0[1]*(xf-up)+col0[4]*up)/xf);
		col[2] = ((col0[2]*(xf-up)+col0[5]*up)/xf);
		colp[0] = ((col1[0]*(xf-up)+col1[3]*up)/xf);
		colp[1] = ((col1[1]*(xf-up)+col1[4]*up)/xf);
		colp[2] = ((col1[2]*(xf-up)+col1[5]*up)/xf);
		col[0] = ((col[0]*(yf-vp)+colp[0]*vp)/yf);
		col[1] = ((col[1]*(yf-vp)+colp[1]*vp)/yf);
		col[2] = ((col[2]*(yf-vp)+colp[2]*vp)/yf);
		p = get_pixel_from_colors(col);
		XPutPixel(ximp, (int)(xf*u+up), (int)(yf*v+vp), p);
	    }
	}
	if (beta) {
	    for (v=0; v<height; v++) {
		i = v*(width+1);
		memcpy(c_buffer+i, 
		       alpha+v*width, width);
		c_buffer[i+width] = c_buffer[i+width-1];
	    }
	    memcpy(c_buffer+i+width+1, c_buffer+i, width+1);
	    for (v=0; v<height; v++)
	    for (u=0; u<width; u++) {
		for (vp=0; vp<=y; vp++)
		for (up=0; up<=x; up++) {
		    col0 = c_buffer + (v*(width+1)+u);
		    col1 = c_buffer + ((v+1)*(width+1)+u);
		    col[0] = ((col0[0]*(xf-up)+col0[1]*up)/xf);
		    colp[0] = ((col1[0]*(xf-up)+col1[1]*up)/xf);
		    i = (int)(xf*u+up);
		    j =  (int)(yf*v+vp);
		    beta[i+j*widthp] = ((col[0]*(yf-vp)+colp[0]*vp)/yf);
		}
	    }
	}
	free(c_buffer);
    }

    if (interp==0 && expand==1) {
	ximp = NewXImage(dpy, NULL, depth, widthp, heightp);
	if (!ximp) goto cancel;
	for (v=0; v<heightp; v++) {
	    vp = (int)(v*yf); if (vp>=height) vp = height-1;
	    for (u=0; u<widthp; u++) {
 		up = (int)(u*xf); if (up>=width) up = width-1;
 		p = XGetPixel(xim, up, vp);
		XPutPixel(ximp, u, v, p);
		if (beta)
		    beta[u+v*widthp] = alpha[up+vp*width];
 
	    }
	}
	XDestroyImage(xim);
    }

    if (interp==1 && expand==1) {
	ximp = NewXImage(dpy, NULL, depth, widthp, heightp);
	if (!ximp) goto cancel;
	m = (x+1)*(y+1);
	for (v=0; v<heightp; v++)
	for (u=0; u<widthp; u++) {
	    r = g = b = gray = 0;
	    for (j=0; j<=y; j++) {
		vp = (int)(v*yf+j); if (vp>=height) vp = height-1;
		for (i=0; i<=x; i++) {
		    up = (int)(u*xf+i); if (up>=width) up = width-1;
 		    p = XGetPixel(xim, up, vp);
		    get_color_components(p, col);
		    r += col[0];
		    g += col[1];
		    b += col[2];
		    if (beta)
			gray += (int) alpha[up+vp*width];
		}
	    }
	    col[0] = r/m;
	    col[1] = g/m;
	    col[2] = b/m;
	    p = get_pixel_from_colors(col);
	    XPutPixel(ximp, u, v, p);
	    if (beta) 
		beta[u+v*widthp] = (unsigned char)(gray/m);
	}
	XDestroyImage(xim);
    }

    XPutImage(dpy, *pixp, gc, ximp, 0, 0, 0, 0, widthp, heightp);
    XDestroyImage(ximp);
    *alphap = beta;

    return 0;
}

static void
expandOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
#define EXPAND_MAX  64.0
    PaintWidget pw = (PaintWidget) paint;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    static Pixmap pix, mask, pixp, maskp;
    static XRectangle rect;
    XImage *maskImg;
    Display *dpy = XtDisplay(paint);
    GC gc;
    Pixel p = 0;
    double xf, yf;
    int width, height, depth, widthp, heightp, interpolation, u, v;
    unsigned char *alphap;

    xf = atof(info->prompts[0].rstr);
    yf = atof(info->prompts[1].rstr);   
    if ((xf < 1.0) || (xf > EXPAND_MAX) || 
	(yf < 1.0) || (yf > EXPAND_MAX)) {
      invalid:
	Notice(paint, msgText[INVALID_SCALING_FACTOR]);
	return;
    }
    ImgProcessInfo.rescale_x = xf;
    ImgProcessInfo.rescale_y = yf;  
    
    if (info->nprompt>=3) 
	ImgProcessInfo.interpolate = (atoi(info->prompts[2].rstr)>0);
    else
	ImgProcessInfo.interpolate = 0;

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

    GetPixmapWHD(dpy, pix, &width, &height, &depth);

    if (Global.depth>=15) 
	interpolation = ImgProcessInfo.interpolate;
    else
	interpolation = 0;

    if (junk) {
	widthp = (int) (width / xf + 0.5);
	heightp = (int) (height / yf + 0.5);
    } else {
	widthp = (int) (width * xf + 0.5);
	heightp = (int) (height * yf + 0.5);
    }    

    gc = XtGetGC(paint, 0, 0);
    u = expandImage(dpy, gc, pix, pw->paint.current.alpha, 
		    depth, width, height, widthp, heightp, 
		    interpolation, (junk)?1:0, xf, yf, &pixp, &alphap);
    XtReleaseGC(paint, gc);

    if (u == -2) goto invalid;
    if (u == -1) return;

    if (mask) {
       maskp = XCreatePixmap(dpy, DefaultRootWindow(dpy), 
			     widthp, heightp, 1);
       gc = XCreateGC(dpy, maskp, 0, 0);
       maskImg = NewXImage(dpy, NULL, 1, width, height);
       XGetSubImage(dpy, mask, 0, 0, width, height,
		    AllPlanes, ZPixmap, maskImg, 0, 0);
       XSetForeground(dpy, gc, 0);
       XFillRectangle(dpy, maskp, gc, 0, 0, widthp, heightp);
       for (v=0; v<heightp; v++)
	   for (u=0; u<widthp; u++) {
	       p = XGetPixel(maskImg, (u*width)/widthp, (v*height)/heightp);
	       if (p) {
		   XSetForeground(dpy, gc, p);
		   XDrawPoint(dpy, maskp, gc, u, v);
	       }
       }
       XDestroyImage(maskImg);
    } else
       maskp = None;

    rect.width = widthp;
    rect.height = heightp;
    rect.x = 0;
    rect.y = 0;
    if (alphap) {
	XtFree((char *)pw->paint.region.alpha);
	pw->paint.region.alpha = NULL;
    }
    PwRegionClear(paint);
    PwRegionSet(paint, &rect, pixp, maskp);
    pw->paint.region.alpha = alphap;
}

void
doRegionToAlpha(Widget w, XtPointer infoArg, XtPointer junk2)
{
    LocalInfo *info = (LocalInfo *) infoArg;
    Colormap cmap;
    Image *output;
    unsigned char *alpha;
    int i, n, width, height;

    if (!Global.numregions) return;
    n = info->mem_index;
    if (n < 0) return;
    alpha = Global.regiondata[n].alpha;
    if (!alpha) return;
    XtVaGetValues(info->paint, XtNcolormap, &cmap, NULL);
    GetPixmapWHD(XtDisplay(info->paint), Global.regiondata[n].pix,
		 &width, &height, NULL);

    output = ImageNewGrey(0, 0);
    output->width = width;
    output->height = height;
    i = width*height;
    output->data =  (unsigned char *) XtMalloc(i);
    memcpy(output->data, alpha, i);
    ImageToMemory(output);
    Global.regiondata[Global.numregions-1].x = Global.regiondata[n].x;
    Global.regiondata[Global.numregions-1].y = Global.regiondata[n].y;
}

static void
doRegionToBWMask(Widget w, XtPointer infoArg, XtPointer junk2)
{
    LocalInfo *info = (LocalInfo *) infoArg;
    Colormap cmap;
    Image *input, *output;
    int n;

    if (!Global.numregions) return;
    n = info->mem_index;
    if (n < 0) return;
    if (!Global.numregions) return;
    XtVaGetValues(info->paint, XtNcolormap, &cmap, NULL);
    input = PixmapToImage(info->paint, Global.regiondata[n].pix, cmap);
    output = ImageBWMask(input);
    if (!output) return;
    ImageDelete(input);
    ImageToMemory(output);
    Global.regiondata[Global.numregions-1].x = Global.regiondata[n].x;
    Global.regiondata[Global.numregions-1].y = Global.regiondata[n].y;
}

static void
doRegionToRGBA(Widget w, XtPointer infoArg, XtPointer junk2)
{
    LocalInfo *info = (LocalInfo *) infoArg;
    Colormap cmap;
    Image *input, *output;
    unsigned char *ip, *op, *alpha;
    int i, x, y, n;

    if (!Global.numregions) return;
    n = info->mem_index;
    if (n < 0) return;
    XtVaGetValues(info->paint, XtNcolormap, &cmap, NULL);
    input = PixmapToImage(info->paint, Global.regiondata[n].pix, cmap);
    alpha = Global.regiondata[n].alpha;

    for (n = 0; n <= 2 ; n++) {
	output = ImageNewGrey(input->width, input->height);
	if (!output || !output->data || !output->cmapData) break;
	output->isGrey = False;
	memset(output->cmapData, 0, 3*256);
	for (i=0; i<256; i++) output->cmapData[3*i+n] = i;
	op = output->data;
	for (y = 0; y < input->height; y++) {
	    for (x = 0; x < input->width; x++) {
		ip = ImagePixel(input, x, y);
		*op++ = ip[n];
	    }
	}
	ImageToMemory(output);
	Global.regiondata[Global.numregions-1].x = Global.regiondata[n].x;
	Global.regiondata[Global.numregions-1].y = Global.regiondata[n].y;
    }

    if (alpha) {
	output = ImageNewGrey(0, 0);
	if (output) {
	    output->width = input->width;
	    output->height = input->height;
	    i = input->width*input->height;
	    output->data =  (unsigned char *) XtMalloc(i);
	    memcpy(output->data, alpha, i);
	    ImageToMemory(output);
	    Global.regiondata[Global.numregions-1].x = Global.regiondata[n].x;
	    Global.regiondata[Global.numregions-1].y = Global.regiondata[n].y;
	}
    }
    ImageDelete(input);
}

static void
mergeMemoryToRGBAOkCallback(Widget paint, void *argArg, XtPointer infoArg)
{
    LocalInfo *localinfo = (LocalInfo *)argArg;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int error = 0, i, i0, width, height, x, y, n[4];
    Image *input, *output;
    Colormap cmap;
    unsigned char *ip, *op;

    for (i=0; i<=3; i++) n[i] = 0;
    i = sscanf(info->prompts[0].rstr, "%d %d %d %d", 
	       &n[0], &n[1], &n[2], &n[3]);

    if (i<3) error = 1;
    if (i==3) n[3] = 0;

    for (i=0; i<=3; i++) if (n[i]<0 || n[i]>Global.numregions) error = 1;
    
    if (error) {
    bail_out:
	Notice(localinfo->paint, msgText[INCORRECT_CHANNEL_LIST_SPECIFICATION]);
	return;
    }

    i0 = -1;
    for (i=0; i<=3; i++) {
	if (n[i]) {
	    i0 = i;
	    break;
	}
    }

    if (i0 == -1) return;
    GetPixmapWHD(XtDisplay(localinfo->paint), Global.regiondata[n[i0]-1].pix,
		 &width, &height, NULL);

    for (i=0; i<=3; i++) {
	if (n[i]) {
	    GetPixmapWHD(XtDisplay(localinfo->paint), 
			 Global.regiondata[n[i]-1].pix, &x, &y, NULL);
	    if (x != width && y != height) {
		error = 1;
		break;
	    }
	}
    }
    if (error) goto bail_out;

    XtVaGetValues(localinfo->paint, XtNcolormap, &cmap, NULL);
    output = ImageNew(width, height);

    if (!output || !output->data) return;

    memset(output->data, 0, 3*width*height);

    for (i=0; i<=3; i++) if (n[i]) {
	input = PixmapToImage(localinfo->paint, 
			      Global.regiondata[n[i]-1].pix, cmap);
	if (i<=2) {
	    op = output->data;
	    for (y=0; y<height; y++)
	    for (x=0; x<width; x++) {
		ip = ImagePixel(input, x, y);
		op[i] = ip[i];
		op += 3;
	    }
	} else {
	    output->alpha = (unsigned char*)xmalloc(width*height);
	    if (!(op = output->alpha)) break;
	    for (y=0; y<height; y++)
	    for (x=0; x<width; x++) {
		ip = ImagePixel(input, x, y);
		*op++ = GreyScale(ip[0], ip[1], ip[2]);
	    }
	}
    }
    ImageToMemory(output);
}


static void
mergeMemoryToRGBA(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo *localinfo = (LocalInfo *) infoArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    static char buf[30];
    int i, m, n[4];

    if (Global.numregions>=4) {
	for (i=0; i<=3; i++) n[i] = Global.numregions+i-3;
	sprintf(buf, "%d %d %d %d", n[0], n[1], n[2], n[3]);
    } else {
	m = MIN(Global.numregions, 1);
	for (i=0; i<=2; i++) n[i] = MAX(m, Global.numregions+i-2);
	sprintf(buf, "%d %d %d", n[0], n[1], n[2]);
    }

    info.prompts = value;
    info.title = msgText[MEMORY_ITEMS_TO_BE_MERGED];
    info.nprompt = 1;
    value[0].prompt = msgText[LIST_OF_ITEMS];
    value[0].str = buf;
    value[0].len = 20;

    TextPrompt(localinfo->paint, msgText[MERGE_RGBA_COMPONENTS],
	       &info, mergeMemoryToRGBAOkCallback, NULL, localinfo);
}

static void
doRegionMergeOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    PaintWidget pw = (PaintWidget)paint;
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    Pixmap pix;
    Colormap cmap;
    int t;
    double p;
 
    t = atoi(info->prompts[0].rstr);
    p = atof(info->prompts[1].rstr);

    if (t<0 || t>3 || p<0.0 || p>100.0) {
      Notice(paint, msgText[INVALID_MERGE_PARAMETERS]);
	return;
    }

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

    pix = GET_PIXMAP(pw);
    XtVaGetValues(paint, XtNcolormap, &cmap, NULL);

    ImgProcessInfo.mergeType = t;
    ImgProcessInfo.mergePercent = (int) (p * 100.0 + 0.5);
    ImgProcessInfo.shiftX = pw->paint.region.rect.x;
    ImgProcessInfo.shiftY = pw->paint.region.rect.y;
    ImgProcessInfo.canvas = PixmapToImage(paint, pix, cmap);
    if (pw->paint.current.alpha)
	ImgProcessInfo.canvas->alpha = pw->paint.current.alpha;

    StdRegionMerge(paint, paint, NULL);
}

static void
doRegionMerge(Widget w, XtPointer paintArg, XtPointer junk)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[2];
    static char buf0[10], buf1[20];

    sprintf(buf0, "%d", ImgProcessInfo.mergeType);
    sprintf(buf1, "%5.2f", ImgProcessInfo.mergePercent*0.01);

    info.prompts = value;
    info.title = msgText[MERGE_REGION_WITH_UNDERLYING_CANVAS];
    info.nprompt = 2;
    value[0].prompt = msgText[TYPE_OF_MERGE];
    value[0].str = buf0;
    value[0].len = 6;
    value[1].prompt = msgText[PERCENTAGE_PROGRESSIVE_LINEAR];
    value[1].str = buf1;
    value[1].len = 16;

    TextPrompt(paint, msgText[MERGE_REGION],
	       &info, doRegionMergeOkCallback, NULL, NULL);
}

static void
doGammaCorrection(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[8];
    static char buf[10];
    static char bufrg[20], bufgg[20], bufbg[20], bufth[20];
    static char bufrl[40], bufgl[40], bufbl[40];

    sprintf(buf, "%s", ImgProcessInfo.RGB_correction);
    sprintf(bufrg, "%4.2f", ImgProcessInfo.r_gamma);
    sprintf(bufgg, "%4.2f", ImgProcessInfo.g_gamma);
    sprintf(bufbg, "%4.2f", ImgProcessInfo.b_gamma);
    sprintf(bufth, "%d %d", ImgProcessInfo.RGB_min, ImgProcessInfo.RGB_max);
    sprintf(bufrl, "%g%% %g%% %g%% %g", 
	    ImgProcessInfo.rgb_mat[0], ImgProcessInfo.rgb_mat[1],
	    ImgProcessInfo.rgb_mat[2], ImgProcessInfo.rgb_mat[3]);
    sprintf(bufgl, "%g%% %g%% %g%% %g", 
	    ImgProcessInfo.rgb_mat[4], ImgProcessInfo.rgb_mat[5],
	    ImgProcessInfo.rgb_mat[6], ImgProcessInfo.rgb_mat[7]);
    sprintf(bufbl, "%g%% %g%% %g%% %g", 
	    ImgProcessInfo.rgb_mat[8], ImgProcessInfo.rgb_mat[9],
	    ImgProcessInfo.rgb_mat[10], ImgProcessInfo.rgb_mat[11]);

    info.prompts = value;
    info.title = msgText[RGB_TRANSFORMATION_MODES];
    info.nprompt = 8;
    value[0].prompt = msgText[TYPE_OF_RGB_TRANSFORMATION];
    value[0].str = buf;
    value[0].len = 4;
    value[1].prompt = msgText[RED_GAMMA_CORRECTION];
    value[1].str = bufrg;
    value[1].len = 6;
    value[2].prompt = msgText[GREEN_GAMMA_CORRECTION];
    value[2].str = bufgg;
    value[2].len = 6;
    value[3].prompt = msgText[BLUE_GAMMA_CORRECTION];
    value[3].str = bufbg;
    value[3].len = 6;
    value[4].prompt = msgText[GRAY_LEVEL_THRESHOLDS_MIN_MAX];
    value[4].str = bufth;
    value[4].len = 12;
    value[5].prompt = msgText[RED_LINEAR_TRANSFORMATION];
    value[5].str = bufrl;
    value[5].len = 20;
    value[6].prompt = msgText[GREEN_LINEAR_TRANSFORMATION];
    value[6].str = bufgl;
    value[6].len = 20;
    value[7].prompt = msgText[BLUE_LINEAR_TRANSFORMATION];
    value[7].str = bufbl;
    value[7].len = 20;

    TextPrompt(paint, msgText[RGB_TRANSFORMATION],
	       &info, GammaCorrectionOkCallback, NULL, NULL);
}

static void
doFloydSteinberg(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char bufc[20], bufp[20], bufg[20];

    sprintf(bufc, "%d", ImgProcessInfo.FSsteps);
    sprintf(bufp, "%d", ImgProcessInfo.FSmidrange);
    sprintf(bufg, "%4.2f", ImgProcessInfo.FSgamma);

    info.prompts = value;
    info.title = msgText[FLOYD_STEINBERG_DITHERING];
    info.nprompt = 3;
    value[0].prompt = msgText[NUMBER_OF_COLORS_PER_CHANNEL];
    value[0].str = bufc;
    value[0].len = 4;
    value[1].prompt = msgText[MID_RANGE_VALUE];
    value[1].str = bufp;
    value[1].len = 4;
    value[2].prompt = msgText[GAMMA_CORRECTION_LOG_SCALE];
    value[2].str = bufg;
    value[2].len = 6;

    TextPrompt(paint, msgText[FLOYD_STEINBERG], 
	       &info, FloydSteinbergOkCallback, 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, msgText[QUANTIZATION_OF_COLORS], 
	       &info, quantizeOkCallback, NULL, NULL);
}

static void
expandRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char buf_scale_x[10], buf_scale_y[10];
    static char buf_interp[10], buf_x[256], buf_y[256];

    sprintf(buf_x, "%s (x)", msgText[EXPANSION_FACTOR]);
    sprintf(buf_y, "%s (y)", msgText[EXPANSION_FACTOR]);   
    sprintf(buf_scale_x, "%g", ImgProcessInfo.rescale_x);
    sprintf(buf_scale_y, "%g", ImgProcessInfo.rescale_y);
    sprintf(buf_interp, "%d", ImgProcessInfo.interpolate);

    info.prompts = value;
    info.title = msgText[EXPANSION_PARAMETERS];
    info.nprompt = 3;
    value[0].prompt = buf_x;
    value[0].str = buf_scale_x;
    value[0].len = 3;
    value[1].prompt = buf_y;
    value[1].str = buf_scale_y;
    value[1].len = 3;   
    value[2].prompt = msgText[DO_INTERPOLATION_OF_PIXELS];
    value[2].str = buf_interp;
    value[2].len = 3;   

    TextPrompt(paint, msgText[IMAGE_EXPANSION], 
	       &info, expandOkCallback, NULL, NULL);
}

static void
downscaleRegion(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char buf_scale_x[10], buf_scale_y[10];
    static char buf_interp[10], buf_x[256], buf_y[256];

    sprintf(buf_x, "%s (x)", msgText[DOWNSCALING_FACTOR]);
    sprintf(buf_y, "%s (y)", msgText[DOWNSCALING_FACTOR]);   
    sprintf(buf_scale_x, "%g", ImgProcessInfo.rescale_x);
    sprintf(buf_scale_y, "%g", ImgProcessInfo.rescale_y);
    sprintf(buf_interp, "%d", ImgProcessInfo.interpolate);

    info.prompts = value;
    info.title = msgText[DOWNSCALING_PARAMETERS];
    info.nprompt = 3;
    value[0].prompt = buf_x;
    value[0].str = buf_scale_x;
    value[0].len = 3;
    value[1].prompt = buf_y;
    value[1].str = buf_scale_y;
    value[1].len = 3;   
    value[2].prompt = msgText[DO_INTERPOLATION_OF_PIXELS];
    value[2].str = buf_interp;
    value[2].len = 3;   

    TextPrompt(paint, msgText[IMAGE_DOWNSCALING],
	       &info, expandOkCallback, NULL, (XtPointer)1);
}

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

    StdLastImgProcess(paint);
}

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_FILTER_LAST], XtNsensitive, True, NULL);
    XtVaSetValues(wlist[W_FILTER_UNDO], XtNsensitive, True, NULL);
    XtVaSetValues(wlist[W_TOPMENU+W_FILTER_LAST], 
		  XtNsensitive, True, NULL);
    XtVaSetValues(wlist[W_TOPMENU+W_FILTER_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);
	Global.bg[0] = (unsigned char) (col->red >> 8);
	Global.bg[1] = (unsigned char) (col->green >> 8);
	Global.bg[2] = (unsigned char) (col->blue >> 8);
	XtVaSetValues(w, XtNbackground, pix, NULL);
	if (Global.patternshell)
	    checkPatternLink(w, 1);
	else
	  setCanvasColorsIcon(w, NULL);
    }
}

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
alphaParametersOkCallback(Widget paint, void *junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    unsigned int alpha_bg, alpha_fg, alpha_threshold;

    alpha_bg = atoi(info->prompts[0].rstr);
    alpha_fg = atoi(info->prompts[1].rstr);
    alpha_threshold = atoi(info->prompts[2].rstr);

    if ((alpha_bg < 0) || (alpha_bg > 255) ||
	(alpha_fg < 0) || (alpha_fg > 255) ||
	(alpha_threshold < 0) || (alpha_threshold > 255)) {
	Notice(paint, msgText[INVALID_ALPHA_CHANNEL_PARAMETERS]);
	return;
    }
    Global.alpha_bg = alpha_bg;
    Global.alpha_fg = alpha_fg;
    Global.alpha_threshold = alpha_threshold;
}

static void
alphaParametersCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char alpha_bg[10], alpha_fg[10], alpha_threshold[10];

    sprintf(alpha_bg, "%d", Global.alpha_bg);
    sprintf(alpha_fg, "%d", Global.alpha_fg);
    sprintf(alpha_threshold, "%d", Global.alpha_threshold);

    info.prompts = value;
    info.title = msgText[ENTER_DESIRED_ALPHA_CHANNEL_PARAMETERS];
    info.nprompt = 3;
    value[0].prompt = msgText[ALPHA_BACKGROUND_VALUE];
    value[0].str = alpha_bg;
    value[0].len = 4;
    value[1].prompt = msgText[ALPHA_FOREGROUND_VALUE];
    value[1].str = alpha_fg;
    value[1].len = 4;
    value[2].prompt = msgText[ALPHA_THRESHOLD_VALUE];
    value[2].str = alpha_threshold;
    value[2].len = 4;

    TextPrompt(paint, msgText[ALPHA_PARAMETERS],
	       &info, alphaParametersOkCallback, NULL, NULL);
}

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[8];
    int nargs = 0;
    Widget shell;

    XtSetArg(args[nargs], XtNtitle, msgText[DEFAULT_TITLE]); nargs++;
    XtSetArg(args[nargs], XtNiconName, msgText[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)
{
    Boolean v;
    LocalInfo *info = (LocalInfo *) infoArg;

    v = IsFullMenuSet(info->paint);
    SetFullMenu(info->paint, !v);
    MenuCheckItem(w, v);
}

void
ManageQuirk(LocalInfo * info)
{
    Display * dpy = XtDisplay(info->shell);
    Position x, x1, x2, y;
    Dimension w2;

    XtUnmanageChild(info->bar);
    XtUnmanageChild(info->resize);
    XtUnmanageChild(info->position);
    XtUnmanageChild(info->palette);
    XtUnmanageChild(info->memory);
    XtUnmanageChild(info->colors);
    XtUnmanageChild(info->tool);
    XtUnmanageChild(info->brush);
    XtUnmanageChild(info->font);
    XtUnmanageChild(info->linestyle);
    XFlush(dpy);
    /* usleep(30000); */
    XtVaSetValues(info->form, XtNshowGrip, True, NULL);
    XtVaSetValues(info->palette, XtNwidth, PALETTE_SIZE*CELL_WIDTH+2*EYE_WIDTH,
		  NULL);
    XtVaSetValues(info->memory,
		  XtNwidth, (2+MEMSLOT_SIZE)*MEMORY_HSTEPS+3, 
		  XtNheight, CELL_WIDTH*info->vsteps+1, NULL);
    /*    
    XMapWindow(dpy, XtWindow(info->bar)); 
    XMapWindow(dpy, XtWindow(info->resize)); 
    XMapWindow(dpy, XtWindow(info->position)); 
    XMapWindow(dpy, XtWindow(info->palette));
    XMapWindow(dpy, XtWindow(info->memory));  
    XMapWindow(dpy, XtWindow(info->colors)); 
    XMapWindow(dpy, XtWindow(info->tool)); 
    XMapWindow(dpy, XtWindow(info->brush)); 
    XMapWindow(dpy, XtWindow(info->font));
    */
    
    XtVaGetValues(info->colors, XtNx, &x1, NULL);
    XtVaGetValues(info->linestyle, XtNy, &y, NULL);
    XtVaGetValues(info->position, XtNx, &x2, XtNwidth, &w2, NULL);
    if ((x=x2+w2+7)<x1) x = x1-1;
    XtMoveWidget(info->linestyle, x, y);
    XtVaGetValues(info->font, XtNx, &x2, XtNwidth, &w2, NULL);
    LINESTYLE_WIDTH = x2+w2 - x - 2;
    XtResizeWidget(info->linestyle, LINESTYLE_WIDTH, LINESTYLE_HEIGHT, 1);

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

static void
hideMenuBar(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    SetMenuBarVisibility(info->paint, False);
    SetFullMenu(info->paint, True);
    XtVaGetValues(info->form, XtNheight, &info->barheight, NULL);
    XtUnmanageChild(info->form);
}

static void
showMenuBar(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    XEvent event;
    Dimension width, height, widthp, heightp;
    int diff = 0;

    XtVaGetValues(info->shell, XtNwidth, &width, XtNheight, &height, NULL);
    XtVaGetValues(info->paint, XtNwidth, &widthp, XtNheight, &heightp, NULL);
    if (!IsMenuBarGlobal() && heightp+Global.btn_height+54>height) {
	 XtResizeWidget(info->shell, width, heightp+Global.btn_height+54, 0);
	 info->barheight = Global.btn_height+53;
	 height = heightp+Global.btn_height+54;
	 diff = 6;
    }
    SetMenuBarVisibility(info->paint, True);
    SetFullMenu(info->paint, IsFullMenuGlobal());
    XtUnmanageChild(info->viewport);
    XtManageChild(info->form);
    XtManageChild(info->viewport);
    XtMoveWidget(info->viewport, 0, info->barheight);
    heightp = height - info->barheight;
    if (heightp < 10) heightp = 10;
    XtResizeWidget(info->viewport, width, heightp, 0);
    XtResizeWidget(info->shell, width, height - 4, 0);
    XFlush(XtDisplay(info->shell));
    XtResizeWidget(info->shell, width, height-diff, 0);
    XtMoveWidget(info->paint, 4, 4);
    event.type = ConfigureNotify;
    shellHandler(info->shell, info, &event, NULL);
}

static void
leftScrollbar(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    WidgetList wlist;

    XtVaGetValues(info->paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;

    info->left_sb = !info->left_sb;
    XtVaSetValues(info->viewport, "sbShiftY1", info->left_sb? 0 : 4, NULL);
    XtVaSetValues(info->viewport, XtNuseRight, !info->left_sb, NULL);
    MenuCheckItem(wlist[W_SELECTOR_LEFT_SCROLLBAR], info->left_sb);
    MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_LEFT_SCROLLBAR], info->left_sb);
}

static void
topScrollbar(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo * info = (LocalInfo *) infoArg;
    WidgetList wlist;

    XtVaGetValues(info->paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;

    XtVaSetValues(info->viewport, "sbShiftX2", info->top_sb? 0 : 
		  (info->left_sb? 20:4) , NULL);
    XtVaSetValues(info->viewport, XtNuseBottom, info->top_sb, NULL);
    info->top_sb = !info->top_sb;
    MenuCheckItem(wlist[W_SELECTOR_TOP_SCROLLBAR], info->top_sb);
    MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_TOP_SCROLLBAR], info->top_sb);
}

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

static void
alphaModeCallback(Widget w, XtPointer wlArg, XtPointer junk)
{
    LocalInfo *info = (LocalInfo *) wlArg;
    char *file, *name;
    PaintWidget pw = (PaintWidget) info->paint;
    WidgetList wlist;

    name = XtName(w);
    if (!pw->paint.current.alpha) return;
    XtVaGetValues(info->paint, XtNfilename, &file, NULL);
    pw->paint.alpha_mode = (name[4]-'0')%4;
    RefreshPaintWidget((Widget)pw);
    MenuCheckItem(w, True);
    XtVaGetValues(info->paint, XtNmenuwidgets, &wlist, NULL);
    if (!wlist) return;
    MenuCheckItem(wlist[W_ALPHA_MODES+pw->paint.alpha_mode], True);
    MenuCheckItem(wlist[W_TOPMENU+W_ALPHA_MODES+pw->paint.alpha_mode], True);
}

static void
alphaRGBEditCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Display *dpy = XtDisplay(paint);
    XImage * xim;
    Image *image;
    Pixmap pix;
    int width, height, zoom, x, y;
    unsigned int v, scale;
    unsigned char *alpha, *c, *u;
    Colormap cmap;

    if (!pw->paint.current.alpha) return;
    XtVaGetValues(paint, XtNcolormap, &cmap, XtNzoom, &zoom, NULL); 

    width = pw->paint.drawWidth;    
    height = pw->paint.drawHeight;
    xim = XGetImage(dpy, pw->paint.current.pixmap, 0, 0, 
		    width, height, AllPlanes, ZPixmap);
    scale = xim->bits_per_pixel >> 3;
    image = ImageNew(width, height);
    for (y=0; y<height; y++) {
	c = image->data + 3 * y * width;
	u = xim->data + y * xim->bytes_per_line;
	alpha = pw->paint.current.alpha + y * width;
	for (x=0; x<width; x++) {
	    v =   255 - *alpha;
	    *c = ((u[2]) * (*alpha) + (v * Global.bg[0])) >> 8; c++;
	    *c = ((u[1]) * (*alpha) + (v * Global.bg[1])) >> 8; c++;
	    *c = ((u[0]) * (*alpha) + (v * Global.bg[2])) >> 8; c++;
	    u += scale;
	    ++alpha;
	}
    }
    XDestroyImage(xim);
    ImageToPixmap(image, paint, &pix, &cmap);
    graphicCreate(makeGraphicShell(Global.toplevel), 
		  0, 0, zoom, pix, cmap, NULL);  
}

static void
canvasResizeToggleCallback(Widget w, XtPointer wlArg, XtPointer junk)
{
    LocalInfo *info = (LocalInfo *) wlArg;
    Pixel green, color;
    int w1, h1;
    char buf[16];
    Boolean state;

    if (Global.popped_up) {
	PopdownMenusGlobal();
	XtVaSetValues(w, XtNstate, info->resize_state, NULL);
	return;
    }

    if (((PaintWidget)info->paint)->paint.current.alpha)
	color = GetPixelByName(w, "blue");
    else
	color = BlackPixelOfScreen(XtScreen(w));
    green = GetPixelByName(w, "green");
    XtVaGetValues(info->resize, XtNstate, &state, NULL);
    XtVaSetValues(info->paint, XtNborderColor, (state)? green : color, NULL);
    XtVaSetValues(info->paint, XtNlocked, state, NULL);
    info->resize_state = (state)? -1:0;
    XtVaGetValues(info->paint, XtNdrawWidth, &w1, XtNdrawHeight, &h1, NULL);
    sprintf(buf, "%d %d", w1, h1);
    XtVaSetValues(info->position, XtNlabel, buf, NULL);
    XtVaSetValues(info->position, XtNwidth, POS_WIDTH, NULL);
    XDefineCursor(XtDisplay(info->paint), XtWindow(info->paint), (state)?
		  XCreateFontCursor(XtDisplay(info->paint), 
					    XC_sizing) :
		  ((PaintWidget)info->paint)->paint.cursor);
    XDefineCursor(XtDisplay(info->viewport), XtWindow(info->viewport), (state)?
		  XCreateFontCursor(XtDisplay(info->viewport), 
					    XC_sizing) :
		  XCreateFontCursor(XtDisplay(info->viewport), XC_left_ptr));
}

/*
**
 */

int
CRC_exists(RegionData *r)
{
    int i;
    for (i=0; i<Global.numregions; i++)
	if (!memcmp(&r->crcdata,&Global.regiondata[i].crcdata, sizeof(CRCdata)))
	    return 1;
    return 0;
}

void
CRC_checksum(RegionData *r)
{
    unsigned int s1 = 0, s2 = 0;
    unsigned char a;
    int width, height, depth, x, y;
    Display *dpy = XtDisplay(Global.toplevel);
    XImage *xim;
    unsigned long p;

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

    p = width;
    while (p) { s1 = CRC32(s1, p&0xff); p = p>>8; }
    p = height;
    while (p) { s1 = CRC32(s1, p&0xff); p = p>>8; }
    p = width^255;
    while (p) { s2 = CRC32(s2, p&0xff); p = p>>8; }
    p = height^255;
    while (p) { s2 = CRC32(s2, p&0xff); p = p>>8; }

    xim = XGetImage(dpy, r->pix, 0, 0, width, height, AllPlanes, ZPixmap);
    for (y=0; y<height; y++)
	for (x=0; x<width; x++) {
	    p = XGetPixel(xim, x, y);
	    while (p) { 
		a = p&0xff;
		s1 = CRC32(s1, a);
		s2 = CRC32(s2, a^(0xff));
		p = p>>8;
	    }
	}
    XDestroyImage(xim);

    if (r->mask) {
	xim = XGetImage(dpy, r->mask, 0, 0, width, height, AllPlanes, ZPixmap);
	for (y=0; y<height; y++)
	    for (x=0; x<width; x++) {
		/* random 8 bit values here ... */
		a = (XGetPixel(xim, x, y))? 141:27;
		s1 = CRC32(s1, a);
		s2 = CRC32(s2, a^(0xff));
	    }
	XDestroyImage(xim);
    }
    if (r->alpha) {
	for (y=0; y<height; y++)
	    for (x=0; x<width; x++) {
		a = r->alpha[x+y*width];
		s1 = CRC32(s1, a);
		s2 = CRC32(s2, a^(0xff));
	    }
    }
    r->crcdata.s1 = s1;
    r->crcdata.s2 = s2;
}

int 
ImageToMemory(Image * image)
{
    Pixmap pix, mask;
    Colormap cmap;
    Widget w = Global.toplevel;
    Display * dpy= XtDisplay(w);
    RegionData r;
    int i;

    if (!image) return 0;
    r.alpha = image->alpha;
    image->alpha = NULL;
    XtVaGetValues(w, XtNcolormap, &cmap, NULL);
    mask = ImageMaskToPixmap(w, image);
    if (!ImageToPixmap(image, w, &pix, &cmap)) {
	XFreePixmap(dpy, mask);
	if (r.alpha) XtFree((char *)r.alpha);
	return -1;
    }

    r.x = 0;
    r.y = 0;
    r.pix = pix;
    r.mask = mask;
    CRC_checksum(&r);

    if (CRC_exists(&r)) {
	XFreePixmap(dpy, pix);
	if (mask) XFreePixmap(dpy, mask);
	if (r.alpha) XtFree((char *)r.alpha);
	return -1;
    }

    i = Global.numregions;
    ++Global.numregions;
    Global.regiondata = (RegionData *) 
	realloc(Global.regiondata, Global.numregions*sizeof(RegionData));
    Global.regiondata[i] = r;
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
    return i;
}

void ImageToPaintWidget(Image * image, Widget paint)
{
   XRectangle rect;
   PaintWidget pw = (PaintWidget)paint;
   Pixmap pix;   
   Colormap cmap = DefaultColormapOfScreen(XtScreen(Global.toplevel));
   rect.x = 0;
   rect.y = 0;
   rect.width = image->width;
   rect.height = image->height;
   if (rect.width > pw->paint.drawWidth) rect.width = pw->paint.drawWidth;
   if (rect.height > pw->paint.drawHeight) rect.height = pw->paint.drawHeight;   
   if (ImageToPixmap(image, paint, &pix, &cmap)) {
      PwRegionSet(paint, &rect, pix, None);
      PwRegionSet(paint, NULL, None, None);
   }
}

static void 
loadMemoryCallback(Widget w, char *file, void *image)
{
    ClipboardSetImage(w, image);
    /* This was (and still is) the way of loading the Copy/Paste clipboard */
    /* GraphicAll(setPasteItemMenu, NULL); */
    /* Put instead image into the more user friendly memory manager */
    ImageToMemory(image);
}

void 
loadMemory(Widget w, XtPointer junk, XtPointer junk2)
{
    GetFileName(GetShell(w), BROWSER_READ, NULL, 
		(XtCallbackProc) loadMemoryCallback, NULL);
}

static void
createAlphaCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    int n;

    if (pw->paint.current.alpha) return;
    n = pw->paint.drawWidth * pw->paint.drawHeight;
    pw->paint.current.alpha = (unsigned char *) xmalloc(n);
    memset(pw->paint.current.alpha, (unsigned char)Global.alpha_fg, n);
    SetAlphaMode(paint, -1);
    RefreshPaintWidget(paint);
}

static void
editAlphaCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Image * image;
    Pixmap pix;
    int width, height, zoom;
    Colormap cmap;

    if (!pw->paint.current.alpha) return;
    XtVaGetValues(paint, XtNcolormap, &cmap, XtNzoom, &zoom, NULL); 

    width = pw->paint.drawWidth;    
    height = pw->paint.drawHeight;    
    image = ImageNewGrey(width, height);
    memcpy(image->data, pw->paint.current.alpha, width*height);
    ImageToPixmap(image, paint, &pix, &cmap);
    graphicCreate(makeGraphicShell(Global.toplevel), 
		  0, 0, zoom, pix, cmap, NULL);
}

static void
memoryAlphaCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Image * image;
    int width, height, zoom;
    Colormap cmap;

    if (!pw->paint.current.alpha) return;
    XtVaGetValues(paint, XtNcolormap, &cmap, XtNzoom, &zoom, NULL); 

    width = pw->paint.drawWidth;    
    height = pw->paint.drawHeight;    
    image = ImageNewGrey(width, height);
    memcpy(image->data, pw->paint.current.alpha, width*height);
    ImageToMemory(image);
}

static void
setAlphaCallback(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;
    Image *image;
    GC gc;
    unsigned char *src, *dst;

    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;
	}
	GetPixmapWHD(XtDisplay(pw), pix, &width, &height, NULL);
	if (width != pw->paint.drawWidth) return;
	if (height != pw->paint.drawHeight) return;
	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);
	}
	image = PixmapToImage(paint, newpix, cmap);
	if (!pw->paint.current.alpha)
	    pw->paint.current.alpha = (unsigned char *)xmalloc(width*height);
	src = image->data;
	dst = pw->paint.current.alpha;
	if (image->cmapData) {
	    for (y=0; y<height; y++)
		for (x=0; x<width; x++) {
		    *dst++ = GreyScale(image->cmapData[3*(*src)],
				       image->cmapData[3*(*src)+1],
				       image->cmapData[3*(*src)+2]);
		    ++src;
		}
	} else {
	    for (y=0; y<height; y++)
		for (x=0; x<width; x++) {
		    *dst++ = GreyScale(src[0], src[1], src[2]);
		    src += 3;
		}
	}
	ImageDelete(image);
	XtReleaseGC(paint, gc);
	XtVaSetValues(paint, XtNdirty, True, NULL);
	pw->paint.alpha_mode = 2;
	PwRegionOff(paint, True);
	SetAlphaMode(paint, -1);
	RefreshPaintWidget(paint);
    } else {
	Notice(paint, msgText[ALPHA_CHANNEL_SHOULD_BE_CREATED]);
    }
}

static void
deleteAlphaCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Image * image;
    int n;

    if (pw->paint.current.alpha) {
	/* put alpha channel in memory as grey image */
	image = ImageNewGrey(0, 0);
	image->width = pw->paint.drawWidth;
	image->height = pw->paint.drawHeight;
	n = image->width * image->height;
	image->data = (unsigned char *)xmalloc(n);
	memcpy(image->data, pw->paint.current.alpha, n);
	/* This may sometimes lead to a memory leak, but on the other hand
	   freeing the alpha channel leads to a segfault through DestroyProc,
	   so it is better to have 10% probability to have a memory leak... */
	pw->paint.current.alpha = NULL;
	XtVaSetValues((Widget)pw, XtNdirty, True, NULL);
	SetAlphaMode((Widget)pw, -1);
	ImageToMemory(image);
	RefreshPaintWidget((Widget)pw);
    }
}

void
FontSelectCallback(Widget w, XtPointer junk, XtPointer junk2)
{
    FontSelect(GetShell(w), NULL);
}

void
LupeCallback(Widget w, XtPointer junk, XtPointer junk2)
{
    StartMagnifier(w);
}

void
tabletOkCallback(Widget w, XtPointer arg, XtPointer data)
{
    LocalInfo * localinfo = (LocalInfo *) arg;
    TextPromptInfo *info = (TextPromptInfo *) data;
    char *buf;    
    
    buf = malloc(128);
    sprintf(buf, "killall -9 %s 2>/dev/null 1>&2",
	    (Global.tablet? Global.tablet : info->prompts[0].rstr));
    if (system(buf));
    free(Global.tablet);
    Global.tablet = strdup(info->prompts[0].rstr);
    sprintf(buf, "%s -wid %08lx &", Global.tablet,
	    (long unsigned int) XtWindow(localinfo->paint));
    if (system(buf));
    free(buf); 
}

void
tabletCallback(Widget w, XtPointer arg, XtPointer junk2)
{
    static TextPromptInfo info;
    static struct textPromptInfo values[2];
    static char buf[80];
    int zoom;
    LocalInfo * localinfo = (LocalInfo *) arg;

    if (Global.tablet)
      strcpy(buf, Global.tablet);
    else
      *buf = '\0';
    info.nprompt = 1;
    info.prompts = values;
    info.title = msgText[CONNECT_GRAPHICS_TABLET_TO_CANVAS];
    values[0].prompt = msgText[DRIVER_EXECUTABLE];
    values[0].len = 4;
    values[0].str = buf;

    TextPrompt(GetShell(localinfo->paint), msgText[DRIVER_EXECUTABLE],
	       &info, tabletOkCallback, NULL, localinfo);
}

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

static void
ToolSelectCallback(Widget w, XtPointer shell, XtPointer junk2)
{
    Display *dpy = XtDisplay(Global.toplevel);
    Global.canvas = (Widget) shell;
    XtVaSetValues(Global.back, XtNsensitive, True, NULL);
    SetWMInputHint(dpy, XtWindow(Global.canvas));
    RaiseWindow(dpy, 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);
    if (Global.patternshell)
	RaiseWindow(XtDisplay(Global.patternshell), 
		    XtWindow(Global.patternshell));
}

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-14) u = up-14;
#if defined(XAW3D) || defined(XAW95)
	u -= 4;
#endif
	XtVaSetValues(shell, XtNwidth, u, NULL);
	uset = 1;
	
    }
    if (v>30) {
	vp = HeightOfScreen(XtScreen(shell));
	if (v > vp-34) v = vp-34;
#if defined(XAW3D) || defined(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);
}

/*
 *  Key actions on selected regions
 */

void 
selectKeyPress(Widget w, void * arg, XKeyEvent * event, void * junk)
{
    WidgetList wlist;
    LocalInfo * info = (LocalInfo *)arg;
    KeySym keysym;
    Boolean locked = False;
    int len, dx, dy;
    char buf[21];
  
    /* Here we are in text operation - don't interfere with key shortcuts */
    if (getIndexOp() == 10) return;

    len = XLookupString(event, buf, sizeof(buf) - 1, &keysym, NULL);
    dx = dy = 0;

    switch (keysym) {
    case XK_Delete:
    case XK_BackSpace:
	PwRegionClear(w);
	return;
    case XK_numbersign:
	if (info) {
	    XtVaGetValues(info->paint, XtNlocked, &locked, NULL);
	    XtVaSetValues(info->paint, XtNlocked, !locked, NULL);
	    XtVaSetValues(info->resize, XtNstate, !locked, NULL);
	    canvasResizeToggleCallback(w, info, NULL);
	}
	break;
    case XK_Up:
	dy = -1;
	break;
    case XK_Down:
	dy = 1;
	break;
    case XK_Left:
	dx = -1;
	break;
    case XK_Right:
	dx = 1;
	break;
    case XK_Escape:
	PopdownMenusGlobal();
	PwRegionFinish(w, True);
	return;
    case XK_space:
	if (event->state & ControlMask) {
	    Global.transparent = !Global.transparent;
	    XtVaGetValues(w, XtNmenuwidgets, &wlist, NULL);
	    if (wlist) {
		MenuCheckItem(wlist[W_SELECTOR_TRANSPARENT],
			      Global.transparent);
		MenuCheckItem(wlist[W_TOPMENU+W_SELECTOR_TRANSPARENT],
			      Global.transparent);
	    }
	    if (!Global.transparent)
		XtVaSetValues(w, XtNtransparent, 2, NULL);
	} else {
	    int value;
	    XtVaGetValues(w, XtNtransparent, &value, NULL);
	    value = 3-(value&1);
	    XtVaSetValues(w, XtNtransparent, value, NULL);
	}
	PwRegionTear(w);
	RegionTransparency((PaintWidget) w);
	break;
    default:
	return;
    }
    
    if (event->state & ControlMask) {
	dx *= 5;
	dy *= 5;
    }

    if (info) {
	XtVaGetValues(info->paint, XtNlocked, &locked, NULL);
	if (locked) {
	    int x, y, z;
	    XtVaGetValues(info->paint, XtNzoom, &z, NULL);
	    XtVaGetValues(info->paint, XtNdrawWidth, &x, NULL);
	    XtVaGetValues(info->paint, XtNdrawHeight, &y, NULL);
	    x += dx;
	    y += dy;
	    resizeCanvas(info, x, y, 0);
	    return;
	}
    }

    if (dx || dy) {
	RegionMove((PaintWidget) w, dx, dy);
	RegionTransparency((PaintWidget) w);
	return;
    }
}

void 
selectKeyRelease(Widget w, void * l, XKeyEvent * event, void * info)
{
    KeySym keysym;
    int len;
    char buf[21];

    len = XLookupString(event, buf, sizeof(buf) - 1, &keysym, NULL);

    /* Noop at this time */
}

/*
 * 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.
 */

WidgetList InitializedWidgetList()
{
    int i, j;
    WidgetList wlist;

    /* 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_FILTER_LAST] = popupFilterMenu[P_FILTER_LAST].widget;
    wlist[W_FILTER_UNDO] = popupFilterMenu[P_FILTER_UNDO].widget;
    wlist[W_SELECTOR_TRANSPARENT] = popupSelectorMenu[P_SELECTOR_TRANSPARENT].widget;
    wlist[W_SELECTOR_INTERPOLATION] = popupSelectorMenu[P_SELECTOR_INTERPOLATION].widget;
    wlist[W_SELECTOR_GRID] = popupSelectorMenu[P_SELECTOR_GRID].widget;
    wlist[W_SELECTOR_SNAP] = popupSelectorMenu[P_SELECTOR_SNAP].widget;
    wlist[W_SELECTOR_LEFT_SCROLLBAR] = popupSelectorMenu[P_SELECTOR_LEFT_SCROLLBAR].widget;
    wlist[W_SELECTOR_TOP_SCROLLBAR] = popupSelectorMenu[P_SELECTOR_TOP_SCROLLBAR].widget;
    wlist[W_MEMORY_RECALL] = popupMemoryMenu[P_MEMORY_RECALL].widget;
    wlist[W_MEMORY_EDIT] = popupMemoryMenu[P_MEMORY_EDIT].widget;
    wlist[W_MEMORY_DISCARD] = popupMemoryMenu[P_MEMORY_DISCARD].widget;
    wlist[W_MEMORY_ERASE] = popupMemoryMenu[P_MEMORY_ERASE].widget;
    wlist[W_MEMORY_ORIGIN] = popupMemoryMenu[P_MEMORY_ORIGIN].widget;
    wlist[W_MEMORY_TO_ALPHA] = popupMemoryMenu[P_MEMORY_TO_ALPHA].widget;
    wlist[W_MEMORY_TO_MASK] = popupMemoryMenu[P_MEMORY_TO_MASK].widget;
    wlist[W_MEMORY_TO_RGBA] = popupMemoryMenu[P_MEMORY_TO_RGBA].widget;
    wlist[W_MEMORY_MERGE_RGBA] = popupMemoryMenu[P_MEMORY_MERGE_RGBA].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_FILTER_LAST] = filterMenu[FILTER_LAST].widget;
    wlist[W_TOPMENU+W_FILTER_UNDO] = filterMenu[FILTER_UNDO].widget;
    wlist[W_TOPMENU+W_SELECTOR_TRANSPARENT] = selectorMenu[SELECTOR_TRANSPARENT].widget;
    wlist[W_TOPMENU+W_SELECTOR_INTERPOLATION] = selectorMenu[SELECTOR_INTERPOLATION].widget;
    wlist[W_TOPMENU+W_SELECTOR_GRID] = selectorMenu[SELECTOR_GRID].widget;
    wlist[W_TOPMENU+W_SELECTOR_SNAP] = selectorMenu[SELECTOR_SNAP].widget;
    wlist[W_TOPMENU+W_SELECTOR_LEFT_SCROLLBAR] = selectorMenu[SELECTOR_LEFT_SCROLLBAR].widget;
    wlist[W_TOPMENU+W_SELECTOR_TOP_SCROLLBAR] = selectorMenu[SELECTOR_TOP_SCROLLBAR].widget;
    wlist[W_TOPMENU+W_MEMORY_STACK] = memoryMenu[MEMORY_STACK].widget;
    wlist[W_TOPMENU+W_MEMORY_RECALL] = memoryMenu[MEMORY_RECALL].widget;
    wlist[W_TOPMENU+W_MEMORY_EDIT] = memoryMenu[MEMORY_EDIT].widget;
    wlist[W_TOPMENU+W_MEMORY_DISCARD] = memoryMenu[MEMORY_DISCARD].widget;
    wlist[W_TOPMENU+W_MEMORY_SCROLL] = memoryMenu[MEMORY_SCROLL].widget;
    wlist[W_TOPMENU+W_MEMORY_ERASE] = memoryMenu[MEMORY_ERASE].widget;
    wlist[W_TOPMENU+W_MEMORY_ORIGIN] = memoryMenu[MEMORY_ORIGIN].widget;
    wlist[W_TOPMENU+W_MEMORY_TO_ALPHA] = memoryMenu[MEMORY_TO_ALPHA].widget;
    wlist[W_TOPMENU+W_MEMORY_TO_MASK] = memoryMenu[MEMORY_TO_MASK].widget;
    wlist[W_TOPMENU+W_MEMORY_TO_RGBA] = memoryMenu[MEMORY_TO_RGBA].widget;
    wlist[W_TOPMENU+W_MEMORY_MERGE_RGBA] = memoryMenu[MEMORY_MERGE_RGBA].widget;

    for (i=0; i<=ALPHA_DELETE; i++) {
	wlist[W_ALPHA_MODES+i] = popupAlphaMenu[P_ALPHA_MODES+i].widget;
	wlist[W_TOPMENU+W_ALPHA_MODES+i] = alphaMenu[ALPHA_MODES+i].widget;
    }

    for (i=0; i<=NUMBER_LINEWIDTHS; i++) {
	j = i+(i==NUMBER_LINEWIDTHS);
	wlist[W_LINE_WIDTHS+i] = popupLineMenu[j+1].widget;
	wlist[W_TOPMENU+W_LINE_WIDTHS+i] = lineMenu[j].widget;
    }
    for (i=0; i<=NUMBER_PREDEF_FONTS; i++) {
	j = i+(i==NUMBER_PREDEF_FONTS);
	wlist[W_FONT_DESCR+i] = popupTextMenu[j+1].widget;
	wlist[W_TOPMENU+W_FONT_DESCR+i] = textMenu[j].widget;
    }
    return wlist;
}

void
CheckMarkItems(Widget paint, WidgetList wlist)
{
  int i, j, k, l, m, u;

    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<NUMBER_PREDEF_FONTS; 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;
	for (u=0; u<NUMBER_PREDEF_FONTS; u++) {
	    if (!strcasecmp(fontNames[u], Global.xft_name)) {
		k = u;
		break;
	    }
	}
	l = 0;
	m = 0;
    }

    GraphicAdd(paint);

    GraphicAll(setLineWidth, (void *)(long)i);
    GraphicAll(setLineWidthMenu, (void *)(long)j);
    GraphicAll(fontMenuCallback, (void *)(long)k);
    GraphicAll(setPasteItemMenu, NULL);
}

static void
zoomMenuSelect(Widget w, XtPointer arg, XtPointer garbage)
{
     LocalInfo * info = (LocalInfo *)arg;
     char *val, *ptr;
     int zoom;
     XtVaGetValues(w, XtNlabel, &val, NULL);
     if (!val) return;
     ptr = strchr(val,'=');
     if (!ptr) return;
     zoom =StrToZoom(ptr+1);
     if (zoom<-9) zoom = -9;
     if (zoom>32) zoom = 32;
     if (zoom==0) zoom = 1;
     setZoom(info->paint, zoom);

     w = XtParent(w);
     if (w) w = XtParent(w);
     if (w) {
       XtVaSetValues(w, XtNlabel, ZoomToStr(zoom), NULL);
       XtVaSetValues(w, XtNwidth, Global.btn_height-1, XtNheight, Global.btn_height, NULL);
     }
}

void
AdjustCanvasSize(LocalInfo *info)
{
    Dimension w, h, wp, hp, ws, hs, dim, cs;
    Position x;
    XEvent event;

    XtVaGetValues(info->paint, XtNwidth, &w, XtNheight, &h, NULL);
    ws = (Dimension) WidthOfScreen(XtScreen(info->paint));
    hs = (Dimension) HeightOfScreen(XtScreen(info->paint));
    dim = info->vsteps*CELL_WIDTH+Global.btn_height + 28;

#if defined(XAW3D) || defined(XAW95)
    cs = Global.btn_height + 3*CELL_WIDTH + 75;
    if (w > ws-24)  wp = ws-24;  else wp = w;
    if (h > hs-cs) hp = hs-cs; else hp = h;
    XtVaGetValues(info->font, XtNx, &x, NULL);
    x += ICONWIDTH - 11;
    if (wp < x) wp = x;
    if (wp > ws-24) wp = ws-24;
    if (hp<50) hp = 50;

    if (wp >= w && hp >= h && wp <= ws-14 && hp <= hs-34) {
	XtVaSetValues(info->viewport, XtNwidth, wp, XtNheight, hp, NULL);
	XtVaSetValues(info->shell, XtNwidth, wp+14, XtNheight, hp+dim, NULL);
    } else {
	if (wp <= ws-40)  wp = wp+16; else wp = ws - 24;
	if (hp <= hs-cs-16) hp = hp+16; else hp = hs - cs;
	XtVaSetValues(info->viewport, XtNwidth, wp, XtNheight, hp, NULL);
	XtVaSetValues(info->shell, XtNwidth, wp+14, XtNheight, hp+dim, NULL);
	XtVaSetValues(info->viewport, XtNallowHoriz, True,
		      XtNallowVert, True, NULL);
    }
#else
    cs = Global.btn_height + 3*CELL_WIDTH + 71;
    if (w > ws-20)  wp = ws-20;  else wp = w;
    if (h > hs-cs) hp = hs-cs; else hp = h;
    if (wp<800) wp = 800; if (wp > ws-20) wp = ws-20;
    if (hp<50) hp = 50;

    if (wp >= w && hp >= h && wp <= ws-10 && h <= hs-30) {
	XtVaSetValues(info->viewport, XtNwidth, wp, XtNheight, hp, NULL);
	XtVaSetValues(info->shell, XtNwidth, wp+10, XtNheight, hp+dim, NULL);
	XtVaSetValues(info->viewport, XtNallowHoriz, False,
		      XtNallowVert, False, NULL);
    } else {
	if (wp <= ws-36) wp = wp+16; else wp = ws - 20;
	if (hp <= hs-cs-16) hp = hp+16; else hp = hs - cs;
	XtVaSetValues(info->viewport, XtNwidth, wp, XtNheight, hp, NULL);
	XtVaSetValues(info->shell, XtNwidth, wp+10, XtNheight, hp+dim, NULL);
	XtVaSetValues(info->viewport, XtNallowHoriz, True,
		      XtNallowVert, True, NULL);
    }
#endif

    event.type = ConfigureNotify;
    shellHandler(info->shell, info, &event, NULL);

    XtSetMinSizeHints(info->shell, 120, 120);
}


Widget
graphicCreate(Widget shell, int width, int height, int zoom,
	      Pixmap pix, Colormap cmap, unsigned char *alpha)
{
    Display *dpy = XtDisplay(shell);
    Widget form, viewport;
    Widget paint;
    Widget pane, paintbox;
    Widget bar, position, resize, palette, memory, mempopup;
    Widget colors, tool, brush, font, linestyle;
    WidgetList wlist;
    PaintWidget pw;
    Palette *map;
    XColor xc;
    Pixel gridcolor, white, black;
    int depth;
    int i;
    LocalInfo *info = XtNew(LocalInfo);
    char name[512];
    char *file = NULL;

    PopdownMenusGlobal();
    info->shell = shell;

    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 = 1;
    info->vsteps = 3;
    info->palette_pixmap = None;
    info->mem_pixmap = None;
    info->rcInfo = NULL;
    info->mem_index = Global.numregions-1;
    info->barheight = Global.btn_height + 53;
    info->left_sb = 0;
    info->top_sb = 0;

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

    /*
     *	Menu Bar
     */
   
    if (menuBar[7].nitems == 0) {
	/* create zoom menu on first invocation */
	menuBar[7].nitems = ZOOMMENU_VALUES;
	for (i = 0; i<ZOOMMENU_VALUES; i++) {
	    if (i<=15)
 		sprintf(name, "zoom = %d", i+1);
	    else
	    if (i==16)
		*name = '\0';
	    else
 		sprintf(name, "zoom = 1:%d", i-15);
	    memset(&zoomMenu[i], 0, sizeof(PaintMenuItem));
	    zoomMenu[i].name = strdup(name);
	    zoomMenu[i].callback = (PaintMenuCallback)zoomMenuSelect;
	}
    }

    for (i = 0; i<ZOOMMENU_VALUES; i++) zoomMenu[i].data = info;
    bar = MenuBarCreate(form, XtNumber(menuBar), menuBar);
    info->bar = bar;
    info->zoom = XtParent(menuBar[7].widget);
    info->alpha =  XtParent(menuBar[8].widget);

    strcpy(name, ZoomToStr(zoom));
    XtVaSetValues(info->zoom, 
		  XtNlabel, name, XtNwidth, Global.btn_height-1, XtNheight, Global.btn_height, NULL);
    XtVaSetValues(info->alpha, XtNwidth, Global.btn_height-1, XtNheight, Global.btn_height, NULL);

    XtVaSetValues(bar, XtNhorizDistance, -1, XtNvertDistance, 0, NULL);
    XtVaSetValues(form, XtNshowGrip, True, NULL);

    resize = XtVaCreateManagedWidget("resize",
				  toggleWidgetClass, form,
				  XtNlabel, "#",
				  XtNwidth, Global.btn_height-1,
				  XtNheight, Global.btn_height,
#ifdef XAWPLAIN
				  XtNborderWidth, 1,
#else
				  XtNborderWidth, 0,
#endif
				  XtNstate, False,
				  XtNfromHoriz, bar,
				  XtNhorizDistance, 0,
				  XtNvertDistance, 4,
				  NULL);
    info->resize = resize;
    info->resize_state = 0;

    XtAddCallback(resize, XtNcallback, 
		   (XtCallbackProc) canvasResizeToggleCallback, (XtPointer) info);
    
    white = WhitePixelOfScreen(XtScreen(shell));
    black = BlackPixelOfScreen(XtScreen(shell));

    position = XtVaCreateManagedWidget(msgText[POSITION],
				  labelWidgetClass, form,
				  XtNbackground, white,   
				  XtNborderWidth, 1,
				  XtNwidth, POS_WIDTH,
				  XtNheight, POS_HEIGHT,
				  XtNfromHoriz, resize,
				  XtNvertDistance, 4,
				  XtNhorizDistance, 3,
				  NULL);
    info->position = position;
    info->boolpos = True;
    XtInsertRawEventHandler(position, 
			    ButtonPressMask | ButtonReleaseMask,
			    False, (XtEventHandler) popupHandler, info, 
			    XtListHead);

    linestyle = XtVaCreateManagedWidget("linestyle",
					coreWidgetClass, form,
					XtNwidth, LINESTYLE_WIDTH,
					XtNheight, LINESTYLE_HEIGHT,
					XtNfromHoriz, position,
					XtNvertDistance, 4,
					XtNhorizDistance, 10,
					NULL);
    info->linestyle = linestyle;
    XtInsertRawEventHandler(linestyle, 
			    ButtonPressMask | ButtonReleaseMask,
			    False, (XtEventHandler) popupHandler, info, 
			    XtListHead);


    palette = XtVaCreateManagedWidget("palette",
				  coreWidgetClass, form,
				  XtNwidth,PALETTE_SIZE*CELL_WIDTH+2*EYE_WIDTH, 
				  XtNheight, CELL_WIDTH*info->vsteps-1,
				  XtNfromVert, bar,
				  XtNvertDistance, 0,
				  XtNhorizDistance, 2,
				  NULL);
    info->palette = palette;

    memory = XtVaCreateManagedWidget("memory",
				  coreWidgetClass, form,
				  XtNwidth, (2+MEMSLOT_SIZE)*MEMORY_HSTEPS+3, 
				  XtNheight, CELL_WIDTH*info->vsteps+1,
				  XtNborderWidth, 0, 
				  XtNfromVert, bar,
				  XtNfromHoriz, palette,
				  XtNvertDistance, 0,
				  XtNhorizDistance, 3,
				  NULL);
    info->memory = memory;
    mempopup = MenuPopupCreate(memory, "popup-menu", 
			       XtNumber(memoryMenu), memoryMenu);
    info->mempopup = mempopup;

    colors = XtVaCreateManagedWidget("",
				  commandWidgetClass, form,
				  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
				  XtNfromVert, bar,
				  XtNfromHoriz, info->memory,
				  XtNvertDistance, 0,
				  XtNhorizDistance, 4,
				  NULL);
    info->colors = colors;
    XtAddCallback(colors, XtNcallback, 
		   (XtCallbackProc) PatternSelectCallback, (XtPointer) info);
    XtInsertRawEventHandler(colors, 
			    ButtonPressMask | ButtonReleaseMask,
			    False, (XtEventHandler) popupHandler, info, 
			    XtListHead);

    tool = XtVaCreateManagedWidget("tool",
				  commandWidgetClass, form,
				  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
				  XtNfromVert, bar,
				  XtNfromHoriz, colors,
				  XtNvertDistance, 0,
				  XtNhorizDistance, 2,
				  NULL);
    info->tool = tool;
    XtAddCallback(tool, XtNcallback, 
		  (XtCallbackProc) ToolSelectCallback, (XtPointer) shell);
    XtInsertRawEventHandler(tool, 
			    ButtonPressMask | ButtonReleaseMask,
			    False, (XtEventHandler) popupHandler, info, 
			    XtListHead);

    brush = XtVaCreateManagedWidget("brush",
 				  commandWidgetClass, form,
				  XtNforeground, black,
				  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
				  XtNfromVert, bar,
  				  XtNfromHoriz, tool,
				  XtNvertDistance, 0,
  				  XtNhorizDistance, 2,
				  NULL);
    info->brush = brush;
    XtAddCallback(brush, XtNcallback,
		   (XtCallbackProc) BrushSelectCallback, (XtPointer) NULL);
    XtInsertRawEventHandler(brush, 
			    ButtonPressMask | ButtonReleaseMask,
			    False, (XtEventHandler) popupHandler, info, 
			    XtListHead);

    sprintf(name, "Abc\n:%g", Global.xft_size);
    font = XtVaCreateManagedWidget(name,
 				  commandWidgetClass, form,
				  XtNwidth, ICONWIDTH, XtNheight, ICONHEIGHT,
				  XtNfromVert, bar,
  				  XtNfromHoriz, brush,
				  XtNvertDistance, 0,
  				  XtNhorizDistance, 2,
				  NULL);
    info->font = font;
    XtAddCallback(font, XtNcallback,
		   (XtCallbackProc) FontSelectCallback, (XtPointer) NULL);
    XtInsertRawEventHandler(font, 
			    ButtonPressMask | ButtonReleaseMask,
			    False, (XtEventHandler) popupHandler, info, 
			    XtListHead);

    /*
     *	Drawing Area
     */
    viewport = XtVaCreateWidget("viewport",
				viewportWidgetClass, pane,
				XtNfromVert, form,
				XtNuseBottom, True,
				XtNuseRight, True,
				XtNtop, XtChainTop,
				NULL);
    info->viewport = viewport;
    XtInsertRawEventHandler(viewport, 
		      ButtonPressMask | ButtonReleaseMask | 
		      KeyPressMask | KeyReleaseMask,
		      False, (XtEventHandler) canvasHandler, info, XtListHead);

    /*
     *	Custom Drawing Widget here
     */
    paintbox = XtVaCreateWidget("paintBox",
			     boxWidgetClass, viewport,
			     XtNbackgroundPixmap,GetBackgroundPixmap(viewport),
			     NULL);
    info->paintbox = paintbox;
    XtInsertRawEventHandler(paintbox,
		      ButtonPressMask | ButtonReleaseMask | 
		      KeyPressMask | KeyReleaseMask,
		      False, (XtEventHandler) canvasHandler, info, XtListHead);

    info->boolcount = 0;

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

    if (depth>8) {
	if (XAllocNamedColor(dpy, cmap, "#cccccc", &xc, &xc) != 0)
	    gridcolor = xc.pixel;
	else
	    gridcolor = black;
    } else
	gridcolor = BlackPixelOfScreen(XtScreen(shell));

    paint = XtVaCreateManagedWidget("paint",
				    paintWidgetClass, paintbox,
				    XtNdrawWidth, width,
				    XtNdrawHeight, height,
				    XtNzoom, zoom,
				    XtNcolormap, cmap,
				    XtNallowResize, True,
				    XtNinterpolation, 1,
				    XtNtransparent, 0,
				    XtNgridcolor, gridcolor,
				    XtNundoSize, GetDefaultUndosize(),
	    			    XtNfillRule, FillSolid,
				    XtNlineFillRule, FillSolid,
				    XtNpattern, None,
				    XtNlinePattern, None,
				    NULL);
    info->paint = paint;

    /* Hack to get a correct display of the menu bar */
    XtVaSetValues(viewport, XtNwidth, 800, "sbShiftY1", 4, NULL);

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

    OperationSetPaint(paint);
    SetMenuBarVisibility(paint, IsMenuBarGlobal());
    SetFullMenu(paint, IsFullMenuGlobal());

    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) canvasHandler, info, XtListHead);
    XtInsertRawEventHandler(paint, PointerMotionMask,
		      False, (XtEventHandler) motionHandler, info, XtListHead);
    XtAddEventHandler(shell, StructureNotifyMask,
		      False, (XtEventHandler) shellHandler, info);
    XtAddEventHandler(form, StructureNotifyMask,
		      False, (XtEventHandler) formHandler, info);
    XtAddEventHandler(position, ButtonPressMask | ButtonReleaseMask,
		      False, (XtEventHandler) positionHandler, info);
    XtInsertRawEventHandler(memory, ButtonPressMask | ButtonReleaseMask,
			    False, (XtEventHandler) memoryHandler, 
			    info, XtListHead);
    XtAddEventHandler(palette, ButtonPressMask | ButtonReleaseMask,
		      False, (XtEventHandler) paletteHandler, info);
    XtAddEventHandler(linestyle, ButtonPressMask | ButtonReleaseMask,
		      False, (XtEventHandler) lineStyleHandler, info);
    XtAddEventHandler(paint, ButtonPressMask,
		      False, (XtEventHandler) mousewheelScroll, (XtPointer) 2);
    XtAddEventHandler(paintbox, ButtonPressMask,
		      False, (XtEventHandler) mousewheelScroll, (XtPointer) 1);
    XtAddEventHandler(paint, KeyPressMask,
		      False, (XtEventHandler) selectKeyPress, info);
    XtAddEventHandler(paint, KeyReleaseMask,
		      False, (XtEventHandler) selectKeyRelease, info);   

    XtManageChild(paintbox);
    XtManageChild(viewport);
    
    ADDCALLBACK(fileMenu, FILE_OPEN, paint, StdOpenFile);
    ADDCALLBACK(fileMenu, FILE_SAVE, paint, StdSaveFile);
    ADDCALLBACK(fileMenu, FILE_SAVEAS, paint, StdSaveAsFile);
    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_LOADED, paint, loadedCallback);
    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);
    ADDCALLBACK(editMenu, EDIT_SELECT_ALL, paint, StdSelectAllCallback);
    ADDCALLBACK(editMenu, EDIT_UNSELECT, paint, unselectRegion);
    ccpAddDuplicate(editMenu[EDIT_DUP].widget, paint);
    ADDCALLBACK(editMenu, EDIT_ERASE_ALL, paint, StdEraseAllCallback);
    ADDCALLBACK(editMenu, EDIT_CLONE_CANVAS, paint, StdCloneCanvasCallback);
    ADDCALLBACK(editMenu, EDIT_CLONE_CANVAS1, paint, StdCloneCanvasCallback);
    ADDCALLBACK(editMenu, EDIT_SNAPSHOT, info, StdScreenshotCallback);

    ADDCALLBACK(textMenu, FONT_SELECT, info, StdFontSet);
    ADDCALLBACK(textMenu, FONT_WRITE, info, StdWriteText);
    ADDCALLBACK(textMenu, FONT_TEXINPUT, info, StdWriteTeXInput);
    ADDCALLBACK(textMenu, FONT_TEXOUTPUT, info, StdWriteTeXOutput);
    
    ADDCALLBACK(regionMenu, REGION_FLIPX, paint, StdRegionFlipX);
    ADDCALLBACK(regionMenu, REGION_FLIPY, paint, StdRegionFlipY);
    ADDCALLBACK(regionMenu, REGION_ROTATE, paint, rotate);
    ADDCALLBACK(regionMenu, REGION_EXPAND, paint, expandRegion);
    ADDCALLBACK(regionMenu, REGION_DOWNSCALE, paint, downscaleRegion);
    ADDCALLBACK(regionMenu, REGION_LINEAR, paint, linearRegion);
    ADDCALLBACK(regionMenu, REGION_RESET, paint, resetMat);
    ADDCALLBACK(regionMenu, REGION_CROP, paint, cropToRegion);
    ADDCALLBACK(regionMenu, REGION_MASK, paint, maskOfRegion);
    ADDCALLBACK(regionMenu, REGION_MERGE, paint, mergeRegion);
    ADDCALLBACK(regionMenu, REGION_AUTOCROP, paint, autocropCallback);
    ADDCALLBACK(regionMenu, REGION_COMPLEMENT, paint, StdRegionComplement);
    ADDCALLBACK(regionMenu, REGION_DELIMIT, paint, delimit);
    ccpAddEditRegion(regionMenu[REGION_EDIT].widget, paint);
    ccpAddOCRRegion(regionMenu[REGION_OCR].widget, paint);
    
    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);
    }    
    for (i = 0; i < XtNumber(delimitMenu); i++) {
	if (delimitMenu[i].name[0] == '\0')
	    continue;
	ADDCALLBACK(delimitMenu, i, paint, delimit);
    }
    for (i = REGION_FLIPX; i <= REGION_OCR; i++) {
        if (i >= REGION_AUTOCROP && i <= REGION_DELIMIT) continue;
	XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		      (XtPointer) regionMenu[i].widget);
	prCallback(paint, regionMenu[i].widget, False);
    }
	   
    ADDCALLBACK(filterMenu, FILTER_INVERT, paint, StdRegionInvert);
    ADDCALLBACK(filterMenu, FILTER_TO_GRAY, paint, StdRegionGrey);
    ADDCALLBACK(filterMenu, FILTER_CONTRAST, paint, doContrast);
    ADDCALLBACK(filterMenu, FILTER_GAMMA, paint, doGammaCorrection);
    ADDCALLBACK(filterMenu, FILTER_FLOYD_STEINBERG, paint, doFloydSteinberg);
    ADDCALLBACK(filterMenu, FILTER_QUANTIZE, paint, doQuantize);
    ADDCALLBACK(filterMenu, FILTER_SOLARIZE, paint, doSolarize);
    ADDCALLBACK(filterMenu, FILTER_SHARPEN, paint, StdRegionSharpen);
    ADDCALLBACK(filterMenu, FILTER_MERGE, paint, doRegionMerge);
    ADDCALLBACK(filterMenu, FILTER_SMOOTH, paint, doSmooth);
    ADDCALLBACK(filterMenu, FILTER_DIRFILT, paint, StdRegionDirFilt);
    ADDCALLBACK(filterMenu, FILTER_EDGE, paint, StdRegionEdge);
    ADDCALLBACK(filterMenu, FILTER_EMBOSS, paint, doEmboss);
    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_DISTORT, paint, distortRegion);
    ADDCALLBACK(filterMenu, FILTER_PERSPECTIVE, paint, projTransformRegion);
    ADDCALLBACK(filterMenu, FILTER_BLEND, paint, StdRegionBlend);
    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, FontSelectCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_LUPE, NULL, LupeCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_TABLET, info, tabletCallback);    
    ADDCALLBACK(selectorMenu, SELECTOR_SCRIPT, NULL, ScriptEditor);
    ADDCALLBACK(selectorMenu, SELECTOR_VXPDATA, paint, ScriptEditor);    
    ADDCALLBACK(selectorMenu, SELECTOR_CHANGE_SIZE, info, WHZSizeCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_SIZE_ZOOM_DEFS, paint, defaultWHZSizeCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_CHANGE_ZOOM, info, zoomCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_SNAP, paint, snapCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_SNAP_SPACING, paint, snapSpacingCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_TRANSPARENT, paint, transparencyCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_INTERPOLATION, paint, interpolationCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_GRID, paint, gridCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_GRID_PARAM, paint, gridParamCallback);
    ADDCALLBACK(selectorMenu, SELECTOR_SIMPLE, info, simpleMenuPopup);
    ADDCALLBACK(selectorMenu, SELECTOR_HIDE_MENUBAR, info, hideMenuBar);
    ADDCALLBACK(selectorMenu, SELECTOR_LEFT_SCROLLBAR, info, leftScrollbar);
    ADDCALLBACK(selectorMenu, SELECTOR_TOP_SCROLLBAR, info, topScrollbar);
    MenuCheckItem(selectorMenu[SELECTOR_TRANSPARENT].widget, True);
    MenuCheckItem(selectorMenu[SELECTOR_INTERPOLATION].widget, True);
    MenuCheckItem(selectorMenu[SELECTOR_SIMPLE].widget, !IsFullMenuSet(paint));

    for (i=0; i<4; i++)
    ADDCALLBACK(alphaMenu, ALPHA_MODES+i, info, alphaModeCallback);
    ADDCALLBACK(alphaMenu, ALPHA_RGBEDIT, paint, alphaRGBEditCallback);
    ADDCALLBACK(alphaMenu, ALPHA_PARAMS, paint, alphaParametersCallback);
    ADDCALLBACK(alphaMenu, ALPHA_CREATE, paint, createAlphaCallback);
    ADDCALLBACK(alphaMenu, ALPHA_SET, paint, setAlphaCallback);
    ADDCALLBACK(alphaMenu, ALPHA_EDIT, paint, editAlphaCallback);
    ADDCALLBACK(alphaMenu, ALPHA_MEMORY, paint, memoryAlphaCallback);
    ADDCALLBACK(alphaMenu, ALPHA_DELETE, paint, deleteAlphaCallback);
    XtVaSetValues(info->zoom, XtNwidth, Global.btn_height-1, XtNheight, Global.btn_height, NULL);

    ADDCALLBACK(memoryMenu, MEMORY_STACK, info, memoryStackCallback);
    ADDCALLBACK(memoryMenu, MEMORY_RECALL, info, memoryRecallCallback);
    ADDCALLBACK(memoryMenu, MEMORY_EDIT, info, memoryEditCallback);
    ADDCALLBACK(memoryMenu, MEMORY_DISCARD, info, memoryDiscardCallback);
    ADDCALLBACK(memoryMenu, MEMORY_SCROLL, info, memoryScrollCallback);
    ADDCALLBACK(memoryMenu, MEMORY_ERASE, info, memoryEraseCallback);
    ADDCALLBACK(memoryMenu, MEMORY_ORIGIN, info, memoryOriginCallback);
    ADDCALLBACK(memoryMenu, MEMORY_TO_ALPHA, info, doRegionToAlpha);
    ADDCALLBACK(memoryMenu, MEMORY_TO_MASK, info, doRegionToBWMask);
    ADDCALLBACK(memoryMenu, MEMORY_TO_RGBA, info, doRegionToRGBA);
    ADDCALLBACK(memoryMenu, MEMORY_MERGE_RGBA, info, mergeMemoryToRGBA);

    wlist = InitializedWidgetList();
    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_LINESTYLE] = linestyle;
    wlist[W_ICON_COLPIXMAP] = (Widget)
      XCreatePixmap(dpy, DefaultRootWindow(dpy), ICONWIDTH, ICONHEIGHT, depth);
    wlist[W_INFO_DATA] = (Widget) info;
    XtVaSetValues(paint, XtNmenuwidgets, wlist, NULL);

    if (!IsMenuBarVisible(paint)) XtUnmanageChild(form);   

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

    XtRealizeWidget(shell);
    
    XtVaGetValues(paint, XtNfilename, &file, NULL);
    if (!file) 
	StoreName(shell, msgText[DEFAULT_TITLE]);

    ManageQuirk(info);
    wlist[W_ICON_LINEPIXMAP] = (Widget)
      XCreatePixmap(dpy, DefaultRootWindow(dpy), LINESTYLE_WIDTH, LINESTYLE_HEIGHT, depth);
    XDefineCursor(dpy, XtWindow(viewport), XCreateFontCursor(dpy, XC_left_ptr));
		    
    XFlush(dpy);

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

    SetIconImage(shell);
    setPalettePixmap(paint, info, 0);
    setToolIconPixmap(paint, NULL);
    setCanvasColorsIcon(paint, NULL);
    setBrushIconPixmap(paint, NULL);
    setMemoryIcon(paint, info);
    CheckMarkItems(paint, wlist);
    setStandardCursor(paint);

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

    pw = (PaintWidget) paint;
    pw->paint.vxp = NULL;
    pw->paint.dovxp = False;    
    pw->paint.filename = NULL;    
    
    if (alpha) {
	pw->paint.current.alpha = alpha;
	pw->paint.alpha_mode = 2;
    } else {
	pw->paint.current.alpha = NULL;
	pw->paint.alpha_mode = 0;
	for (i=1; i<=ALPHA_DELETE; i++)
	if (i!=ALPHA_PARAMS && i!=ALPHA_CREATE && i!=ALPHA_SET) {
	    prCallback(paint, alphaMenu[ALPHA_MODES+i].widget, False);
	    prCallback(paint, popupAlphaMenu[P_ALPHA_MODES+i].widget, False);
	}
    }

    MenuCheckItem(alphaMenu[ALPHA_MODES+pw->paint.alpha_mode].widget, True);
    MenuCheckItem(popupAlphaMenu[P_ALPHA_MODES+pw->paint.alpha_mode].widget, True);

    /* Avoid unnecessary scrollbars */
    AdjustCanvasSize(info);
    checkPaintDimension(info->shell);

    /* Set tooltips */
#ifdef XAW3DXFT
    if (TooltipsCmdOption()) {
	XawTipEnable(info->zoom, msgText[TOOLTIP_ZOOM]);
	XawTipEnable(info->alpha, msgText[TOOLTIP_ALPHA_CHANNEL_MANAGEMENT]);
	XawTipEnable(resize, msgText[TOOLTIP_CANVAS_RESIZE_PROCEDURE]);
	XawTipEnable(linestyle, msgText[TOOLTIP_LINE_STYLE_SELECTOR]);
	sprintf(name, "%s\n%s", msgText[TOOLTIP_MEMORY_MANAGER],
		msgText[TOOLTIP_AUXILIARY_MENU]);
	XawTipEnable(memory, name);
	XawTipEnable(colors, msgText[TOOLTIP_PRIMARY_SECONDARY_COLORS]);
	XawTipEnable(tool, msgText[TOOLTIP_TOOL_CURRENTLY_ACTIVE]);
	XawTipEnable(brush, msgText[TOOLTIP_BRUSH_CURRENTLY_ACTIVE]);
	XawTipEnable(font, msgText[TOOLTIP_FONT_SELECTOR]);
    }
#else
#ifdef XAWPLAIN
	XtVaSetValues(info->zoom, XtNtip, msgText[TOOLTIP_ZOOM], NULL);
	XawTipEnable(info->zoom);
	XtVaSetValues(info->alpha, XtNtip, msgText[TOOLTIP_ALPHA_CHANNEL_MANAGEMENT], NULL);
	XawTipEnable(info->alpha);
	XtVaSetValues(resize, XtNtip, msgText[TOOLTIP_CANVAS_RESIZE_PROCEDURE], NULL);
	XawTipEnable(resize);
	XtVaSetValues(linestyle, XtNtip, msgText[TOOLTIP_LINE_STYLE_SELECTOR], NULL);
	XawTipEnable(linestyle);
	sprintf(name, "%s\n%s", msgText[TOOLTIP_MEMORY_MANAGER],
		msgText[TOOLTIP_AUXILIARY_MENU]);
	XtVaSetValues(memory, XtNtip, name, NULL);
	XawTipEnable(memory);
	XtVaSetValues(colors, XtNtip, msgText[TOOLTIP_PRIMARY_SECONDARY_COLORS], NULL);
	XawTipEnable(colors);
	XtVaSetValues(tool, XtNtip, msgText[TOOLTIP_TOOL_CURRENTLY_ACTIVE], NULL);
	XawTipEnable(tool);
	XtVaSetValues(brush, XtNtip, msgText[TOOLTIP_BRUSH_CURRENTLY_ACTIVE], NULL);
	XawTipEnable(brush);
	XtVaSetValues(font, XtNtip, msgText[TOOLTIP_FONT_SLECTOR], NULL);
	XawTipEnable(font);
#endif
#endif

    RaiseWindow(dpy, XtWindow(shell));

    pwlast = (Widget)paint;
    return paint;
}

void
CreateCanvas(Widget w, int width, int height, int zoom)
{
    graphicCreate(makeGraphicShell(w), width, height, zoom, None, None, NULL);
}


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

void
AddFileToGlobalList(char * file)
{
    int i;
    char buf[256];
    char *path;

    if (!file) return;
    if (!*file) return;
    if (*file == '/')
	path = strdup(file);
    else {
	if (getcwd(buf, 256)) {
	    path = (char *)xmalloc(strlen(buf)+strlen(file)+3);
	    sprintf(path, "%s/%s", buf, file);
	} else
	    path = strdup(file);
    }
    for (i=0; i<Global.numfiles; i++)
	if (!strcmp(path, Global.loadedfiles[i])) {
	    free(path);
	    return;
	}
    i = Global.numfiles;
    ++Global.numfiles;
    Global.loadedfiles = (char **) 
	realloc(Global.loadedfiles, Global.numfiles*sizeof(char*));
    Global.loadedfiles[i] = path;
}

void
RemoveFileFromGlobalList(char * file)
{
    int i, j;
    char buf[256];
    char *path;

    if (!file) return;
    if (!*file) return;
    if (*file == '/')
	path = strdup(file);
    else {
	if (getcwd(buf, 256)) {
	    path = (char *)xmalloc(strlen(buf)+strlen(file)+3);
	    sprintf(path, "%s/%s", buf, file);
	} else
	    path = strdup(file);
    }
    for (i=0; i<Global.numfiles; i++)
	if (!strcmp(path, Global.loadedfiles[i])) {
	    free(path);
	    free(Global.loadedfiles[i]);
	    --Global.numfiles;
	    for (j=i; j<Global.numfiles; j++)
		Global.loadedfiles[j] = Global.loadedfiles[j+1];
	    return;
	}
}

Widget
GraphicOpenFileZoom(Widget w, char *file, XtPointer imageArg, int zoom)
{
    Image *image = (Image *) imageArg;
    unsigned char *alpha;
    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 (image && image->alpha) {
	 alpha = image->alpha;
	 image->alpha = NULL;
    } else
	 alpha = NULL;

    if (ImageToPixmap(image, shell, &pix, &cmap)) {
	/*
	 * If mask != None, set the mask region color to the BG color of the Canvas
	 */
	AddFileToGlobalList(file);
	if ((paint = (PaintWidget)
	     graphicCreate(shell, 0, 0, zoom, pix, cmap, alpha))!=None) {
	    char *cp = strrchr(file, '/');
	    if (cp == NULL)
		cp = file;
	    else
		cp++;
	    StoreName(shell, file);
	    cp = (char *)xmalloc(strlen(file) + 1);
	    strcpy(cp, file);
	    paint->paint.filename = cp;
	    EnableRevert((Widget) paint);
	    XtVaSetValues((Widget)paint, XtNdirty, False, NULL);
	    if (file_isSpecialImage)
		GetFileName(Global.toplevel, BROWSER_LOADED, NULL, NULL, NULL);
	    SetAlphaMode((Widget)paint, -1);
	    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)
{
    GraphicOpenFileZoom(w, (char *) fileArg, imageArg, Global.default_zoom);
}

/*
 * 0: Create new (blank) canvas
 * 1: Open a file
 * 2: Create new (blank) canvas, querying for size
 */
void
GraphicCreate(Widget w, int value)
{
    switch (value) {
    case 0:
	GetFileName(GetToplevel(w), BROWSER_MULTIREAD, 
		    NULL, GraphicOpenFile, NULL);
	break;
    case 1:
	GetFileName(GetToplevel(w), BROWSER_LOADED, 
		    NULL, GraphicOpenFile, NULL);
	break;
    case 2:
	graphicCreate(makeGraphicShell(w), 
		      Global.default_width, Global.default_height, 
		      Global.default_zoom, None, None, NULL);
	break;
    case 3:
	WHZSizeSelect(w, NULL, 1);
	break;
    case 4:
	StartMagnifier(w);
	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.clipboard.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 pix, Pixmap mask, unsigned char *alpha)
{
    RegionData r;
    Display *dpy = XtDisplay(w);
    GC gc;
    int i, m, width, height, depth;

    r.x = x;
    r.y = y;
    r.pix = pix;
    r.mask = mask;
    r.alpha = alpha;
    CRC_checksum(&r);
    for (i=0; i<Global.numregions; i++)
      if (!memcmp(&r.crcdata,&Global.regiondata[i].crcdata, sizeof(CRCdata))) {
	 Global.regiondata[i].x = x;
	 Global.regiondata[i].y = y;
	 return;
      }

    /* Duplicate data into region */
    i = Global.numregions;
    ++Global.numregions;
    Global.regiondata = (RegionData *) 
	realloc(Global.regiondata, Global.numregions*sizeof(RegionData));
    Global.regiondata[i] = r;

    GetPixmapWHD(dpy, pix, &width, &height, &depth);
    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);
    }
    if (alpha) {
	m = width * height;
	Global.regiondata[i].alpha = (unsigned char*) XtMalloc(m);
	memcpy(Global.regiondata[i].alpha, alpha, m);
    }
}

static void 
StdCloneCanvasCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    Widget paint = (Widget) paintArg;
    PaintWidget pw  = (PaintWidget) paint;
    Display *dpy;
    Pixmap pix, pixp;
    int width, height, widthp, heightp, depth, zoom, dozoom;
    int i, z, interpolation;
    unsigned char *alphap;
    GC gc;

    if (strstr(XtName(w), "zoom")) dozoom = 1; else dozoom = 0;
    dpy = XtDisplay(paint);
    XtVaGetValues(paint, XtNzoom, &zoom, 
		  XtNinterpolation, &interpolation, NULL);
    depth = DefaultDepthOfScreen(XtScreen(paint));
    if (dozoom) {
	if (zoom > 0) {
	    z = zoom;
	    width = pw->paint.drawWidth;
	    height = pw->paint.drawHeight;
	    widthp = pw->paint.drawWidth * z;
	    heightp = pw->paint.drawHeight * z;
	} else {
	    z = -zoom;
	    width = pw->paint.drawWidth;
	    height = pw->paint.drawHeight;
	    widthp = (width+z-1)/z;
	    heightp = (height+z-1)/z;
	}
    } else {
	z = 1;
	width = widthp = pw->paint.drawWidth;
	height = heightp = pw->paint.drawHeight;
    }
    pix = GET_PIXMAP(pw);
    gc = XtGetGC(paint, 0, 0);
    StateSetBusy(True);

    if (z == 1) {
	pixp = XCreatePixmap(dpy,
		    XtWindow(Global.toplevel), width, height, depth);
	XCopyArea(dpy, pix, pixp, gc, 0, 0, width, height, 0, 0);
	if (pw->paint.current.alpha) {
	    i = width * height;
	    alphap = (unsigned char *) malloc(i);
	    memcpy(alphap, pw->paint.current.alpha, i);
	} else 
	    alphap = NULL;
	w = graphicCreate(makeGraphicShell(Global.toplevel),
			  width, height, ((dozoom)?1:zoom), 
			  pixp, None, alphap);
    } else {
	i = expandImage(dpy, gc, pix, pw->paint.current.alpha, 
			depth, width, height, widthp, heightp, 
			interpolation, (zoom<0)?1:0, 
			(double)z, (double) z, &pixp, &alphap);
	if (i<0) goto failure;
	w = graphicCreate(makeGraphicShell(Global.toplevel), 
			  widthp, heightp, 1, pixp, None, alphap);
    }
    SetAlphaMode(w, -1);

 failure:
    XtReleaseGC(paint, gc);
    StateSetBusy(False);
}

void 
StdMemorySetCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
    LocalInfo * info = (LocalInfo *) paintArg;
    Widget paint = info->paint;
    Pixmap pix, mask;
    XRectangle rect;
    int width, height, 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;

    setMemoryRegion(paint, pw->paint.region.rect.x, pw->paint.region.rect.y,
		    pix, mask, pw->paint.region.alpha);
    info->mem_index = Global.numregions-1;
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);

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

static void
memoryRecallOkCallback(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;
    memoryRecallCallback(localinfo->paint, localinfo, NULL);
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
}

void
StdMemoryRecallCallback(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;
       memoryRecallCallback(localinfo->paint, localinfo, NULL);
       GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
       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, msgText[RECALL_FROM_MEMORY], 
	       &info, memoryRecallOkCallback, NULL, localinfo);
}

void 
memoryEditOkCallback(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;
    }
    localinfo->mem_index = n;
    memoryEditCallback(localinfo->paint, localinfo, NULL);
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
}

void
StdMemoryEditCallback(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;
       memoryEditCallback(localinfo->paint, localinfo, NULL);
       GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
       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, msgText[RECALL_FROM_MEMORY], 
	       &info, memoryEditOkCallback, NULL, localinfo);
}

void 
memoryRemoveOkCallback(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 = Global.numregions-1;
    GraphicAll((GraphicAllProc) setMemoryIcon, NULL);
}

void 
StdMemoryRemoveCallback(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, msgText[REMOVE_FROM_MEMORY], 
	       &info, memoryRemoveOkCallback, NULL, localinfo);
}

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

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

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

    if (Global.clipboard.pix != None)
	XFreePixmap(XtDisplay(paint), Global.clipboard.pix);
    if (Global.clipboard.mask != None)
	XFreePixmap(XtDisplay(paint), Global.clipboard.mask);
    if (Global.clipboard.alpha != NULL) {
	XtFree((char *)Global.clipboard.alpha);
	Global.clipboard.alpha = NULL;
    }

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

    XtVaGetValues(paint, XtNcolormap, &Global.clipboard.cmap, NULL);
    if (pw->paint.region.alpha) {
	m = width * height;
	Global.clipboard.alpha = (unsigned char *)XtMalloc(m);
	memcpy(Global.clipboard.alpha, pw->paint.region.alpha, m);
    }	

    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);
    PaintWidget pw = (PaintWidget) paint;
    XRectangle rect;
    Pixmap pix;
    Colormap cmap;
    Pixmap newMask = None;
    unsigned char *alpha = NULL;
    int m;
    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.clipboard.pix != None)
		XFreePixmap(dpy, Global.clipboard.pix);
	    if (Global.clipboard.mask != None)
		XFreePixmap(dpy, Global.clipboard.mask);
	    if (Global.clipboard.alpha != NULL)
		XtFree((char *)Global.clipboard.alpha);
	    if (Global.clipboard.image != NULL)
		ImageDelete((Image *) Global.clipboard.image);

	    Global.clipboard.pix = None;
	    Global.clipboard.mask = None;
	    Global.clipboard.alpha = NULL;
	    Global.clipboard.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.clipboard.width = info->width;
	    Global.clipboard.height = info->height;
	    Global.clipboard.pix = np;

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

    rect.x = 0;
    rect.y = 0;
    rect.width = Global.clipboard.width;
    rect.height = Global.clipboard.height;
    alpha = Global.clipboard.alpha;

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

    XtVaGetValues(paint, XtNcolormap, &cmap, NULL);
    if (cmap != Global.clipboard.cmap) {
	Image *image;
	Pixmap npix = None;

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

	if (Global.clipboard.pix == None) {
	    image = (Image *) Global.clipboard.image;
	    image->refCount++;
	    alpha = image->alpha;
	    image->alpha = NULL;
	} else
	    image = PixmapToImage(paint, Global.clipboard.pix, Global.clipboard.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.clipboard.pix, pix, gc,
		  0, 0, rect.width, rect.height, 0, 0);
	XtReleaseGC(paint, gc);
	PwRegionSet(paint, &rect, pix, newMask);
    }

    if (alpha) {
	m = rect.width * rect.height;
	if (!pw->paint.region.alpha) 
	    pw->paint.region.alpha = (unsigned char *)XtMalloc(m);
	memcpy(pw->paint.region.alpha, alpha, m);
    }
}

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 
StdScreenshotCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
     LocalInfo * info = (LocalInfo *) paintArg; 
     Widget paint;
     PaintWidget pw;
     Boolean selection;
     XRectangle rect;
     Position x1, y1, x2, y2;
     Dimension width, height;
     WidgetList wlist;
#ifdef XAW3D
     Pixel pixel;
#endif

     /* Disable screenshot from FatBits !! */
     if (!info) return;

     paint = info->paint;
     pw = (PaintWidget) paint;

     /* First unselect selected region, if any */
     PwRegionSet(paint, NULL, None, None);

     /* Calculate menu position */
     XtVaGetValues(paint, XtNmenuwidgets, &wlist, NULL);

     if (wlist && wlist[0]) {
	Widget ww;
	ww = wlist[W_TOPMENU+W_EDIT_PASTE];
	if (ww && !IsFullMenuSet(paint)) {
	    XtVaGetValues(ww, XtNwidth, &width, XtNheight, &height, NULL);
	    XtTranslateCoords(paint, 0, 0, &x1, &y1);
	    XtTranslateCoords(ww, 0, 0, &x2, &y2);
	    y2 += height;
	    /* Popdown menu */
	    if ((ww=XtParent(ww)) && XtIsRealized(ww)) XtPopdown(ww);
	    /* Redraw area erased by menu popup */
	    rect.x = x2-x1 - 3;
	    rect.y = 0;
	    rect.width = 5 + width;
	    if (rect.width>pw->paint.drawWidth) 
	    rect.width = pw->paint.drawWidth;
	    rect.height = y2-y1 + 1;
	    if (rect.height>pw->paint.drawHeight) 
		rect.height = pw->paint.drawHeight;
	} else {
	    rect.x = 0;
	    rect.y = 0;
	    rect.width = pw->paint.drawWidth;
	    rect.height = pw->paint.drawHeight;
	}
	zoomUpdate(pw, True, &rect);
#if defined XAW3D && !defined NEXTAW
	/* redraw border of viewport window with Xaw3d widgets ... */
	XtVaGetValues((Widget)
	       ((ViewportWidget)(info->viewport))->viewport.threeD, 
		   XtNbottomShadowPixel, &pixel, 
		      XtNshadowWidth, &rect.y, NULL);
	XSetWindowBackground(XtDisplay(info->viewport),
			     XtWindow(((ViewportWidget)(info->viewport))->viewport.threeD), pixel);
	XClearArea(XtDisplay(info->viewport),
		       XtWindow(((ViewportWidget)(info->viewport))->viewport.threeD),
		   rect.x, 0, rect.width+4, rect.y, True);
#endif
	XFlush(XtDisplay(paint));
     }
   
     /* Clear selection issues */
     selection = selectionOwner;
     selectionOwner = True;

     /* Now, really start screenshot ! */
     ScreenshotImage(paint, pw, 1);
     selectionOwner = selection;
}

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

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);
    StdRefreshCallback(w, paintArg, junk2);
    SetAlphaMode((Widget)paintArg, -1);
}

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);
    RefreshWidget((Widget) pw);
    SetAlphaMode((Widget)paintArg, -1);
}

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

    if (Global.clipboard.pix != None)
	XFreePixmap(XtDisplay(Global.toplevel), Global.clipboard.pix);
    if (Global.clipboard.mask != None)
	XFreePixmap(XtDisplay(Global.toplevel), Global.clipboard.mask);
    if (Global.clipboard.alpha != NULL)
	XtFree((char *)Global.clipboard.alpha);
    if (Global.clipboard.image != NULL)
	ImageDelete(Global.clipboard.image);

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

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

/*
**  End of "edit" menu functions
**
 */

void 
StdOpenFile(Widget w, XtPointer paintArg, XtPointer junk)
{
    GetFileName(GetToplevel(w), BROWSER_READ, NULL, GraphicOpenFile, NULL);
}

/*
**  "Text" menu function
**
 */

void 
StdWriteText(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo *l = (LocalInfo *) infoArg; 
    ScriptEditor(l->paint, l->paint, (XtPointer)2);
}

void
StdWriteTeXInput(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo *l = (LocalInfo *) infoArg;
    static TextPromptInfo info;
    static struct textPromptInfo value[3];

    ScriptEditor(l->paint, l->paint, (XtPointer)1);
}

void
StdWriteTeXOutput(Widget w, XtPointer infoArg, XtPointer junk)
{
    LocalInfo *l = (LocalInfo *) infoArg;
    PaintWidget pw = (PaintWidget) l->paint;
    if (DoVxp(l->paint)) {
	 sprintf(Global.vxpinput, "\n*textoutput %d,%d %d,%d",
                 pw->paint.region.rect.x, pw->paint.region.rect.y,
		 pw->paint.region.rect.width, pw->paint.region.rect.height);
	 RecordVxp(l->paint);
    }
    PwRegionMergeOption(2);
    PwRegionSet(l->paint, NULL, None, None);
    PwRegionMergeOption(0);   
}

/*
**  Start "Region" menu functions
**
 */

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

    PwRegionAddScale(paint, &v, NULL);
}

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

    PwRegionAddScale(paint, NULL, &v);
}

void 
StdRegionEdit(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;
    unsigned char *alpha;
    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;
	    if (zoom>0) {
		width = pw->paint.region.rect.width/zoom;
		height = pw->paint.region.rect.height/zoom;
	    } else {
		width = pw->paint.region.rect.width * (-zoom);
		height = pw->paint.region.rect.height * (-zoom);
	    }
	 } else {
	    if (zoom>0) {
		width = pw->paint.region.orig.width/zoom;
		height = pw->paint.region.orig.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);
	 }

	 if (pw->paint.region.alpha) {
	     x = pw->paint.region.orig.width * pw->paint.region.orig.height;
	     alpha = (unsigned char*) xmalloc(x);
	     if (alpha) memcpy(alpha, pw->paint.region.alpha, x);
	 } else
	     alpha = NULL;
	 pw = (PaintWidget) 
	      graphicCreate(makeGraphicShell(Global.toplevel), 
			    0, 0, zoom, newpix, cmap, alpha);
	 XtReleaseGC(paint, gc);
	 XtVaSetValues((Widget)pw, XtNdirty, True, NULL);
    }
}

void 
StdRegionOCR(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;
    Image * image;
    int x, y, width, height, depth, zoom;
    char tmpfile[512];
    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);
	 }

	 image = PixmapToImage(paint, newpix, cmap);
	 sprintf(tmpfile, "%s%s/%s",
		 (getenv("HOME")?getenv("HOME"):""),
		 (getenv("HOME")?"/.xpaint/tmp":"/tmp"),
		 "xpaint_output.ppm");
	 WritePNM(tmpfile, image);
	 sprintf(tmpfile, "%s/%s %s", GetShareDir(), "bin/xpaint_ocr",
		 Global.ocrlang);
	 if (system(tmpfile));
	 if (!Global.ocrinfo) ScriptEditor(paint, NULL, (XtPointer)4);
         AppendTextFile(2);
	 ImageDelete(image);
    }
}

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.pixmap, 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.pixmap, 
			 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 
StdRegionComplement(Widget w, XtPointer paintArg, XtPointer junk2)
{
    PaintWidget pw = (PaintWidget) paintArg;
    Widget paint = (Widget) paintArg;
    Display *dpy = XtDisplay(paint);
    unsigned char * alpha;
    XImage * source;
    Boolean *maskdata;
    Pixmap pix, mask;
    Pixel bg, p;
    XRectangle rect;
    int i, 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 (pw->paint.current.alpha) {
	i = dw * dh;
	alpha = pw->paint.current.alpha;
	pw->paint.current.alpha = (unsigned char *) XtMalloc(i);
	memset(pw->paint.current.alpha, (unsigned char)Global.alpha_bg, i);
    } else
	alpha = NULL;

    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++) {
	    i = (y+rect.y) * dw + rect.x;
	    for (x=0; x<rect.width; x++, i++) {
		p = XGetPixel(source, x, y);
		if (p) {
		    maskdata[i] = True;
		    if (alpha) {
			pw->paint.current.alpha[i] = alpha[i];
			alpha[i] = (unsigned char)Global.alpha_bg;
		    }
		}
	    }
	}
	XDestroyImage(source);
    } else {
	for (y=0; y<rect.height; y++) {
	    i = (y+rect.y) * dw + rect.x;
	    for (x=0; x<rect.width; x++, i++) {
		maskdata[i] = True;
		if (alpha) {
		     pw->paint.current.alpha[i] = alpha[i];
		     alpha[i] = (unsigned char)Global.alpha_bg;
		}
	    }
	}
    }

    PwRegionSet(paint, NULL, None, None);    
    createRegionFromMaskData(paint, maskdata, False);
    pw->paint.region.alpha = alpha;
    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 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))
	StdSelectAllCallback(paint, (XtPointer) paint, NULL);

    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);
    ImgProcessPix = None;

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

/* 
 * Function should be : Image * (*func) (Image *)
 */

int GetFilterNumber(void * func)
{
    int i = -1;
    if (func == ImageInvert) i = FILTER_INVERT; else
    if (func == ImageGrey) i = FILTER_TO_GRAY; else
    if (func == ImageNormalizeContrast) i = FILTER_CONTRAST; else
    if (func == ImageGammaCorrection) i = FILTER_GAMMA;    
    if (func == ImageFloydSteinberg) i = FILTER_FLOYD_STEINBERG; else
    if (func == ImageQuantize) i = FILTER_QUANTIZE; else
    if (func == ImageSolarize) i = FILTER_SOLARIZE; else
    if (func == ImageSharpen) i = FILTER_SHARPEN; else
    if (func == ImageMerge) i = FILTER_MERGE; else
    if (func == ImageSmooth) i = FILTER_SMOOTH; else
    if (func == ImageDirectionalFilter) i = FILTER_DIRFILT; else
    if (func == ImageDespeckle) i = FILTER_DESPECKLE; else
    if (func == ImageEdge) i = FILTER_EDGE; else
    if (func == ImageEmboss) i = FILTER_EMBOSS; else
    if (func == ImageOilPaint) i = FILTER_OIL; else
    if (func == ImageAddNoise) i = FILTER_NOISE; else
    if (func == ImageSpread) i = FILTER_SPREAD; else
    if (func == ImagePixelize) i = FILTER_PIXELIZE; else
    if (func == ImageDistort) i = FILTER_DISTORT; else
    if (func == ImageProjTransform) i = FILTER_PERSPECTIVE; else
    if (func == ImageBlend) i = FILTER_BLEND; else
    if (func == ImageUserDefined) i = FILTER_USERDEF; else
    if (func == lastFilter) i = FILTER_LAST;
    return i;
}

void 
ImageRegionProcess(Widget paint, void * func)
{
    Image *in, *out;
    int i = GetFilterNumber(func);
    lastFilter = func;
    EnableLast(paint);

    if ((in = ImgProcessSetup(paint)) == NULL)
	return;
    out = ImgProcessFinish(paint, in, func);
    RefreshWidget(paint);

    if (DoVxp(paint) && i >= 0) {
        sprintf(Global.vxpinput, "\n*filter %s", filterMenu[i].name);
        RecordVxp(paint);
    }    
}

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

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

    ImageRegionProcess(paint, ImageInvert);
}

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

    ImageRegionProcess(paint, ImageSharpen);
}

void 
StdRegionMerge(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((Widget) paintArg, ImageMerge);
    if (ImgProcessInfo.canvas) {
	ImgProcessInfo.canvas->alpha = NULL;
	ImageDelete(ImgProcessInfo.canvas);
	ImgProcessInfo.canvas = NULL;
    }
}

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

    ImageRegionProcess(paint, ImageSmooth);
}

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

    ImageRegionProcess(paint, ImageEdge);
}

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

    ImageRegionProcess(paint, ImageEmboss);
}

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

    ImageRegionProcess(paint, ImageOilPaint);
}

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

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

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

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

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

void 
StdRegionNormalizeContrast(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((Widget) paintArg, ImageNormalizeContrast);
}

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

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

void 
StdRegionBWMask(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((Widget) paintArg, ImageBWMask);
}

void 
StdRegionGammaCorrection(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((Widget) paintArg, ImageGammaCorrection);
}

void 
StdRegionFloydSteinberg(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((Widget) paintArg, ImageFloydSteinberg);
}

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

void 
StdRegionDirFilt(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((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);

    if (DoVxp(paint)) {
        strcpy(Global.vxpinput, "\n*filter undo");
        RecordVxp(paint);
    }    
}

void 
StdRegionDistort(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((Widget) paintArg, ImageDistort);
}

void 
StdRegionProjTransform(Widget w, XtPointer paintArg, XtPointer junk2)
{
    ImageRegionProcess((Widget) paintArg, ImageProjTransform);
}

/*
**  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.clipboard.pix || Global.clipboard.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 
ccpAddEditRegion(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdRegionEdit);
}

void 
ccpAddOCRRegion(Widget w, Widget paint)
{
    addFunction(w, paint, (XtCallbackProc) StdRegionOCR);
}

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

static void 
generalPopupHandler(Widget w, void * arg, XKeyEvent * event, void * data)
{
    Global.popped_up = w;
}

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

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

    XtAddEventHandler(w, ButtonPressMask,
			 False, (XtEventHandler) generalPopupHandler, paint);

    if (!info)
	XtAddEventHandler(paint, KeyPressMask,
			  False, (XtEventHandler) selectKeyPress, NULL);

    ADDCALLBACK(popupFileMenu, P_FILE_OPEN, paint, StdOpenFile);
    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_LOADED, paint, loadedCallback);
    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);
    else
	prCallback(paint, popupFileMenu[P_FILE_CLOSE].widget, False);

    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);
    ccpAddSelectAll(popupEditMenu[P_EDIT_SELECT_ALL].widget, paint);
    prCallback(paint, popupEditMenu[P_EDIT_SELECT_ALL].widget, True);
    ADDCALLBACK(popupEditMenu, P_EDIT_UNSELECT, paint, unselectRegion);
    ccpAddDuplicate(popupEditMenu[P_EDIT_DUP].widget, paint);
    ccpAddEraseAll(popupEditMenu[P_EDIT_ERASE_ALL].widget, paint);
    prCallback(paint, popupEditMenu[P_EDIT_ERASE_ALL].widget, True);
    ADDCALLBACK(popupEditMenu, P_EDIT_CLONE_CANVAS, paint, StdCloneCanvasCallback);
    ADDCALLBACK(popupEditMenu, P_EDIT_CLONE_CANVAS1, paint, StdCloneCanvasCallback);
    ADDCALLBACK(popupEditMenu, P_EDIT_SNAPSHOT, info, StdScreenshotCallback);
   
    ADDCALLBACK(popupTextMenu, P_FONT_SELECT, info, StdFontSet);
    ADDCALLBACK(popupTextMenu, P_FONT_WRITE, info, StdWriteText);
    ADDCALLBACK(popupTextMenu, P_FONT_TEXINPUT, info, StdWriteTeXInput);
    ADDCALLBACK(popupTextMenu, P_FONT_TEXOUTPUT, info, StdWriteTeXOutput);
    
    ADDCALLBACK(popupRegionMenu, P_REGION_FLIPX, paint, StdRegionFlipX);
    ADDCALLBACK(popupRegionMenu, P_REGION_FLIPY, paint, StdRegionFlipY);
    ADDCALLBACK(popupRegionMenu, P_REGION_ROTATE, paint, rotate);
    ADDCALLBACK(popupRegionMenu, P_REGION_EXPAND, paint, expandRegion);
    ADDCALLBACK(popupRegionMenu, P_REGION_DOWNSCALE, paint, downscaleRegion);
    ADDCALLBACK(popupRegionMenu, P_REGION_LINEAR, paint, linearRegion);
    ADDCALLBACK(popupRegionMenu, P_REGION_RESET, paint, resetMat);
    ADDCALLBACK(popupRegionMenu, P_REGION_CROP, paint, cropToRegion);
    ADDCALLBACK(popupRegionMenu, P_REGION_MASK, paint, maskOfRegion);
    ADDCALLBACK(popupRegionMenu, P_REGION_MERGE, paint, mergeRegion);
    ADDCALLBACK(popupRegionMenu, P_REGION_AUTOCROP, paint, autocropCallback);
    ADDCALLBACK(popupRegionMenu, P_REGION_COMPLEMENT, paint, StdRegionComplement);       
    ADDCALLBACK(popupRegionMenu, P_REGION_DELIMIT, paint, delimit);
    ccpAddEditRegion(popupRegionMenu[P_REGION_EDIT].widget, paint);
    ccpAddOCRRegion(popupRegionMenu[P_REGION_OCR].widget, paint);

    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);
    }    
    for (i = 0; i < XtNumber(popupDelimitMenu); i++) {
	if (popupDelimitMenu[i].name[0] == '\0')
	    continue;
	ADDCALLBACK(popupDelimitMenu, i, paint, delimit);
    }
    for (i = P_REGION_FLIPX; i <= P_REGION_OCR; i++) {
        if (i >= P_REGION_AUTOCROP && i <= P_REGION_DELIMIT) continue;
	XtAddCallback(paint, XtNregionCallback, (XtCallbackProc) prCallback,
		      (XtPointer) popupRegionMenu[i].widget);
	prCallback(paint, popupRegionMenu[i].widget, False);
    }
 
    ADDCALLBACK(popupFilterMenu, P_FILTER_INVERT, paint, StdRegionInvert);
    ADDCALLBACK(popupFilterMenu, P_FILTER_TO_GRAY, paint, StdRegionGrey);
    ADDCALLBACK(popupFilterMenu, P_FILTER_CONTRAST, paint, doContrast);
    ADDCALLBACK(popupFilterMenu, P_FILTER_GAMMA, paint, doGammaCorrection);
    ADDCALLBACK(popupFilterMenu, P_FILTER_FLOYD_STEINBERG, paint, doFloydSteinberg);
    ADDCALLBACK(popupFilterMenu, P_FILTER_QUANTIZE, paint, doQuantize);
    ADDCALLBACK(popupFilterMenu, P_FILTER_SOLARIZE, paint, doSolarize);
    ADDCALLBACK(popupFilterMenu, P_FILTER_SHARPEN, paint, StdRegionSharpen);
    ADDCALLBACK(popupFilterMenu, P_FILTER_MERGE, paint, doRegionMerge);
    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_DISTORT, paint, distortRegion);
    ADDCALLBACK(popupFilterMenu, P_FILTER_PERSPECTIVE, paint, projTransformRegion);
    ADDCALLBACK(popupFilterMenu, P_FILTER_BLEND, paint, StdRegionBlend);
    ADDCALLBACK(popupFilterMenu, P_FILTER_USERDEF, paint, StdRegionUserDefined);
    ADDCALLBACK(popupFilterMenu, P_FILTER_LAST, paint, doLast);
    prCallback(paint, popupFilterMenu[P_FILTER_LAST].widget, False);
    ADDCALLBACK(popupFilterMenu, P_FILTER_UNDO, paint, StdRegionUndo);
    prCallback(paint, popupFilterMenu[P_FILTER_UNDO].widget, False);

    if (info) {
       ADDCALLBACK(popupSelectorMenu, P_SELECTOR_PATTERNS, info, PatternSelectCallback);
    } else {
       prCallback(paint, popupSelectorMenu[P_SELECTOR_PATTERNS].widget, False);
    }

    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, FontSelectCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_LUPE, NULL, LupeCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_TABLET, paint, tabletCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SCRIPT, NULL, ScriptEditor);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_VXPDATA, paint, ScriptEditor);    
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_CHANGE_SIZE, info, WHZSizeCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SIZE_ZOOM_DEFS, paint, defaultWHZSizeCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_CHANGE_ZOOM, info, zoomCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SNAP, paint, snapCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SNAP_SPACING, paint, snapSpacingCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_TRANSPARENT, paint, transparencyCallback);
    MenuCheckItem(popupSelectorMenu[P_SELECTOR_TRANSPARENT].widget, True);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_INTERPOLATION, paint, interpolationCallback);
    MenuCheckItem(popupSelectorMenu[P_SELECTOR_INTERPOLATION].widget, True);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_GRID, paint, gridCallback);
    ADDCALLBACK(popupSelectorMenu, P_SELECTOR_GRID_PARAM, paint, gridParamCallback);
    if (info) {
	ADDCALLBACK(popupSelectorMenu, P_SELECTOR_HIDE_MENUBAR, info, hideMenuBar);
	ADDCALLBACK(popupSelectorMenu, P_SELECTOR_SHOW_MENUBAR, info, showMenuBar);
	ADDCALLBACK(popupSelectorMenu, P_SELECTOR_LEFT_SCROLLBAR, info, leftScrollbar);
	ADDCALLBACK(popupSelectorMenu, P_SELECTOR_TOP_SCROLLBAR, info, topScrollbar);
    } else {
       prCallback(paint, popupSelectorMenu[P_SELECTOR_HIDE_MENUBAR].widget, False);
       prCallback(paint, popupSelectorMenu[P_SELECTOR_SHOW_MENUBAR].widget, False);
    }

    for (i=0; i<4; i++)
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_MODES+i, info, alphaModeCallback);
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_RGBEDIT, paint, alphaRGBEditCallback);
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_PARAMS, paint, alphaParametersCallback);
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_CREATE, paint, createAlphaCallback);
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_SET, paint, setAlphaCallback);
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_EDIT, paint, editAlphaCallback);
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_MEMORY, paint, memoryAlphaCallback);
    ADDCALLBACK(popupAlphaMenu, P_ALPHA_DELETE, paint, deleteAlphaCallback);
    MenuCheckItem(popupAlphaMenu[P_ALPHA_MODES].widget, True);

    if (info) {
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_STACK, info, StdMemorySetCallback);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_RECALL, info, StdMemoryRecallCallback);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_EDIT, info, StdMemoryEditCallback);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_DISCARD, info, StdMemoryRemoveCallback);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_ERASE, info, memoryEraseCallback);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_ORIGIN, info, memoryOriginCallback);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_TO_ALPHA, info, doRegionToAlpha);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_TO_MASK, info, doRegionToBWMask);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_TO_RGBA, info, doRegionToRGBA);
       ADDCALLBACK(popupMemoryMenu, P_MEMORY_MERGE_RGBA, info, mergeMemoryToRGBA);
    } else {
       prCallback(paint, popupMemoryMenu[P_MEMORY_STACK].widget, False);
    }

    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);
    prCallback(paint, popupMemoryMenu[P_MEMORY_RECALL].widget, False);
    prCallback(paint, popupMemoryMenu[P_MEMORY_DISCARD].widget, False);
}
