/*
 * Author:      William Chia-Wei Cheng (william@cs.ucla.edu)
 *
 * Copyright (C) 1990-1996, William Chia-Wei Cheng.
 *
 * Permission limited to the use, copy, display, distribute without
 * charging for a fee, and produce derivative works of "tgif" and
 * its documentation for not-for-profit purpose is hereby granted by
 * the Author, provided that the above copyright notice appears in
 * all copies made of "tgif" and that both the copyright notice
 * and this permission notice appear in supporting documentation,
 * and that the name of the Author not be used in advertising or
 * publicity pertaining to distribution of the software without
 * specific, written prior permission.  The Author makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied
 * warranty.  All other rights (including, but not limited to, the
 * right to sell "tgif", the right to sell derivative works of
 * "tgif", and the right to distribute "tgif" for a fee) are
 * reserved by the Author.
 *
 * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL THE AUTHOR 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.
 */
#ifndef lint
static char RCSid[] =
      "@(#)$Header: /n/opus/u/guest/william/src/tgif/v3/RCS/obj.c,v 3.1 1996/05/12 05:17:21 william Exp $";
#endif

#include <stdio.h>
#include <math.h>
#include <X11/Xlib.h>
#include "const.h"
#include "types.h"

#include "arc.e"
#include "attr.e"
#include "auxtext.e"
#include "box.e"
#include "cmd.e"
#include "cursor.e"
#include "group.e"
#include "msg.e"
#ifndef _NO_EXTERN
#include "obj.e"
#endif
#include "oval.e"
#include "page.e"
#include "poly.e"
#include "polygon.e"
#include "rcbox.e"
#include "rect.e"
#include "setup.e"
#include "spline.e"
#include "stretch.e"
#include "text.e"
#include "xbitmap.e"
#include "xpixmap.e"

struct ObjRec	* topObj = NULL, * botObj = NULL;

void AddObj (PrevPtr, NextPtr, ObjPtr)
   struct ObjRec	* PrevPtr, * NextPtr, * ObjPtr;
   /* add ObjPtr between PrevPtr and NextPtr */
{
   ObjPtr->prev = PrevPtr;
   ObjPtr->next = NextPtr;

   if (PrevPtr == NULL)
      curPage->top = topObj = ObjPtr;
   else
      PrevPtr->next = ObjPtr;

   if (NextPtr == NULL)
      curPage->bot = botObj = ObjPtr;
   else
      NextPtr->prev = ObjPtr;
}

void UnlinkObj (ObjPtr)
   struct ObjRec	*ObjPtr;
{
   if (topObj == ObjPtr)
      curPage->top = topObj = ObjPtr->next;
   else
      ObjPtr->prev->next = ObjPtr->next;

   if (botObj == ObjPtr)
      curPage->bot = botObj = ObjPtr->prev;
   else
      ObjPtr->next->prev = ObjPtr->prev;
}

void FreeObj (ObjPtr)
   register struct ObjRec	* ObjPtr;
{
   if (ObjPtr->ctm != NULL) {
      free(ObjPtr->ctm);
      ObjPtr->ctm = NULL;
   }
   switch (ObjPtr->type) {
   case OBJ_POLY: DelAllAttrs(ObjPtr->fattr); FreePolyObj(ObjPtr); break;
   case OBJ_BOX: DelAllAttrs(ObjPtr->fattr); FreeBoxObj(ObjPtr); break;
   case OBJ_OVAL: DelAllAttrs(ObjPtr->fattr); FreeOvalObj(ObjPtr); break;
   case OBJ_TEXT: FreeTextObj(ObjPtr); break;
   case OBJ_POLYGON: DelAllAttrs(ObjPtr->fattr); FreePolygonObj(ObjPtr); break;
   case OBJ_ARC: DelAllAttrs(ObjPtr->fattr); FreeArcObj(ObjPtr); break;
   case OBJ_RCBOX: DelAllAttrs(ObjPtr->fattr); FreeRCBoxObj(ObjPtr); break;
   case OBJ_XBM: DelAllAttrs(ObjPtr->fattr); FreeXBmObj(ObjPtr); break;
   case OBJ_XPM: DelAllAttrs(ObjPtr->fattr); FreeXPmObj(ObjPtr); break;
   case OBJ_SYM:
   case OBJ_ICON:
   case OBJ_GROUP: DelAllAttrs(ObjPtr->fattr); FreeGroupObj(ObjPtr); break;
   }
}

void DelObj (ObjPtr)
   struct ObjRec	*ObjPtr;
{
   if (topObj == ObjPtr)
      curPage->top = topObj = ObjPtr->next;
   else
      ObjPtr->prev->next = ObjPtr->next;

   if (botObj == ObjPtr)
      curPage->bot = botObj = ObjPtr->prev;
   else
      ObjPtr->next->prev = ObjPtr->prev;

   FreeObj (ObjPtr);
}

void DelAllObj ()
{
   register struct ObjRec	* ptr, * next_obj;

   if (topObj == NULL) return;

   for (ptr = topObj; ptr != NULL; ptr = next_obj)
   {
      next_obj = ptr->next;
      FreeObj (ptr);
   }
   if (curPage != NULL) curPage->top = curPage->bot = NULL;
   topObj = botObj = NULL;
}

void AdjObjOBBox(ObjPtr)
   struct ObjRec *ObjPtr;
{
   struct ObjRec *obj_ptr;
   int oltx, olty, orbx, orby;

   switch (ObjPtr->type) {
   case OBJ_ARC:
      CalcArcOBBox(ObjPtr);
      break;
   case OBJ_TEXT:
      switch (ObjPtr->detail.t->rotate) {
      case ROTATE0:
      case ROTATE180:
         SetTextOBBox(ObjPtr, ObjPtr->detail.t->just,
               ObjPtr->obbox.rbx-ObjPtr->obbox.ltx,
               ObjPtr->obbox.rby-ObjPtr->obbox.lty,
               ObjPtr->detail.t->min_lbearing,
               ObjPtr->detail.t->max_rextra, ObjPtr->detail.t->rotate);
         break;
      case ROTATE90:
      case ROTATE270:
         SetTextOBBox(ObjPtr, ObjPtr->detail.t->just,
               ObjPtr->obbox.rby-ObjPtr->obbox.lty,
               ObjPtr->obbox.rbx-ObjPtr->obbox.ltx,
               ObjPtr->detail.t->min_lbearing,
               ObjPtr->detail.t->max_rextra, ObjPtr->detail.t->rotate);
         break;
      }
      break;
   case OBJ_SYM:
   case OBJ_GROUP:
   case OBJ_ICON:
      obj_ptr = ObjPtr->detail.r->last;
      oltx = obj_ptr->obbox.ltx; olty = obj_ptr->obbox.lty;
      orbx = obj_ptr->obbox.rbx; orby = obj_ptr->obbox.rby;
      for (obj_ptr = obj_ptr->prev; obj_ptr != NULL; obj_ptr = obj_ptr->prev) {
         if (obj_ptr->obbox.ltx < oltx) oltx = obj_ptr->obbox.ltx;
         if (obj_ptr->obbox.lty < olty) olty = obj_ptr->obbox.lty;
         if (obj_ptr->obbox.rbx > orbx) orbx = obj_ptr->obbox.rbx;
         if (obj_ptr->obbox.rby > orby) orby = obj_ptr->obbox.rby;
      }
      ObjPtr->obbox.ltx = oltx; ObjPtr->obbox.lty = olty;
      ObjPtr->obbox.rbx = orbx; ObjPtr->obbox.rby = orby;
      break;
   }
}

void AdjObjBBox(ObjPtr)
   struct ObjRec *ObjPtr;
{
   register int w=0;
   register struct ObjRec *obj_ptr;
   register struct AttrRec *attr_ptr;
   int ltx, lty, rbx, rby;

   AdjObjOBBox(ObjPtr);

   switch (ObjPtr->type) {
   case OBJ_POLY:
      CalcPolyBBox(ObjPtr);
      break;
   case OBJ_ARC:
      CalcArcBBox(ObjPtr, ObjPtr->obbox, &(ObjPtr->bbox));
      break;
   case OBJ_POLYGON: w = HALF_W(ObjPtr->detail.g->width); break;
   case OBJ_BOX: w = HALF_W(ObjPtr->detail.b->width); break;
   case OBJ_OVAL: w = HALF_W(ObjPtr->detail.o->width); break;
   case OBJ_RCBOX: w = HALF_W(ObjPtr->detail.rcb->width); break;
   case OBJ_XBM: break;
   case OBJ_XPM: break;

   case OBJ_SYM:
   case OBJ_GROUP:
   case OBJ_ICON:
      obj_ptr = ObjPtr->detail.r->last;
      ltx = obj_ptr->bbox.ltx; lty = obj_ptr->bbox.lty;
      rbx = obj_ptr->bbox.rbx; rby = obj_ptr->bbox.rby;
      for (obj_ptr = obj_ptr->prev; obj_ptr != NULL; obj_ptr = obj_ptr->prev) {
         if (obj_ptr->bbox.ltx < ltx) ltx = obj_ptr->bbox.ltx;
         if (obj_ptr->bbox.lty < lty) lty = obj_ptr->bbox.lty;
         if (obj_ptr->bbox.rbx > rbx) rbx = obj_ptr->bbox.rbx;
         if (obj_ptr->bbox.rby > rby) rby = obj_ptr->bbox.rby;
      }
      ObjPtr->bbox.ltx = ltx; ObjPtr->bbox.lty = lty;
      ObjPtr->bbox.rbx = rbx; ObjPtr->bbox.rby = rby;
      break;
   }
   switch (ObjPtr->type) {
   case OBJ_POLY:
   case OBJ_ARC:
      break;
   case OBJ_POLYGON:
   case OBJ_BOX:
   case OBJ_OVAL:
   case OBJ_RCBOX:
   case OBJ_XBM:
   case OBJ_XPM:
      ObjPtr->bbox.ltx = ObjPtr->obbox.ltx - w;
      ObjPtr->bbox.lty = ObjPtr->obbox.lty - w;
      ObjPtr->bbox.rbx = ObjPtr->obbox.rbx + w;
      ObjPtr->bbox.rby = ObjPtr->obbox.rby + w;
      break;
   case OBJ_TEXT:
      switch (ObjPtr->detail.t->rotate) {
      case ROTATE0:
      case ROTATE180:
         SetTextBBox(ObjPtr, ObjPtr->detail.t->just,
               ObjPtr->obbox.rbx-ObjPtr->obbox.ltx,
               ObjPtr->obbox.rby-ObjPtr->obbox.lty,
               ObjPtr->detail.t->min_lbearing,
               ObjPtr->detail.t->max_rextra, ObjPtr->detail.t->rotate);
         break;
      case ROTATE90:
      case ROTATE270:
         SetTextBBox(ObjPtr, ObjPtr->detail.t->just,
               ObjPtr->obbox.rby-ObjPtr->obbox.lty,
               ObjPtr->obbox.rbx-ObjPtr->obbox.ltx,
               ObjPtr->detail.t->min_lbearing,
               ObjPtr->detail.t->max_rextra, ObjPtr->detail.t->rotate);
         break;
      }
      break;
   }

   attr_ptr = ObjPtr->fattr;

   ltx = ObjPtr->bbox.ltx;
   lty = ObjPtr->bbox.lty;
   rbx = ObjPtr->bbox.rbx;
   rby = ObjPtr->bbox.rby;
   for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
      if (attr_ptr->shown) {
         if (attr_ptr->obj->bbox.ltx < ltx) ltx = attr_ptr->obj->bbox.ltx;
         if (attr_ptr->obj->bbox.lty < lty) lty = attr_ptr->obj->bbox.lty;
         if (attr_ptr->obj->bbox.rbx > rbx) rbx = attr_ptr->obj->bbox.rbx;
         if (attr_ptr->obj->bbox.rby > rby) rby = attr_ptr->obj->bbox.rby;
      }
   }
   ObjPtr->bbox.ltx = ltx;
   ObjPtr->bbox.lty = lty;
   ObjPtr->bbox.rbx = rbx;
   ObjPtr->bbox.rby = rby;

   if (ObjPtr->type == OBJ_SYM) {
      if (ObjPtr->obbox.ltx-QUARTER_INCH < ObjPtr->bbox.ltx) {
         ObjPtr->bbox.ltx = ObjPtr->obbox.ltx - QUARTER_INCH;
      }
      if (ObjPtr->obbox.lty-QUARTER_INCH < ObjPtr->bbox.lty) {
         ObjPtr->bbox.lty = ObjPtr->obbox.lty - QUARTER_INCH;
      }
      if (ObjPtr->obbox.rbx+QUARTER_INCH > ObjPtr->bbox.rbx) {
         ObjPtr->bbox.rbx = ObjPtr->obbox.rbx + QUARTER_INCH;
      }
      if (ObjPtr->obbox.rby+QUARTER_INCH > ObjPtr->bbox.rby) {
         ObjPtr->bbox.rby = ObjPtr->obbox.rby + QUARTER_INCH;
      }
   }
}

static
void InvalidateObjCache(ObjPtr)
   struct ObjRec *ObjPtr;
{
   int w, h, num_rows, num_cols;
   struct TextRec *text_ptr;
   struct XBmRec *xbm_ptr;
   struct XPmRec *xpm_ptr;
   struct ObjRec *obj_ptr;

   w = ObjPtr->obbox.rbx - ObjPtr->obbox.ltx;
   h = ObjPtr->obbox.rby - ObjPtr->obbox.lty;
   switch (ObjPtr->type) {
   case OBJ_POLY:
      if (NeedsToCachePolyObj(ObjPtr)) {
         struct PolyRec *poly_ptr=ObjPtr->detail.p;

         if (poly_ptr->rotated_vlist != NULL) free(poly_ptr->rotated_vlist);
         if (poly_ptr->rotated_asvlist != NULL) {
            free(poly_ptr->rotated_asvlist);
         }
         poly_ptr->rotated_vlist = poly_ptr->rotated_asvlist = NULL;
         poly_ptr->rotated_n = poly_ptr->rotated_asn = 0;
      }
      break;
   case OBJ_BOX: break;

   case OBJ_OVAL:
      /* atan2(61/112) = 0.498721 rad = 28.5746 deg */
      if (NeedsToCacheOvalObj(ObjPtr)) {
         struct OvalRec *oval_ptr=ObjPtr->detail.o;

         if (oval_ptr->rotated_vlist != NULL) free(oval_ptr->rotated_vlist);
         oval_ptr->rotated_vlist = NULL;
         oval_ptr->rotated_n = 0;
      }
      break;
   case OBJ_POLYGON:
      if (NeedsToCachePolygonObj(ObjPtr)) {
         struct PolygonRec *polygon_ptr=ObjPtr->detail.g;

         if (polygon_ptr->rotated_vlist != NULL) {
            free(polygon_ptr->rotated_vlist);
         }
         polygon_ptr->rotated_vlist = NULL;
         polygon_ptr->rotated_n = 0;
      }
      break;
   case OBJ_ARC:
      if (NeedsToCacheArcObj(ObjPtr)) {
         struct ArcRec *arc_ptr=ObjPtr->detail.a;

         if (arc_ptr->rotated_vlist != NULL) free(arc_ptr->rotated_vlist);
         if (arc_ptr->rotated_asvlist != NULL) free(arc_ptr->rotated_asvlist);
         arc_ptr->rotated_vlist = arc_ptr->rotated_asvlist = NULL;
         arc_ptr->rotated_n = arc_ptr->rotated_asn = 0;
      }
      break;
   case OBJ_RCBOX:
      if (NeedsToCacheRCBoxObj(ObjPtr)) {
         struct RCBoxRec *rcbox_ptr=ObjPtr->detail.rcb;

         if (rcbox_ptr->rotated_vlist != NULL) free(rcbox_ptr->rotated_vlist);
         rcbox_ptr->rotated_vlist = NULL;
         rcbox_ptr->rotated_n = 0;
      }
      break;
   case OBJ_TEXT:
      text_ptr = ObjPtr->detail.t;
      if (NeedsToCacheTextObj(ObjPtr)) {
         if (text_ptr->cached_bitmap != None) {
            XFreePixmap(mainDisplay, text_ptr->cached_bitmap);
         }
         text_ptr->cached_bitmap = None;
         if (zoomScale != 0) {
            text_ptr->cached_zoom = 0;
         } else {
            text_ptr->cached_rotate = INVALID;
         }
      }
      break;
   case OBJ_XBM:
      xbm_ptr = ObjPtr->detail.xbm;
      num_cols = (zoomedIn) ? (w<<zoomScale) : (w>>zoomScale);
      num_rows = (zoomedIn) ? (h<<zoomScale) : (h>>zoomScale);
      if (NeedsToCacheXBmObj(ObjPtr)) {
         if (xbm_ptr->cached_bitmap != None) {
            XFreePixmap(mainDisplay, xbm_ptr->cached_bitmap);
         }
         xbm_ptr->cached_bitmap = None;
         if (zoomScale != 0) {
            xbm_ptr->cached_zoom = 0;
         } else {
            xbm_ptr->cached_rotate = INVALID;
         }
      }
      break;
   case OBJ_XPM:
      xpm_ptr = ObjPtr->detail.xpm;
      num_cols = (zoomedIn) ? (w<<zoomScale) : (w>>zoomScale);
      num_rows = (zoomedIn) ? (h<<zoomScale) : (h>>zoomScale);
      if (NeedsToCacheXPmObj(ObjPtr)) {
         if (xpm_ptr->cached_pixmap != None) {
            XFreePixmap(mainDisplay, xpm_ptr->cached_pixmap);
         }
         xpm_ptr->cached_pixmap = None;
         if (xpm_ptr->cached_bitmap != None) {
            XFreePixmap(mainDisplay, xpm_ptr->cached_bitmap);
         }
         xpm_ptr->cached_bitmap = None;
         if (xpm_ptr->clip_mask != None) {
            XFreePixmap(mainDisplay, xpm_ptr->clip_mask);
         }
         xpm_ptr->clip_mask = None;
         if (zoomScale != 0) {
            xpm_ptr->cached_zoom = 0;
         } else {
            xpm_ptr->cached_rotate = INVALID;
         }
         xpm_ptr->cached_color = (-1);
      }
      break;

   case OBJ_SYM:
   case OBJ_ICON:
   case OBJ_GROUP:
      for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
            obj_ptr=obj_ptr->next) {
         InvalidateObjCache(obj_ptr);
      }
      break;
   }
}

void AdjObjCache (ObjPtr)
   struct ObjRec	* ObjPtr;
{
   register struct ObjRec *obj_ptr;
   register struct AttrRec *attr_ptr;

   GetTransformedOBBoxOffsetVs(ObjPtr, ObjPtr->rotated_obbox);
   switch (ObjPtr->type) {
   case OBJ_SYM:
   case OBJ_ICON:
   case OBJ_GROUP:
      for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
            obj_ptr=obj_ptr->next) {
         AdjObjCache(obj_ptr);
      }
      break;
   default:
      InvalidateObjCache(ObjPtr);
      break;
   }
   for (attr_ptr=ObjPtr->fattr; attr_ptr!=NULL; attr_ptr=attr_ptr->next) {
      if (attr_ptr->shown) {
         AdjObjCache(attr_ptr->obj);
      }
   }
}

void AdjCaches ()
{
   register struct ObjRec *obj_ptr, *ptr;
   register struct AttrRec *attr_ptr;

   if (topObj == NULL) return;

   for (obj_ptr = topObj; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
      switch (obj_ptr->type) {
      case OBJ_TEXT:
      case OBJ_XBM:
      case OBJ_XPM:
         AdjObjCache(obj_ptr);
         break;
      case OBJ_SYM:
      case OBJ_ICON:
      case OBJ_GROUP:
         for (ptr=obj_ptr->detail.r->first; ptr != NULL; ptr=ptr->next) {
            AdjObjCache(ptr);
         }
         break;
      }
      for (attr_ptr=obj_ptr->fattr; attr_ptr!=NULL; attr_ptr=attr_ptr->next) {
         if (attr_ptr->shown) {
            AdjObjCache(attr_ptr->obj);
         }
      }
   }
}

static int dontAutoRetractArrow=FALSE;

#define RETREAT (0.8)

static
void AdjArcCache(ObjPtr)
   struct ObjRec *ObjPtr;
{
   struct ArcRec *arc_ptr=ObjPtr->detail.a;
   int a_angle1, a_angle2;

   if (arc_ptr->style == LS_PLAIN) return;

   a_angle1 = arc_ptr->angle1;
   a_angle2 = arc_ptr->angle2;
   GetArcArrowInfo(ObjPtr, NULL, NULL, NULL, &a_angle1,
         NULL, NULL, NULL, &a_angle2);
   arc_ptr->a_angle1 = a_angle1;
   arc_ptr->a_angle2 = a_angle2;
}

static
void AdjPolyCache(ObjPtr)
   struct ObjRec *ObjPtr;
{
   struct PolyRec *poly_ptr=ObjPtr->detail.p;

   if (poly_ptr->rotated_vlist != NULL) {
      free(poly_ptr->rotated_vlist);
   }
   if (poly_ptr->rotated_asvlist != NULL) {
      free(poly_ptr->rotated_asvlist);
   }
   poly_ptr->rotated_vlist = poly_ptr->rotated_asvlist = NULL;
   poly_ptr->rotated_n = poly_ptr->rotated_asn = 0;

   switch (poly_ptr->curved) {
   case LT_STRAIGHT:
   case LT_SPLINE:
      if (poly_ptr->svlist != NULL) {
         free(poly_ptr->svlist);
      }
      poly_ptr->svlist = MakeMultiSplinePolyVertex(
            &(poly_ptr->sn), poly_ptr->smooth,
            drawOrigX, drawOrigY,
            poly_ptr->n, poly_ptr->vlist);
      break;
   case LT_INTSPLINE:
      if (poly_ptr->svlist != NULL) {
         free(poly_ptr->svlist);
      }
      if (poly_ptr->intvlist != NULL) {
         free(poly_ptr->intvlist);
      }
      if (!undoingOrRedoing && !dontAutoRetractArrow) {
         if (AutoRetractedArrowAttr(ObjPtr, FALSE)) {
            struct PolyRec *poly_ptr=poly_ptr;
            int new_x=0, new_y=0;
            IntPoint *v=NULL;

            if (poly_ptr->n != 3) {
               int x0, y0, x2, y2;

               v = poly_ptr->vlist;
               x0 = v[0].x; y0 = v[0].y;
               x2 = v[poly_ptr->n-1].x; y2 = v[poly_ptr->n-1].y;
               if (poly_ptr->smooth != NULL) free(poly_ptr->smooth);
               poly_ptr->smooth = (char*)malloc(4*sizeof(char));
               if (poly_ptr->smooth == NULL) FailAllocMessage();
               poly_ptr->smooth[0] = poly_ptr->smooth[1] =
                     poly_ptr->smooth[2] = FALSE;
               if (poly_ptr->vlist != NULL) free(poly_ptr->vlist);
               poly_ptr->vlist = (IntPoint*)malloc(4*sizeof(IntPoint));
               if (poly_ptr->vlist == NULL) FailAllocMessage();
               poly_ptr->vlist[0].x = x0; poly_ptr->vlist[0].y = y0;
               poly_ptr->vlist[2].x = x2; poly_ptr->vlist[2].y = y2;
               poly_ptr->n = 3;
            }
            v = poly_ptr->vlist;
            CalcAutoRetractedArrowAttrBend(poly_ptr->style,
                  v[0].x, v[0].y, v[2].x, v[2].y, &new_x, &new_y);
            v[1].x = new_x;
            v[1].y = new_y;
         }
      }
      poly_ptr->svlist = MakeIntSplinePolyVertex(
            &(poly_ptr->sn), &(poly_ptr->intn),
            &(poly_ptr->intvlist), drawOrigX, drawOrigY,
            poly_ptr->n, poly_ptr->vlist);
      break;
   }
   if (poly_ptr->style != LS_PLAIN) {
      IntPoint		*v=poly_ptr->vlist, *v_copy;
      int			aw = poly_ptr->aw;
      int			i, n = poly_ptr->n, dx, dy;
      int			retracted_arrow;
      double		len, sin_val, cos_val;

      retracted_arrow = (RetractedArrowAttr(ObjPtr) ||
            AutoRetractedArrowAttr(ObjPtr, TRUE));
      if (poly_ptr->asvlist != NULL) {
         free(poly_ptr->asvlist);
      }
      if (poly_ptr->curved != LT_INTSPLINE) {
         v = poly_ptr->vlist;
         n = poly_ptr->n;
      } else {
         v = poly_ptr->intvlist;
         n = poly_ptr->intn;
      }
      v_copy = (IntPoint *)malloc((n+1)*sizeof(IntPoint));
      if (v_copy == NULL) FailAllocMessage();
      for (i = 0; i < n; i++) {
         v_copy[i].x = v[i].x;
         v_copy[i].y = v[i].y;
      }
      if (aw == 0) aw = 1;

      dx = v_copy[1].x - v_copy[0].x;
      dy = v_copy[1].y - v_copy[0].y;
      if (!retracted_arrow && (poly_ptr->style & LS_LEFT) &&
            (dx != 0 || dy != 0)) {
         len = (double)sqrt((double)(((double)dx)*((double)dx) +
               ((double)dy)*((double)dy)));
         sin_val = ((double)dy)/len;
         cos_val = ((double)dx)/len;

         v_copy[0].x = round(v_copy[0].x+RETREAT*aw*cos_val);
         v_copy[0].y = round(v_copy[0].y+RETREAT*aw*sin_val);
      }
      dx = v_copy[n-1].x - v_copy[n-2].x;
      dy = v_copy[n-1].y - v_copy[n-2].y;
      if (!retracted_arrow && (poly_ptr->style & LS_RIGHT) &&
            (dx != 0 || dy != 0)) {
         len = (double)sqrt((double)(((double)dx)*((double)dx) +
               ((double)dy)*((double)dy)));
         sin_val = ((double)dy)/len;
         cos_val = ((double)dx)/len;

         v_copy[n-1].x = round(v_copy[n-1].x-RETREAT*aw*cos_val);
         v_copy[n-1].y = round(v_copy[n-1].y-RETREAT*aw*sin_val);
      }
      switch (poly_ptr->curved) {
      case LT_STRAIGHT:
      case LT_SPLINE:
         poly_ptr->asvlist = MakeMultiSplinePolyVertex(
               &(poly_ptr->asn), poly_ptr->smooth,
               drawOrigX, drawOrigY,
               n, v_copy);
         break;
      case LT_INTSPLINE:
         poly_ptr->asvlist = MakeSplinePolyVertex(&(poly_ptr->asn),
               drawOrigX, drawOrigY, n, v_copy);
         break;
      }
      free(v_copy);
   }
}

static
void AdjPolygonCache(ObjPtr)
   struct ObjRec *ObjPtr;
{
   struct PolygonRec *polygon_ptr=ObjPtr->detail.g;

   if (polygon_ptr->rotated_vlist != NULL) {
      free(polygon_ptr->rotated_vlist);
   }
   polygon_ptr->rotated_vlist = NULL;
   polygon_ptr->rotated_n = 0;

   switch (polygon_ptr->curved) {
   case LT_STRAIGHT:
      if (polygon_ptr->svlist != NULL) {
         free(polygon_ptr->svlist);
      }
      polygon_ptr->svlist = MakeMultiSplinePolygonVertex(
            &(polygon_ptr->sn), polygon_ptr->smooth,
            drawOrigX, drawOrigY,
            polygon_ptr->n, polygon_ptr->vlist);
      break;
   case LT_SPLINE:
      if (polygon_ptr->svlist != NULL) {
         free(polygon_ptr->svlist);
      }
      polygon_ptr->svlist = MakeMultiSplinePolygonVertex(
            &(polygon_ptr->sn), polygon_ptr->smooth,
            drawOrigX, drawOrigY,
            polygon_ptr->n, polygon_ptr->vlist);
      break;
   case LT_INTSPLINE:
      if (polygon_ptr->svlist != NULL) {
         free(polygon_ptr->svlist);
      }
      if (polygon_ptr->intvlist != NULL) {
         free(polygon_ptr->intvlist);
      }
      polygon_ptr->svlist = MakeIntSplinePolygonVertex(
            &(polygon_ptr->sn), &(polygon_ptr->intn),
            &(polygon_ptr->intvlist), drawOrigX, drawOrigY,
            polygon_ptr->n, polygon_ptr->vlist);
      break;
   }
}

void AdjObjSplineVs (ObjPtr)
   struct ObjRec	* ObjPtr;
{
   struct ObjRec *obj_ptr;

   switch (ObjPtr->type) {
   case OBJ_BOX: break;
   case OBJ_XBM: break;
   case OBJ_XPM: break;
   case OBJ_OVAL: InvalidateObjCache(ObjPtr); break;
   case OBJ_TEXT: break;
   case OBJ_RCBOX: InvalidateObjCache(ObjPtr); break;
   case OBJ_ARC: InvalidateObjCache(ObjPtr); AdjArcCache(ObjPtr); break;
   case OBJ_POLY: AdjPolyCache(ObjPtr); break;
   case OBJ_POLYGON: AdjPolygonCache(ObjPtr); break;

   case OBJ_SYM:
   case OBJ_ICON:
   case OBJ_GROUP:
      for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
            obj_ptr=obj_ptr->next) {
         AdjObjSplineVs(obj_ptr);
      }
      break;
   }
   GetTransformedOBBoxOffsetVs(ObjPtr, ObjPtr->rotated_obbox);
}

void AdjSplineVs()
{
   register struct ObjRec *obj_ptr, *ptr;

   if (topObj == NULL) return;

   dontAutoRetractArrow = TRUE;
   for (obj_ptr = topObj; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
      switch (obj_ptr->type) {
      case OBJ_POLY:
      case OBJ_BOX:
      case OBJ_OVAL:
      case OBJ_TEXT:
      case OBJ_POLYGON:
      case OBJ_ARC:
      case OBJ_RCBOX:
      case OBJ_XBM:
      case OBJ_XPM:
         AdjObjSplineVs (obj_ptr);
         break;
      case OBJ_SYM:
      case OBJ_ICON:
      case OBJ_GROUP:
         for (ptr = obj_ptr->detail.r->first; ptr != NULL; ptr = ptr->next) {
            AdjObjSplineVs (ptr);
         }
         GetTransformedOBBoxOffsetVs (obj_ptr, obj_ptr->rotated_obbox);
         break;
      }
   }
   dontAutoRetractArrow = FALSE;
}

void MoveRotatedObjCache (ObjPtr, AbsDx, AbsDy)
   struct ObjRec	* ObjPtr;
   int			AbsDx, AbsDy;
{
   register int	i;
   int		dx_off, dy_off, n;
   XPoint	* v=NULL;

   if (ObjPtr->ctm == NULL) return;

   dx_off = ZOOMED_SIZE(AbsDx);
   dy_off = ZOOMED_SIZE(AbsDy);
   for (i=0; i < 5; i++)
   {
      ObjPtr->rotated_obbox[i].x += dx_off;
      ObjPtr->rotated_obbox[i].y += dy_off;
   }
   ObjPtr->orig_obbox.ltx += AbsDx; ObjPtr->orig_obbox.lty += AbsDy;
   ObjPtr->orig_obbox.rbx += AbsDx; ObjPtr->orig_obbox.rby += AbsDy;
   switch (ObjPtr->type)
   {
      case OBJ_POLY:
      case OBJ_POLYGON:
      case OBJ_OVAL:
      case OBJ_ARC:
      case OBJ_RCBOX:
         switch (ObjPtr->type)
         {
            case OBJ_POLY:
               n = ObjPtr->detail.p->rotated_n;
               v = ObjPtr->detail.p->rotated_vlist;
               break;
            case OBJ_POLYGON:
               n = ObjPtr->detail.g->rotated_n;
               v = ObjPtr->detail.g->rotated_vlist;
               break;
            case OBJ_OVAL:
               n = ObjPtr->detail.o->rotated_n;
               v = ObjPtr->detail.o->rotated_vlist;
               break;
            case OBJ_ARC:
               n = ObjPtr->detail.a->rotated_n;
               v = ObjPtr->detail.a->rotated_vlist;
               break;
            case OBJ_RCBOX:
               n = ObjPtr->detail.rcb->rotated_n;
               v = ObjPtr->detail.rcb->rotated_vlist;
               break;
         }
         if (v != NULL)
         {
            for (i=0; i < n; i++)
            {
               v[i].x += dx_off;
               v[i].y += dy_off;
            }
         }
         if (ObjPtr->type == OBJ_POLY)
         {
            n = ObjPtr->detail.p->rotated_asn;
            v = ObjPtr->detail.p->rotated_asvlist;
            if (v != NULL)
            {
               for (i=0; i < n; i++)
               {
                  v[i].x += dx_off;
                  v[i].y += dy_off;
               }
            }
         }
         break;
      case OBJ_BOX:
      case OBJ_GROUP:
      case OBJ_SYM:
      case OBJ_ICON:
      case OBJ_XBM:
      case OBJ_XPM:
         break;
      case OBJ_TEXT:
         ObjPtr->detail.t->orig_bbox.ltx += AbsDx;
         ObjPtr->detail.t->orig_bbox.lty += AbsDy;
         ObjPtr->detail.t->orig_bbox.rbx += AbsDx;
         ObjPtr->detail.t->orig_bbox.rby += AbsDy;
         break;
   }
}

void AdjHotSpots()
{
}

void AdjObjHotSpot()
{
}
