 /*
  * Khoros: $Id: object.c,v 1.4 1992/01/17 00:43:18 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: object.c,v 1.4 1992/01/17 00:43:18 dkhoros Exp $";
#endif

 /*
  * $Log: object.c,v $
 * Revision 1.4  1992/01/17  00:43:18  dkhoros
 * HellPatch4
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * 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: object.c
   >>>>               
   >>>>   description: contains code to interactively create/edit
   >>>>                 overlays
   >>>>              
   >>>>      routines:
   >>>>			translate_object
   >>>>			resize_object
   >>>>			lower_object
   >>>>			raise_object
   >>>>			insert_object
   >>>>			copy_object
   >>>>			set_object_color
   >>>>			allocate_object_color
   >>>>			display_object
   >>>>			draw_object
   >>>>			read_object
   >>>>			delete_object
   >>>>			find_object
   >>>>			check_line
   >>>>			BOUND
   >>>>			extent_object
   >>>>			check_object
   >>>>			create_object
   >>>>			write_object
   >>>>			cut_new_line
   >>>>              
   >>>> modifications:
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */

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

extern XObject *obj_list;


/********************************************************
*
*  Routine Name: translate_object
*
*       Purpose:
*
*         Input: 
*
*        Output:
*
*     Called By: application program
*
*   Written By:  Mark Young
*
********************************************************/


XObject *translate_object(widget, object, x, y)

Widget	widget;
XObject *object;
int	x, y;
{
	int		i;

	switch (object->type)
	{
	   case LINE:
	   case RECTANGLE:
	   case CIRCLE:
	        object->points[0].x += x;
	        object->points[0].y += y;
	        object->points[1].x += x;
	        object->points[1].y += y;
		break;

	   case TEXT:
	        object->points[0].x += x;
	        object->points[0].y += y;
		break;

	   case POLYGON:
	        for (i = 0; i < object->num; i++)
		{
	           object->points[i].x += x;
	           object->points[i].y += y;
		}
		break;
	}
	object = extent_object(widget, object);
	return(object);
}



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


XObject *resize_object(widget, object, width, height, corner)

Widget	widget;
XObject *object;
int	width, height;
{
	switch (object->type)
	{
	   case LINE:
	   case RECTANGLE:
	        if (corner == 1)
		{
		}
	        else if (corner == 2)
		{
		}
		else if (corner == 3)
		{
		}
		else if (corner == 4)
		{
		}
		break;

	   case TEXT:
	   case POLYGON:
	        return(translate_object(widget, object, width, height));
		break;

	   case CIRCLE:
	        object->points[1].x += width;
	        object->points[1].y += height;
		break;
	}
	object = extent_object(widget, object);
	return(object);
}



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


XObject *lower_object(widget, list, object)
Widget	widget;
XObject *list, *object;
{
	if (object != list)
	{
	   erase_overlays(widget, list);

	   object->last->next = object->next;
	   if (object->next != NULL)
	      object->next->last = object->last;

	   object->last  = list->last;
	   list->last	 = object;
	   object->next  = list;
	   list		 = object;

	   redraw_overlays(widget, list);
	}
	return(list);
}



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


XObject *raise_object(widget, list, object)
Widget	widget;
XObject *list, *object;
{
	XObject *temp = object;

	while (temp->next != NULL)
	    temp = temp->next;

	if (temp != object)
	{
	   erase_overlays(widget, list);

	   object->next->last = object->last;
	   if (object != list)
	      object->last->next = object->next;
	   else
	      list = object->next;

	   object->last = temp;
	   object->next = NULL;
	   temp->next   = object;

	   redraw_overlays(widget, list);
	}
	return(list);
}



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


XObject *insert_object(widget, list, object)
Widget	widget;
XObject *list, *object;
{
	XObject *current;

	/*
	 *  Display the object and then insert it into the display
	 *  list.
	 */
	display_object(widget, object);
	if (list == NULL)
	   return(object);

	current = list;
	while(current->next != NULL)		/* go to end of list */
	{
	   current = current->next;
	}
	current->next = object;
	object->last = current;

	return(list);
}



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


XObject *copy_object(widget, object)

Widget	widget;
XObject *object;
{
	int	i, num;
	XObject *copy;

	if ((copy = create_object(object->type)) == NULL)
	   return(NULL);

	num = copy->num = object->num;
	copy->isfilled   = object->isfilled;
	copy->line_width = object->line_width;

	if (object->text)
	   copy->text = xvf_strcpy(object->text);

	if (object->color)
	   copy->color = xvf_strcpy(object->color);

	if (object->font)
	   copy->font = xvf_strcpy(object->font);

	if (num > 0)
	{
	   if (object->type == POLYGON)
	      copy->points = (XPoint *) XtMalloc(sizeof(XPoint) * num);

	   for (i = 0; i < num; i++)
	   {
	      copy->points[i].x = object->points[i].x + 15;
	      copy->points[i].y = object->points[i].y + 15;
	   }
	}
	copy = extent_object(widget, copy);
	return(copy);
}


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


allocate_object_color(graphics, object)

X3DGraphics *graphics;
XObject *object;
{
	int	 screen;
	Colormap colormap;
	Window   window;
	XWindowAttributes  xwa;

	object->xcolor = (XColor *) XtCalloc(1, sizeof(XColor));
	screen = XDefaultScreen(graphics->display);
	if (object->color == NULL)
	   object->color = VStrcpy("white");

	if (XDefaultDepth(graphics->display, screen) == 1)
	{
	   colormap = XDefaultColormap(graphics->display, screen);
	   if (strcmp(object->color,"white") == 0)
	   {
	      object->xcolor->pixel = WhitePixel(graphics->display, screen);
	      object->xcolor->flags = DoRed | DoBlue | DoGreen;
	      XQueryColor(graphics->display, colormap, object->xcolor);
	   }
	   else
	   {
	      object->xcolor->pixel = BlackPixel(graphics->display, screen);
	      object->xcolor->flags = DoRed | DoBlue | DoGreen;
	      XQueryColor(graphics->display, colormap, object->xcolor);
	   }
	}
	else
	{
	   if (graphics->widget != NULL)
	      window = XtWindow(graphics->widget);
	   else
	      window = graphics->workspace;

	   XGetWindowAttributes(graphics->display, window, &xwa);
	   colormap = xwa.colormap;

	   if (!XParseColor(graphics->display, colormap, object->color,
				object->xcolor))
	   {
	      fprintf(stderr,"Overlay: overlay object has unknown ");
	      fprintf(stderr,"color (%s), using black pixel\n", object->color);
	      object->xcolor->pixel = BlackPixel(graphics->display, screen);
	      object->xcolor->flags = DoRed | DoBlue | DoGreen;
	      XQueryColor(graphics->display, colormap, object->xcolor);
	   }
	   else
	   {
	      if (!XAllocColor(graphics->display, colormap, object->xcolor))
	      {
	         fprintf(stderr,"Overlay: unable to allocate any more color\n");
	         fprintf(stderr,"cells.  Color (%s) not allocated; using black \
pixel\n", object->color);
		 object->xcolor->pixel = BlackPixel(graphics->display, screen);
		 object->xcolor->flags = DoRed | DoBlue | DoGreen;
		 XQueryColor(graphics->display, colormap, object->xcolor);
	      }
	   }
	}
}



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


set_object_color(graphics, gc, object)

X3DGraphics *graphics;
GC	    gc;
XObject	    *object;
{
	if (object->xcolor == NULL)
	   allocate_object_color(graphics, object);

	if (object->isfilled)
	   X3D_draw[graphics->device].fill_color(graphics, object->xcolor,TRUE);
	else
	   X3D_draw[graphics->device].draw_color(graphics, object->xcolor,TRUE);
	XSetForeground(graphics->display, gc, object->xcolor->pixel);
}



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


display_object(widget, object)
Widget	widget;
XObject	*object;
{
	save_area(widget, ov_struct->gc, object);
	draw_object(XtDisplay(widget), XtWindow(widget), object);
}



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


draw_object(display, window, obj)

Display	*display;
Window	window;
XObject *obj;
{
	XFontStruct     *font;
	XPoint		*points;
	Pixel		fg;
	unsigned int    width, height, border_width, depth, line_width;
	int	 	i,x, y, deltax, deltay, start_x, start_y;
	int	 	radius;

	GC		gc, gc_draw, gc_fill;
	Coord		scale;
	Window		root;
	X3DGraphics     *graphics;
    
	if (obj == NULL)
	   return;

	if (!(graphics = _X3D_get_graphics(ov_struct->id)))
        {
	   fprintf (stderr,"X2D_draw_polyline:\n");
	   fprintf (stderr,"\t unknown graphics id\n");
	   return;
        }

	width  = ABS(graphics->X11_xmax - graphics->X11_xmin);
	height = ABS(graphics->X11_ymax - graphics->X11_ymin);
	if (width == 0 || height == 0)
	{
	   XGetGeometry(display, window, &root, &x, &y, &width, &height,
		     &border_width, &depth);
	}

	if (graphics->device == X11)
	{
	   scale.x = 1;
	   scale.y = 1;
	   x = y =0;
	}
	else if (graphics->device == POSTSCR)
	{
	   scale.x = ABS(graphics->POS_xmax - graphics->POS_xmin)/
 		     (float)(width);
	   scale.y = ABS(graphics->POS_ymax - graphics->POS_ymin)/
 		     (float)(height);
	   x = y =0;
	}
	else if (graphics->device == IMPRESS)
	{
	   scale.x = ABS(graphics->IMP_xmax - graphics->IMP_xmin)/
 		     (float)(width);
	   scale.y = ABS(graphics->IMP_ymax - graphics->IMP_ymin)/
 		     (float)(height);
	   x = graphics->IMP_xmin;
	   y = graphics->IMP_ymin;
	}
	else if (graphics->device == HPGL)
	{
	   scale.x = 30000.0/(float)(width);
	   scale.y = 30000.0/(float)(height);
	   x = y =0;
	}

	gc = ov_struct->gc;
	gc_fill = graphics->gc_fill;
	gc_draw = graphics->gc_draw;
	fg = graphics->fg;
	line_width =  graphics->line_width;
	set_object_color(graphics, gc, obj);
	X3D_draw[graphics->device].line_width(graphics, (int) obj->line_width);
	graphics->gc_draw = gc;
	graphics->gc_fill = gc;

	XSetRegion(display, gc, obj->region);
	XSetLineAttributes(display, gc, (unsigned int) obj->line_width,
			   LineSolid, CapButt, JoinBevel);

	points = (XPoint *) malloc( (unsigned)(obj->num) * sizeof(XPoint));
	for (i=0; i < obj->num; i++)
	{
	   points[i].x = (short) (obj->points[i].x * scale.x + 0.5 + x);
	   if(graphics->device == X11)
		points[i].y = (short) (obj->points[i].y * scale.y + y + 0.5);
	   else 
		points[i].y =(short)
			     ((height - obj->points[i].y)*scale.y + y + 0.5);
	}

	switch (obj->type)
	{
	   case LINE:
		X3D_draw[graphics->device].line(graphics, points[0].x,
			points[0].y, points[1].x, points[1].y);
		break;

	   case CIRCLE:
		deltax = ABS((points[0].x - points[1].x ));
		deltay = ABS((points[0].y - points[1].y ));
		radius =  (int) sqrt((double) (SQR(deltax) + SQR(deltay)));

		if (obj->isfilled == FALSE)
		{
		   X3D_draw[graphics->device].arc(graphics, 
			points[0].x, points[0].y, 
			((unsigned int)2*radius),
			((unsigned int)2*radius), 0, 360);
		}
		else
		{
		   X3D_draw[graphics->device].fill_arc(graphics,
			points[0].x, points[0].y,
			((unsigned int)2*radius),
			((unsigned int)2*radius), 0, 360);
		}
		break;

	   case RECTANGLE:
		start_x = MIN(points[0].x, points[1].x );
		if(graphics->device == X11)
		   start_y = MIN(points[0].y, points[1].y );
		else
		   start_y = MAX(points[0].y, points[1].y );
		width   = ABS((points[0].x - points[1].x ));
		height  = ABS((points[0].y - points[1].y ));

		if (obj->isfilled == FALSE)
		   X3D_draw[graphics->device].rectangle(graphics, start_x,
				start_y, width, height);
		else
		   X3D_draw[graphics->device].fill_rectangle(graphics, start_x,
				start_y, width, height);
		break;

	   case TEXT:
		if (obj->text == NULL) return;
		if (obj->font == NULL)
		   font = ov_struct->font;
		else if ((font = XLoadQueryFont(display, obj->font)) == NULL)
		   font = ov_struct->font;

		if(graphics->device == X11)
		{
		   XSetFont(display, gc, font->fid);
		}
		X3D_draw[graphics->device].text(graphics, points[0].x,
			points[0].y, obj->font, obj->text, strlen(obj->text));
		break;

	   case POLYGON:
		if (obj->isfilled == TRUE && obj->num > 2)
		{
		   X3D_draw[graphics->device].fill_polygon(graphics, points,
			obj->num, Complex, CoordModeOrigin, FALSE);
		}
		else
		{
		   X3D_draw[graphics->device].lines(graphics, points, obj->num,
			CoordModeOrigin);
		}
		break;
	}
	free(points);

	XSetClipMask(display, gc, None);
	graphics->gc_draw = gc_draw;
	graphics->gc_fill = gc_fill;
	graphics->fg = fg;
	graphics->line_width = line_width;
}



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

   
XObject *read_object(widget, file)

Widget	widget;
FILE	*file;
{
	int	i, c, type, done = FALSE;
	char	character, command[512];
	XObject	*object;

	do
	{
	   if (fscanf(file, "%[#]", &character) == 1)
	   {
	      /*
	       *  Read to the rest of the comment which should be until
	       *  the end of the line.
	       */
	      do
	      {
	         (void) fgets(dummy, 512, file);
	      } while (VStrlen(dummy) >= 512);
	   }
	   else if (fscanf(file, "%[/]%[*]", &character, &character) == 2)
	   {
	      /*
	       *  Read to the end of the comment.
	       */
	     for (;;)
	     {
	        while ((c = fgetc(file)) != '*' && c != EOF && c != '\0')
			   ;

	        if (c == '*')
	        {
	           if ((c = fgetc(file)) == '/')
		      break;
	        }
	        else
	           break;
	     }
	   }
	   else done = TRUE;
	} while (!done);

	if (fgets(command,512,file) == NULL)
	{
	   return(NULL);
	}
	cut_new_line(command);

	if (strcmp(command,"line") == 0)
	   type = LINE;
	else if (strcmp(command,"circle") == 0)
	   type = CIRCLE;
	else if (strcmp(command,"rectangle") == 0)
	   type = RECTANGLE;
	else if (strcmp(command,"text") == 0)
	   type = TEXT;
	else if (strcmp(command,"polygon") == 0)
	   type = POLYGON;
	else
	{
	   xvf_error_wait("Unknown object type!", "check_object", NULL);
	   return(NULL);
	}

	if ((object = create_object(type)) == NULL)
	   return(NULL);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->num = atoi(command);

	object->points = (XPoint *)malloc(object->num*sizeof(XPoint));

	for (i=0; i<object->num; i++)
	{
	   if(fgets(command,512,file) == NULL)
	      return(NULL);
	   cut_new_line(command);
	   object->points[i].x = atoi(command);

	   if (fgets(command,512,file) == NULL)
	      return(NULL);
	   cut_new_line(command);
	   object->points[i].y = atoi(command);
	}

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->line_width = atoi(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->color = xvf_strcpy(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->isfilled = atoi(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	if (strcmp(command,"*") == 0)
	   object->text = NULL;
	else
	   object->text = xvf_strcpy(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	if (strcmp(command,"*") == 0)
	   object->font = NULL;
	else
	   object->font = xvf_strcpy(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->x = atoi(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->y = atoi(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->width = atoi(command);

	if (fgets(command,512,file) == NULL)
	   return(NULL);
	cut_new_line(command);
	object->height = atoi(command);

	object = check_object(widget, object);
	return(object);
}



/********** routines for deleting an object from the list ***********/

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


XObject *delete_object(widget, list, object)

Widget	widget;
XObject	*list, *object;
{
	/*
	 *  Restore area before deleting the object
	 */
	restore_area(widget, ov_struct->gc, object);

	/* delete object from list */
	if (list == object)
	{
	   list = list->next;
	   if (list != NULL)
	      list->last = NULL;
	}
	else if (object->next == NULL)
	{
	   object->last->next = NULL;
	}
	else
	{
	   object->last->next = object->next;
	   object->next->last = object->last;
	}

	/*
	 *  Free the object and it's associated fields
	 */
	destroy_region(widget, object);
	destroy_area(widget, object);

	if (object->text)  free(object->text);
	if (object->font)  free(object->font);
	if (object->color) free(object->color);
	if (object->num > 0) free(object->points);

	free(object);

	/*  return the list */
	return(list);
}



/********** this section of code is for selecting objects *************/

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



XObject *find_object(list, x, y)

XObject *list;
int	    x,y;
{
   int 		i;
   XObject	*obj;
   int		line, deltax, deltay, radius, eqn;
   XPoint	*points;

   if (list == NULL)	/* no items in list */
      return(NULL);

   /* go to end of list */
   obj = list;
   while (obj->next != NULL)
      obj = obj->next;

   while (obj != NULL)
   {
      /*  see if the point is within the bounding box */
      if (BOUND((obj->x-2),x,(obj->x+obj->width+2)) &&
          BOUND((obj->y-2),y,(obj->y+obj->height+2)))
      {
         line = (obj->line_width + 1) / 2;
         if (line < 5) line = 5;

	 points = obj->points;
         switch (obj->type)
         {
            case TEXT:
		 return(obj);

            case LINE:
		 if (check_line(points[0], points[1], x, y, line))
                 {
                    return(obj);
                 }
		 break;

	    case RECTANGLE:
		 if (obj->isfilled == TRUE)
		    return(obj);
                 else
                 {
                    if (BOUND((points[0].x - line), x, (points[0].x + line)) ||
                        BOUND((points[1].x - line), x, (points[1].x + line)) ||
                        BOUND((points[0].y - line), y, (points[0].y + line)) ||
                        BOUND((points[1].y - line), y, (points[1].y + line)))
                    {
		         return(obj);
                    }
                 }
		 break;

	    case CIRCLE:
	         deltax = ABS((points[0].x - points[1].x ));
                 deltay = ABS((points[0].y - points[1].y ));
		 radius = (int )sqrt((double)(SQR(deltax) + SQR(deltay)));
		 eqn = SQR((x - points[0].x)) + SQR((y - points[0].y));

		 if (obj->isfilled == TRUE)
                 {
		    if (eqn <= SQR(radius))
		       return(obj);
                 }
                 else
                 {
		    if (BOUND(SQR(radius - line), eqn, SQR(radius + line)))
		       return(obj);
                 }
        	 break;

	    case POLYGON:
		 if (obj->isfilled == TRUE)
                 {
		    if (XPointInRegion(obj->region,x,y))
		       return(obj);
		 }
                 else
                 {
                    for (i = 0;  i < obj->num -1; i++)
                    {
                       if (check_line(points[i], points[i+1], x, y, line))
			  return(obj);
		    }
		 }
                 break;
         }
      }
      obj = obj->last;
   }
   return(NULL);
}



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


int check_line(start, end, x, y, line_width)

XPoint	start, end;
int	x, y, line_width;
{
	int deltax, deltay;
	int butx, buty;
	int tmp_y;

	deltax = start.x - end.x ;
	deltay = start.y - end.y ;
	butx = x - start.x ;
	buty = y - start.y ;
	if (BOUND(-4,deltax,4))
	{
	   if (BOUND(-6,butx,6))
	   {
	      return(TRUE);
	   }
	}
	else
	{
	   tmp_y = (deltay * butx) / deltax;
	   if (BOUND((tmp_y - line_width), buty, (tmp_y + line_width)))
	   {
	      return(TRUE);
	   }
	}
	return(FALSE);
}



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


int BOUND(bottom, value, top)

int	bottom, value, top;
{
	if ((bottom <= value) && (value <= top))
	   return(TRUE);
	else
	   return(FALSE);
}






/** calculates the extent of the object given **/

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


XObject *extent_object(widget, obj)

Widget	widget;
XObject *obj;
{
	Display	 *display = XtDisplay(widget);

	int	 i, radius, deltax, deltay;
	int	 line_width = obj->line_width, length;
	int  	 ascent, descent, dir_return;

	XFontStruct *font;
	XCharStruct ov_return;
	XRectangle  rectangle;


	if (line_width < 5)
	   line_width = 5;

	switch (obj->type)
	{
	    case LINE:
	    case RECTANGLE:
	         obj->x = MIN(obj->points[0].x ,obj->points[1].x) - line_width;
	         obj->y = MIN(obj->points[0].y ,obj->points[1].y) - line_width;
	         obj->width = ABS((obj->points[0].x - obj->points[1].x)) 
				+ 2*line_width;
	         obj->height = ABS((obj->points[0].y - obj->points[1].y))
				+ 2*line_width;
	         break;

	    case CIRCLE:
	         deltax = ABS((obj->points[0].x - obj->points[1].x ));
	         deltay = ABS((obj->points[0].y - obj->points[1].y ));
	         radius = (int )sqrt((double)(SQR(deltax) + SQR(deltay)));
	         obj->x = obj->points[0].x - radius - line_width;
	         obj->y = obj->points[0].y - radius - line_width;
	         obj->width = obj->height = 2*(radius+line_width);
	         break;

	    case TEXT:
	         if (obj->font == NULL)
	            font = ov_struct->font;
                 else if ((font = XLoadQueryFont(display, obj->font)) == NULL)
                    font = ov_struct->font;
		 XSetFont(display, ov_struct->gc, font->fid);

		 length = xvf_strlen(obj->text);
		 XTextExtents(font, obj->text, length, &dir_return, &ascent,
			      &descent, &ov_return);

	         obj->x = obj->points[0].x + ov_return.lbearing - 2;
	         obj->y = obj->points[0].y - ov_return.ascent - 2;
		 obj->height = ov_return.ascent + ov_return.descent + 4;
		 obj->width  = ov_return.lbearing + ov_return.rbearing + 4;
	         break;

	    case POLYGON:

		 if (obj->num > 2)
	         {
		    create_region(widget, obj);
		    XClipBox(obj->region, &rectangle);
		 }
		 else
		 {
		    rectangle.x = MIN(obj->points[0].x, obj->points[1].x);
		    rectangle.y = MIN(obj->points[0].y, obj->points[1].y);
		    rectangle.width = ABS(obj->points[1].x - obj->points[0].x);
		    rectangle.height = ABS(obj->points[1].y - obj->points[0].y);
		 }

	         obj->x   =  rectangle.x - line_width;
	         obj->y   =  rectangle.y - line_width;
	         obj->width  = rectangle.width  + 2*line_width;
	         obj->height = rectangle.height + 2*line_width;
	         break;
	}
	create_region(widget, obj);
	return(obj);
}



/* checks to make sure an object has valid contents for insertion */

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


XObject *check_object(widget, obj)

Widget	  widget;
XObject   *obj;
{
	Display	 *display = XtDisplay(widget);
	Window	 window   = XtWindow(widget);

	int 	 stat;
	char	 temp[512];
	Colormap colormap;
	XColor	 xcolor;
	XWindowAttributes  xwa;

	XGetWindowAttributes(display, window, &xwa);
	colormap = xwa.colormap;

	if (obj->points[0].x < 0)
	   obj->points[0].x = 0;

	if (obj->points[0].y < 0)
	   obj->points[0].y = 0;

	if (obj->type != TEXT)
	{
	   if (obj->points[1].x < 0)
	      obj->points[1].x = 0;

	   if (obj->points[1].y < 0)
	      obj->points[1].y = 0;

	   if (obj->width < 0)
	   {
	      XtFree(obj);
	      return(NULL);
	   }
	}

	if (obj->color == NULL)
	{
	   (void) sprintf(temp, "Object color not specified!  Please enter a\
color for the desired object.\n");
	   xvf_error_wait(temp, "check_object", NULL);
	   XtFree(obj); return(NULL);
	}

	stat = XParseColor(display, colormap, obj->color, &xcolor);
	if ((stat == BadAlloc) || (stat == BadColor) || (stat == BadName))
	{
	   (void) sprintf(temp, "Unknown color! (%s) is not a valid color.\n",
			  obj->color);
	   xvf_error_wait(temp, "check_object", NULL);
	   XtFree(obj); return(NULL);
	}

	if (obj->type == TEXT)
	{
	   if (obj->text == NULL)
	   {
	      (void) sprintf(temp, "No text specified!  Please enter text for \
the text object.\n");
	      xvf_error_wait(temp, "check_object", NULL);
	      XtFree(obj); return(NULL);
	   }

	   if (obj->font == NULL)
	   {
	      (void) sprintf(temp, "No font specified!  Please enter a font for\
 the text object.\n");
	      xvf_error_wait(temp, "check_object", NULL);
	      XtFree(obj); return(NULL);
	   }

	   if (XLoadQueryFont(display, obj->font) == NULL)
	   {
	      (void) sprintf(temp, "Unknown font! (%s) is not a valid font, \
please enter a new font name for the text object. (Note:  use the 'xlsfonts' \
command for a list of known fonts for your server).\n", obj->font);
	      xvf_error_wait(temp, "check_object", NULL);
	      XtFree(obj); return(NULL);
	   }
	}

	obj = extent_object(widget, obj);
	return(obj);
}



/**  create object routine  **/

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


XObject *create_object(type)

int	type;
{
	int	 num_points;
	XObject  *object;

	if (!(object = (XObject *) malloc(sizeof(XObject))))
	{
	   xvf_error_wait("malloc failed in overlays", "create_object", NULL);
	   return(NULL);
	}
	object->type = type;
	object->next = object->last  = NULL;
	object->text = object->color = object->font = NULL;
	object->xcolor = NULL;

	object->line_width = 0;
	object->num	   = 0;
	object->width	   = 0;
	object->height	   = 0;
	object->area	   = NULL;
	object->image	   = NULL;
	object->region	   = NULL;
	object->isfilled   = FALSE;

	switch (type)
	{
	   case TEXT:
		num_points = 1;
		break;

	   case LINE:
	   case ARROW:
	   case CIRCLE:
	   case ELLIPSE:
	   case RECTANGLE:
		num_points = 2;
		break;

	   case POLYGON:
	   case IMAGE:
		num_points = 0;
		break;

	   default:
		fprintf(stderr, "unknown object type\n");
		return(NULL);
	}

	if (num_points != 0)
	{
	   if(!(object->points = (XPoint *)malloc(num_points * sizeof(XPoint))))
	   {
	      fprintf(stderr,"Overlay: malloc failed in overlay\n");
	      free(object); return(NULL);
	   }  
	   object->num = num_points;
	}
	else
	   object->points = NULL;

	return(object);
}



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


write_object(file, obj)

FILE		*file;
XObject	*obj;
{
	int i;

	switch (obj->type)
	{
	   case LINE:
		fprintf(file,"line\n");
		break;

	   case RECTANGLE:
		fprintf(file,"rectangle\n");
		break;

	   case CIRCLE:
		fprintf(file,"circle\n");
		break;

	   case TEXT:
		fprintf(file,"text\n");
		break;

	   case POLYGON:
		fprintf(file,"polygon\n");
		break;
	}
	fprintf(file,"%d\n",obj->num);
	for (i = 0; i < obj->num; i++)
	    fprintf(file,"%d\n%d\n",obj->points[i].x,obj->points[i].y);

	fprintf(file,"%d\n",obj->line_width);
	fprintf(file,"%s\n",obj->color);
	fprintf(file,"%d\n",obj->isfilled);
	if (obj->type == TEXT)
	{
	   fprintf(file,"%s\n",obj->text);
	   fprintf(file,"%s\n",obj->font);
	}
	else
	{
	   fprintf(file,"*\n");
	   fprintf(file,"*\n");
	}
	fprintf(file,"%d\n%d\n", obj->x, obj->y);
	fprintf(file,"%d\n%d\n", obj->width, obj->height);
}



/******** this section reads in an overlay file into an object_list ********/

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


cut_new_line(command)

char	*command;
{
	int i = 0;

	while (command[i] != '\n')
	   i++;

	command[i] = command[i+1];
}




/************************************************************
*
*  MODULE NAME: place_object
*
*      PURPOSE: 
*
*       OUTPUT: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

#define  MaxPoints 500

place_object(widget, object, position)
Widget	widget;
XObject	*object;
int	position;
{
	unsigned long mask;


	if (object->type == POLYGON || object->type == IMAGE)
	   object->points = (XPoint *) XtMalloc(sizeof(XPoint) * MaxPoints);

	if ((object = check_object(widget, object)) == NULL)
	{
	   return;
	}

	/*
	 *  If the user is currently editing an object, then delete
	 *  the edit before creating the object.
	 */
	if (edit_obj != NULL)
	{
	   restore_area(widget, ov_struct->gc, edit_obj);
	   display_object(widget, edit_obj);
	   edit_obj = NULL;
	}

	if (position == MOUSE)
	{
	   /*
	    *  Add the event handler that will be responsible placing the
	    *  object.
	    */
	   object->num = 0;
	   mask = ButtonPressMask | PointerMotionHintMask | PointerMotionMask;
	   XtInsertEventHandler(widget, mask, FALSE, update_object,
			(char*) object, XtListHead);
	}
	else
	{
	   obj_list = insert_object(widget, obj_list, object);
	}
}



/************************************************************
*
*  MODULE NAME: update_object
*
*      PURPOSE: 
*
*       OUTPUT: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

void update_object(widget, clientData, event, dispatch)

Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	XPoint	 *points;
	XObject  *object = (XObject *) clientData;
	int	 x, y, i, check_motion_event();
	unsigned long mask;

	i      = object->num;
	points = object->points;
	*dispatch = False;

	if (event->type == ButtonPress)
	{
	   points[i].x = event->xbutton.x;
	   points[i].y = event->xbutton.y;
	   object->num++;

	   if (object->type != TEXT && object->num == 1)
	   {
	      return;
	   }
	   else if (object->type == POLYGON || object->type == IMAGE)
	   {
	      x = points[i -1].x - points[i].x;
	      y = points[i -1].y - points[i].y;
	      if ((x != 0 || y != 0) && (i < MaxPoints))
	      {
	         return;
	      }
	   }
	}
	else
	{
	   if (i == 0 && object->type != TEXT)
	      return;

	   if (!check_motion_event(widget, &x, &y))
	   {
	      restore_area(widget, ov_struct->gc, object);

              points[i].x = x;
              points[i].y = y;
	      object->num++;
	      object = extent_object(widget, object);
	      create_area(widget, object);
	      display_object(widget, object);
	      object->num--;
	    }
	    return;
	}

	/*
	 *  Remove the event handler, now that we have finished placing the
	 *  newly created object.
	 */
	XtRemoveEventHandler(widget, XtAllEvents, TRUE, update_object,
			     (char *) object);


	if ((object->type == POLYGON || object->type == IMAGE) &&
	     object->num < MaxPoints)
	{
	   object->points = (XPoint *) XtMalloc(sizeof(XPoint)*object->num);
	   for (i = 0; i < object->num; i++)
	       object->points[i] = points[i];

	   free(points);
	}

	restore_area(widget, ov_struct->gc, object);
	if ((object = check_object(widget, object)) != NULL)
	{
	   obj_list = insert_object(widget, obj_list, object);
	}
}
