/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
#include <stdio.h>
#include <Xm/Xm.h>
#include <Xm/Label.h>
#include <X11/Intrinsic.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/CoreP.h>
#include <VnsResizeP.h>

#include "stipple.xbm"

#define RSZ(w) (((VnsResizeWidget)(w))->vnsResize)
#define ABS(x) ((x < 0) ? (-x) : (x));

static ClassInitialize();
static Initialize();
static void Realize();
static Destroy();
static Resize();
static ReDisplay();
static Boolean SetValues();
static void ReManageChildren() ;
static XtGeometryResult GeometryManager() ;

static ac_select() ;
static ac_unselect() ;
static ac_motion() ;

static char defaultTranslations[] = "\
	<Btn1Down>:		ResizeSelect()\n\
	<Btn1Motion>:	ResizeMotion()\n\
	<Btn1Up>:		ResizeUnSelect()\n" ;

static XtActionsRec actionsList[] =
{
	{"ResizeSelect",(XtActionProc)ac_select},
	{"ResizeMotion",(XtActionProc)ac_motion},
	{"ResizeUnSelect",(XtActionProc)ac_unselect},
} ;

static XtResource resources[] =
{
	{EtNtargetWidget,
		XtCWidget,
		XtRWidget,
		sizeof(Widget),
		XtOffset(VnsResizeWidget,vnsResize.target_widget),
		XtRWidget,
		(caddr_t)NULL},
	{EtNreConfigured,
		XtCCallback,
		XtRCallback,
		sizeof(XtCallbackProc),
		XtOffset(VnsResizeWidget,vnsResize.reconfigured),
		XtRCallback,
		NULL},
	{EtNmovedOffParent,
		XtCCallback,
		XtRCallback,
		sizeof(XtCallbackProc),
		XtOffset(VnsResizeWidget,vnsResize.moved_off),
		XtRCallback,
		NULL},
	{EtNallowMoveX,
		EtCAllowMoveX,
		XtRBoolean,
		sizeof(Boolean),
		XtOffset(VnsResizeWidget,vnsResize.allow_move_x),
		XtRString,
		"TRUE"},
	{EtNallowMoveY,
		EtCAllowMoveY,
		XtRBoolean,
		sizeof(Boolean),
		XtOffset(VnsResizeWidget,vnsResize.allow_move_y),
		XtRString,
		"TRUE"},
	{EtNallowResizeX,
		EtCAllowResizeX,
		XtRBoolean,
		sizeof(Boolean),
		XtOffset(VnsResizeWidget,vnsResize.allow_resize_x),
		XtRString,
		"TRUE"},
	{EtNallowResizeY,
		EtCAllowResizeY,
		XtRBoolean,
		sizeof(Boolean),
		XtOffset(VnsResizeWidget,vnsResize.allow_resize_y),
		XtRString,
		"TRUE"},
	{EtNresizeBorderWidth,
		EtCResizeBorderWidth,
		XtRInt,
		sizeof(int),
		XtOffset(VnsResizeWidget,vnsResize.resize_border),
		XtRString,
		"4"},
	{XtNforeground,
		XtCForeground,
		XtRPixel,
		sizeof(unsigned long),
		XtOffset(VnsResizeWidget,vnsResize.foreground),
		XtRString,
		XtDefaultForeground},
} ;

VnsResizeClassRec vnsResizeWidgetClassRec =
{
	{
		(WidgetClass)&compositeClassRec,		/* superclass */
		"VnsResize",							/* class name */
		sizeof(VnsResizeRec),					/* widget size */
		(XtWidgetClassProc)ClassInitialize,		/* class_initialize */
		NULL,									/* class_part_initialize */
		FALSE,									/* class inited */
		(XtInitProc)Initialize,					/* initialize */
		NULL,									/* initialize_hook */
		Realize,								/* realize */
		actionsList,							/* actions */
		XtNumber(actionsList),					/* num_actions */
		resources,								/* resources */
		XtNumber(resources),					/* num_resources */
		NULLQUARK,								/* xrm_class */
		TRUE,									/* compress_motion */
		TRUE,									/* compress_exposure */
		TRUE,									/* compress_enterleave */
		FALSE,									/* visible_interest */
		(XtWidgetProc)Destroy,					/* destroy */
		(XtWidgetProc)Resize,					/* resize */
		(XtExposeProc)ReDisplay,				/* expose */
		SetValues,								/* set_values */
		NULL,									/* set_values_hook */
		XtInheritSetValuesAlmost,				/* set_values_almost */
		NULL,									/* get_values_hook */
		XtInheritAcceptFocus,					/* accept_focus */
		XtVersion,								/* version */
		NULL,									/* callback private */
		defaultTranslations,					/* tm_table */
		NULL,									/* query_geometry */
		XtInheritDisplayAccelerator,			/* display_accelerator */
		NULL									/* extension */
	},
	{
		GeometryManager,
		ReManageChildren,
		XtInheritInsertChild,
		XtInheritDeleteChild,
		NULL,
	},
	{
		0,
	}
} ;

WidgetClass vnsResizeWidgetClass = (WidgetClass)&vnsResizeWidgetClassRec ;

/*ARGSUSED*/
static
ClassInitialize()
{
}

/*ARGSUSED*/
static
Initialize(request,new)
	Widget request ;
	Widget new ;
{
	XmString xm_str;

	/* make sure that we have a valid window size */
	if (!new->core.width)
	{
		new->core.width = 1;
	}
	if (!new->core.height)
	{
		new->core.height = 1;
	}

	/* start with no border width */
	new->core.border_width = 0;

	RSZ(new).drag_type = DRAG_NONE ;
	RSZ(new).stip_gray = XCreateBitmapFromData(XtDisplay(new),DefaultRootWindow(XtDisplay(new)),
		stipple_bits,stipple_width,stipple_height);
	make_gcs(new) ;

	xm_str = XmStringCreateLtoR("100%",XmSTRING_DEFAULT_CHARSET);
	RSZ(new).size = XtVaCreateWidget("size",xmLabelWidgetClass,new,
		XmNlabelString,xm_str,
		XmNwidth,40,
		XmNheight,20,
		NULL);
	XmStringFree(xm_str);
}

/*ARGSUSED*/
static
void
Realize(w,mask,attr)
	Widget w ;
	Mask *mask;
	XSetWindowAttributes *attr;
{
	XtCreateWindow(w,CopyFromParent,CopyFromParent, *mask, attr);

	if (RSZ(w).size != NULL)
	{
		XtSetMappedWhenManaged(RSZ(w).size,FALSE);
		XtRealizeWidget(RSZ(w).size);
	}
}

/*ARGSUSED*/
static
Destroy(w)
	Widget w ;
{
	release_gcs(w) ;
	XFreePixmap(XtDisplay(w),RSZ(w).stip_gray);
	/* destroy the size display label */
	XtDestroyWidget(RSZ(w).size);
}

/*ARGSUSED*/
static
ReDisplay(w, event)
	Widget w ;
	XExposeEvent *event ;
{
	Widget target = RSZ(w).target_widget;
	int ew = RSZ(w).resize_border ;

	/* if we have a resize target then check if the target has changed */
	if (target != NULL)
	{
		XtWidgetGeometry req, reply;
		int rx,ry;
		int rw,rh;
	
		rw = target->core.width + 2 * RSZ(w).resize_border + 2 * target->core.border_width;
		rh = target->core.height + 2 * RSZ(w).resize_border + 2 * target->core.border_width;
		rx = target->core.x - RSZ(w).resize_border;
		ry = target->core.y - RSZ(w).resize_border;

		req.request_mode = 0 ;
		if (rw != w->core.width)
		{
			req.request_mode |= CWWidth ;
			req.width = rw ;
		}
		if (rh != w->core.height)
		{
			req.request_mode |= CWHeight ;
			req.height = rh ;
		}
		if (rx != w->core.x)
		{
			req.request_mode |= CWX ;
			req.x = rx ;
		}
		if (ry != w->core.y)
		{
			req.request_mode |= CWY ;
			req.y = ry ;
		}

		/* make geometry request */
		if (req.request_mode)
		{
			XtMakeGeometryRequest(w,&req,&reply);
		}
	}

	/* if we have a resize border, then draw the border around the child */
	if (ew)
	{
		int gl = ew * 4 ;
		int midw = (w->core.width - gl) / 2 ;
		int midh = (w->core.height - gl) / 2 ;
		int x = 0;
		int y = 0;
		GC gc = RSZ(w).gc ;

		/* first, fill everything with a shade of gray */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_gray,
			x,y,w->core.width,w->core.height) ;

		/* TOP */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			midw,y,gl,ew);

		/* LEFT */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			x,midh,ew,gl);

		/* BOTTOM */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			midw,w->core.height - ew,gl,ew);

		/* RIGHT */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			w->core.width - ew,midh,ew,gl);

		/* TOP LEFT */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			x,y,gl,gl);

		/* TOP RIGHT */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			w->core.width - gl,y,gl,gl);

		/* BOTTOM RIGHT */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			w->core.width - gl,w->core.height - gl,gl,gl);

		/* BOTTOM LEFT */
		XFillRectangle(XtDisplay(w),XtWindow(w),RSZ(w).gc_black,
			x,w->core.height - gl,gl,gl);
	}
}

static
Window
find_window_at_point(dpy,x,y,x1,y1)
	Display *dpy ;
	int *x1 ;
	int *y1 ;
{
	Window root = DefaultRootWindow(dpy) ;
	Window child = root ;
	Window w = root ;
	while(child != None)
	{
		XTranslateCoordinates(dpy,root,w = child,x,y,x1,y1,&child) ;
	}
	return w ;
}

static
GC
make_gen_drag_gc(w)
        Widget w ;
{
	Display *dpy = XtDisplay(w) ;
	int screen = DefaultScreen(dpy) ;
	XGCValues values ;
	Pixel white = WhitePixel(dpy,screen) ;
	Pixel black = BlackPixel(dpy,screen) ;
	values.foreground = black ^ white ;
	values.background = white ;
	values.line_width = 0 ;
	values.function = GXxor ;
	values.plane_mask = values.foreground ;
	values.subwindow_mode = IncludeInferiors ;
	return XtGetGC(w,
		(unsigned long)GCForeground |
		(unsigned long)GCBackground |
		(unsigned long)GCLineWidth  |
		(unsigned long)GCSubwindowMode |
		(unsigned long)GCPlaneMask  |
		(unsigned long)GCFunction,
		&values);
}

static
release_gcs(w)
	Widget w ;
{
	XFreeGC(XtDisplay(w),RSZ(w).gc) ;
	XFreeGC(XtDisplay(w),RSZ(w).gc_gray) ;
	XFreeGC(XtDisplay(w),RSZ(w).gc_black) ;
	XtReleaseGC(w,RSZ(w).drag_gc) ;
}

static
make_gcs(w)
	Widget w ;
{
	XGCValues values ;

	RSZ(w).drag_gc = make_gen_drag_gc(w) ;

	values.foreground = RSZ(w).foreground ;
	values.background = w->core.background_pixel ;
	RSZ(w).gc = XCreateGC(XtDisplay(w),DefaultRootWindow(XtDisplay(w)),
		(unsigned long)GCForeground |
		(unsigned long)GCBackground,
		&values) ;

	values.background = WhitePixelOfScreen(XtScreen(w)) ;
	values.function = GXxor ;
	values.line_width = 0 ;
	values.plane_mask = values.foreground ;
	values.subwindow_mode = IncludeInferiors ;
	values.foreground =
		(w->core.background_pixel == BlackPixelOfScreen(XtScreen(w))) ?
			WhitePixelOfScreen(XtScreen(w)) :
			BlackPixelOfScreen(XtScreen(w)) ;
	RSZ(w).gc_black = XCreateGC(XtDisplay(w),DefaultRootWindow(XtDisplay(w)),
		(unsigned long)GCForeground,
		&values);

	values.stipple = RSZ(w).stip_gray ;
	values.fill_style = FillStippled ;
	RSZ(w).gc_gray = XCreateGC(XtDisplay(w),DefaultRootWindow(XtDisplay(w)),
		(unsigned long)GCForeground |
		(unsigned long)GCStipple |
		(unsigned long)GCFillStyle,
		&values);
}

/*ARGSUSED*/
static
Boolean
SetValues(old, request, new)
	Widget old ;
	Widget request ;
	Widget new ;
{
	Widget target = RSZ(new).target_widget;

	/* free the old gcs */
	release_gcs(old);

	/* create new gcs */
	make_gcs(new);

	/* make sure that any new border width is passed on to the target */
	if (target->core.border_width != new->core.border_width)
	{
		XtWidgetGeometry req, reply;

		/* change the size of the resize widget to fit around the target */
		req.request_mode = 0;
		req.request_mode |= CWBorderWidth;
		req.border_width = new->core.border_width;
		XtMakeGeometryRequest(target,&req,&reply);
	}

	new->core.border_width = 0;

	/* if we have a resize target then reconfigure ourseleves w.r.t. target */
	if (target != NULL)
	{
		XtWidgetGeometry req, reply;

		/* change the size of the resize widget to fit around the target */
		req.request_mode = 0;
		req.request_mode |= CWWidth;
		req.request_mode |= CWHeight;
		req.request_mode |= CWX;
		req.request_mode |= CWY;
		req.width = target->core.width + 2 * RSZ(new).resize_border + 
			2 * target->core.border_width;
		req.height = target->core.height + 2 * RSZ(new).resize_border + 
			2 * target->core.border_width;
		req.x = target->core.x - RSZ(new).resize_border - target->core.border_width;
		req.y = target->core.y - RSZ(new).resize_border - target->core.border_width;

		switch(XtMakeGeometryRequest(new,&req,&reply))
		{
			case XtGeometryYes:
				break;
			default:
				break;
		}
	}
	return True ;
}

/*ARGSUSED*/
static
XtGeometryResult
GeometryManager(w,req,rep)
	Widget w ;
	XtWidgetGeometry *req ;
	XtWidgetGeometry *rep ;
{
	/* VnsResize does not manipulate children */
	return XtGeometryNo ;
}

static
void
ReManageChildren(w)
	Widget w ;
{
	/* VnsResize resizes the target_widget resource, not children */
	XtWarning("VnsResize does not accept children. Use XtNtargetWidget resource");
}

/*ARGSUSED*/
static
Resize(w)
	Widget w ;
{
	XtWidgetGeometry req, reply;
	Widget target = RSZ(w).target_widget;
	int tx, ty;
	int tw, th;

	tw = w->core.width - 2 * RSZ(w).resize_border - 2 * target->core.border_width;
	th = w->core.height - 2 * RSZ(w).resize_border - 2 * target->core.border_width;
	tx = w->core.x + RSZ(w).resize_border + target->core.border_width;
	ty = w->core.y + RSZ(w).resize_border + target->core.border_width;

	req.request_mode = 0 ;
	if (tw != target->core.width)
	{
		req.request_mode |= CWWidth ;
		req.width = tw ;
	}
	if (th != target->core.height)
	{
		req.request_mode |= CWHeight ;
		req.height = th ;
	}
	if (tx != target->core.x)
	{
		req.request_mode |= CWX ;
		req.x = tx ;
	}
	if (ty != target->core.y)
	{
		req.request_mode |= CWY ;
		req.y = ty ;
	}

	/* make geometry request */
	if (req.request_mode)
	{
		if (XtMakeGeometryRequest(target,&req,&reply) == XtGeometryYes)
		{
		 	XtCallCallbacks(w,EtNreConfigured,(caddr_t)NULL) ;
		}
	}
}

/*
 * draws a outline around the resize widget when moving the resize widget 
 */
static
draw_outline(w)
	Widget w;
{
	int gx = RSZ(w).grab_x;
	int gy = RSZ(w).grab_y;
	int gw = RSZ(w).grab_w;
	int gh = RSZ(w).grab_h;
/*
	XDrawRectangle(XtDisplay(w),DefaultRootWindow(XtDisplay(w)),RSZ(w).drag_gc,
		RSZ(w).last_x + RSZ(w).del_x,RSZ(w).last_y + RSZ(w).del_y,
		RSZ(w).last_width,RSZ(w).last_height) ;
*/
	/* check for negative width */
	if (gw < 0)
	{
		gx += gw;
		gw = ABS(gw);
	}

	/* check for negative height */
	if (gh < 0)
	{
		gy += gh;
		gh = ABS(gh);
	}

	XDrawRectangle(XtDisplay(w),DefaultRootWindow(XtDisplay(w)),RSZ(w).drag_gc,
		gx,gy,(unsigned)gw,(unsigned)gh);
}

/*
 * Action when button 1 is pressed down
 */
static
ac_select(wd,event)
	Widget wd ;
	XButtonEvent *event ;
{
	int ew = RSZ(wd).resize_border;
	int gl = 4 * ew ;
	int midw = (wd->core.width - gl) / 2 ;
	int midh = (wd->core.height - gl) / 2 ;

	/* check for a shift-copy */
	if (event->state & ShiftMask)
	{
		RSZ(wd).shift = TRUE;
	}
	else
	{
		RSZ(wd).shift = FALSE;
	}

	/* determine where the user has selected the resize widget */
	if (event->x >= wd->core.width - ew)
	{
		if (event->y < gl)
		{
			RSZ(wd).drag_type = DRAG_TOP_RIGHT ;
		}
		else if (event->y > wd->core.height - gl)
		{
			RSZ(wd).drag_type = DRAG_BOTTOM_RIGHT ;
		}
		else if (event->y >= midh && event->y < midh + gl)
		{
			RSZ(wd).drag_type = DRAG_RIGHT ;
		}
		else
		{
			RSZ(wd).drag_type = DRAG_MOVE ;
		}
	}
	else if (event->x < ew)
	{
		if (event->y < gl)
		{
			RSZ(wd).drag_type = DRAG_TOP_LEFT ;
		}
		else if (event->y > wd->core.height - gl)
		{
			RSZ(wd).drag_type = DRAG_BOTTOM_LEFT ;
		}
		else if (event->y >= midh && event->y < midh + gl)
		{
			RSZ(wd).drag_type = DRAG_LEFT ;
		}
		else
		{
			RSZ(wd).drag_type = DRAG_MOVE ;
		}
	}
	else if (event->y < ew)
	{
		if (event->x >= wd->core.width - gl)
		{
			RSZ(wd).drag_type = DRAG_TOP_RIGHT ;
		}
		else if (event->x < gl)
		{
			RSZ(wd).drag_type = DRAG_TOP_LEFT ;
		}
		else if (event->x >= midw && event->x < midw + gl)
		{
			RSZ(wd).drag_type = DRAG_TOP ;
		}
		else
		{
			RSZ(wd).drag_type = DRAG_MOVE ;
		}
	}
	else if (event->y >= wd->core.height - ew)
	{
		if (event->x >= wd->core.width - gl)
		{
			RSZ(wd).drag_type = DRAG_BOTTOM_RIGHT ;
		}
		else if (event->x < gl)
		{
			RSZ(wd).drag_type = DRAG_BOTTOM_LEFT ;
		}
		else if (event->x >= midw && event->x < midw + gl)
		{
			RSZ(wd).drag_type = DRAG_BOTTOM ;
		}
		else
		{
			RSZ(wd).drag_type = DRAG_MOVE ;
		}
	}
	else
	{
		RSZ(wd).drag_type = DRAG_MOVE ;
	}

	/* activate percentage display when shift button is held */
	if ((RSZ(wd).drag_type != DRAG_MOVE) && RSZ(wd).shift)
	{
		if (XtIsRealized(RSZ(wd).size))
		{
			int x = (wd->core.width / 2) - 20;
			int y = (wd->core.height / 2) - 10;
			XtMoveWidget(RSZ(wd).size,x,y);	
			XtMapWidget(RSZ(wd).size);
			XRaiseWindow(XtDisplay(RSZ(wd).size),XtWindow(RSZ(wd).size));
		}
	}

	/* set coordinates */
	RSZ(wd).orig_x = event->x ;
	RSZ(wd).orig_y = event->y ;
	RSZ(wd).del_x = event->x_root - event->x ;
	RSZ(wd).del_y = event->y_root - event->y ;
	RSZ(wd).last_x = 0 ;
	RSZ(wd).last_y = 0 ;
	RSZ(wd).last_width = wd->core.width ;
	RSZ(wd).last_height = wd->core.height ;

	{
		Position tx, ty;
		XtTranslateCoords(wd,0,0,&tx,&ty);
		RSZ(wd).grab_w = wd->core.width;
		RSZ(wd).grab_h = wd->core.height;
		RSZ(wd).grab_x_offset = event->x_root - tx;
		RSZ(wd).grab_y_offset = event->y_root - ty;
		RSZ(wd).grab_x = event->x_root - RSZ(wd).grab_x_offset;
		RSZ(wd).grab_y = event->y_root - RSZ(wd).grab_y_offset;
	}

	/* draw an outline around the resize widget */
	draw_outline(wd);
}

/*
 * Button motion
 */
static
ac_motion(wd,event)
	Widget wd ;
	XMotionEvent *event ;
{
	/* erase the previous line */
	draw_outline(wd) ;

	if (RSZ(wd).drag_type == DRAG_MOVE)
	{
		if (RSZ(wd).allow_move_x)
		{
			RSZ(wd).last_x = event->x - RSZ(wd).orig_x ;
			RSZ(wd).grab_x = event->x_root - RSZ(wd).grab_x_offset;
		}
		if (RSZ(wd).allow_move_y)
		{
			RSZ(wd).last_y = event->y - RSZ(wd).orig_y ;
			RSZ(wd).grab_y = event->y_root - RSZ(wd).grab_y_offset;
		}
	}
	else
	{
		switch(RSZ(wd).drag_type)
		{
			case DRAG_RIGHT :
			case DRAG_BOTTOM_RIGHT :
			case DRAG_TOP_RIGHT :
				if (RSZ(wd).allow_resize_x)
				{
					RSZ(wd).last_width = wd->core.width + event->x - RSZ(wd).orig_x ;
					RSZ(wd).grab_w = event->x_root - RSZ(wd).grab_x;
				}
				break ;
		}
		switch(RSZ(wd).drag_type)
		{
			case DRAG_BOTTOM :
			case DRAG_BOTTOM_RIGHT :
			case DRAG_BOTTOM_LEFT :
				if (RSZ(wd).allow_resize_y)
				{
					RSZ(wd).last_height = wd->core.height + event->y - RSZ(wd).orig_y ;
					RSZ(wd).grab_h = event->y_root - RSZ(wd).grab_y;
				}
				break ;
		}
		switch(RSZ(wd).drag_type)
		{
			case DRAG_LEFT :
			case DRAG_BOTTOM_LEFT :
			case DRAG_TOP_LEFT :
				if (RSZ(wd).allow_move_x && RSZ(wd).allow_resize_x)
				{
					int dx = RSZ(wd).grab_x - event->x_root;
					RSZ(wd).last_x = event->x - RSZ(wd).orig_x ;
					RSZ(wd).last_width = wd->core.width - RSZ(wd).last_x ;
					RSZ(wd).grab_x -= dx;
					RSZ(wd).grab_w += dx;
				}
				break ;
		}
		switch(RSZ(wd).drag_type)
		{
			case DRAG_TOP :
			case DRAG_TOP_LEFT :
			case DRAG_TOP_RIGHT :
				if (RSZ(wd).allow_move_y && RSZ(wd).allow_resize_y)
				{
					int dy = RSZ(wd).grab_y - event->y_root;
					RSZ(wd).last_y = event->y - RSZ(wd).orig_y ;
					RSZ(wd).last_height = wd->core.height - RSZ(wd).last_y ;
					RSZ(wd).grab_y -= dy;
					RSZ(wd).grab_h += dy;
				}
				break ;
		}
		if (RSZ(wd).shift)
		{
			char buf[20];
			XmString xm_str;
			int orig_vol = wd->core.width * wd->core.height;
			int new_vol = RSZ(wd).last_width * RSZ(wd).last_height;
			int diff = (int)(100.0 + 100.0 * (double)((double)(new_vol - orig_vol) / 
				(double)orig_vol));
			sprintf(buf,"%d%%",diff);
			xm_str = XmStringCreateLtoR(buf,XmSTRING_DEFAULT_CHARSET);
			XtVaSetValues(RSZ(wd).size,
				XmNlabelString,xm_str,
				NULL);
			XmStringFree(xm_str);
		}
	}
	draw_outline(wd) ;
}

/*
 * Action when button 1 is released (unselected)
 */
static
ac_unselect(wd,event)
	Widget wd ;
	XButtonEvent *event ;
{
	if (RSZ(wd).drag_type == DRAG_MOVE)
	{
		Widget parent = XtParent(wd);
		Window root = DefaultRootWindow(XtDisplay(wd));
		Window real_window;
		int wx, wy;
		Position px,py;

		/* find the root coordinates for the parent */
		XtTranslateCoords(parent,0,0,&px,&py);

		/* erase the previous outline */
		draw_outline(wd) ;

		/* find the real window at the current cursor location */
		real_window = find_window_at_point(XtDisplay(wd),event->x_root,event->y_root,&wx,&wy);

		/* if dropped on the root window, then do nothing */
		if (real_window == root) return;

		/* If we have moved off of the page or we are doing a shift-button copy then... */
		if (RSZ(wd).shift ||
			event->x_root < px || event->y_root < py ||
			event->x_root >= px + parent->core.width ||
			event->y_root >= py + parent->core.height)
		{
			MovedOff mo;

			mo.x = wx - RSZ(wd).orig_x;
			mo.y = wy - RSZ(wd).orig_y;
			mo.w = real_window;
			mo.shift_pressed = RSZ(wd).shift;
			XtCallCallbacks(wd,EtNmovedOffParent,(XtPointer)&mo);
		}
		/* we have moved somewhere else on the parent */
		else if (real_window == XtWindow(parent))
		{
			Widget target = RSZ(wd).target_widget;
			XtWidgetGeometry req, reply;

			/* first, we make a geometry request for the target */
			req.request_mode = 0;
			req.request_mode |= CWX;
			req.request_mode |= CWY;
			req.x = wx - RSZ(wd).orig_x + RSZ(wd).resize_border;
			req.y = wy - RSZ(wd).orig_y + RSZ(wd).resize_border;

			/* make geometry request for target */
			switch(XtMakeGeometryRequest(target,&req,&reply))
			{
				case XtGeometryYes:
					break;
				case XtGeometryNo:
					break;
			}

			/* now, make a geometry request for ourselves based on target coords. */
			req.request_mode = 0;
			req.request_mode |= CWX;
			req.request_mode |= CWY;
			req.x = target->core.x - RSZ(wd).resize_border;
			req.y = target->core.y - RSZ(wd).resize_border;

			/* make geometry request for ourselves */
			switch(XtMakeGeometryRequest(wd,&req,&reply))
			{
				case XtGeometryYes:
					break;
				case XtGeometryNo:
					break;
			}

			/* call the reconfigure callback */
			XtCallCallbacks(wd,EtNreConfigured,(caddr_t)NULL) ;
		}
		/*
		 * else we are on top of another widget. In this case, we
		 * must search to see of this object has the same parent
		 * as the resize widget. If so, we locate the resize widget on top
		 * of the object located on the same parent. If not, we have dropped
		 * the object on some overlapping widget that does not have the
		 * same parent
		 */
		else
		{
			Widget real_parent;
			Widget real_widget;

			real_widget = XtWindowToWidget(XtDisplay(wd),real_window);
			if (real_widget == NULL)
			{
				return;
			}

			real_parent = XtParent(real_widget);
			while((real_parent != parent) && (real_parent != NULL))
			{
				real_parent = XtParent(real_parent);
			}

			if (real_parent == parent)
			{
				Widget target = RSZ(wd).target_widget;
				XtWidgetGeometry req, reply;

				/* first, we make a geometry request for the target */
				req.request_mode = 0;
				req.request_mode |= CWX;
				req.request_mode |= CWY;
				req.x = event->x_root - px - RSZ(wd).orig_x - wd->core.border_width +
					RSZ(wd).resize_border;
				req.y = event->y_root - py - RSZ(wd).orig_y - wd->core.border_width +
					RSZ(wd).resize_border;

				/* make geometry request for target */
				switch(XtMakeGeometryRequest(target,&req,&reply))
				{
					case XtGeometryYes:
						break;
					case XtGeometryNo:
						break;
				}

				/* now, make a geometry request for ourselves based on target coords. */
				req.request_mode = 0;
				req.request_mode |= CWX;
				req.request_mode |= CWY;
				req.x = target->core.x - RSZ(wd).resize_border;
				req.y = target->core.y - RSZ(wd).resize_border;

				/* make geometry request for ourselves */
				switch(XtMakeGeometryRequest(wd,&req,&reply))
				{
					case XtGeometryYes:
						break;
					case XtGeometryNo:
						break;
				}

				/* call the reconfigure callback */
				XtCallCallbacks(wd,EtNreConfigured,(caddr_t)NULL) ;
			}
			/* else we have moved of the page */
			else
			{
				MovedOff mo;

				mo.x = wx - RSZ(wd).orig_x;
				mo.y = wy - RSZ(wd).orig_y;
				mo.w = real_window;
				mo.shift_pressed = RSZ(wd).shift;
				XtCallCallbacks(wd,EtNmovedOffParent,(XtPointer)&mo);
			}
		}
	}
	/* resize the window */
	else
	{
		int x = wd->core.x + RSZ(wd).last_x;
		int y = wd->core.y + RSZ(wd).last_y;
		int right = x + RSZ(wd).grab_w - 1 ;
		int bottom = y + RSZ(wd).grab_h - 1 ;
		int width, height;

		/* draw the outline using old coordinates */
		draw_outline(wd) ;

		if (x > right)
		{
			int t = right ;
			right = x ;
			x = t;
		}
		if (y > bottom)
		{
			int t = bottom ;
			bottom = y ;
			y = t;
		}

		RSZ(wd).last_width = right - x + 1 ;
		RSZ(wd).last_height = bottom - y + 1 ;
		RSZ(wd).last_x = x ;
		RSZ(wd).last_y = y ;

		/* if resize proportionally or not */
		if (RSZ(wd).shift)
		{
			float rat_x = ((double)RSZ(wd).last_width) / wd->core.width;
			float rat_y = ((double)RSZ(wd).last_height) / wd->core.height;
			if (rat_x > rat_y)
			{
				rat_y = rat_x;
			}
			width = wd->core.width * rat_x;
			height = wd->core.height * rat_y;
		}
		else
		{
			width = ABS(RSZ(wd).last_width);
			height = ABS(RSZ(wd).last_height);
		}

		/* make a geometry request for the target based on the resize info */
		{
			Widget target = RSZ(wd).target_widget;
			XtWidgetGeometry req, rep;
			int tx,ty;
			int tw,th;
	
			tw = width - 2 * RSZ(wd).resize_border - 2 * target->core.border_width;
			th = height - 2 * RSZ(wd).resize_border - 2 * target->core.border_width;
			tx = RSZ(wd).last_x + RSZ(wd).resize_border;
			ty = RSZ(wd).last_y + RSZ(wd).resize_border;

			/* check for negative X coordinate */
			if (tx < 0)
			{
				tx = 0;
			}

			/* check for negative Y coordinate */
			if (ty < 0)
			{
				ty = 0;
			}

			/* check for a negative width */
			if (tw <= 0)
			{
				tw = 1;
			}

			/* check for a negative height */
			if (th <= 0)
			{
				th = 1;
			}

			req.request_mode = 0;
			if (tw != target->core.width)
			{
				req.request_mode |= CWWidth;
				req.width = (Dimension)tw;
			}
			if (th != target->core.height)
			{
				req.request_mode |= CWHeight;
				req.height = (Dimension)th;
			}
			if (tx != target->core.x)
			{
				req.request_mode |= CWX;
				req.x = (Position)tx;
			}
			if (ty != target->core.y)
			{
				req.request_mode |= CWY;
				req.y = (Position)ty;
			}

			if (req.request_mode)
			{
				switch (XtMakeGeometryRequest(target,&req,&rep))
				{
					case XtGeometryYes:
						break;
					case XtGeometryNo:
						break;
					default:
						break;
				}
			}

			/* this is a hack!!! I am calling the targets resize functin directly */
			if (XtClass(target)->core_class.resize != (XtWidgetProc)NULL)
			{
				(*(target->core.widget_class->core_class.resize))(target);
			}
/*
			XtConfigureWidget(target,tx,ty,tw,th,target->core.border_width);
*/
			/* call callbacks that target has been reconfigured */
			XtCallCallbacks(wd,EtNreConfigured,(caddr_t)NULL) ;
		}

		/* make a geometry request for ourselves */
		{
			Widget target = RSZ(wd).target_widget;
			XtWidgetGeometry req, reply;
			int rx,ry;
			int rw,rh;
	
			rw = target->core.width + 2 * RSZ(wd).resize_border + 2 * target->core.border_width;
			rh = target->core.height + 2 * RSZ(wd).resize_border + 2 * target->core.border_width;
			rx = target->core.x - RSZ(wd).resize_border;
			ry = target->core.y - RSZ(wd).resize_border;

			req.request_mode = 0 ;
			if (rw != wd->core.width)
			{
				req.request_mode |= CWWidth ;
				req.width = rw ;
			}
			if (rh != wd->core.height)
			{
				req.request_mode |= CWHeight ;
				req.height = rh ;
			}
			if (rx != wd->core.x)
			{
				req.request_mode |= CWX ;
				req.x = rx ;
			}
			if (ry != wd->core.y)
			{
				req.request_mode |= CWY ;
				req.y = ry ;
			}

			/* make geometry request */
			if (req.request_mode)
			{
				XtMakeGeometryRequest(wd,&req,&reply);
			}
		}

		/* unmap the percentage display box */
		XtUnmapWidget(RSZ(wd).size);
	}
}
