/* +-------------------------------------------------------------------+ */
/* | 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: operation.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */

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

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/cursorfont.h>
#include <X11/xpm.h>

#include "xaw_incdir/Form.h"
#include "xaw_incdir/SmeBSB.h"
#include "xaw_incdir/Box.h"
#if !defined(NEXTAW) && !defined(XAW95)
#include "xaw_incdir/Tip.h"
#endif
#include "xaw_incdir/Toggle.h"
#include "xaw_incdir/Viewport.h"

#ifdef XAW3DXFT
#include "xaw_incdir/Xaw3dXft.h"
#endif

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

/* Pixmaps for toolbox icons */

#ifdef XAW3DXFT
extern Xaw3dXftData *xaw3dxft_data;
#endif

#define NUM XtNumber(iconList)

/* static void changeLineAction(Widget, XEvent *); */
static void changeDynPencilAction(Widget, XEvent *);
static void changeBrushAction(Widget, XEvent *);
static void changeBrushParamAction(Widget, XEvent *);
static void changeSprayAction(Widget, XEvent *);
static void changeArrowAction(Widget, XEvent *);
static void changeFontAction(Widget, XEvent *);
static void changeBoxParamAction(Widget, XEvent *);
static void changePolygonParamAction(Widget, XEvent *);
static void changeGradientFillParamAction(Widget, XEvent *);
static void changeFractalFillParamAction(Widget, XEvent *);

static void 
setOperation(Widget w, XtPointer opArg, XtPointer junk2)
{
    OperationPair *op = (OperationPair *) opArg;
    struct paintWindows *cur;

    if (op == CurrentOp || op == None)
	return;

    routine = XtName(w);

    /* terminate possibly unfinished operation */
    if (CurrentOp)
        GraphicSetOp((OperationRemoveProc) CurrentOp->remove, 
                     (OperationAddProc) NULL);


    /* start new one */
    CurrentOp = op;
    for (cur = getPaintHead(); cur != NULL; cur = cur->next) {
	if (!cur->done) continue;
	if (CheckVxp(cur->paint)) RecordVxp(cur->paint);
    }
    GraphicAll(setToolIconPixmap, NULL);
    GraphicSetOp((OperationRemoveProc) NULL, 
                 (OperationAddProc) CurrentOp->add);
}

/*
** Set brush parameters callbacks
 */
static void 
brushOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int opacity = atof(info->prompts[0].rstr);

    if (opacity < 0 || opacity > 100) {
	Notice(w, msgText[OPACITY_SHOULD_BE_BETWEEN_ZERO_AND_HUNDRED_PERCENT]);
	return;
    }

    brushOpacity = opacity / 100.0;
    sprintf(Global.vxpinput, "\n*brushopacity %g", brushOpacity);
}

static void 
brushMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    static char brushOpacityStr[16];

    sprintf(brushOpacityStr, "%g", brushOpacity * 100);
	    
    value[0].prompt = msgText[OPACITY];
    value[0].str = brushOpacityStr;
    value[0].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_BRUSH_PARAMETERS];
    info.nprompt = 1;

    TextPrompt(w, msgText[BRUSH_PARAMETERS],
	       &info, brushOkCallback, NULL, NULL);
}

/*
** Set fractal parameter callbacks
 */
static void 
fractalOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int opacity = atof(info->prompts[0].rstr);

    if (opacity < 0 || opacity > 100) {
	Notice(w, msgText[OPACITY_SHOULD_BE_BETWEEN_ZERO_AND_HUNDRED_PERCENT]);
	return;
    }
    
    fractalDensity = opacity / 100.0;
    sprintf(Global.vxpinput, "\n*fractaldensity %g", fractalDensity);
}

static void 
fractalMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[1];
    char fractalDensityStr[16];

    sprintf(fractalDensityStr, "%g", fractalDensity * 100);
	    
    value[0].prompt = msgText[DENSITY];
    value[0].str = fractalDensityStr;
    value[0].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_FRACTAL_DENSITY];
    info.nprompt = 1;

    TextPrompt(w, msgText[FRACTAL_PARAMETERS],
	       &info, fractalOkCallback, NULL, NULL);
}

/*
** Set dynamic pencil parameters callbacks
*/

static void dynPencilOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo*)infoArg;
    double width = atof(info->prompts[0].rstr);
    double mass = atof(info->prompts[1].rstr);
    double drag = atof(info->prompts[2].rstr);
    
    if (width < 1 || width > 100) {
	Notice(w, msgText[WIDTH_SHOULD_BE_BETWEEN_ONE_AND_ONE_HUNDRED]);
	return;
    }

    if (mass < 10 || mass > 2000) {
	Notice(w, msgText[MASS_SHOULD_BE_BETWEEN_TEN_AND_TWO_THOUSAND]);
	return;
    }

    if (drag < 0 || drag > 50) {
	Notice(w, msgText[DRAG_SHOULD_BE_BETWEEN_ZERO_AND_FIFTY]);
	return;
    }

    dynWidth = width;
    dynMass = mass;
    dynDrag = drag;
    sprintf(Global.vxpinput, "\n*dynpenparams %0.4g %0.4g %0.4g",
	    width, mass, drag);
}

static void dynPencilMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char dynPencilWidthStr[16];
    static char dynPencilMassStr[16];
    static char dynPencilDragStr[16];

    sprintf(dynPencilWidthStr, "%g", dynWidth);
    sprintf(dynPencilMassStr, "%g", dynMass);
    sprintf(dynPencilDragStr, "%g", dynDrag);
    
    value[0].prompt = msgText[DYNAMIC_WIDTH];
    value[0].str = dynPencilWidthStr;
    value[0].len = 8;
    value[1].prompt = msgText[DYNAMIC_MASS];
    value[1].str = dynPencilMassStr;
    value[1].len = 8;
    value[2].prompt = msgText[DYNAMIC_DRAG];
    value[2].str = dynPencilDragStr;
    value[2].len = 8;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_DYNAMIC_PENCIL_PARAMETERS];
    info.nprompt = 3;

    TextPrompt(w, msgText[DYNAMIC_PENCIL_PARAMETERS],
	       &info, dynPencilOkCallback, NULL, NULL);
}

/*
** Set spray parameters callbacks
 */
static void 
sprayOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    int radius = atoi(info->prompts[0].rstr);
    int density = atoi(info->prompts[1].rstr);
    int rate = atoi(info->prompts[2].rstr);

    if (radius < 1 || radius > 100) {
	Notice(w, msgText[RADIUS_SHOULD_BE_BETWEEN_ONE_AND_ONE_HUNDRED]);
	return;
    }
    if (density < 1 || density > 500) {
	Notice(w, msgText[DENSITY_SHOULD_BE_BETWEEN_ONE_AND_FIVE_HUNDRED]);
	return;
    }
    if (rate < 1 || rate > 500) {
	Notice(w, msgText[RATE_SHOULD_BE_BETWEEN_ONE_AND_FIVE_HUNDRED]);
	return;
    }
    
    sprayDensity = density;
    sprayRadius = radius;
    sprayRate = rate;
    sprintf(Global.vxpinput, "\n*sprayparams %d %d %d", density, radius, rate);
}

static void 
sprayMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char sprayDensityStr[16];
    static char sprayRadiusStr[16];
    static char sprayRateStr[16];

    sprintf(sprayDensityStr, "%d", sprayDensity);
    sprintf(sprayRadiusStr, "%d", sprayRadius);
    sprintf(sprayRateStr, "%d", sprayRate);
    
    value[0].prompt = msgText[SPRAY_RADIUS];
    value[0].str = sprayRadiusStr;
    value[0].len = 4;
    value[1].prompt = msgText[SPRAY_DENSITY];
    value[1].str = sprayDensityStr;
    value[1].len = 4;
    value[2].prompt = msgText[SPRAY_RATE];
    value[2].str = sprayRateStr;
    value[2].len = 4;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_SPRAY_PARAMETERS];
    info.nprompt = 3;

    TextPrompt(w, msgText[SPRAY_PARAMETERS], &info, sprayOkCallback, NULL, NULL);
}

/*
** Set arrow head parameters callbacks
*/

static void arrowHeadOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo*)infoArg;
    int head = atoi(info->prompts[0].rstr);
    int size = atoi(info->prompts[1].rstr);
    double angle = atof(info->prompts[2].rstr);
    
    if (head < 1 || head > 3) {
	Notice(w, msgText[ARROWHEAD_TYPE_SHOULD_BE_BETWEEN_ONE_AND_MAXTYPE]);
	return;
    }

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

    if (angle < 0.0 || angle > 60.0) {
	Notice(w, msgText[ARROWHEAD_ANGLE_SHOULD_BE_BETWEEN_ZERO_AND_SIXTY]);
	return;
    }

    headType = head;
    headSize = size;
    headAngle = angle;
    objectMode &= ~TICKFLAG;
    objectMode |= HEADFLAG;
    MenuLineCheck();
    sprintf(Global.vxpinput,
	    "\n*objectmode %d\n*arrowheadparams %d %d %g",
	    objectMode, head, size, angle);
}

static void arrowParamMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[3];
    static char headTypeStr[16];
    static char headSizeStr[16];
    static char headAngleStr[16];

    sprintf(headTypeStr, "%d", headType);
    sprintf(headSizeStr, "%d", headSize);
    sprintf(headAngleStr, "%g", headAngle);
    
    value[0].prompt = msgText[ARROWHEAD_TYPE];
    value[0].str = headTypeStr;
    value[0].len = 8;
    value[1].prompt = msgText[ARROWHEAD_SIZE];
    value[1].str = headSizeStr;
    value[1].len = 8;
    value[2].prompt = msgText[ARROWHEAD_ANGLE];
    value[2].str = headAngleStr;
    value[2].len = 8;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_ARROWHEAD_PARAMETERS];
    info.nprompt = 3;

    TextPrompt(w, msgText[ARROWHEAD_PARAMETERS], &info, arrowHeadOkCallback, NULL, NULL);
}

static void tickOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo*)infoArg;
    int tick = atoi(info->prompts[0].rstr);
    int end0 = (atoi(info->prompts[1].rstr)!=0);
    int end1 = (atoi(info->prompts[2].rstr)!=0);
    int number = atoi(info->prompts[3].rstr);
    double size = atof(info->prompts[4].rstr);
    double angle = atof(info->prompts[5].rstr);
    
    if (tick < 1 || tick > 4) {
	Notice(w, msgText[TICK_TYPE_SHOULD_BE_BETWEEN_ONE_AND_MAXTYPE]);
	return;
    }

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

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

    if (angle < 0.0 || angle > 90.0) {
	Notice(w, msgText[TICK_ANGLE_SHOULD_BE_BETWEEN_ZERO_AND_NINETY]);
	return;
    }

    tickType = tick;
    tickEnds = end0 + 2*end1;
    tickNumber = number;
    tickSize = size;
    tickAngle = angle;
    objectMode &= ~HEADFLAG;
    objectMode |= TICKFLAG;
    MenuLineCheck();
    sprintf(Global.vxpinput,
	    "\n*objectmode %d\n*tickparams %d %d %d %d %g %g",
	    objectMode, tick, end0, end1, number, size, angle);
    
}

static void tickParamMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[6];
    static char tickTypeStr[16];
    static char tickOriginStr[16];
    static char tickEndStr[16];
    static char tickNumberStr[16];    
    static char tickSizeStr[16];
    static char tickAngleStr[16];

    sprintf(tickTypeStr, "%d", tickType);
    sprintf(tickOriginStr, "%d", tickEnds & 1);    
    sprintf(tickEndStr, "%d", (tickEnds >> 1)&1);
    sprintf(tickNumberStr, "%d", tickNumber);
    sprintf(tickSizeStr, "%g", tickSize);
    sprintf(tickAngleStr, "%g", tickAngle);
    
    value[0].prompt = msgText[TICK_TYPE];
    value[0].str = tickTypeStr;
    value[0].len = 8;
    value[1].prompt = msgText[TICK_ORIGIN];
    value[1].str = tickOriginStr;
    value[1].len = 8;
    value[2].prompt = msgText[TICK_END];
    value[2].str = tickEndStr;
    value[2].len = 8;
    value[3].prompt = msgText[TICK_INTERVAL_NUMBER];
    value[3].str = tickNumberStr;
    value[3].len = 8;
    value[4].prompt = msgText[TICK_SIZE];
    value[4].str = tickSizeStr;
    value[4].len = 8;
    value[5].prompt = msgText[TICK_ANGLE];
    value[5].str = tickAngleStr;
    value[5].len = 8;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_TICK_PARAMETERS];
    info.nprompt = 6;

    TextPrompt(w, msgText[TICK_PARAMETERS], &info, tickOkCallback, NULL, NULL);
}

/*
** Gradient fill set parameters callbacks
 */
static void 
gradientFillOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo *) infoArg;
    double Hoffset, Voffset, Pad, Angle;
    int Quant;

    Angle = atof(info->prompts[0].rstr);
    Pad = atof(info->prompts[1].rstr);
    Hoffset = atof(info->prompts[2].rstr);
    Voffset = atof(info->prompts[3].rstr);
    Quant = atoi(info->prompts[4].rstr);
    if (Hoffset < -100 || Hoffset > 100) {
	Notice(w, msgText[
            HORIZONTAL_OFFSET_SHOULD_BE_BETWEEN_PLUS_MINUS_HUNDRED_PERCENT]);
	return;
    }
    if (Voffset < -100 || Voffset > 100) {
	Notice(w, msgText[
            VERTICAL_OFFSET_SHOULD_BE_BETWEEN_PLUS_MINUS_HUNDRED_PERCENT]);
	return;
    }
    if (Pad < -49 || Pad > 49) {
	Notice(w, msgText[
            PAD_SHOULD_BE_BETWEEN_PLUS_MINUS_FOURTY_NINE_PERCENT]);
	return;
    }
    if (Angle < -360 || Angle > 360) {
	Notice(w, msgText[
            ANGLE_SHOULD_BE_BETWEEN_PLUS_MINUS_THREE_HUNDRED_SIXTY_DEGREES]);
	return;
    }
    if (Quant < 1 || Quant > 300) {
	Notice(w, msgText[
            NUMBER_OF_STEPS_SHOULD_BE_BETWEEN_ONE_AND_THREE_HUNDRED]);
	return;
    }
    
    gradientFillVoffset = Voffset;
    gradientFillHoffset = Hoffset;
    gradientFillPad = Pad;
    gradientFillAngle = Angle;
    gradientFillQuant = Quant;
    sprintf(Global.vxpinput,
	    "\n*gradientfillparams %3.4g %3.4g %3.4g %3.4g %3d",
	    Voffset, Hoffset, Pad, Angle, Quant);
}

static void 
gradientFillMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[5];
    static char gradientFillAngleStr[16];
    static char gradientFillPadStr[16];
    static char gradientFillHoffsetStr[16];
    static char gradientFillVoffsetStr[16];
    static char gradientFillQuantStr[16];

    if (!gradientFillQuantStr[0]) {
	 if (DefaultDepthOfScreen(XtScreen(GetShell(w))) <= 8)
	      gradientFillQuant = 25;
	 else
	      gradientFillQuant = 300;
    }

    sprintf(gradientFillAngleStr, "%g", gradientFillAngle);    
    sprintf(gradientFillVoffsetStr, "%g", gradientFillVoffset);
    sprintf(gradientFillHoffsetStr, "%g", gradientFillHoffset);
    sprintf(gradientFillPadStr, "%g", gradientFillPad);
    sprintf(gradientFillQuantStr, "%g", gradientFillQuant);
	    
    value[0].prompt = msgText[GRADIENT_ANGLE];
    value[0].str = gradientFillAngleStr;
    value[0].len = 8;
    value[1].prompt = msgText[GRADIENT_PAD];
    value[1].str = gradientFillPadStr;
    value[1].len = 8;
    value[2].prompt = msgText[GRADIENT_HORIZONTAL_OFFSET];
    value[2].str = gradientFillHoffsetStr;
    value[2].len = 8;
    value[3].prompt = msgText[GRADIENT_VERTICAL_OFFSET];
    value[3].str = gradientFillVoffsetStr;
    value[3].len = 8;
    value[4].prompt = msgText[GRADIENT_STEPS];
    value[4].str = gradientFillQuantStr;
    value[4].len = 6;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_GRADIENT_FILL_PARAMETERS];
    info.nprompt = 5;

    TextPrompt(w, msgText[GRADIENT_FILL_PARAMETERS], &info, gradientFillOkCallback, NULL, NULL);
}

/*
** Set box parameters callbacks
*/
static void boxOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo*)infoArg;
    int   size = atoi(info->prompts[0].rstr);
    double ratio = atof(info->prompts[1].rstr);
    
    if (size < 0 || size > 1000) {
	Notice(w, msgText[BOX_ROUND_CORNER_ABSOLUTE_SIZE_SHOULD_BE_BETWEEN_ZERO_AND_ONE_THOUSAND]);
        return;
    }
    if (ratio < 0.0 || ratio > 100.0) {
	Notice(w, msgText[BOX_ROUND_CORNER_RELATIVE_SIZE_SHOULD_BE_BETWEEN_ZERO_AND_ONE_HUNDRED]);
	return;
    }
    if (size > 0 && ratio > 0.0) {
	Notice(w, msgText[BOX_ROUND_CORNER_ABSOLUTE_AND_RELATIVE_SIZE_CANNOT_BE_BOTH_NON_ZERO]);
	return;
    }

    boxSize = size;
    boxRatio = ratio * 0.01;
    sprintf(Global.vxpinput, "\n*boxparams %d %g", size, ratio);
}

static void boxMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[2];
    static char boxSizeStr[16];
    static char boxRatioStr[16];

    sprintf(boxSizeStr, "%d", boxSize);
    sprintf(boxRatioStr, "%g", 100.0 * boxRatio);
    
    value[0].prompt = msgText[BOX_ROUND_CORNER_ABSOLUTE_SIZE];
    value[0].str = boxSizeStr;
    value[0].len = 8;
    value[1].prompt = msgText[BOX_ROUND_CORNER_RELATIVE_SIZE];
    value[1].str = boxRatioStr;
    value[1].len = 8;
    info.prompts = value;
    info.title = msgText[ENTER_THE_DESIRED_ROUND_CORNER_SIZE];
    info.nprompt = 2;

    TextPrompt(w, msgText[BOX_PARAMETERS], &info, boxOkCallback, NULL, NULL);
}

/*
** Set polygon parameters callbacks
*/

static void polygonOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo*)infoArg;
    int sides = atoi(info->prompts[0].rstr);
    double ratio = atof(info->prompts[1].rstr);

    if (sides < 3 || sides > 100) {
	Notice(w, msgText[NUMBER_OF_SIDES_SHOULD_BE_BETWEEN_THREE_AND_ONE_HUNDRED]);
	return;
    }

    if (ratio < 0.0 || ratio > 1.0) {
	Notice(w, msgText[INNER_RATIO_SHOULD_BE_BETWEEN_ZERO_AND_ONE]);
	return;
    }

    PolygonSetParameters(0, sides, ratio);
  
    polygonSides = sides;
    polygonRatio = ratio;
    sprintf(Global.vxpinput, "\n*polygonparams %d %2.4g", sides, ratio);
}

static void polygonMenuCallback(Widget w)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[2];
    static char polygonSidesStr[16];
    static char polygonRatioStr[16];

    sprintf(polygonSidesStr, "%d", polygonSides);
    sprintf(polygonRatioStr, "%d", polygonRatio);    
    
    value[0].prompt = msgText[NUMBER_OF_SIDES_OF_POLYGON];
    value[0].str = polygonSidesStr;
    value[0].len = 8;
    value[1].prompt = msgText[STARLIKE_POLYGON_INNER_RATIO];
    value[1].str = polygonRatioStr;
    value[1].len = 8;
    info.prompts = value;
    info.title = msgText[ENTER_THE_REGULAR_POLYGON_PARAMETERS];
    info.nprompt = 2;

    TextPrompt(w, msgText[POLYGON_PARAMETERS], &info, polygonOkCallback, NULL, NULL);
}

/*
**  Exit callback (simple)
 */
static void 
exitOkCallback(Widget w, XtPointer junk, XtPointer junk2)
{
#if 0
    XtDestroyWidget(GetToplevel(w));
    XtDestroyApplicationContext(XtWidgetToApplicationContext(w));
    Global.timeToDie = True;
#else
    exit(0);
#endif
}

static void 
exitCancelCallback(Widget paint, XtPointer junk, XtPointer junk2)
{
}

static void 
exitPaintCheck(Widget paint, void *sumArg)
{
    int *sum = (int *) sumArg;
    Boolean flg;
    XtVaGetValues(paint, XtNdirty, &flg, NULL);
    *sum += flg ? 1 : 0;
}

void 
exitPaint(Widget w, XtPointer junk, XtPointer junk2)
{
    int total = 0;

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

    if (total == 0) {
	exitOkCallback(w, NULL, NULL);
	return;
    }
    AlertBox(GetShell(w), msgText[UNSAVED_CHANGES_WISH_TO_QUIT],
	     exitOkCallback, exitCancelCallback, NULL);
}

/*
**
 */
void 
takeScreenshot(Widget w, XtPointer junk, XtPointer junk2)
{
    if (XtParent(w)) w = GetToplevel(XtParent(w));
    if (w) {
       PopdownMenusGlobal();
       XUnmapWindow(XtDisplay(Global.toplevel), XtWindow(Global.toplevel));
       XFlush(XtDisplay(Global.toplevel));
       usleep(200000);
       ScreenshotImage(w, junk, 0);
       XMapWindow(XtDisplay(Global.toplevel), XtWindow(Global.toplevel));
    }
}

static Bool can_run_command(const char *cmd) {
    if(strchr(cmd, '/')) {
        return access(cmd, X_OK)==0;
    }
    const char *path = getenv("PATH");
    if(!path) return False;
    char *buf = malloc(strlen(path)+strlen(cmd)+3);
    if(!buf) return False;

    for(; *path; ++path) {
        char *p = buf;
        for(; *path && *path!=':'; ++path,++p) {
            *p = *path;
        }
        if(p==buf) *p++='.';
        if(p[-1]!='/') *p++='/';
        strcpy(p, cmd);
        if(access(buf, X_OK)==0) {
            free(buf);
            return True;
        }
        if(!*path) break;
    }
    free(buf);
    return False;
}

static void tooliconsOkCallback(Widget w, XtPointer junk, XtPointer infoArg)
{
    TextPromptInfo *info = (TextPromptInfo*)infoArg;
    int size = atoi(info->prompts[0].rstr);
    char buf[128];

    if (!can_run_command("inkscape")) {
	Notice(w, msgText[INKSCAPE_NOT_INSTALLED]);  
        return;
    }
    
    if (size < 20 || size > 90) {
	Notice(w, msgText[TOOLICON_SIZE_SHOULD_BE_BETWEEN_TWENTY_AND_NINETY]);
	return;
    }
    sprintf(buf, "%s/bin/xpaint_generate_toolicons %d", SHAREDIR, size);
    if (system(buf));
    
    Notice(w, msgText[RESTART_XPAINT_TO_MAKE_CHANGE_OF_ICONS_EFFECTIVE]);
}

void 
makeToolIcons(Widget w, XtPointer junk, XtPointer junk2)
{
    static TextPromptInfo info;
    static struct textPromptInfo value[1];

    value[0].prompt = msgText[SIZE_OF_TOOLICONS];
    value[0].str = "36";
    value[0].len = 8;
    info.prompts = value;
    info.title = msgText[RESIZE_TOOLICONS_WITH_INKSCAPE];
    info.nprompt = 1;

    TextPrompt(w, msgText[SIZE_OF_TOOLICONS], &info, tooliconsOkCallback, NULL, NULL);
}

void 
resetToolIcons(Widget w, XtPointer junk, XtPointer junk2)
{
    char buf[256];
    if (getenv("HOME")) {
      sprintf(buf, "rm -rf \"%s\"/.xpaint/toolicons", getenv("HOME"));
      if (system(buf));
      Notice(w, msgText[RESTART_XPAINT_TO_MAKE_CHANGE_OF_ICONS_EFFECTIVE]);
    }
}

void 
cleanTempDir(Widget w, XtPointer junk, XtPointer junk2)
{
    char buf[256];
    if (getenv("HOME")) {
      sprintf(buf, "rm -rf \"%s\"/.xpaint/tmp/*", getenv("HOME"));
      if (system(buf));
      Notice(w, msgText[TEMPORARY_DIRECTORY_HAS_BEEN_EMPTIED],
	     getenv("HOME"));
    }
}

/*
**  Button popups 
 */
void brushPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void erasePopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void arcPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void polygonPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void fillPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void gradientFillPopupCB2(Widget w, XtPointer junk, XtPointer junk2);
#ifdef FEATURE_FRACTAL
static void fractalFillPopupCB2(Widget w, XtPointer junk, XtPointer junk2);
#endif
static void selectPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void dynPencilPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void sprayPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void objectPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void arrowNoTailCB(Widget w, XtPointer junk, XtPointer junk2);
static void ellipsePopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void freehandPopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void splinePopupCB(Widget w, XtPointer junk, XtPointer junk2);
static void boxPopupCB(Widget w, XtPointer junk, XtPointer junk2);

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

GENERATE_HELP(pencilPopup, "toolbox.tools.pencil")

GENERATE_HELP(dotPencilPopup, "toolbox.tools.dotPencil")

static PaintMenuItem dynPencilPopup[] = 
{
    MI_SEPARATOR(),
    MI_FLAGCB("autofinish", MF_CHECK, dynPencilPopupCB, 0),
    MI_SIMPLECB("select", changeDynPencilAction, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.dynpencil"),
};

static PaintMenuItem brushPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("opaque", MF_CHECKON | MF_GROUP1, brushPopupCB, 0), 
    MI_FLAGCB("transparent", MF_CHECK | MF_GROUP1, brushPopupCB, 1),
    MI_FLAGCB("stain", MF_CHECK | MF_GROUP1, brushPopupCB, 2),
    MI_SIMPLECB("select", BrushSelect, NULL),
    MI_SIMPLECB("param", changeBrushParamAction, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.brush"),
};

static PaintMenuItem sprayPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("gauss", MF_CHECKON, sprayPopupCB, 0),
    MI_SIMPLECB("select", changeSprayAction, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.spray"),
};

static PaintMenuItem smearPopup[] =
{
    MI_SEPARATOR(),
    MI_SIMPLECB("select", BrushSelect, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.smear"),
};

static PaintMenuItem linePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("segment", MF_CHECK, objectPopupCB, 1),    
    MI_FLAGCB("vector", MF_CHECK, objectPopupCB, 2),
    MI_FLAGCB("ticks", MF_CHECK, objectPopupCB, 3),
    MI_SEPARATOR(),    
    MI_FLAGCB("multi", MF_CHECK, objectPopupCB, 4),    
    MI_SEPARATOR(),
    MI_FLAGCB("position", MF_CHECK, objectPopupCB, 5),    
    MI_FLAGCB("measure", MF_CHECK, objectPopupCB, 6),
    MI_SEPARATOR(),
    MI_SIMPLECB("arrowparam", arrowParamMenuCallback, NULL),
    MI_SIMPLECB("tickparam", tickParamMenuCallback, NULL),
    MI_SEPARATOR(),    
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.line"),
};

GENERATE_HELP(brokenlinePopup, "toolbox.tools.brokenline")

static PaintMenuItem arcPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("connect", MF_CHECKON | MF_GROUP1, arcPopupCB, 0),
    MI_FLAGCB("quadrant", MF_CHECK | MF_GROUP1, arcPopupCB, 1),
    MI_FLAGCB("centered", MF_CHECK | MF_GROUP1, arcPopupCB, 2),
    MI_FLAGCB("boxed", MF_CHECK | MF_GROUP1, arcPopupCB, 3),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.arc"),
};

static PaintMenuItem arrowPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("head_only", MF_CHECK, arrowNoTailCB, 0),
    MI_SIMPLECB("param", arrowParamMenuCallback, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.arrow"),
};

static PaintMenuItem textPopup[] =
{
    MI_SEPARATOR(),
    MI_SIMPLECB("select", changeFontAction, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.text"),
};

static PaintMenuItem erasePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("original", MF_CHECKON, erasePopupCB, 0),
    MI_SIMPLECB("select", changeBrushAction, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.erase"),
};

static PaintMenuItem boxPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("center", MF_CHECK, boxPopupCB, 0),
    MI_FLAGCB("round", MF_CHECK, boxPopupCB, 1),
    MI_SIMPLECB("param", changeBoxParamAction, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.box"),
};

static PaintMenuItem filledBoxPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("center", MF_CHECK, boxPopupCB, 0),
    MI_FLAGCB("round", MF_CHECK, boxPopupCB, 1),
    MI_SIMPLECB("param", changeBoxParamAction, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.box"),
};

static PaintMenuItem selectBoxPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("center", MF_CHECK, boxPopupCB, 0),
    MI_FLAGCB("round", MF_CHECK, boxPopupCB, 1),
    MI_SIMPLECB("param", changeBoxParamAction, NULL),
    MI_SEPARATOR(),
    MI_FLAGCB("shape", MF_CHECKON | MF_GROUP1, selectPopupCB, 0),
    MI_FLAGCB("not_color", MF_CHECK | MF_GROUP1, selectPopupCB, 1),
    MI_FLAGCB("only_color", MF_CHECK | MF_GROUP1, selectPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.select"),
};

static PaintMenuItem ellipsePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("center", MF_CHECK, ellipsePopupCB, 0),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.ellipse"),
};

static PaintMenuItem filledEllipsePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("center", MF_CHECK, ellipsePopupCB, 0),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.ellipse"),
};

static PaintMenuItem selectEllipsePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("center", MF_CHECK, ellipsePopupCB, 0),
    MI_SEPARATOR(),
    MI_FLAGCB("shape", MF_CHECKON | MF_GROUP1, selectPopupCB, 0),
    MI_FLAGCB("not_color", MF_CHECK | MF_GROUP1, selectPopupCB, 1),
    MI_FLAGCB("only_color", MF_CHECK | MF_GROUP1, selectPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.select"),
};

static PaintMenuItem freehandPopup[] = 
{
    MI_SEPARATOR(),
    MI_FLAGCB("open", MF_CHECKON | MF_GROUP1, freehandPopupCB, 0),
    MI_FLAGCB("closed", MF_CHECK | MF_GROUP1, freehandPopupCB, 1),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.freehand")
};

GENERATE_HELP(filledFreehandPopup, "toolbox.tools.freehand")

static PaintMenuItem selectFreehandPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("shape", MF_CHECKON | MF_GROUP1, selectPopupCB, 0),
    MI_FLAGCB("not_color", MF_CHECK | MF_GROUP1, selectPopupCB, 1),
    MI_FLAGCB("only_color", MF_CHECK | MF_GROUP1, selectPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.select"),
};

static PaintMenuItem polygonPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("arbitrary", MF_CHECKON | MF_GROUP1, polygonPopupCB, 0), 
    MI_FLAGCB("regular", MF_CHECK | MF_GROUP1, polygonPopupCB, 1),
    MI_FLAGCB("starlike", MF_CHECK | MF_GROUP1, polygonPopupCB, 2),
    MI_SIMPLECB("param", polygonMenuCallback, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.polygon"),
};

static PaintMenuItem filledPolygonPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("arbitrary", MF_CHECKON | MF_GROUP1, polygonPopupCB, 0), 
    MI_FLAGCB("regular", MF_CHECK | MF_GROUP1, polygonPopupCB, 1),
    MI_FLAGCB("starlike", MF_CHECK | MF_GROUP1, polygonPopupCB, 2),
    MI_SIMPLECB("param", polygonMenuCallback, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.polygon"),
};

static PaintMenuItem selectPolygonPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("arbitrary", MF_CHECKON | MF_GROUP1, polygonPopupCB, 0), 
    MI_FLAGCB("regular", MF_CHECK | MF_GROUP1, polygonPopupCB, 1),
    MI_FLAGCB("starlike", MF_CHECK | MF_GROUP1, polygonPopupCB, 2),
    MI_SIMPLECB("param", polygonMenuCallback, NULL),
    MI_SEPARATOR(),
    MI_FLAGCB("shape", MF_CHECKON | MF_GROUP2, selectPopupCB, 0),
    MI_FLAGCB("not_color", MF_CHECK | MF_GROUP2, selectPopupCB, 1),
    MI_FLAGCB("only_color", MF_CHECK | MF_GROUP2, selectPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.select"),
};

static PaintMenuItem splinePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("open", MF_CHECKON | MF_GROUP1, splinePopupCB, 0),
    MI_FLAGCB("closed", MF_CHECK | MF_GROUP1, splinePopupCB, 1),
    MI_FLAGCB("closed_up", MF_CHECK | MF_GROUP1, splinePopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.spline"),
};

static PaintMenuItem filledSplinePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("open", MF_CHECKON | MF_GROUP1, splinePopupCB, 0),
    MI_FLAGCB("closed", MF_CHECK | MF_GROUP1, splinePopupCB, 1),
    MI_FLAGCB("closed_up", MF_CHECK | MF_GROUP1, splinePopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.spline"),
};

static PaintMenuItem selectSplinePopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("open", MF_CHECKON | MF_GROUP1, splinePopupCB, 0),
    MI_FLAGCB("closed", MF_CHECK | MF_GROUP1, splinePopupCB, 1),
    MI_FLAGCB("closed_up", MF_CHECK | MF_GROUP1, splinePopupCB, 2),
    MI_SEPARATOR(),
    MI_FLAGCB("shape", MF_CHECKON | MF_GROUP2, selectPopupCB, 0),
    MI_FLAGCB("not_color", MF_CHECK | MF_GROUP2, selectPopupCB, 1),
    MI_FLAGCB("only_color", MF_CHECK | MF_GROUP2, selectPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.select"),
};

static PaintMenuItem fillPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("fill", MF_CHECKON | MF_GROUP1, fillPopupCB, 0),
    MI_FLAGCB("fill_color", MF_CHECK | MF_GROUP1, fillPopupCB, 1),
    MI_FLAGCB("fill_range", MF_CHECK | MF_GROUP1, fillPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.fill"),
};

static PaintMenuItem gradientFillPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("radial", MF_CHECKON | MF_GROUP1, gradientFillPopupCB2, GFILL_RADIAL),
    MI_FLAGCB("linear", MF_CHECK | MF_GROUP1, gradientFillPopupCB2, GFILL_LINEAR),
    MI_FLAGCB("conical", MF_CHECK | MF_GROUP1, gradientFillPopupCB2, GFILL_CONE),
    MI_FLAGCB("square", MF_CHECK | MF_GROUP1, gradientFillPopupCB2, GFILL_SQUARE),
    MI_SIMPLECB("param", changeGradientFillParamAction, NULL),
    MI_SEPARATOR(),
    MI_FLAGCB("fill", MF_CHECKON | MF_GROUP2, fillPopupCB, 0),
    MI_FLAGCB("fill_color", MF_CHECK | MF_GROUP2, fillPopupCB, 1),
    MI_FLAGCB("fill_range", MF_CHECK | MF_GROUP2, fillPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.gradientFill"),
};

#ifdef FEATURE_FRACTAL
static PaintMenuItem fractalFillPopup[] =
{
    MI_SEPARATOR(),
    MI_FLAGCB("plasma", MF_CHECKON | MF_GROUP1, fractalFillPopupCB2, 0),
    MI_FLAGCB("clouds", MF_CHECK | MF_GROUP1, fractalFillPopupCB2, 1),
    MI_FLAGCB("landscape", MF_CHECK | MF_GROUP1, fractalFillPopupCB2, 2),
    MI_SIMPLECB("param", changeFractalFillParamAction, NULL),
    MI_SEPARATOR(),
    MI_FLAGCB("fill", MF_CHECKON | MF_GROUP2, fillPopupCB, 0),
    MI_FLAGCB("fill_color", MF_CHECK | MF_GROUP2, fillPopupCB, 1),
    MI_FLAGCB("fill_range", MF_CHECK | MF_GROUP2, fillPopupCB, 2),
    MI_SEPARATOR(),
    MI_SIMPLECB("help", HelpDialog, "toolbox.tools.fractalFill"),
};
#endif

void 
brushPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;

    brushMode = nm;
    MenuCheckItem(brushPopup[nm + 1].widget, True);
    sprintf(Global.vxpinput, "\n*brushmode %d", nm);
}

static void 
ellipsePopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    centerMode = !centerMode;  
    MenuCheckItem(ellipsePopup[1].widget, centerMode);
    MenuCheckItem(filledEllipsePopup[1].widget, centerMode);
    MenuCheckItem(selectEllipsePopup[1].widget, centerMode);
    sprintf(Global.vxpinput, "\n*ellipsemode %d", centerMode);
}

static void 
polygonPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;
    int i;

    PolygonGetParameters(&i, NULL, NULL);

    MenuCheckItem(polygonPopup[nm + 1].widget, True);
    MenuCheckItem(filledPolygonPopup[nm + 1].widget, True);
    MenuCheckItem(selectPolygonPopup[nm + 1].widget, True);
    PolygonSetParameters(nm+1, 0.0, 0.0);
    sprintf(Global.vxpinput, "\n*polygonmode %d", nm+1);
}

static void 
boxPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    Boolean b;
    int m, mp;
    int nm = (int)(long)junk;

    m = mp = boxMode; 
    b = (m & (1<<nm))? False : True;
    if (nm == 0) m = (m&2) + 1-(m&1);
    if (nm == 1) m = 2 - (m&2) + (m&1);
    boxMode = m;
    MenuCheckItem(boxPopup[nm + 1].widget, b);
    MenuCheckItem(filledBoxPopup[nm + 1].widget, b);
    MenuCheckItem(selectBoxPopup[nm + 1].widget, b);
    sprintf(Global.vxpinput, "\n*boxmode %d", m);
}

static void 
arcPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;

    arcMode = nm;
    MenuCheckItem(w, True);
    sprintf(Global.vxpinput, "\n*arcmode %d", (int)nm);
}

static void 
freehandPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;

    MenuCheckItem(freehandPopup[nm + 1].widget, True);
    MenuCheckItem(freehandPopup[nm + 1].widget, True);
    MenuCheckItem(freehandPopup[nm + 1].widget, True);
    freehandMode = nm;
    sprintf(Global.vxpinput, "\n*freehandmode %d", nm);
}

static void 
splinePopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;

    MenuCheckItem(splinePopup[nm + 1].widget, True);
    MenuCheckItem(filledSplinePopup[nm + 1].widget, True);
    MenuCheckItem(selectSplinePopup[nm + 1].widget, True);
    splineMode = nm;
    sprintf(Global.vxpinput, "\n*splinemode %d", nm);
}

void 
OperationSelectCall(int n)
{
    MenuCheckItem(selectBoxPopup[n + 5].widget, True);
    MenuCheckItem(selectEllipsePopup[n + 3].widget, True);
    MenuCheckItem(selectFreehandPopup[n + 1].widget, True);
    MenuCheckItem(selectPolygonPopup[n + 6].widget, True);
    MenuCheckItem(selectSplinePopup[n + 5].widget, True);
    cutMode = n;
#if RANGEBUTTONS
    ChromaSetCutMode(n);
#endif    
}

static void 
selectPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;
    OperationSelectCall(nm);
    sprintf(Global.vxpinput, "\n*selectmode %d", (int)nm);
}

static void 
fillPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;
    fillMode = nm;
    MenuCheckItem(fillPopup[nm+1].widget, True);
    MenuCheckItem(gradientFillPopup[nm+7].widget, True);
    MenuCheckItem(fractalFillPopup[nm+6].widget, True);
    sprintf(Global.vxpinput, "\n*fillmode %d", (int)nm);
}

static void 
gradientFillPopupCB2(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;
    gfillMode = nm;
    MenuCheckItem(w, True);
    sprintf(Global.vxpinput, "\n*gradientfillmode %d", (int)nm);
}

#ifdef FEATURE_FRACTAL
static void 
fractalFillPopupCB2(Widget w, XtPointer junk, XtPointer junk2)
{
    int nm = (int)(long)junk;
    ffillMode = nm;
    MenuCheckItem(w, True);
    sprintf(Global.vxpinput, "\n*fractalfillmode %d", nm);
}

#endif

static void
dynPencilPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    dynFinish = !dynFinish;
    MenuCheckItem(w, dynFinish);
    sprintf(Global.vxpinput, "\n*dynpenmode %d", dynFinish);
}

static void 
sprayPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    sprayMode = !sprayMode;
    MenuCheckItem(w, sprayMode);
    sprintf(Global.vxpinput, "\n*spraymode %d", sprayMode);
}

void
MenuLineCheck()
{
    int i;
    if (objectMode & (POSITIONFLAG|MEASUREFLAG)) {
	for (i = 1; i <= 3; i++)
	    MenuCheckItem(linePopup[i].widget, False);
        MenuCheckItem(linePopup[7].widget, (objectMode & POSITIONFLAG) != 0);
        MenuCheckItem(linePopup[8].widget, (objectMode & MEASUREFLAG) != 0);
    } else {
        MenuCheckItem(linePopup[1].widget,
		      (objectMode & (HEADFLAG|TICKFLAG)) == 0);
        MenuCheckItem(linePopup[2].widget, (objectMode & HEADFLAG) != 0);
        MenuCheckItem(linePopup[3].widget, (objectMode & TICKFLAG) != 0);
        for (i = 7; i <= 8; i++)
            MenuCheckItem(linePopup[i].widget, False);	
    }
    MenuCheckItem(linePopup[5].widget, (objectMode & MULTIFLAG) != 0);
}

static void 
objectPopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    int i, t, m = (int)(long)junk;
    char *str;

    if (m == 0) return;

    objectMode &= ~(POSITIONFLAG|MEASUREFLAG);
    
    if (m <= 4) {
         if (m == 4) {
	     objectMode = objectMode ^ MULTIFLAG;
         } else {
	   objectMode &= ~(HEADFLAG|TICKFLAG);
	     if (m == 2) objectMode |= HEADFLAG; else
	     if (m == 3) objectMode |= TICKFLAG;
	 }
         sprintf(Global.vxpinput, "\n*objectmode %d", objectMode);
	 
    } else
    if (m >= 5 && m <= 6) {
	 objectMode |= (1<<m);
         DataXYPopup();
	 XtVaGetValues(w, XtNlabel, &str, NULL);
	 StoreName(Global.data_xy, str);
    }
    MenuLineCheck();
}


static void 
arrowNoTailCB(Widget w, XtPointer junk, XtPointer junk2)
{
    Boolean m;
    objectMode = objectMode ^ NOTAILFLAG;
    m = ((objectMode & NOTAILFLAG) != 0);
    MenuCheckItem(arrowPopup[1].widget, m);
    sprintf(Global.vxpinput, "\n*objectmode %d", objectMode);
}


static void 
erasePopupCB(Widget w, XtPointer junk, XtPointer junk2)
{
    eraseMode = !eraseMode;
    MenuCheckItem(w, eraseMode);
    sprintf(Global.vxpinput, "\n*erasemode %d", eraseMode);
}

#ifdef XAW3DXFT
static void enableTooltipsCB(Widget w, XtPointer junk, XtPointer junk2);
#endif

/*
**  Done with operation popup menus
 */

#define GENERATE_OP(name)				\
	static OperationPair  CONCAT(name,Op) = {	\
	  CONCAT(name,Add), CONCAT(name,Remove)		\
	};

GENERATE_OP(DotPencil)
GENERATE_OP(Pencil)
GENERATE_OP(DynPencil)
GENERATE_OP(Line)
GENERATE_OP(Brokenline)
GENERATE_OP(Arc)
GENERATE_OP(Arrow)
GENERATE_OP(Erase)
GENERATE_OP(Box)
GENERATE_OP(Ellipse)
GENERATE_OP(Brush)
GENERATE_OP(Font)
GENERATE_OP(Smear)
GENERATE_OP(Spray)
GENERATE_OP(Polygon)
GENERATE_OP(Freehand)
GENERATE_OP(Spline)
GENERATE_OP(Fill)
GENERATE_OP(GradientFill)
#ifdef FEATURE_FRACTAL
GENERATE_OP(FractalFill)
#endif
GENERATE_OP(FilledBox)
GENERATE_OP(FilledEllipse)
GENERATE_OP(FilledPolygon)
GENERATE_OP(FilledFreehand)
GENERATE_OP(FilledSpline)
GENERATE_OP(SelectBox)
GENERATE_OP(SelectEllipse)
GENERATE_OP(SelectFreehand)
GENERATE_OP(SelectPolygon)
GENERATE_OP(SelectSpline)

#define TOOLICONDIR	SHAREDIR"/bitmaps/toolicons/"
#define MENU(name)	XtNumber(CONCAT(name,Popup)), CONCAT(name,Popup)

typedef struct {
    char *name;
    char *xpmmap;
    OperationPair *data;
    char *translations;
    int nitems;
    PaintMenuItem *popupMenu;
    Pixmap icon;
    Widget widget;
} IconListItem;

static Widget iconListWidget;
static IconListItem iconList[] =
{
    /* Vertical arrangement */

    {"pencil", "pencilOp.xpm", &PencilOp,
     NULL, MENU(pencil), None, None},
    {"dynpencil", "dynPenOp.xpm", &DynPencilOp, 	
     "<BtnDown>(2): changeDynPencil()", MENU(dynPencil), None, None},
    {"dotPencil", "dotPenOp.xpm", &DotPencilOp,
     NULL, MENU(dotPencil), None, None},

    {"brush", "brushOp.xpm", &BrushOp,
     "<BtnDown>(2): changeBrush()", MENU(brush), None, None},
    {"spray", "sprayOp.xpm", &SprayOp,
     "<BtnDown>(2): changeSpray()", MENU(spray), None, None},
    {"smear", "smearOp.xpm", &SmearOp,
     "<BtnDown>(2): changeBrush()", MENU(smear), None, None},

    {"line", "lineOp.xpm", &LineOp,
     "<BtnDown>(2): changeArrow()", MENU(line), None, None},
    {"brokenline", "brokenlineOp.xpm", &BrokenlineOp,
     NULL, MENU(brokenline), None, None},
    {"arc", "arcOp.xpm", &ArcOp,
     NULL, MENU(arc), None, None},

    {"arrow", "arrowOp.xpm", &ArrowOp,
     "<BtnDown>(2): changeArrow()", MENU(arrow), None, None},
    {"text", "textOp.xpm", &FontOp,
     "<BtnDown>(2): changeFont()", MENU(text), None, None},
    {"erase", "eraseOp.xpm", &EraseOp,
     "<BtnDown>(2): changeBrush()", MENU(erase), None, None},

    {"box", "boxOp.xpm", &BoxOp,
     "<BtnDown>(2): changeBoxParam()", MENU(box), None, None},
    {"filledBox", "filledBoxOp.xpm", &FilledBoxOp,
     "<BtnDown>(2): changeBoxParam()", MENU(filledBox), None, None},
    {"selectBox", "selectBoxOp.xpm", &SelectBoxOp,
     "<BtnDown>(2): changeBoxParam()", MENU(selectBox), None, None},

    {"ellipse", "ellipseOp.xpm", &EllipseOp,
     NULL, MENU(ellipse), None, None},
    {"filledEllipse", "filledEllipseOp.xpm", &FilledEllipseOp,
     NULL, MENU(filledEllipse), None, None},
    {"selectEllipse", "selectEllipseOp.xpm", &SelectEllipseOp,
     NULL, MENU(selectEllipse), None, None},

    {"freehand", "freehandOp.xpm", &FreehandOp,
     NULL, MENU(freehand), None, None},
    {"filledFreehand", "filledFreehandOp.xpm", &FilledFreehandOp,
     NULL, MENU(filledFreehand), None, None},
    {"selectFreehand", "selectFreehandOp.xpm", &SelectFreehandOp,
     NULL, MENU(selectFreehand), None, None},

    {"polygon", "polygonOp.xpm", &PolygonOp,
     "<BtnDown>(2): changePolygonParam()", MENU(polygon), None, None},
    {"filledPolygon", "filledPolygonOp.xpm", &FilledPolygonOp,
     "<BtnDown>(2): changePolygonParam()", MENU(filledPolygon), None, None},
    {"selectPolygon", "selectPolygonOp.xpm", &SelectPolygonOp,
     "<BtnDown>(2): changePolygonParam()", MENU(selectPolygon), None, None},

    {"spline", "splineOp.xpm", &SplineOp,
     NULL, MENU(spline), None, None},
    {"filledSpline", "filledSplineOp.xpm", &FilledSplineOp,
     NULL, MENU(filledSpline), None, None},
    {"selectSpline", "selectSplineOp.xpm", &SelectSplineOp,
     NULL, MENU(selectSpline), None, None},

    {"fill", "fillOp.xpm", &FillOp,
     NULL, MENU(fill), None, None},
    {"gradientFill", "gradientFillOp.xpm", &GradientFillOp,
     "<BtnDown>(2): changeGradientFillParam()", MENU(gradientFill), None, None},
#ifdef FEATURE_FRACTAL
    {"fractalFill", "fractalFillOp.xpm", &FractalFillOp,
     "<BtnDown>(2): changeFractalFillParam()", MENU(fractalFill), None, None},
#endif

    /* Vertical arrangement */
};

int 
getIndexOp()
{
    int i;
    for (i=0; i<XtNumber(iconList); i++)
         if (CurrentOp == iconList[i].data) return i;
    return 0;
}

void
SetToolIcons(int mode)
{
    Dimension w, h;
    Position xp, yp, x, y;
    int i, j;
    int val[10];

    XtVaGetValues(Global.toplevel, XtNwidth, &w, XtNheight, &h, NULL);

    i = j = 36;
    switch(mode%4) {
    case 0:
      i = (w - 14) / 3;
      j = (h - Global.btn_height - 25) / 10;
      break;
    case 1:
      i = (w - 14) / 10;
      j = (h - Global.btn_height - 25) / 3;
      break;
    case 2:
      i = (w - 14) / 6;
      j = (h - Global.btn_height - 25) / 5;
      break;
    case 3:
      i = (w - 14) / 5;
      j = (h - Global.btn_height - 25) / 6;
      break;
    }
    if (i <= Global.btn_height) i = Global.btn_height;
    if (j <= Global.btn_height) j = Global.btn_height;
    
    val[0] = i;
    val[1] = j;
#ifdef XAW3D    
    val[2] = 3 * val[0] + 16;
    val[3] = 10 * val[0] + 8;
    val[4] = 10 * val[0] + 16;
    val[5] =  3 * val[0] + 20;
    val[6] =  6 * val[0] + 16;
    val[7] =  5 * val[0] + 16;
    val[8] =  5 * val[0] + 16;
    val[9] =  6 * val[0] + 16;    
#else
    val[2] = 3 * val[0] + 13;
    val[3] = 10 * val[0] + 2;
    val[4] = 10 * val[0] + 10;
    val[5] =  3 * val[0] + 16;
    val[6] =  6 * val[0] + 12;
    val[7] =  5 * val[0] + 12;
    val[8] =  5 * val[0] + 12;
    val[9] =  6 * val[0] + 12;    
#endif    
    
    XtVaGetValues(iconList[0].widget, XtNx, &x, XtNy, &y, NULL);
    XtVaGetValues(Global.toplevel, XtNwidth, &w, XtNheight, &h, NULL);
    if (mode >= 4) {
      mode = mode % 4;
      XtResizeWidget(Global.toplevel,
		     val[2*mode+2], val[2*mode+3] + Global.btn_height, 0);
      XtUnmanageChild(Global.topvport);
      XtResizeWidget(Global.topvport,
		     val[2*mode+2] - 8 , val[2*mode+3] - 8, 0);
    }
    XMapWindow(XtDisplay(Global.topvport), XtWindow(Global.topvport));
#ifdef XAW3D		   
    XMoveWindow(XtDisplay(Global.topvport), XtWindow(Global.topvport),
	        4, Global.btn_height+14);
#else
    XMoveWindow(XtDisplay(Global.topvport), XtWindow(Global.topvport),
	        4, Global.btn_height+15);
#endif
    XtResizeWidget(iconList[0].widget, val[0]-3, val[1]-3, 0);
    
    for (i=1; i<NUM; i++) {
      XtUnmanageChild(iconList[i].widget);
      XtResizeWidget(iconList[i].widget, val[0]-3, val[1]-3, 0);
      if (mode == 0)
          XMoveWindow(XtDisplay(iconList[i].widget),
		      XtWindow(iconList[i].widget),
		      x + (i%3)*val[0], y + (i/3)*val[1]);	  
      else
      if (mode == 1)
          XMoveWindow(XtDisplay(iconList[i].widget),
		      XtWindow(iconList[i].widget),
		      x + (i/3)*val[0], y + (i%3)*val[1]);
      else
      if (mode == 2)	
          XMoveWindow(XtDisplay(iconList[i].widget),
		      XtWindow(iconList[i].widget),
		      x + (i%6)*val[0], y + (i/6)*val[1]);	
      else
      if (mode == 3)	
          XMoveWindow(XtDisplay(iconList[i].widget),
		      XtWindow(iconList[i].widget),
		      x + (i/6)*val[0], y + (i%6)*val[1]);	
      XMapWindow(XtDisplay(iconList[i].widget),
		   XtWindow(iconList[i].widget));
    }
}

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

    if (num<0) {
        i = -num-1;
        if (i >= XtNumber(iconList)) i = 0;
        match = &iconList[i];
        if (CurrentOp == match->data) return;
	XawToggleSetCurrent(iconListWidget, (XtPointer) match->name);
        CurrentOp = match->data;
        return;
    }

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

static PaintMenuItem canvasMenu[] =
{
    MI_SIMPLECB("open", GraphicCreate, 0),
    MI_SIMPLECB("loaded", GraphicCreate, 1),
    MI_SIMPLECB("new", GraphicCreate, 2),
    MI_SIMPLECB("new-size", GraphicCreate, 3),
    MI_SEPARATOR(),    
    MI_SIMPLECB("magnifier", GraphicCreate, 4),
    MI_SIMPLECB("screenshot", takeScreenshot, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("toolicons", makeToolIcons, NULL),
    MI_SIMPLECB("reseticons", resetToolIcons, NULL),
    MI_SEPARATOR(),
    MI_SIMPLECB("cleantempdir", cleanTempDir, NULL),     
    MI_SEPARATOR(),    
    MI_SIMPLECB("quit", exitPaint, NULL),
};

static PaintMenuItem helpMenu[] =
{
#ifdef XAW3DXFT
    #define TOOLTIPS_INDEX 0
    MI_FLAGCB("tooltips", MF_CHECK, enableTooltipsCB, 0),
    MI_SEPARATOR(),        
#endif
    MI_SIMPLECB("intro", HelpDialog, "introduction"),
    MI_SIMPLECB("tools", HelpDialog, "toolbox.tools"),
    MI_SIMPLECB("canvas", HelpDialog, "canvas"),
    MI_SEPARATOR(),
    MI_SIMPLECB("about", HelpDialog, "about"),
    MI_SIMPLECB("copyright", HelpDialog, "copyright"),
};

static PaintMenuBar menuBar[] =
{
    {None, "canvas", XtNumber(canvasMenu), canvasMenu},
    {None, "help", XtNumber(helpMenu), helpMenu},
};

/*
**  Now for the callback functions
 */
static int argListLen = 0;
static Arg *argList;

#ifdef XAW3DXFT
static void
enableTooltipsCB(Widget w, XtPointer junk, XtPointer junk2)
{
    if (xaw3dxft_data)
        xaw3dxft_data->show_tips = !xaw3dxft_data->show_tips;
    MenuCheckItem(helpMenu[TOOLTIPS_INDEX].widget, xaw3dxft_data->show_tips);
}
#endif

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

void 
OperationAddArg(Arg arg)
{
    int i;

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

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

    argList[argListLen++] = arg;
}

/*
**  Double click action callback functions.
 */
void 
changeFontAction(Widget w, XEvent * event)
{
    FontSelect(Global.toplevel, None);
}

static void
changeDynPencilAction(Widget w, XEvent *event)
{
    dynPencilMenuCallback(w);
}

static void 
changeSprayAction(Widget w, XEvent * event)
{
    sprayMenuCallback(w);
}

static void 
changeArrowAction(Widget w, XEvent * event)
{
    arrowParamMenuCallback(w);
}

static void 
changeGradientFillParamAction(Widget w, XEvent * event)
{
    gradientFillMenuCallback(w);
}

static void 
changeBrushAction(Widget w, XEvent * event)
{
    BrushSelect(w);
}

static void 
changeBrushParamAction(Widget w, XEvent * event)
{
    brushMenuCallback(w);
}

static void 
changeBoxParamAction(Widget w, XEvent * event)
{
    boxMenuCallback(w);
}

static void 
changePolygonParamAction(Widget w, XEvent * event)
{
    polygonMenuCallback(w);
}

static void 
changeFractalFillParamAction(Widget w, XEvent * event)
{
    fractalMenuCallback(w);
}

static void 
switchtoCanvasCallback(Widget w, XtPointer junk, XEvent * event, Boolean * flg)
{
  if (Global.canvas && event->type == ButtonRelease)
       RaiseWindow(XtDisplay(Global.canvas), XtWindow(Global.canvas));
}

void
processEvent(Widget w, XtPointer arg, XEvent * event, Boolean * flg)
{
    KeySym keysym;
    char buf[12];
    int len;

    if (event->type == KeyPress || event->type == KeyRelease) {
        len = XLookupString((XKeyEvent *)event, 
                            buf, sizeof(buf) - 1, &keysym, NULL);
        if (keysym == XK_Escape)
            PopdownMenusGlobal();
        return;
    }

    if (event->type == ButtonPress || event->type == ButtonRelease) {
        if (Global.popped_up) {
	    if (event->type == ButtonRelease)
                PopdownMenusGlobal();
            event->type = None;
            return;
	}
        if (arg) {
            XtVaSetValues(w, XtNstate, True, NULL);
	    setOperation(w, arg, NULL);
	}
    }
}

void
toolsResized(Widget w, XtPointer l, XConfigureEvent * event, Boolean * flg)
{
    Dimension width, height;
    Position x, y, xp, yp;
    int i, u, v;
    
    if (event->type != ConfigureNotify) return;

    XtVaGetValues(w, XtNwidth, &width,
		     XtNheight, &height, NULL);
    if (width<40 || height<Global.btn_height+50)
        XtVaSetValues(w, XtNsensitive, False, NULL);

    width = width - 8;
    height =  height - (Global.btn_height+20);
    
    XtUnmanageChild(Global.topvport);
    if (width > 40 && height > 40)
        XtResizeWidget(Global.topvport, width, height, 0);

#ifdef XAW3DXFT
    XtMoveWidget(Global.back, width - 2*Global.btn_height/3 + 2, 8);
#else
    if (width>18)
        XtMoveWidget(Global.back, width-10, 8);
#endif

    if (height > 2*width) Global.toolmode = 0;
    else
    if (width > 2*height) Global.toolmode = 1;
    else
    if (width > height) Global.toolmode = 2;
    else Global.toolmode = 3;

    SetToolIcons(Global.toolmode);
}

static
char * try_dir(char *name, int mode)
{
    static char buf[1024];
    if (mode == 0) {
        if (getenv("HOME"))
	    sprintf(buf, "%s/.xpaint/toolicons/%s", getenv("HOME"), name);
        else
	    strcpy(buf, name);
    } else
        sprintf(buf, "%s/%s", TOOLICONDIR, name);
    return buf;
}

/*
**  The real init function
 */
void 
OperationInit(Widget toplevel)
{
    static XtActionsRec acts[] =
    {
	{"changeDynPencil", (XtActionProc) changeDynPencilAction},
	{"changeBrush", (XtActionProc) changeBrushAction},
	{"changeSpray", (XtActionProc) changeSprayAction},
	{"changeArrow", (XtActionProc) changeArrowAction},
	{"changeFont", (XtActionProc) changeFontAction},
	{"changeBoxParam", (XtActionProc) changeBoxParamAction},
	{"changePolygonParam", (XtActionProc) changePolygonParamAction},
	{"changeGradientFillParam", (XtActionProc) changeGradientFillParamAction},
	{"changeFractalFillParam", (XtActionProc) changeFractalFillParamAction},
    };
    int i;
    Pixmap pix;
    char *str;
    char buf[512];
    Widget form, vport, box, icon, firstIcon = None;
    char *defTrans = "\
<Key>Escape: escape()\n\
<BtnDown>,<BtnUp>: set() notify()\n\
";

    XtTranslations trans = XtParseTranslationTable(defTrans);
    IconListItem *cur;
    XpmAttributes attributes;
#ifdef XAW3D
    Pixel bg;
#endif
    Boolean b;
    
    form = XtVaCreateManagedWidget("toolbox",
				   formWidgetClass, toplevel,
				   XtNborderWidth, 0,
				   NULL);
    XtAppAddActions(XtWidgetToApplicationContext(toplevel),
		    acts, XtNumber(acts));

    /*
    **  Create the menu bar
     */
    Global.topbar = MenuBarCreate(form, XtNumber(menuBar), menuBar);
    XtVaSetValues(Global.topbar, 
#ifdef XAW3D
#ifdef XAW3DG
                  XtNhorizDistance, 0,
#else
                  XtNhorizDistance, 1,
#endif
		  XtNvertDistance, 3,
#else
                  XtNhorizDistance, 0,
#endif		  
		  NULL);
    /*
    **  Create the operation icon list
     */
    vport = XtVaCreateManagedWidget("vport",
				    viewportWidgetClass, form,
				    XtNallowVert, True,
				    XtNuseRight, True,
				    XtNfromVert, Global.topbar,
				    XtNborderWidth, 0,
#ifdef XAW95
				    XtNvertDistance, 3,
#endif				    	       
#ifdef XAW3D
				    XtNvertDistance, 5,
                                    XtNborderWidth, 0,
#endif				    
				    NULL);
    Global.topvport = vport;
    
    box = XtVaCreateManagedWidget("box",
				    boxWidgetClass, vport,
#ifdef XAW3D
 			            XtNwidth, 32*Global.btn_height,
				    XtNheight, 32*Global.btn_height,
#endif				  
				    XtNtop, XtChainTop,
				    NULL);

    Global.back = XtVaCreateManagedWidget("!", labelWidgetClass, form,
				      XtNwidth, 2*Global.btn_height/3,
				      XtNvertDistance, 8,
                                      XtNborderWidth, 1,
                                      XtNsensitive, False,
                                      NULL);

    XtAddEventHandler(Global.back, ButtonPressMask | ButtonReleaseMask, False,
        (XtEventHandler) switchtoCanvasCallback, NULL);

    attributes.valuemask = XpmCloseness;
    attributes.closeness =  40000;

    XtVaGetValues(Global.toplevel, XtNmappedWhenManaged, &b, NULL);

    for (i = 0; i < XtNumber(iconList); i++) {
	cur = &iconList[i];
	icon = XtVaCreateManagedWidget(cur->name,
				       toggleWidgetClass, box,
				       XtNtranslations, trans,
				       XtNradioGroup, firstIcon,
				       XtNradioData, cur->name,
				       XtNwidth, 36,
				       XtNheight, 36,
				       NULL);

	pix = None;
	if (b && cur->xpmmap != NULL) {
	    XpmReadFileToPixmap(XtDisplay(box),
				DefaultRootWindow(XtDisplay(box)),
				try_dir(cur->xpmmap, 0),
				&pix, NULL, &attributes);
	    if (pix == None)
	    XpmReadFileToPixmap(XtDisplay(box),
				DefaultRootWindow(XtDisplay(box)),
				try_dir(cur->xpmmap, 1),
				&pix, NULL, &attributes);	      
	    if (pix == None) {
	        fprintf(stderr, "!! %s :\n", cur->xpmmap);
		fprintf(stderr, "%s", msgText[UNABLE_TO_ALLOCATE_SUFFICIENT_COLOR_ENTRIES]);
		fprintf(stderr, "%s", msgText[TRY_EXITING_OTHER_COLOR_INTENSIVE_APPLICATIONS]);
		exit(1);
	    }
	}
	
	cur->widget = icon;
	cur->icon = pix;

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

	    moreTrans = XtParseTranslationTable(cur->translations);
	    XtAugmentTranslations(icon, moreTrans);
	}
	if (firstIcon == NULL) {
	    XtVaSetValues(icon, XtNstate, True, NULL);
	    setOperation(icon, cur->data, NULL);

	    firstIcon = icon;
	    iconListWidget = icon;
	}

        XtAddEventHandler(icon, ButtonPressMask | ButtonReleaseMask |
                          KeyPressMask | KeyReleaseMask, False, 
			  (XtEventHandler) processEvent, cur->data);

	/* 
         *  More sophisticated event handler above
         *  XtAddCallback(icon, XtNcallback, setOperation, cur->data);
         */

#ifdef XAW3D
        XtVaGetValues(icon, XtNbackground, &bg, NULL);
        XtVaSetValues(icon, XtNforeground, bg, NULL);
#endif
	if (cur->nitems != 0 && cur->popupMenu != NULL)
	    MenuPopupCreate(icon, "popup-menu", cur->nitems, cur->popupMenu);

#if defined(XAW3DXFT) || defined(XAW3D)
#if !defined(NEXTAW) && !defined(XAW95)
	if (TooltipsCmdOption()) {
	    XtVaGetValues(XtParent(cur->popupMenu->widget), XtNlabel, &str, NULL);
            sprintf(buf, "%s : %s\n%s", msgText[TOOLTIP_TOOL], str,
                    msgText[TOOLTIP_AUXILIARY_MENU]);
            XawTipEnable(icon, buf);
	}
#endif
#else
#ifdef XAWPLAIN
        XtVaGetValues(XtParent(cur->popupMenu->widget), XtNlabel, &str, NULL);
        sprintf(buf, "%s : %s\n%s", msgText[TOOLTIP_TOOL], str,
                msgText[TOOLTIP_AUXILIARY_MENU]);
	XtVaSetValues(icon, XtNtip, buf, NULL);
        XawTipEnable(icon);
#endif
#endif
    }

    if (Global.toolmode == 0)
       XtResizeWidget(vport, 5 * attributes.height,
		             16 * attributes.height + Global.btn_height + 3, 0);
    else
    if (Global.toolmode == 1)
       XtResizeWidget(vport, 16 * attributes.height,
		              5 * attributes.height + Global.btn_height + 1, 0);
    else
    if (Global.toolmode == 2)
       XtResizeWidget(vport,  11 * attributes.height,
		              10 * attributes.height + Global.btn_height, 0);
    else
    if (Global.toolmode == 3)
       XtResizeWidget(vport,  10 * attributes.height,
		              11 * attributes.height + Global.btn_height, 0);

    AddDestroyCallback(toplevel, (DestroyCallbackFunc) exitPaint, NULL);

    XtAddEventHandler(toplevel, StructureNotifyMask, False,
		      (XtEventHandler) toolsResized, (XtPointer) vport);

    XtAddEventHandler(vport, 
                ButtonPressMask | ButtonReleaseMask |
                KeyPressMask | KeyReleaseMask, False, 
                (XtEventHandler) processEvent, (XtPointer)NULL);

    XtAddEventHandler(toplevel, 
                ButtonPressMask | ButtonReleaseMask |
                KeyPressMask | KeyReleaseMask, False, 
                (XtEventHandler) processEvent, (XtPointer)NULL);

#ifdef XAW3DXFT
    if (xaw3dxft_data)
        MenuCheckItem(helpMenu[TOOLTIPS_INDEX].widget,xaw3dxft_data->show_tips);
#endif
    MenuCheckItem(linePopup[1].widget, True);
}

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

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

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

void setToolIconOnWidget(Widget w)
{
    if (w != None)
        XtVaSetValues(w, XtNbitmap, iconList[getIndexOp()].icon, NULL);
}
