 /*
  * Khoros: $Id: XImage.c,v 1.5 1992/03/20 22:48:45 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: XImage.c,v 1.5 1992/03/20 22:48:45 dkhoros Exp $";
#endif

 /*
  * $Log: XImage.c,v $
 * Revision 1.5  1992/03/20  22:48:45  dkhoros
 * VirtualPatch5
 *
  */

/*
 *----------------------------------------------------------------------
 *
 *            Copyright 1992 University of New Mexico
 *  
 *  Permission to use, copy, modify, distribute, and sell 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 docu-
 *  mentation, and that the name of UNM not be used in advertis-
 *  ing or publicity pertaining to distribution of the software
 *  without specific, written prior permission.  UNM makes no
 *  representations about the suitability of this software for
 *  any purpose.  It is provided "as is" without express or
 *  implied warranty.
 *  
 *  UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 *  INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 *  NESS, IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 *  INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 *  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 *  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 *  OF THIS SOFTWARE.
 *  
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1992 by UNM */

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name: XImage.c					<<<<
   >>>>								<<<<
   >>>>   description: The image display widget used by the	<<<<
   >>>>		       display programs.			<<<<
   >>>>								<<<<
   >>>>      routines:						<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include "XImageP.h"

static void	Initialize();
static void	Realize();
static void	Redisplay();
static void	Resize();
static void	Reset();
static void	Destroy();
static void	Install();
static void	Uninstall();

static void	CheckPan();
static void	ResizePan();
static void	UpdatePan();
static void	RebuildPan();
static void	PanImage();

static Boolean	SetValues();
static XtGeometryResult	PerferredSize();

static Pixmap CreateIconPixmap();

/************************************************
*
*   Full class record constant
*
************************************************/

/*
 *  Defines for installing and de-installing the colormap.
 *  these are used since some non-icccm window managers
 *  don't install their colormaps.  Uncomment these lines and
 *  add them to the appropriate fields in the XImageClassRec
 *  structure if you want this widget to install and de-install
 *  the colormap.
 *
 */
static char defaultTranslations[] =
     "<LeaveWindow>:	uninstall() \n\
     <EnterWindow>:	install()   ";

static XtActionsRec actionsList[] =
{
	{"install",	Install},
	{"uninstall",	Uninstall},
};

/************************************************
*
*   Full class record constant
*
************************************************/

static XtResource resources[] = { 
    {XtNvisual, XtCVisual, XtRPointer, sizeof(Visual *),
      XtOffset(XImageWidget, ximage.visual), XtRCallback, (caddr_t) NULL},
    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
      XtOffset(XImageWidget, ximage.foreground), XtRString,XtDefaultForeground},
    {XtNcolormap, XtCColormap, XtRPointer, sizeof(Colormap),
      XtOffset(XImageWidget, core.colormap), XtRCallback, (caddr_t) NULL},
    {XtNimage, XtCImage, XtRPointer, sizeof(XImage *),
      XtOffset(XImageWidget, ximage.image), XtRPointer, (caddr_t) NULL},
    {XtNpixmap, XtCPixmap, XtRPixmap, sizeof(Pixmap),
      XtOffset(XImageWidget, ximage.pixmap), XtRPixmap, (caddr_t) NULL},
    {XtNxoffset, XtCXoffset, XtRDimension, sizeof(Dimension),
	XtOffset(XImageWidget, ximage.xoffset), XtRImmediate, (caddr_t) 0},
    {XtNyoffset, XtCYoffset, XtRDimension, sizeof(Dimension),
	XtOffset(XImageWidget, ximage.yoffset), XtRImmediate, (caddr_t) 0},
    {XtNmaxWidth, XtCMaxWidth, XtRDimension, sizeof(Dimension),
        XtOffset(XImageWidget, ximage.max_width), XtRImmediate, (caddr_t) 1024},
    {XtNmaxHeight, XtCMaxHeight, XtRDimension, sizeof(Dimension),
        XtOffset(XImageWidget, ximage.max_height), XtRImmediate, (caddr_t)1024},
    {XtNminWidth, XtCMinWidth, XtRDimension, sizeof(Dimension),
        XtOffset(XImageWidget, ximage.min_width), XtRImmediate, (caddr_t) 10},
    {XtNminHeight, XtCMinHeight, XtRDimension, sizeof(Dimension),
        XtOffset(XImageWidget, ximage.min_height), XtRImmediate, (caddr_t) 10},
    {XtNpanWidth, XtCPanWidth, XtRDimension, sizeof(Dimension),
        XtOffset(XImageWidget, ximage.pan_width), XtRImmediate, (caddr_t) 100},
    {XtNpanHeight, XtCPanHeight, XtRDimension, sizeof(Dimension),
        XtOffset(XImageWidget, ximage.pan_height), XtRImmediate, (caddr_t) 100},
    {XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t), 
      XtOffset(XImageWidget, ximage.callbacks), XtRCallback, (caddr_t) NULL},
    {XtNshape, XtCShape, XtRPixmap, sizeof(Pixmap),
      XtOffset(XImageWidget, ximage.shape), XtRPixmap, (caddr_t) None},
    {XtNshapeParent, XtCShapeParent, XtRBoolean, sizeof(Boolean),
      XtOffset(XImageWidget, ximage.shape_parent), XtRImmediate,(caddr_t) True},
    {XtNclip, XtCClip, XtRPixmap, sizeof(Pixmap),
      XtOffset(XImageWidget, ximage.clip), XtRPixmap, (caddr_t) None},
    {XtNoverlay, XtCOverlay, XtRPixmap, sizeof(Pixmap),
      XtOffset(XImageWidget, ximage.overlay), XtRPixmap, (caddr_t) None},
    {XtNoverlayMask, XtCOverlayMask, XtRBitmap, sizeof(Pixmap),
      XtOffset(XImageWidget, ximage.overlay_mask), XtRPixmap, (caddr_t) None},
    {XtNpanImage,  XtCPanImage, XtRBoolean, sizeof(Boolean),
      XtOffset(XImageWidget, ximage.pan_image), XtRImmediate, (caddr_t) False},
    {XtNpanBox,  XtCPanBox, XtRBoolean, sizeof(Boolean),
      XtOffset(XImageWidget, ximage.pan_box), XtRImmediate, (caddr_t) False},
};


#define superclass (&simpleClassRec)

XImageClassRec xImageClassRec =
{
  {
    (WidgetClass) superclass,		/* superclass		  */	
    "XImage",				/* class_name		  */
    sizeof(XImageRec),			/* size			  */
    NULL,				/* class_initialize	  */
    NULL,				/* class_part_initialize  */
    FALSE,				/* class_inited		  */
    Initialize,				/* initialize		  */
    NULL,				/* initialize_hook	  */
    Realize,				/* realize		  */
    NULL,				/* actions		  */
    0,					/* num_actions		  */
    resources,				/* resources		  */
    XtNumber(resources),		/* resource_count	  */
    NULLQUARK,				/* xrm_class		  */
    TRUE,				/* compress_motion	  */
    TRUE,				/* compress_exposure	  */
    TRUE,				/* compress_enterleave    */
    FALSE,				/* visible_interest	  */
    Destroy,				/* destroy		  */
    Resize,				/* resize		  */
    Redisplay,				/* expose		  */
    SetValues,				/* set_values		  */
    NULL,				/* set_values_hook	  */
    XtInheritSetValuesAlmost,		/* set_values_almost	  */
    NULL,				/* get_values_hook	  */
    NULL,				/* accept_focus		  */
    XtVersion,				/* version		  */
    NULL,				/* callback_private	  */
    NULL,				/* tm_table		  */
    PerferredSize,			/* query_geometry	  */
    XtInheritDisplayAccelerator,	/* display_accelerator	  */
    NULL				/* extension		  */
  },  /* CoreClass fields initialization */
  {
    XtInheritChangeSensitive		/* change_sensitive	  */
  },
  {
    0,                                     /* field not used    */
  },  /* XImageClass fields initialization */
};

  /* for public consumption */
WidgetClass xImageWidgetClass = (WidgetClass)&xImageClassRec;


/****************************************************************
*
* Initialization & Query geometry
*
****************************************************************/

static XtGeometryResult PerferredSize(widget, constraint, preferred)

Widget	widget;
XtWidgetGeometry  *constraint,		/*  unused  */
		  *preferred;
{
	XImageWidget xwid = (XImageWidget) widget;


	preferred->request_mode = CWWidth | CWHeight;
	preferred->width  =
	preferred->height = 0;

	if (xwid->ximage.width != 0)
	   preferred->width = xwid->ximage.width;
	if (xwid->ximage.height != 0)
	   preferred->height = xwid->ximage.height;

	if (preferred->width == 0)
	   preferred->width = xwid->core.width;
	if (preferred->height == 0)
	   preferred->height = xwid->core.height;

	/*
	 *  In asking for a preferred width or height do not request a geometry
	 *  that is larger than the allowed max_width or max_height or less than
	 *  min_width or min_height
	 */
	if (preferred->width > xwid->ximage.max_width)
	   preferred->width = xwid->ximage.max_width;
	else if (preferred->width < xwid->ximage.min_width)
	   preferred->width = xwid->ximage.min_width;

	if (preferred->height > xwid->ximage.max_height)
	   preferred->height = xwid->ximage.max_height;
	else if (preferred->height < xwid->ximage.min_height)
	   preferred->height = xwid->ximage.min_height;

	return(XtGeometryAlmost);
}

static void Initialize(request, new)

Widget request, new;
{
	XImageWidget xwid = (XImageWidget) new;

	XGCValues    values;


        values.foreground = xwid->ximage.foreground;
        values.background = xwid->core.background_pixel;
        xwid->ximage.gc = XtGetGC((Widget) xwid, GCForeground | GCBackground,
				&values);
	xwid->ximage.gc_mask = XtGetGC((Widget) xwid, 0, NULL);
	XSetForeground(XtDisplay(xwid), xwid->ximage.gc_mask,
			xwid->ximage.foreground);
	XSetBackground(XtDisplay(xwid), xwid->ximage.gc_mask,
			xwid->core.background_pixel);

	xwid->ximage.xoffset = 
	xwid->ximage.yoffset =  0;
	xwid->ximage.pan     =  NULL;
	xwid->ximage.current_shape = None;
}

static void Realize(widget, valuemask, attributes)

Widget		widget;
XtValueMask	*valuemask;
XSetWindowAttributes *attributes;
{
	XImageWidget xwid = (XImageWidget) widget;

	int	   mode;
	EventMask  mask;
	Visual	   *visual;


	*valuemask |= CWColormap;
	attributes->colormap = xwid->core.colormap;

	/*
	 *  Make sure that the core width & height is not greater or less
	 *  than our maximum or minimum width & height.
	 */
	if (xwid->core.width > xwid->ximage.max_width)
	   xwid->core.width = xwid->ximage.max_width;
	else if (xwid->core.width < xwid->ximage.min_width)
	   xwid->core.width = xwid->ximage.min_width;

	if (xwid->core.height > xwid->ximage.max_height)
	   xwid->core.height = xwid->ximage.max_height;
	else if (xwid->core.height < xwid->ximage.min_height)
	   xwid->core.height = xwid->ximage.min_height;

	if ((attributes->cursor = xwid->simple.cursor) != None)
	   *valuemask |= CWCursor;

	if (xwid->ximage.visual == NULL)
	   visual = (Visual *) CopyFromParent;
	else
	   visual = xwid->ximage.visual;

	if ((mode = XDoesBackingStore(XtScreen(widget))) != NotUseful)
	{
	   *valuemask |= CWBackingStore;
	   attributes->backing_store = mode;
	} 
	XtCreateWindow(widget, InputOutput, visual, *valuemask, attributes);

	if (xwid->ximage.pan_image == True)
	{
	   mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
	   XtInsertEventHandler(widget, mask, False, PanImage, NULL,XtListTail);
	}
	xwid->ximage.pan_active = False;

	mask = StructureNotifyMask;
	XtInsertEventHandler(widget, mask, False, CheckPan, NULL, XtListTail);

	Reset(widget);
	Resize(widget);
}



/***************************************************
*
*  Resize
*
***************************************************/

static void Resize(widget)

Widget  widget;
{
	/* don't need to do anything */
}


/***************************************************
*
*  Reset
*
***************************************************/

static void Reset(widget)

Widget  widget;
{
	XImageWidget xwid = (XImageWidget) widget;
	Display      *display = XtDisplay(widget);

	Widget	     parent;
	int	     shape_event, shape_error;

	int	     x, y;
	Window	     root;
	unsigned     int  width, height, border_width, depth;
#ifdef SGI_SHAPE_OVERRIDE
	int sgi_shape_over = True;
#else
	int sgi_shape_over = False;
#endif


	/*
	 *  See if the server supports the shape extension or if we are
	 *  running the Xsgi server
	 */
	if (XShapeQueryExtension(display, &shape_event, &shape_error)
	    && !(VendorRelease(display) == 4 && sgi_shape_over == False &&
	    strcmp("Silicon Graphics Inc.", ServerVendor(display)) == 0))
	{
	   x = y = 0;
	   for (parent = widget; XtParent(parent); parent = XtParent(parent))
	   {
	     x += parent->core.x + parent->core.border_width;
	     y += parent->core.y + parent->core.border_width;
	   }
	   if (!xwid->ximage.shape_parent)
	      x = y = 0;

	   if (xwid->ximage.shape != None && xwid->ximage.shape != 
	       xwid->ximage.current_shape)
	   {
	      XShapeCombineMask(display, XtWindow(parent), ShapeBounding,
			x, y, xwid->ximage.shape, ShapeSet);
	   }
	   else if (xwid->ximage.shape != xwid->ximage.current_shape)
	   {
	      XShapeCombineMask(display, XtWindow(parent), ShapeBounding,
			0, 0, None, ShapeSet);
	   }
	   xwid->ximage.current_shape = xwid->ximage.shape;
	}
	else if (xwid->ximage.shape != None)
	{
	   XtWarning("XImage:  Server does not support shape extension. \
Ignoring shape request.");

	   xwid->ximage.shape =
	   xwid->ximage.current_shape = None;
	}

	/*
	 *  Set clip mask for the main image specified by the clip mask
	 *  if it's not None.  Otherwise use the shape mask for masking
	 *  the main image.  If this is also None then set the mask to
	 *  None.
	 */
	if (xwid->ximage.clip != None)
	   XSetClipMask(display, xwid->ximage.gc, xwid->ximage.clip);

	else
	   XSetClipMask(display, xwid->ximage.gc, None);

	/*
	 *  Set clip mask for the overlay image
	 */
	if (xwid->ximage.overlay)
	{
	   if (XGetGeometry(display, xwid->ximage.overlay, &root, &x, &y,
			&width, &height,  &border_width, &depth))
	   {
	      xwid->ximage.ov_width  = width;
	      xwid->ximage.ov_height = height;
	      xwid->ximage.ov_depth  = depth;
	      XSetClipMask(display, xwid->ximage.gc_mask,
			xwid->ximage.overlay_mask);
	   }
	   else
	   {
	      XtWarning("XImage error!  Overlay Pixmap doesn't exist");
	      xwid->ximage.ov_width  = 0;
	      xwid->ximage.ov_height = 0;
	      xwid->ximage.ov_depth  = 0;
	      xwid->ximage.overlay   = None;
	      XSetClipMask(display, xwid->ximage.gc_mask, None);
	   }
	}
	else
	{
	   xwid->ximage.ov_width  = 0;
	   xwid->ximage.ov_height = 0;
	   xwid->ximage.ov_depth  = 0;
	   XSetClipMask(display, xwid->ximage.gc_mask, None);
	}


	/*
	 *  Compute the current width, height and depth according to the
	 *  type of images we have to display.
	 */
	if (xwid->ximage.image != NULL)
	{
	   xwid->ximage.width   = xwid->ximage.image->width;
	   xwid->ximage.height  = xwid->ximage.image->height;
	   xwid->ximage.depth   = xwid->ximage.image->depth;
	}
	else if (xwid->ximage.pixmap != NULL)
	{
	   if (XGetGeometry(display, xwid->ximage.pixmap, &root,
			    &x, &y, &width, &height,  &border_width, &depth))
	   {
	      xwid->ximage.width    = width;
	      xwid->ximage.height   = height;
	      xwid->ximage.depth    = depth;
	   }
	   else
	   {
	      XtWarning("XImage error!  Image Pixmap doesn't exist");
	      xwid->ximage.pixmap = None;
	      xwid->ximage.width   = xwid->ximage.ov_width;
	      xwid->ximage.height  = xwid->ximage.ov_height;
	      xwid->ximage.depth   = xwid->core.depth;
	   }
	}
	else
	{
	   xwid->ximage.width   = xwid->ximage.ov_width;
	   xwid->ximage.height  = xwid->ximage.ov_height;
	   xwid->ximage.depth   = xwid->core.depth;
	}
}



/***************************
*
*  Action Procedures
*
***************************/


static void Install(widget, event, params, num_params)

Widget	 widget;
XEvent	 *event;		   /* unused */
String	 *params;		   /* unused */
Cardinal *num_params;		   /* unused */
{
	XImageWidget xwid = (XImageWidget) widget;

	XWindowAttributes  xwa;

	XFlush(XtDisplay(widget));
	XGetWindowAttributes(XtDisplay(widget), XtWindow(widget), &xwa);

	/*
	 *  Install the colormap if it not already installed.  This really
	 *  should not be necessary, but there still seems to be some window
	 *  managers that need help in installing and de-installing the
	 *  colormap.
	 */
	if (xwa.map_installed == FALSE)
	   XInstallColormap(XtDisplay(xwid), xwid->core.colormap);
}


static void Uninstall(widget, event, params, num_params)

Widget	 widget;
XEvent	 *event;		   /* unused */
String	 *params;		   /* unused */
Cardinal *num_params;		   /* unused */
{
	XImageWidget xwid = (XImageWidget) widget;

	XWindowAttributes  xwa;

	XFlush(XtDisplay(widget));
	XGetWindowAttributes(XtDisplay(widget), XtWindow(widget), &xwa);

	/*
	 *  Uninstall the colormap if is currently installed.  This really
	 *  should not be necessary, but there still seems to be some window
	 *  managers that need help in installing and de-installing the
	 *  colormap.
	 */
	if (xwa.map_installed == TRUE)
	   XUninstallColormap(XtDisplay(xwid), xwid->core.colormap);
}



/************************
*
*  REDISPLAY (DRAW)
*
************************/

static void Redisplay(widget, event, region)

Widget	widget;
XEvent	*event;
Region  region;
{
	RefreshImage(widget, event);
}


static void Destroy(widget)

Widget widget;
{
	XImageWidget xwid = (XImageWidget) widget;

	/*
	 *  Release the GC's.
	 */
	XtReleaseGC(widget, xwid->ximage.gc);
	XtReleaseGC(widget, xwid->ximage.gc_mask);

	/*
	 *  Destroy the Pan widget if one exists
	 */
	if (xwid->ximage.pan != NULL)
	{
	   XtUnmapWidget(xwid->ximage.pan);
	   XtDestroyWidget(xwid->ximage.pan);
	   XSync(XtDisplay(widget), 0);
	}
}


/*
 * Set specified arguments into widget
 */
/* ARGSUSED */
static Boolean SetValues (current, request, new)

Widget current, request, new;
{
	XImageWidget cxwid = (XImageWidget) current;
	XImageWidget nxwid = (XImageWidget) new;

	EventMask mask;


	if (!XtIsRealized(new))
	   return(FALSE);

	if (cxwid->core.colormap != nxwid->core.colormap)
	{
	   if (nxwid->core.colormap == NULL)
	   {
	      nxwid->core.colormap = XDefaultColormap(XtDisplay(current),
				XDefaultScreen(XtDisplay(current)));
	   }
	   XSetWindowColormap(XtDisplay(new), XtWindow(new),
			nxwid->core.colormap);
	}

        /*
         *  If we changed the clip mask or shape mask we will need to update
         *  the window to reflect this.
         */
	if (cxwid->ximage.foreground != nxwid->ximage.foreground ||
	    cxwid->core.background_pixel != nxwid->core.background_pixel)
	{
	   XSetForeground(XtDisplay(nxwid), nxwid->ximage.gc,
			nxwid->ximage.foreground);
	   XSetBackground(XtDisplay(nxwid), nxwid->ximage.gc,
			nxwid->core.background_pixel);
	   XSetForeground(XtDisplay(nxwid), nxwid->ximage.gc_mask,
			nxwid->ximage.foreground);
	   XSetBackground(XtDisplay(nxwid), nxwid->ximage.gc_mask,
			nxwid->core.background_pixel);
	}

	if (cxwid->ximage.pan_image != nxwid->ximage.pan_image)
	{
	   if (nxwid->ximage.pan_image == True)
	   {
	      mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
	      XtInsertEventHandler(new, mask, False, PanImage, NULL,XtListTail);
	   }
	   else
	   {
	      mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
	      XtRemoveEventHandler(new, mask, False, PanImage, NULL);
	   }
	}

	/*
	 *  Make sure that the core width & height is not greater or less
	 *  than our maximum or minimum width & height.
	 */
	if (nxwid->core.width > nxwid->ximage.max_width)
	   nxwid->core.width = nxwid->ximage.max_width;
	else if (nxwid->core.width < nxwid->ximage.min_width)
	   nxwid->core.width = nxwid->ximage.min_width;

	if (nxwid->core.height > nxwid->ximage.max_height)
	   nxwid->core.height = nxwid->ximage.max_height;
	else if (nxwid->core.height < nxwid->ximage.min_height)
	   nxwid->core.height = nxwid->ximage.min_height;

        Reset(new);
	RebuildPan(nxwid);
	return(True);
}

RefreshImage(widget, event)

Widget	widget;
XEvent  *event;
{
	int	 x, y, xoff, yoff;
	unsigned int width, height, depth;
	XImageWidget xwid = (XImageWidget) widget;


	if (!XtIsRealized(widget) || xwid->ximage.width == 0 ||
		xwid->ximage.height == 0)
	   return;

	/*
	 *  Find out the size of the area to be refreshed.  If the event is
	 *  NULL then refresh the whole image.
	 */
	xoff = xwid->ximage.xoffset;
	yoff = xwid->ximage.yoffset;
	if (event == NULL)
	{
	   x = 0;
	   y = 0;
	   width = xwid->core.width;
	   height = xwid->core.height;

	}
	else
	{
	   x = event->xexpose.x;
	   y = event->xexpose.y;
	   width  = event->xexpose.width;
	   height = event->xexpose.height;
	}

	if ((xwid->ximage.width - (x+xoff)) < width)
	   width  = xwid->ximage.width - (x+xoff);

	if ((xwid->ximage.height - (y+yoff)) < height)
	   height  = xwid->ximage.height - (y+yoff);

	/*
	 *  Refresh the image
	 */
	if (xwid->ximage.pixmap != None)
	{
	   if (xwid->ximage.depth > 1)
	      XCopyArea(XtDisplay(widget), xwid->ximage.pixmap,XtWindow(widget),
		     xwid->ximage.gc, x+xoff, y+yoff, width, height, x, y);
	   else
	      XCopyPlane(XtDisplay(widget),xwid->ximage.pixmap,XtWindow(widget),
		     xwid->ximage.gc, x+xoff, y+yoff, width, height, x, y, 1L);
	}
	else if (xwid->ximage.image != NULL)
	{
	   XPutImage(XtDisplay(widget), XtWindow(widget), xwid->ximage.gc,
		     xwid->ximage.image, x+xoff, y+yoff, x, y, width, height);
	}

	/*
	 *  Refresh the overlay pixmap if this is any.  Use the overlay_mask
	 *  if it exists as the clip mask.
	 */
	if (xwid->ximage.overlay != None)
	{
	   if ((x+xoff) < xwid->ximage.ov_width &&
	       (y+yoff) < xwid->ximage.ov_height)
	   {
	      /*
	       *  Compute the geometry, so we know how much to refresh
	       */
	      depth = xwid->ximage.ov_depth;
	      if (width > xwid->ximage.ov_width)
	         width = xwid->ximage.ov_width;
	      if (height > xwid->ximage.ov_height)
	         height = xwid->ximage.ov_height;

	      width  -= x+xoff;
	      height -= y+yoff;
	      if (depth > 1)
	      {
	          XCopyArea(XtDisplay(widget), xwid->ximage.overlay,
			XtWindow(widget), xwid->ximage.gc_mask, x+xoff, y+yoff,
			width, height, x, y);
	      }
	      else
	      {
	         XCopyPlane(XtDisplay(widget), xwid->ximage.overlay,
			XtWindow(widget), xwid->ximage.gc_mask, x+xoff, y+yoff,
			width, height, x, y, 1L);
	      }
	   }
	}
}



/***************************************************
*
*  CheckPan
*
***************************************************/


static Pixmap CreateIconPixmap(xwid, icon_ximage)

XImageWidget xwid;
XImage *icon_ximage;
{
	GC	gc;
	Pixmap  icon_pixmap;
	Window  window   = XtWindow((Widget) xwid);
	Display *display = XtDisplay((Widget) xwid);


	if (icon_ximage->depth > 1)
	{
	   icon_pixmap = XCreatePixmap(display, window, icon_ximage->width,
		icon_ximage->height, icon_ximage->depth);

	   gc = XCreateGC(display, icon_pixmap, (unsigned long) 0, NULL);
	   XPutImage(display, icon_pixmap, gc, icon_ximage, 0, 0, 0, 0,
			icon_ximage->width, icon_ximage->height);
	   XFreeGC(display, gc);
	}
	else
	{
	   icon_pixmap = XCreatePixmapFromBitmapData(display, window,
				icon_ximage->data, icon_ximage->width,
				icon_ximage->height, xwid->ximage.foreground,
				xwid->core.background_pixel, xwid->core.depth);
	}
	return(icon_pixmap);
}

static void RebuildPan(xwid)

XImageWidget xwid;
{
	Window    window;
	Display   *display;
	Pixmap    icon_pixmap;
	XImage    *icon_ximage, *xvd_shrink_ximage();


	if (xwid->ximage.pan == NULL || xwid->ximage.pan_box == False ||
	    (xwid->core.width > xwid->ximage.width &&
	     xwid->core.height > xwid->ximage.height))
	{
	   return;
	}

	/*
	 *  Create an icon ximage
	 */
	display = XtDisplay((Widget) xwid);
	window  = XtWindow(xwid->ximage.pan);
	icon_ximage = xvd_shrink_ximage(display, xwid->ximage.image,
			xwid->ximage.pan_width, xwid->ximage.pan_height, NULL);
	icon_pixmap = CreateIconPixmap(xwid, icon_ximage);

	XSetWindowBackgroundPixmap(display, window, icon_pixmap);
	XClearWindow(display, window);
	XDestroyImage(icon_ximage);
	XFreePixmap(display, icon_pixmap);
}

static void CheckPan(widget, clientData, event, dispatch)

Widget   widget;
caddr_t  clientData;
XEvent   *event;                   /* unused */
Boolean  *dispatch;
{
        XImageWidget xwid = (XImageWidget) widget;

	int	  i;
	EventMask mask;
	static    int count = 0;
	Arg	  args[10];
	char	  geometry[50];
	char	  name[50];
	Position  xpos, ypos;

	Pixmap	  icon_pixmap;
	XImage	  *icon_ximage, *xvd_shrink_ximage();
	Display   *display = XtDisplay(widget);


	/*
	 *  Make sure that we have an image to pan on.
	 */
	if (xwid->ximage.image == NULL)
	   return;

	/*
 	 *  Check to see if the image's size is larger than the
 	 *  widget's width and height.  If it is and "pan_image" or "pan_box"
 	 *  is true then create a icon that allows the user to pan around
 	 *  in it.
 	 */
        if ((xwid->ximage.pan_box == True || xwid->ximage.pan_image == True) &&
	    (xwid->core.width < xwid->ximage.width ||
             xwid->core.height < xwid->ximage.height))
        {
	   /*
	    *  Set panning to be active.  This is used by certain routines
	    *  to see if are currently panning.
	    */
	   xwid->ximage.pan_active = True;

	   if (xwid->ximage.pan_box)
	   {
	      /*
	       *  Create an icon ximage
	       */
              icon_ximage = xvd_shrink_ximage(display, xwid->ximage.image,
                        xwid->ximage.pan_width, xwid->ximage.pan_height, NULL);
	      icon_pixmap = CreateIconPixmap(xwid, icon_ximage);

	      if (xwid->ximage.pan == NULL)
	      {
	         XtTranslateCoords(widget, 0, 0, &xpos, &ypos);
	         (void) sprintf(geometry, "+%d+%d", xpos, ypos);
	         (void) sprintf(name, "pan_icon%d",count++);

	         i = 0;
	         XtSetArg(args[i], XtNgeometry, geometry);		i++;
	         XtSetArg(args[i], XtNallowShellResize, True);		i++;
	         XtSetArg(args[i], XtNwindowGroup, XtWindow(widget));	i++;
	         XtSetArg(args[i], XtNwidth, icon_ximage->width);	i++;
	         XtSetArg(args[i], XtNheight, icon_ximage->height);	i++;
	         XtSetArg(args[i], XtNbackgroundPixmap, icon_pixmap);	i++;
	         XtSetArg(args[i], XtNdepth, xwid->core.depth);		i++;
	         XtSetArg(args[i], XtNtitle, "Pan Icon");		i++;
	         XtSetArg(args[i], XtNcolormap, xwid->core.colormap); i++;
	         if (xwid->ximage.visual != NULL)
	         {
	            XtSetArg(args[i], XtNvisual, xwid->ximage.visual); i++;
	         }
	         xwid->ximage.pan = XtAppCreateShell(name, name,
				applicationShellWidgetClass, display, args, i);

		 /*
		  *  Set a handler to refresh the image via the Pan Box
		  */
	         mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
		        PointerMotionHintMask | ExposureMask;
	         XtAddEventHandler(xwid->ximage.pan, mask, False, UpdatePan,
				xwid);

		 /*
		  *  The user may want to resize the pan box.  If so then allow
		  *  via this routine "CheckPan".  All we will do is re-sample
		  *  the XImage to be the same size.
		  */
		 mask = StructureNotifyMask;
		 XtInsertEventHandler(xwid->ximage.pan, mask, False, ResizePan,
					xwid, XtListTail);

		 /*
		  * Realize and map the Pan Box Icon
		  */
	         XtRealizeWidget(xwid->ximage.pan);
	         XRaiseWindow(display, XtWindow(xwid->ximage.pan));
	      }
	      else
	      {
	         XSetWindowBackgroundPixmap(display, XtWindow(xwid->ximage.pan),
				icon_pixmap);
	         XSetWindowColormap(display, XtWindow(xwid->ximage.pan),
				xwid->core.colormap);
	         XtMapWidget(xwid->ximage.pan);
	      }
	      XDestroyImage(icon_ximage);
	      XFreePixmap(display, icon_pixmap);
	   }
	   else
	   {
	      if (xwid->ximage.pan != NULL)
		 XtUnmapWidget(xwid->ximage.pan);
	   }
	   DrawPan(widget);
        }
        else
	{
	   xwid->ximage.xoffset = 0;
	   xwid->ximage.yoffset = 0;

	   if (xwid->ximage.overlay != None)
	      XSetClipOrigin(XtDisplay(widget), xwid->ximage.gc_mask, 0, 0);

	   xwid->ximage.pan_active = False;
	   if (xwid->ximage.pan != NULL)
              XtUnmapWidget(xwid->ximage.pan);
	}
}

static void ResizePan(widget, clientData, event, dispatch)

Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
        XImageWidget xwid = (XImageWidget) clientData;

	int       x, y;
	Window    root;
	unsigned  int  width, height, border_width, depth;
	Display   *display = XtDisplay(widget);


	if (xwid->ximage.pan == NULL)
	   return;

	if (!XGetGeometry(display, XtWindow(xwid->ximage.pan), &root,
			&x, &y, &width, &height,  &border_width, &depth))
	{
	   return;
	}
	else if (width == xwid->ximage.pan_width &&
		 height == xwid->ximage.pan_width)
	{
	   return;
	}
	else
	{
	   xwid->ximage.pan_width = width;
	   xwid->ximage.pan_height = height;
	}
	RebuildPan(xwid);
}


static void UpdatePan(widget, clientData, event, dispatch)

Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
        XImageWidget xwid = (XImageWidget) clientData;

	register float xfactor, yfactor;
	int	 x, y;
	int	 width, height;
	static   int selected = False;


	if (event->type == MotionNotify)
	{
	   if (!selected)
	      return;

	   if (xvd_query_position(widget, &x, &y, False))
	      return;
	}
	else if (event->type == ButtonPress)
	{
	   selected = True;
	   x = event->xbutton.x;
	   y = event->xbutton.y;
	}
	else if (event->type == ButtonRelease)
	{
	   selected = False;
	   x = event->xbutton.x;
	   y = event->xbutton.y;
	}
	else if (event->type == Expose)
	{
	   DrawPan((Widget) xwid);
	   return;
	}

	xfactor = ((float) xwid->ximage.width)/xwid->ximage.pan_width;
	yfactor = ((float) xwid->ximage.height)/xwid->ximage.pan_height;
	width  =  xwid->core.width/xfactor;
	height =  xwid->core.height/yfactor;

	x -= width/2;  x *= xfactor;
	y -= height/2; y *= yfactor;
	if (ClipPan((Widget)xwid,x,y) == False && event->type != ButtonRelease)
	   return;

	if (xwid->ximage.pixmap != None)
	{
	   RefreshImage((Widget) xwid, NULL);
	}
	else if (event->type == ButtonRelease)
	{
	   XClearWindow(XtDisplay((Widget) xwid), XtWindow((Widget) xwid));
	   RefreshImage((Widget) xwid, NULL);
	}
}


#undef  ABS
#define ABS(x) (((x) >= 0)  ? (x) : (-(x)))

static void PanImage(widget, clientData, event, dispatch)

Widget	 widget;
caddr_t  clientData;
XEvent	 *event;		   /* unused */
Boolean  *dispatch;
{
	XImageWidget xwid = (XImageWidget) widget;

	register int    x, y;
	static   int xpos, ypos, xoffset, yoffset,
		     selected = False, moved = False;


	if (xwid->ximage.pan_active == False)
	   return;


	if (event->type == ButtonPress)
	{
	   moved = False;
	   selected = True;
	  /* *dispatch = False; */

	   xpos = event->xbutton.x;
	   ypos = event->xbutton.y;
	   xoffset = xwid->ximage.xoffset;
	   yoffset = xwid->ximage.yoffset;

	   DrawPan(widget);
	   return;
	}
	else if (!selected)
	      return;

	if (event->type == MotionNotify)
	{
	   x = (xpos - event->xbutton.x) + xoffset;
	   y = (ypos - event->xbutton.y) + yoffset;
	   if (ABS(x-xoffset) >= 5 || ABS(y-yoffset) >= 5) moved = True;
	}
	else if (event->type == ButtonRelease)
	{
	   selected  = False;

	   x = (xpos - event->xbutton.x) + xoffset;
	   y = (ypos - event->xbutton.y) + yoffset;
	   if ((ABS(x-xoffset) >= 5 || ABS(y-yoffset) >= 5) || moved == True)
	      *dispatch = False;
	}

	if (ClipPan(widget, x, y) == False && event->type != ButtonRelease)
	   return;

	if (xwid->ximage.pixmap != None)
	{
	   RefreshImage(widget, NULL);
	}
	else if (event->type == ButtonRelease)
	{
	   XClearWindow(XtDisplay(widget), XtWindow(widget));
	   RefreshImage(widget, NULL);
	}
}

int ClipPan(widget, x, y)

Widget widget;
int    x, y;
{
        XImageWidget xwid = (XImageWidget) widget;

	if (x < 0)
	   x = 0;
	if ((x + xwid->core.width) > xwid->ximage.width)
	   x = xwid->ximage.width - xwid->core.width;

	if (y < 0)
	   y = 0;
	if ((y + xwid->core.height) > xwid->ximage.height)
	   y = xwid->ximage.height - xwid->core.height;

	xwid->ximage.xoffset = x;
	xwid->ximage.yoffset = y;
	if (xwid->ximage.overlay != None)
	   XSetClipOrigin(XtDisplay(widget), xwid->ximage.gc_mask, -x, -y);

	DrawPan(widget);
	return(True);
}

DrawPan(widget)

Widget widget;
{
        XImageWidget xwid = (XImageWidget) widget;

	float   xfactor, yfactor;
	int	x, y, width, height;
	GC	gc  = xwid->ximage.gc;
	Display *display;
	int	screen;
	Window  window;


	/*
	 *  Make sure that the Pan icon exists.
	 */
	if (xwid->ximage.pan == NULL)
	   return;

	/*
	 *  Redraw the panning...
	 */
	display = XtDisplay(widget);
	screen = XDefaultScreen(display);
	window = XtWindow(xwid->ximage.pan);

	XClearWindow(display, window);
	xfactor = ((float) xwid->ximage.width)/xwid->ximage.pan_width;
	yfactor = ((float) xwid->ximage.height)/xwid->ximage.pan_height;

	x = xwid->ximage.xoffset/xfactor;
	y = xwid->ximage.yoffset/yfactor;
	width  =  xwid->core.width/xfactor;
	height =  xwid->core.height/yfactor;

	XSetForeground(display, gc, XBlackPixel(display, screen));
	XDrawRectangle(display, window, gc, x+1, y+1, width-2, height-2);
	XSetForeground(display, gc, XWhitePixel(display, screen));
	XDrawRectangle(display, window, gc, x, y, width, height);
	XSetForeground(display, gc, xwid->ximage.foreground);
}



/************************
*
*  Public Routines
*
************************/

int XImageSetOffset(widget, xoffset, yoffset)

Widget widget;
Dimension xoffset, yoffset;
{
	ClipPan(widget, xoffset, yoffset);
	RefreshImage(widget, NULL);
}

int XImageGetOffset(widget, xoffset, yoffset)

Widget widget;
Dimension *xoffset, *yoffset;
{
	XImageWidget xwid = (XImageWidget) widget;

	*xoffset = xwid->ximage.xoffset;
	*yoffset = xwid->ximage.yoffset;
}

int XImageGetGeometry(widget, width, height)

Widget widget;
Dimension *width, *height;
{
	XImageWidget xwid = (XImageWidget) widget;

	*width = xwid->core.width;
	*height = xwid->core.height;
}
