 /*
  * Khoros: $Id: overlay.c,v 1.2 1991/10/02 00:14:51 khoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: overlay.c,v 1.2 1991/10/02 00:14:51 khoros Exp $";
#endif

 /*
  * $Log: overlay.c,v $
 * Revision 1.2  1991/10/02  00:14:51  khoros
 * HellPatch2
 *
  */ 


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


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    file name: overlay.c
   >>>>               
   >>>>   description: contains code to interactively create/edit
   >>>>                overlays for an image
   >>>>              
   >>>>      routines:
   >>>>			init_overlays
   >>>>			redraw_overlays
   >>>>			draw_overlays
   >>>>			erase_overlays
   >>>>			refresh_overlays
   >>>>			write_overlays
   >>>>			read_overlays
   >>>>			update_overlays
   >>>>			destroy_overlays
   >>>>              
   >>>> modifications:
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


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

static  num_fonts = 5;
static  char *fonts[] =
{
          "variable",
          "fixed",
          "-adobe-times-medium-r-normal--12*",
          "-adobe-times-bold-r-normal--12*",
          "6x10"
};

extern XObject *obj_list;

/********************************************************
*
*  Routine Name:  init_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


init_overlays(id, widget)

int		id;
Widget	    widget;
{
	Display	        *display = XtDisplay(widget);
	int		screen = XDefaultScreen(display);

	int		i;
	Window		rootwindow;
	XGCValues	values;
	unsigned	long mask;

	ov_struct = (Overlay *) XtMalloc(sizeof(Overlay));
	ov_struct->widget = widget;
	ov_struct->id = id;		/* set the graphics id */

	rootwindow =  XDefaultRootWindow(display);
	mask = GCLineWidth | GCFunction | GCSubwindowMode | GCForeground |
	       GCBackground | GCGraphicsExposures;
	values.line_width = 0;
	values.foreground = XWhitePixel(display, screen);
	values.background = XBlackPixel(display, screen);
	values.function   = GXcopy;
	values.subwindow_mode = IncludeInferiors;
	values.graphics_exposures = False;
	ov_struct->gc = XCreateGC(display, rootwindow, mask, &values);

	/*
	 *  Add the event handler responsible for editing objects
	 *  currently on the screen.
	 */
	mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
	       PointerMotionHintMask;
	XtInsertEventHandler(widget, mask, FALSE, update_overlays, NULL,
			XtListHead);

	/*
	 *  Add an event handler for expose events.  "refresh_overlays"
	 *  will repaint the objects to the screen.
	 */
	mask = ExposureMask;
	XtInsertEventHandler(widget, mask, TRUE, refresh_overlays, NULL,
			XtListTail);

        /* font to be used with text on the workspace */
        for (i = 0; i < num_fonts; i++)
        {
            if ((ov_struct->font = XLoadQueryFont(display, fonts[i])) != NULL)
               break;
        }
	edit_obj = NULL;
}




/********************************************************
*
*  Routine Name:  redraw_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


redraw_overlays(widget, list)

Widget  widget;
XObject	*list;
{
	XObject  *current;

	current = list;
	if (list == NULL || widget == NULL)
	   return;

	while (current != NULL)
	{
	   display_object(widget, current);
	   current = current->next;
	}
}



/********************************************************
*
*  Routine Name:  draw_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


draw_overlays(display, window, list)
Display *display;
Window  window;
XObject	*list;
{
	XObject  *current;

	current = list;
	if (list == NULL)
	   return;

	while (current != NULL)
	{
	   draw_object(display, window, current);
	   current = current->next;
	}
}



/********************************************************
*
*  Routine Name:  erase_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


erase_overlays(widget, list)

Widget	widget;
XObject	*list;
{
	XObject  *current = list;

	if (list == NULL)
	   return;

	while (current->next != NULL)
	{
	   current = current->next;
	}

	while (current != NULL)
	{
	   restore_area(widget, ov_struct->gc, current);
	   current = current->last;
	}
}



/********************************************************
*
*  Routine Name:  refresh_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


void refresh_overlays(widget, clientData, event)
Widget  widget;
caddr_t clientData;
XEvent  *event;
{
	Display	    *display;
	Window	    window;

	XObject	*current;

	if (obj_list == NULL || widget == NULL)
	   return;

	redraw_overlays(widget, obj_list);
}



/********************************************************
*
*  Routine Name: write_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


write_overlays(filename, list)

char	    *filename;
XObject *list;
{
	FILE		*file;
	XObject	*current;
	char		temp[512];


	if (!(file = fopen(filename, "w+")))
	{
           sprintf(temp, "Unable to open file %s for output.", filename);
           xvf_error_wait(temp, "write_overlays", NULL);
	   return;
	}
	fprintf(file,"Khoros overlay file\n");
   
	current = list;
	while (current != NULL)
	{
	   write_object(file, current);
	   current = current->next;
	}
	fclose(file);
}



/********************************************************
*
*  Routine Name: write_image_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


write_image_overlays(filename, list, width, height)

char	*filename;
XObject *list;
int	width, height;
{
	FILE		*file;
	XObject	*current;
	char		temp[512];


	if (!(file = fopen(filename, "w+")))
	{
           sprintf(temp, "Unable to open file %s for output.", filename);
           xvf_error_wait(temp, "write_overlays", NULL);
	   return;
	}
	fprintf(file,"Khoros overlay file\n");
   
	fprintf(file,"#\n# Overlay File created with an image of\n# \
the following dimensions:\n#\n#\twidth   = %d\n#\theight  = %d\n#\n",
			width, height);
	current = list;
	while (current != NULL)
	{
	   write_object(file, current);
	   current = current->next;
	}
	fclose(file);
}



/********************************************************
*
*  Routine Name: read_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


XObject *read_overlays(widget, filename)
Widget	widget;
char	*filename;
{
	FILE	*file;
	XObject	*object;
	char	line[512], temp[512];

	if (!(file = fopen(filename, "r")))
	{
           sprintf(temp, "Unable to open file %s for input.", filename);
           xvf_error_wait(temp, "read_overlays", NULL);
	   return(NULL);
	}

	fgets(line, 30, file);
	cut_new_line(line);

	if (strcmp(line,"Khoros overlay file") != 0)
	{
           sprintf(temp, "File '%s' is not an khoros overlay file.", filename);
           xvf_error_wait(temp, "read_overlays", NULL);
	   return(NULL);
	}
	
	while (feof(file) == FALSE)
	{
	   if ((object = read_object(widget, file)) != NULL)
	   {
	      obj_list = insert_object(widget, obj_list, object);
	   }
	}
	fclose(file);
	return(obj_list);
}



/********************************************************
*
*  Routine Name:  update_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


void update_overlays(widget, clientData, event, dispatch)

Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	unsigned long mask;
	static	 int	xpos, ypos, x, y;
	static   unsigned int width, height;
	int	 tmpx, tmpy, distx, disty, check_motion_event();

	Window	    root;
	int	    wx, wy;
	unsigned    int border_width, depth;

	XObject	    *object;
	Display	    *display = XtDisplay(widget);
	Window	    window   = XtWindow(widget);


	if (event->type == ButtonPress)
	{
	   xpos = event->xbutton.x;
	   ypos = event->xbutton.y;
	   object = find_object(obj_list, xpos, ypos);

	   if (object != edit_obj && obj_list != NULL)
	   {
	      if (edit_obj != NULL)
	      {
	         restore_area(widget, ov_struct->gc, edit_obj);
	         display_object(widget, edit_obj);
	         edit_obj = NULL;
	      }

	      if (object != NULL)
	      {
		 obj_list = raise_object(widget, obj_list, object);
	      }
	   }

	   if (object != NULL)
	   {
	      x  = object->x;
	      y  = object->y;
	      width  = object->width;
	      height = object->height;
	      *dispatch = False;
	   }
	   edit_obj = object;
	}
	else if (event->type == ButtonRelease)
	{
	   if (edit_obj == NULL)
	   {
	      return;
	   }

	   *dispatch = False;
	   if (x != edit_obj->x  || width != edit_obj->width ||
	            y != edit_obj->y  || height != edit_obj->height)
	   {
              if ( !XGetGeometry(display, window, &root, &wx, &wy, 
			&width, &height, &border_width, &depth))
              {
                   xvf_error_wait("Window does not exist.", "update_overlays",
				  NULL);
                   return;
              }
	      x = event->xbutton.x;
	      y = event->xbutton.y;

	      if (x < 0)
		 x = 0;
	      else if (x > width)
		 x = width;

	      if (y < 0)
		 y = 0;
	      else if (y > height)
		 y = height;

	      distx = x - xpos;
	      disty = y - ypos;

	      restore_area(widget, ov_struct->gc, edit_obj);
	      edit_obj = translate_object(widget, edit_obj, distx, disty);
	      display_object(widget, edit_obj);
	   }
	}
	else if (event->type == MotionNotify)
	{
	   if (!event->xmotion.state || edit_obj == NULL)
	      return;

	   *dispatch = False;
	   if (check_motion_event(widget, &tmpx, &tmpy))
	      return;

	   distx = tmpx - xpos;
	   disty = tmpy - ypos;
	   xpos = tmpx;
	   ypos = tmpy;

	   if (distx != 0 || disty != 0)
	   {
	      restore_area(widget, ov_struct->gc, edit_obj);
	      edit_obj = translate_object(widget, edit_obj, distx, disty);
	      display_object(widget, edit_obj);
	   }
	}
}



/********************************************************
*
*  Routine Name:  destroy_overlays
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Stephanie Hallet & Mark Young
*
********************************************************/


destroy_overlays(widget, list)

Widget	widget;
XObject *list;
{
	while (list != NULL)
	{
	   list = delete_object(widget, list, list);
	}
}



/************************************************************
*
*  MODULE NAME: check_motion_event
*
*      PURPOSE: This routine is used to tell if the event is
*		of type MotionNotify.  The routine is used to
*		aid other routines in compressing motion events.
*
*       OUTPUT: returns true if the next event is a MotionNotify
*		for the given window.  Otherwise it queries the
*		mouse and returns the x and y position.
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

int  check_motion_event(widget, x, y)

Widget	widget;
int	*x, *y;
{
	Display	 *display = XtDisplay(widget);
	Window	 window   = XtWindow(widget);

	XEvent   next;
	unsigned int buttons;
	Window   root, child;
	int      rx, ry, wx, wy;
	unsigned int width, height, border_width, depth;


	if (XEventsQueued(display, QueuedAfterReading))
	{
	   XPeekEvent(display, &next);
	   if (next.type == MotionNotify)
	   {
	      if (next.xmotion.window == window)
		 return(TRUE);
	   }
	}

	/*
	 *  Query the pointer to see where we are relative to the overlay
	 *  window.
	 */
	XQueryPointer(display, window, &root, &child, &rx, &ry, x, y, &buttons);

	/*
	 *  Get the geometry of that window so that we might clip the mouse to
	 *  that window.  We do this so that objects cannot be dragged outside
	 *  the window and thus be lost forever.
	 */
        if ( !XGetGeometry(display, window, &root, &wx, &wy, &width,
                           &height, &border_width, &depth))
        {
           xvf_error_wait("Warning! Overlay window does not exist.  This should not be\
 possible .","create_area",NULL);
	   return(TRUE);
        }

	if (*x < 0)
	   *x = 0;
	else if (*x > width)
	   *x = width;

	if (*y < 0)
	   *y = 0;
	else if (*y > height)
	   *y = height;

	return(FALSE);
}
