 /*
  * Khoros: $Id$
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id$";
#endif

 /*
  * $Log$
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability 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 OTHER DAMAGES WHAT-
 * SOEVER 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 PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "spectrum.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>		Routines To Control Zoom Mechanism            <<<<
   >>>>                                                       <<<<
   >>>>			create_zoom_display()	              <<<<
   >>>>			spc_zoom()		              <<<<
   >>>>			redisplay_zoom()		      <<<<
   >>>>			refresh_zoom()			      <<<<
   >>>>			redraw_rubberband()		      <<<<
   >>>>			resize_ximage()		      <<<<
   >>>>                                                       <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  */
   


/************************************************************
*
*  MODULE NAME: create_zoom_display
*
*      PURPOSE: creates & initializes a zoom box
*
*        INPUT: parent - the parent widget in which to create the zoom box
*		xvdisplay    - xvdisplay structure for the display widget 
*			       with which this position cursor is associated
*		zoom         - zoom structure for the zoom widget 
*			       with which this position cursor is associated
*		horiz_offset - widget from which to do the horizontal offset
*		vert_offset  - widget from which to do the vertical offset
* Output:  none
*
* Written By:  Mark Young & Danielle Argiro & Tom Sauer
*
****************************************************************/

create_zoom_display(parent)
Widget parent;
{
        int 	   i; 
        unsigned   long mask;
        Widget     toplevel;
	Arg	   args[MaxArgs];
	static int count;

	if (count > 0) return;

	zoom = (ZoomStructure *) XtMalloc(sizeof(ZoomStructure));

	if (xvdisplay == NULL) return;
	if (xvdisplay->ximage != NULL)
	{
	    if (zoom->ximage = xvd_shrink_ximage(display, xvdisplay->ximage, 
					         ZoomDisplayWidth, 
						 ZoomDisplayHeight, 
						 NULL))
            {
               if (zoom->ximage->depth == 1)
                   zoom->ximage->format = XYBitmap;
            }
	}
	zoom->assoc_image  = xvdisplay->image;
	zoom->assoc_ximage = xvdisplay->ximage;
	zoom->assoc_raster = xvdisplay->raster;
	zoom->zoomfactor = 2.0;
	zoom->xoffset = zoom->yoffset = 0;

	if (zoom->assoc_ximage != NULL)
        {
	   zoom->min_x = zoom->assoc_ximage->width/2;
	   zoom->min_y = zoom->assoc_ximage->height/2;
        }

	/*
	 *  create zoom widget
	 */
	 i = 0;
         XtSetArg(args[i], XtNwidth, ZoomDisplayWidth);           i++;
         XtSetArg(args[i], XtNheight, ZoomDisplayHeight);         i++;
         XtSetArg(args[i], XtNcursor, display_cursor);		  i++;
         XtSetArg(args[i], XtNbackground, black);		  i++;
         zoom->raster = XtCreateManagedWidget("zoom_workspace", 
				simpleWidgetClass, parent, args, i);

         mask = StructureNotifyMask | ExposureMask;
         XtInsertEventHandler(zoom->raster, mask, FALSE, 
			      redisplay_zoom, zoom, XtListHead);

         toplevel = parent;
         while (XtParent(toplevel) != NULL)
            toplevel = XtParent(toplevel);

         mask = StructureNotifyMask;
         XtAddEventHandler(toplevel, mask, FALSE, redisplay_zoom, zoom);

	 xvd_set_colormap(parent, xvdisplay->colormap);

	 refresh_zoom(zoom);
	 XtInsertEventHandler(zoom->raster, ButtonPressMask, FALSE,
                             add_cluster_from_img, NULL, XtListHead);

	 XtInsertEventHandler(zoom->raster,
                              ExposureMask | PointerMotionMask |
                              PointerMotionHintMask, FALSE,
                              update_printclass, NULL, XtListHead);

	/*
	 *  create position box underneath zoom box which
	 *  will give the value of the pixel at the cursor position
	 */
	i = 0;
        XtSetArg(args[i], XtNlabel, VStrcpy("     x               "));  i++;
 	XtSetArg(args[i], XtNfromVert, zoom->raster);			i++;
	zoom->position = XtCreateManagedWidget("zoom_position",
				labelWidgetClass, parent, args, i);

	mask = PointerMotionHintMask | PointerMotionMask;
	XtInsertEventHandler(zoom->raster, mask, False,
                              update_zoom_position, NULL, XtListTail);
	XtInsertEventHandler(zoom->raster, mask, False,
                             highlight_cursor_position, NULL, XtListTail);
	count++;

} /* end create_zoom_display */



/************************************************************
*
*  MODULE NAME: spc_zoom
*
*      PURPOSE: spc_zoom is the event handler that gets the button 
*		press event in the display raster window, and decides
*		what to do with it.
*
*        INPUT: widget     -  the widget for the event
*               clientData -  not used
*               event      -  the event
*
*       OUTPUT: none - redisplay_zoom() does the actual refreshing
*
*   WRITTEN BY: Danielle Argiro 
*
************************************************************/

void spc_zoom(widget, clientData, event, dispatch)
Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
    Arg args[MaxArgs];
    Position xoffset, yoffset;
    int i;


    if (xvdisplay->image == NULL) return;

    i =0;
    XtSetArg(args[i], XtNxoffset, &xoffset); i++;
    XtSetArg(args[i], XtNyoffset, &yoffset); i++;
    XtGetValues(xvdisplay->raster, args, i);

    zoom->min_x = event->xbutton.x + xoffset;
    zoom->min_y = event->xbutton.y + yoffset;

    refresh_zoom(zoom);

    XtRemoveEventHandler(xvdisplay->raster, ButtonPressMask, FALSE, 
			 spc_zoom, NULL);
    XtInsertEventHandler(xvdisplay->raster, ButtonPressMask,
                         FALSE, add_cluster_from_img, NULL, XtListHead);

}


/************************************************************
*
*  MODULE NAME: redisplay_zoom
*
*      PURPOSE: redisplay_zoom is the event handler that
*		is used to refresh the zoomed image to the
*		zoom window when zoom window is exposed or resized.  
*		calls refresh_zoom() to do the actual displaying.
*
*
*	 INPUT: widget     -  the widget for the event
*		clientData -  not used
*		event      -  the event
*
*       OUTPUT: none - refresh_zoom() does the actual refreshing
*
*   WRITTEN BY: Mark Young
*
************************************************************/


void redisplay_zoom(widget, clientData, event)
Widget	widget;
caddr_t clientData;
XEvent  *event;
{
        ZoomStructure  *zoom = (ZoomStructure *) clientData;

	Window	    root;
	int	    x, y;
	unsigned    int width, height, border_width, depth;


	/*
	 *  Make sure that there is a current image to zoom with.
	 */
	if (zoom->assoc_image == NULL || zoom->ximage == NULL)
	   return;

	if (event->type == Expose && zoom->raster == widget)
	{
	   /*
	    *  refresh the zoom display.
	    */
	   refresh_zoom(zoom);
	}
	else if (event->type == ConfigureNotify)
	{
           if ( !XGetGeometry(XtDisplay(zoom->raster), XtWindow(zoom->raster),
	            &root, &x, &y, &width, &height,  &border_width, &depth))
           {
	      xvf_error_wait("Window does not exist.","redisplay_zoom", NULL);
	      return;
           }

	   if (zoom->ximage->width != width || zoom->ximage->height != height)
	   {
	      XtFree(zoom->ximage->data);
	      zoom->ximage->width = width;
	      zoom->ximage->height = height;
	      zoom->ximage->bytes_per_line = width;
	      zoom->ximage->data = (char *) XtMalloc(width * height);

	      refresh_zoom(zoom);
	   }
	}
	else
	{
	   if (xvd_check_visibility(widget) == False)
	      return;

	   refresh_zoom(zoom);
	}
}




/************************************************************
*
*  MODULE NAME: refresh_zoom
*
*      PURPOSE: refreshes the zoom image in the zoom window.
*        INPUT: none 
*	OUTPUT: displays a zoomed image to the pixel window
*   WRITTEN BY: Mark Young
*
************************************************************/

refresh_zoom(zoom)
ZoomStructure *zoom;
{
	register  float	  factor;
	unsigned  char	  *data, *line, *imgdata;
	register  int	  xpos, ypos, i, j, k, x, y, width, height;
	int	          num_pixels_width, num_pixels_height;
	Arg 		  args[MaxArgs];
	Position          xoffset, yoffset;

	XImage *ximage    = zoom->assoc_ximage;

	if (xvd_check_visibility(zoom->raster) == False)
	      return;

	i = 0;
        XtSetArg(args[i], XtNxoffset, &xoffset);             i++;
        XtSetArg(args[i], XtNyoffset, &yoffset);             i++;
        XtGetValues(xvdisplay->raster, args, i);


	/*
	 *  Make sure that there is a current image to zoom with.
	 */
	if (zoom->assoc_image == NULL || zoom->ximage == NULL)
	   return;

	factor   = zoom->zoomfactor;
        zoom->x_zoom_loc = 0;
        zoom->y_zoom_loc = 0;
	x = zoom->min_x;
	y = zoom->min_y;

	width = ZoomDisplayWidth;
	height = ZoomDisplayHeight;
	num_pixels_width = (int) ((float) ZoomDisplayWidth / factor);
	num_pixels_height = (int) ((float) ZoomDisplayHeight / factor);
	x = x - num_pixels_width / 2;
        if (x < 0)
	{
              zoom->x_zoom_loc = abs(x);
	      x = 0;
	}
             
	y = y - num_pixels_height / 2;
	if (y < 0)
	{
              zoom->y_zoom_loc = abs(y);
	      y = 0;
	}

	/*
	 * need to generate a new ximage for the zoom because of the
	 * change in the width and height of the zoom area. 
	 */

	resize_ximage(zoom->ximage, width, height);

	data     = (unsigned char *) zoom->ximage->data;
	imgdata  = (unsigned char *) ximage->data;


	i = 0; ypos = y;
	while ((i < height) && (ypos < ximage->height - 1))
	{
	    ypos = y + i/factor;

	       /* save the start of the scan line */
	    line = data;
	    imgdata = (unsigned char *)(ximage->data + ximage->width * ypos);

	    xpos = 0;
	    for (j = 0; (j < width) && (xpos < ximage->width - 1); j++)
	    {
		   xpos = x + j/factor;
		   *(data++) = *(imgdata + xpos);
	    }
	    data = line + width;
	    i++;

	       /* duplicate the rest of the scan lines */
	    for (k = 1; (k < factor) && (i < height); k++, i++)
	    {
		   bcopy(line, data, j);
		   data += width;
	    }
	}
	XClearWindow(display, XtWindow(zoom->raster));
	XPutImage(XtDisplay(zoom->raster), XtWindow(zoom->raster), gc_zoom,
	          zoom->ximage, 0, 0, zoom->x_zoom_loc, zoom->y_zoom_loc, j, i);

		/* 
		 * set the width and height for the zoom now that
		 * we know what it is for the Point and Click mode
		 */
	zoom->xoffset = j;
	zoom->yoffset = i;

 	XFlush(XtDisplay(zoom->raster));
}




/************************************************************
*
*  MODULE NAME: resize_ximage
*
*      PURPOSE: Takes the ximage that is contained in the xvdisplay structure,
*		and returns a shrinked ximage of the desired width and 
*		height.  This is commonly used when creating an icon
*		or image to zoom in.
*
*        INPUT: display - pointer to the X display structure
*		ximage  - the ximage to subsampled
*		desired_width - desired width of the subsampled image
*		desired_height - desired height of the subsampled image
*
*       OUTPUT:	the new ximage of a new size
*
*    CALLED BY: the application program
*
*   WRITTEN BY: Tom Sauer 
*		Modified from xvd_shrink_ximage written by Mark Young
*
*
*************************************************************/


resize_ximage(ximage, desired_width, desired_height)

XImage  *ximage;
int	desired_width, desired_height;
{
	unsigned   int width, height, size;

	/*
	 *  Make sure there is a ximage 
	 */
	if (ximage == NULL)
	   return;

	ximage->width = width  = desired_width;
	ximage->height = height = desired_height;

	/*
	 *  Check to see if the image is a dithered image
	 */
	if (ximage->depth == 1)
	{
	   /*
	    * compute the new size of the image and allocate new space
	    */
	   ximage->bytes_per_line = (width + 7)/8;
	   size   = ximage->bytes_per_line * height;
	   free(ximage->data);
	   ximage->data   = (char *) XtMalloc((unsigned) sizeof(char) * size);
        }
	else
	{
	   ximage->bytes_per_line = width;
	   /*
	    * compute the new size of the image and allocate new space
	    */
	   size   = ximage->bytes_per_line * height;
	   free(ximage->data);
           ximage->data   = (char *) XtMalloc((unsigned) sizeof(char) * size);
	}
}


/************************************************************
*
*  MODULE NAME: update_zoom_position
*
*      PURPOSE: event handler which updates the information label 
*		on the position widget according to the position 
*		of the cursor in the zoom widget
*
*	 INPUT: widget - the widget that had the event
*		clientdata - used for the xvdisplay struct
*		event - the event that evoked this event handler
*
*       OUTPUT: none
*
*   WRITTEN BY: Mark Young
*
*************************************************************/

void update_zoom_position(widget, clientData, event, dispatch)

Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	double	    value;
        char	    buf[25];
        Arg	    args[2];
	int	    i, x, y, allocated;

	get_zoom_position(widget, zoom, &x, &y);
	if (xvd_query_value(xvdisplay, x, y, &value, NULL, &allocated))
	{
	   if (allocated)
	      sprintf(buf,"%3d x %3d  = %0.3g", x, y, value);
	   else
	      sprintf(buf,"%3d x %3d  = %0.3g *", x, y, value);
	}
	else
	   sprintf(buf,"%3d x %3d", x, y);

	i = 0;
	XtSetArg(args[i], XtNlabel, buf);	  i++;
        XtSetValues(zoom->position, args, i);
	XFlush(XtDisplay(widget));
}


/************************************************************
*
*  MODULE NAME: get_zoom_position
*
*      PURPOSE: Computes the x and y position of the pixel
*		in the image from the zoom cursors location
*		in the zoom box.
*
*	 INPUT: widget - the widget that had the event
*		zoom - a pointer to the zoom structure
*		x - a pointer to the x location computed
*		y - a pointer to the y location computed
*
*       OUTPUT: none
*
*   WRITTEN BY: Tom Sauer
*
*************************************************************/

get_zoom_position(widget, zoom, x, y)

Widget  widget;
ZoomStructure *zoom;
int *x, *y;
{
	int  width, height;

	if (xvd_query_position(widget, x, y, True))
	   return;

		/* 
		 * Figure out the x and y location of the cursor from
		 * that coorespond to the location in the actual image.
		 */

	width  = (int) ((float) ZoomDisplayWidth / zoom->zoomfactor);
	height = (int) ((float) ZoomDisplayHeight / zoom->zoomfactor);

		/* calculate for x */
	if (*x < zoom->x_zoom_loc || *x > zoom->xoffset + zoom->x_zoom_loc)
	       *x = -1;
	else
	{
	       *x -= zoom->x_zoom_loc;
	       if (zoom->min_x < width /2)
	          *x  = ((int) ((float) *x / zoom->zoomfactor));
	       else
	          *x  = ((int) ((float) *x / zoom->zoomfactor)) +
				(zoom->min_x - width /2);
	}

		/* calculate for y */
	if (*y < zoom->y_zoom_loc || *y > zoom->yoffset + zoom->y_zoom_loc)
	       *y = -1;
	else
	{
	       *y -= zoom->y_zoom_loc;
	       if (zoom->min_y < (height/2))
	          *y  = (int) ((float) *y / zoom->zoomfactor);
	       else
	          *y  = ((int) ((float) *y / zoom->zoomfactor)) +
				(zoom->min_y - height /2);
	 }

	  /* 
	   * if either the x or the y is not zooming in the image
	   * then set both to -1 
           */

	if (*x == -1 || *y == -1)
	{
	   *x = -1;
	   *y = -1;
	}
}
