/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkcanvline.c
ViewVC logotype

Diff of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkcanvline.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

to_be_filed/sf_code/esrgpcpj/shared/tk_base/tkcanvline.c revision 29 by dashley, Sat Oct 8 07:08:47 2016 UTC projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkcanvline.c revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkcanvline.c,v 1.1.1.1 2001/06/13 04:56:46 dtashley Exp $ */  
   
 /*  
  * tkCanvLine.c --  
  *  
  *      This file implements line items for canvas widgets.  
  *  
  * Copyright (c) 1991-1994 The Regents of the University of California.  
  * Copyright (c) 1994-1997 Sun Microsystems, Inc.  
  * Copyright (c) 1998-1999 by Scriptics Corporation.  
  *  
  * See the file "license.terms" for information on usage and redistribution  
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.  
  *  
  * RCS: @(#) $Id: tkcanvline.c,v 1.1.1.1 2001/06/13 04:56:46 dtashley Exp $  
  */  
   
 #include <stdio.h>  
 #include "tkInt.h"  
 #include "tkPort.h"  
 #include "tkCanvas.h"  
   
 /*  
  * The structure below defines the record for each line item.  
  */  
   
 typedef enum {  
     ARROWS_NONE, ARROWS_FIRST, ARROWS_LAST, ARROWS_BOTH  
 } Arrows;  
   
 typedef struct LineItem  {  
     Tk_Item header;             /* Generic stuff that's the same for all  
                                  * types.  MUST BE FIRST IN STRUCTURE. */  
     Tk_Outline outline;         /* Outline structure */  
     Tk_Canvas canvas;           /* Canvas containing item.  Needed for  
                                  * parsing arrow shapes. */  
     int numPoints;              /* Number of points in line (always >= 0). */  
     double *coordPtr;           /* Pointer to malloc-ed array containing  
                                  * x- and y-coords of all points in line.  
                                  * X-coords are even-valued indices, y-coords  
                                  * are corresponding odd-valued indices. If  
                                  * the line has arrowheads then the first  
                                  * and last points have been adjusted to refer  
                                  * to the necks of the arrowheads rather than  
                                  * their tips.  The actual endpoints are  
                                  * stored in the *firstArrowPtr and  
                                  * *lastArrowPtr, if they exist. */  
     int capStyle;               /* Cap style for line. */  
     int joinStyle;              /* Join style for line. */  
     GC arrowGC;                 /* Graphics context for drawing arrowheads. */  
     Arrows arrow;               /* Indicates whether or not to draw arrowheads:  
                                  * "none", "first", "last", or "both". */  
     float arrowShapeA;          /* Distance from tip of arrowhead to center. */  
     float arrowShapeB;          /* Distance from tip of arrowhead to trailing  
                                  * point, measured along shaft. */  
     float arrowShapeC;          /* Distance of trailing points from outside  
                                  * edge of shaft. */  
     double *firstArrowPtr;      /* Points to array of PTS_IN_ARROW points  
                                  * describing polygon for arrowhead at first  
                                  * point in line.  First point of arrowhead  
                                  * is tip.  Malloc'ed.  NULL means no arrowhead  
                                  * at first point. */  
     double *lastArrowPtr;       /* Points to polygon for arrowhead at last  
                                  * point in line (PTS_IN_ARROW points, first  
                                  * of which is tip).  Malloc'ed.  NULL means  
                                  * no arrowhead at last point. */  
     Tk_SmoothMethod *smooth;    /* Non-zero means draw line smoothed (i.e.  
                                  * with Bezier splines). */  
     int splineSteps;            /* Number of steps in each spline segment. */  
 } LineItem;  
   
 /*  
  * Number of points in an arrowHead:  
  */  
   
 #define PTS_IN_ARROW 6  
   
 /*  
  * Prototypes for procedures defined in this file:  
  */  
   
 static int              ArrowheadPostscript _ANSI_ARGS_((Tcl_Interp *interp,  
                             Tk_Canvas canvas, LineItem *linePtr,  
                             double *arrowPtr));  
 static void             ComputeLineBbox _ANSI_ARGS_((Tk_Canvas canvas,  
                             LineItem *linePtr));  
 static int              ConfigureLine _ANSI_ARGS_((Tcl_Interp *interp,  
                             Tk_Canvas canvas, Tk_Item *itemPtr, int argc,  
                             Tcl_Obj *CONST argv[], int flags));  
 static int              ConfigureArrows _ANSI_ARGS_((Tk_Canvas canvas,  
                             LineItem *linePtr));  
 static int              CreateLine _ANSI_ARGS_((Tcl_Interp *interp,  
                             Tk_Canvas canvas, struct Tk_Item *itemPtr,  
                             int argc, Tcl_Obj *CONST argv[]));  
 static void             DeleteLine _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, Display *display));  
 static void             DisplayLine _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, Display *display, Drawable dst,  
                             int x, int y, int width, int height));  
 static int              GetLineIndex _ANSI_ARGS_((Tcl_Interp *interp,  
                             Tk_Canvas canvas, Tk_Item *itemPtr,  
                             Tcl_Obj *obj, int *indexPtr));  
 static int              LineCoords _ANSI_ARGS_((Tcl_Interp *interp,  
                             Tk_Canvas canvas, Tk_Item *itemPtr,  
                             int argc, Tcl_Obj *CONST argv[]));  
 static void             LineDeleteCoords _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, int first, int last));  
 static void             LineInsert _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj));  
 static int              LineToArea _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, double *rectPtr));  
 static double           LineToPoint _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, double *coordPtr));  
 static int              LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp,  
                             Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));  
 static int              ArrowParseProc _ANSI_ARGS_((ClientData clientData,  
                             Tcl_Interp *interp, Tk_Window tkwin,  
                             CONST char *value, char *recordPtr, int offset));  
 static char *           ArrowPrintProc _ANSI_ARGS_((ClientData clientData,  
                             Tk_Window tkwin, char *recordPtr, int offset,  
                             Tcl_FreeProc **freeProcPtr));  
 static int              ParseArrowShape _ANSI_ARGS_((ClientData clientData,  
                             Tcl_Interp *interp, Tk_Window tkwin,  
                             CONST char *value, char *recordPtr, int offset));  
 static char *           PrintArrowShape _ANSI_ARGS_((ClientData clientData,  
                             Tk_Window tkwin, char *recordPtr, int offset,  
                             Tcl_FreeProc **freeProcPtr));  
 static void             ScaleLine _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, double originX, double originY,  
                             double scaleX, double scaleY));  
 static void             TranslateLine _ANSI_ARGS_((Tk_Canvas canvas,  
                             Tk_Item *itemPtr, double deltaX, double deltaY));  
   
 /*  
  * Information used for parsing configuration specs.  If you change any  
  * of the default strings, be sure to change the corresponding default  
  * values in CreateLine.  
  */  
   
 static Tk_CustomOption arrowShapeOption = {  
     (Tk_OptionParseProc *) ParseArrowShape,  
     PrintArrowShape, (ClientData) NULL  
 };  
 static Tk_CustomOption arrowOption = {  
     (Tk_OptionParseProc *) ArrowParseProc,  
     ArrowPrintProc, (ClientData) NULL  
 };  
 static Tk_CustomOption smoothOption = {  
     (Tk_OptionParseProc *) TkSmoothParseProc,  
     TkSmoothPrintProc, (ClientData) NULL  
 };  
 static Tk_CustomOption stateOption = {  
     (Tk_OptionParseProc *) TkStateParseProc,  
     TkStatePrintProc, (ClientData) 2  
 };  
 static Tk_CustomOption tagsOption = {  
     (Tk_OptionParseProc *) Tk_CanvasTagsParseProc,  
     Tk_CanvasTagsPrintProc, (ClientData) NULL  
 };  
 static Tk_CustomOption dashOption = {  
     (Tk_OptionParseProc *) TkCanvasDashParseProc,  
     TkCanvasDashPrintProc, (ClientData) NULL  
 };  
 static Tk_CustomOption offsetOption = {  
     (Tk_OptionParseProc *) TkOffsetParseProc,  
     TkOffsetPrintProc,  
     (ClientData) (TK_OFFSET_RELATIVE|TK_OFFSET_INDEX)  
 };  
 static Tk_CustomOption pixelOption = {  
     (Tk_OptionParseProc *) TkPixelParseProc,  
     TkPixelPrintProc, (ClientData) NULL  
 };  
   
 static Tk_ConfigSpec configSpecs[] = {  
     {TK_CONFIG_CUSTOM, "-activedash", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.activeDash),  
         TK_CONFIG_NULL_OK, &dashOption},  
     {TK_CONFIG_COLOR, "-activefill", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.activeColor),  
         TK_CONFIG_NULL_OK},  
     {TK_CONFIG_BITMAP, "-activestipple", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.activeStipple),  
         TK_CONFIG_NULL_OK},  
     {TK_CONFIG_CUSTOM, "-activewidth", (char *) NULL, (char *) NULL,  
         "0.0", Tk_Offset(LineItem, outline.activeWidth),  
         TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},  
     {TK_CONFIG_CUSTOM, "-arrow", (char *) NULL, (char *) NULL,  
         "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT, &arrowOption},  
     {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL,  
         "8 10 3", Tk_Offset(LineItem, arrowShapeA),  
         TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},  
     {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL,  
         "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT},  
     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,  
         "black", Tk_Offset(LineItem, outline.color), TK_CONFIG_NULL_OK},  
     {TK_CONFIG_CUSTOM, "-dash", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.dash),  
         TK_CONFIG_NULL_OK, &dashOption},  
     {TK_CONFIG_PIXELS, "-dashoffset", (char *) NULL, (char *) NULL,  
         "0", Tk_Offset(LineItem, outline.offset),  
         TK_CONFIG_DONT_SET_DEFAULT},  
     {TK_CONFIG_CUSTOM, "-disableddash", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.disabledDash),  
         TK_CONFIG_NULL_OK, &dashOption},  
     {TK_CONFIG_COLOR, "-disabledfill", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.disabledColor),  
         TK_CONFIG_NULL_OK},  
     {TK_CONFIG_BITMAP, "-disabledstipple", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.disabledStipple),  
         TK_CONFIG_NULL_OK},  
     {TK_CONFIG_CUSTOM, "-disabledwidth", (char *) NULL, (char *) NULL,  
         "0.0", Tk_Offset(LineItem, outline.disabledWidth),  
         TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},  
     {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,  
         "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},  
     {TK_CONFIG_CUSTOM, "-offset", (char *) NULL, (char *) NULL,  
         "0,0", Tk_Offset(LineItem, outline.tsoffset),  
         TK_CONFIG_DONT_SET_DEFAULT, &offsetOption},  
     {TK_CONFIG_CUSTOM, "-smooth", (char *) NULL, (char *) NULL,  
         "0", Tk_Offset(LineItem, smooth),  
         TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},  
     {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,  
         "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},  
     {TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK,  
         &stateOption},  
     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,  
         (char *) NULL, Tk_Offset(LineItem, outline.stipple),  
         TK_CONFIG_NULL_OK},  
     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},  
     {TK_CONFIG_CUSTOM, "-width", (char *) NULL, (char *) NULL,  
         "1.0", Tk_Offset(LineItem, outline.width),  
         TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},  
     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, 0}  
 };  
   
 /*  
  * The structures below defines the line item type by means  
  * of procedures that can be invoked by generic item code.  
  */  
   
 Tk_ItemType tkLineType = {  
     "line",                             /* name */  
     sizeof(LineItem),                   /* itemSize */  
     CreateLine,                         /* createProc */  
     configSpecs,                        /* configSpecs */  
     ConfigureLine,                      /* configureProc */  
     LineCoords,                         /* coordProc */  
     DeleteLine,                         /* deleteProc */  
     DisplayLine,                        /* displayProc */  
     TK_CONFIG_OBJS,                     /* flags */  
     LineToPoint,                        /* pointProc */  
     LineToArea,                         /* areaProc */  
     LineToPostscript,                   /* postscriptProc */  
     ScaleLine,                          /* scaleProc */  
     TranslateLine,                      /* translateProc */  
     (Tk_ItemIndexProc *) GetLineIndex,  /* indexProc */  
     (Tk_ItemCursorProc *) NULL,         /* icursorProc */  
     (Tk_ItemSelectionProc *) NULL,      /* selectionProc */  
     (Tk_ItemInsertProc *) LineInsert,   /* insertProc */  
     LineDeleteCoords,                   /* dTextProc */  
     (Tk_ItemType *) NULL,               /* nextPtr */  
 };  
   
 /*  
  * The definition below determines how large are static arrays  
  * used to hold spline points (splines larger than this have to  
  * have their arrays malloc-ed).  
  */  
   
 #define MAX_STATIC_POINTS 200  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * CreateLine --  
  *  
  *      This procedure is invoked to create a new line item in  
  *      a canvas.  
  *  
  * Results:  
  *      A standard Tcl return value.  If an error occurred in  
  *      creating the item, then an error message is left in  
  *      the interp's result;  in this case itemPtr is left uninitialized,  
  *      so it can be safely freed by the caller.  
  *  
  * Side effects:  
  *      A new line item is created.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 CreateLine(interp, canvas, itemPtr, argc, argv)  
     Tcl_Interp *interp;                 /* Interpreter for error reporting. */  
     Tk_Canvas canvas;                   /* Canvas to hold new item. */  
     Tk_Item *itemPtr;                   /* Record to hold new item;  header  
                                          * has been initialized by caller. */  
     int argc;                           /* Number of arguments in argv. */  
     Tcl_Obj *CONST argv[];              /* Arguments describing line. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     int i;  
   
     /*  
      * Carry out initialization that is needed to set defaults and to  
      * allow proper cleanup after errors during the the remainder of  
      * this procedure.  
      */  
   
     Tk_CreateOutline(&(linePtr->outline));  
     linePtr->canvas = canvas;  
     linePtr->numPoints = 0;  
     linePtr->coordPtr = NULL;  
     linePtr->capStyle = CapButt;  
     linePtr->joinStyle = JoinRound;  
     linePtr->arrowGC = None;  
     linePtr->arrow = ARROWS_NONE;  
     linePtr->arrowShapeA = (float)8.0;  
     linePtr->arrowShapeB = (float)10.0;  
     linePtr->arrowShapeC = (float)3.0;  
     linePtr->firstArrowPtr = NULL;  
     linePtr->lastArrowPtr = NULL;  
     linePtr->smooth = (Tk_SmoothMethod *) NULL;  
     linePtr->splineSteps = 12;  
   
     /*  
      * Count the number of points and then parse them into a point  
      * array.  Leading arguments are assumed to be points if they  
      * start with a digit or a minus sign followed by a digit.  
      */  
   
     for (i = 0; i < argc; i++) {  
         char *arg = Tcl_GetStringFromObj(argv[i], NULL);  
         if ((arg[0] == '-') && (arg[1] >= 'a')  
                 && (arg[1] <= 'z')) {  
             break;  
         }  
     }  
     if (i && (LineCoords(interp, canvas, itemPtr, i, argv) != TCL_OK)) {  
         goto error;  
     }  
     if (ConfigureLine(interp, canvas, itemPtr, argc-i, argv+i, 0) == TCL_OK) {  
         return TCL_OK;  
     }  
   
     error:  
     DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));  
     return TCL_ERROR;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * LineCoords --  
  *  
  *      This procedure is invoked to process the "coords" widget  
  *      command on lines.  See the user documentation for details  
  *      on what it does.  
  *  
  * Results:  
  *      Returns TCL_OK or TCL_ERROR, and sets the interp's result.  
  *  
  * Side effects:  
  *      The coordinates for the given item may be changed.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 LineCoords(interp, canvas, itemPtr, argc, argv)  
     Tcl_Interp *interp;                 /* Used for error reporting. */  
     Tk_Canvas canvas;                   /* Canvas containing item. */  
     Tk_Item *itemPtr;                   /* Item whose coordinates are to be  
                                          * read or modified. */  
     int argc;                           /* Number of coordinates supplied in  
                                          * argv. */  
     Tcl_Obj *CONST argv[];              /* Array of coordinates: x1, y1,  
                                          * x2, y2, ... */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     int i, numPoints;  
     double *coordPtr;  
   
     if (argc == 0) {  
         int numCoords;  
         Tcl_Obj *subobj, *obj = Tcl_NewObj();  
   
         numCoords = 2*linePtr->numPoints;  
         if (linePtr->firstArrowPtr != NULL) {  
             coordPtr = linePtr->firstArrowPtr;  
         } else {  
             coordPtr = linePtr->coordPtr;  
         }  
         for (i = 0; i < numCoords; i++, coordPtr++) {  
             if (i == 2) {  
                 coordPtr = linePtr->coordPtr+2;  
             }  
             if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) {  
                 coordPtr = linePtr->lastArrowPtr;  
             }  
             subobj = Tcl_NewDoubleObj(*coordPtr);  
             Tcl_ListObjAppendElement(interp, obj, subobj);  
         }  
         Tcl_SetObjResult(interp, obj);  
         return TCL_OK;  
     }  
     if (argc == 1) {  
         if (Tcl_ListObjGetElements(interp, argv[0], &argc,  
                 (Tcl_Obj ***) &argv) != TCL_OK) {  
             return TCL_ERROR;  
         }  
     }  
     if (argc & 1) {  
         Tcl_AppendResult(interp,  
                 "odd number of coordinates specified for line",  
                 (char *) NULL);  
         return TCL_ERROR;  
     } else if (argc < 4) {  
         Tcl_AppendResult(interp,  
                 "too few coordinates specified for line",  
                 (char *) NULL);  
         return TCL_ERROR;  
     } else {  
         numPoints = argc/2;  
         if (linePtr->numPoints != numPoints) {  
             coordPtr = (double *) ckalloc((unsigned)  
                     (sizeof(double) * argc));  
             if (linePtr->coordPtr != NULL) {  
                 ckfree((char *) linePtr->coordPtr);  
             }  
             linePtr->coordPtr = coordPtr;  
             linePtr->numPoints = numPoints;  
         }  
         coordPtr = linePtr->coordPtr;  
         for (i = 0; i <argc; i++) {  
             if (Tk_CanvasGetCoordFromObj(interp, canvas, argv[i],  
                     coordPtr++) != TCL_OK) {  
                 return TCL_ERROR;  
             }  
         }  
   
         /*  
          * Update arrowheads by throwing away any existing arrow-head  
          * information and calling ConfigureArrows to recompute it.  
          */  
   
         if (linePtr->firstArrowPtr != NULL) {  
             ckfree((char *) linePtr->firstArrowPtr);  
             linePtr->firstArrowPtr = NULL;  
         }  
         if (linePtr->lastArrowPtr != NULL) {  
             ckfree((char *) linePtr->lastArrowPtr);  
             linePtr->lastArrowPtr = NULL;  
         }  
         if (linePtr->arrow != ARROWS_NONE) {  
             ConfigureArrows(canvas, linePtr);  
         }  
         ComputeLineBbox(canvas, linePtr);  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ConfigureLine --  
  *  
  *      This procedure is invoked to configure various aspects  
  *      of a line item such as its background color.  
  *  
  * Results:  
  *      A standard Tcl result code.  If an error occurs, then  
  *      an error message is left in the interp's result.  
  *  
  * Side effects:  
  *      Configuration information, such as colors and stipple  
  *      patterns, may be set for itemPtr.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 ConfigureLine(interp, canvas, itemPtr, argc, argv, flags)  
     Tcl_Interp *interp;         /* Used for error reporting. */  
     Tk_Canvas canvas;           /* Canvas containing itemPtr. */  
     Tk_Item *itemPtr;           /* Line item to reconfigure. */  
     int argc;                   /* Number of elements in argv.  */  
     Tcl_Obj *CONST argv[];      /* Arguments describing things to configure. */  
     int flags;                  /* Flags to pass to Tk_ConfigureWidget. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     XGCValues gcValues;  
     GC newGC, arrowGC;  
     unsigned long mask;  
     Tk_Window tkwin;  
     Tk_State state;  
   
     tkwin = Tk_CanvasTkwin(canvas);  
     if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, (char **) argv,  
             (char *) linePtr, flags|TK_CONFIG_OBJS) != TCL_OK) {  
         return TCL_ERROR;  
     }  
   
     /*  
      * A few of the options require additional processing, such as  
      * graphics contexts.  
      */  
   
     state = itemPtr->state;  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     if (linePtr->outline.activeWidth > linePtr->outline.width ||  
             linePtr->outline.activeDash.number != 0 ||  
             linePtr->outline.activeColor != NULL ||  
             linePtr->outline.activeStipple != None) {  
         itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;  
     } else {  
         itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;  
     }  
     mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr,  
             &(linePtr->outline));  
     if (mask) {  
         if (linePtr->arrow == ARROWS_NONE) {  
             gcValues.cap_style = linePtr->capStyle;  
             mask |= GCCapStyle;  
         }  
         gcValues.join_style = linePtr->joinStyle;  
         mask |= GCJoinStyle;  
         newGC = Tk_GetGC(tkwin, mask, &gcValues);  
         gcValues.line_width = 0;  
         arrowGC = Tk_GetGC(tkwin, mask, &gcValues);  
     } else {  
         newGC = arrowGC = None;  
     }  
     if (linePtr->outline.gc != None) {  
         Tk_FreeGC(Tk_Display(tkwin), linePtr->outline.gc);  
     }  
     if (linePtr->arrowGC != None) {  
         Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC);  
     }  
     linePtr->outline.gc = newGC;  
     linePtr->arrowGC = arrowGC;  
   
     /*  
      * Keep spline parameters within reasonable limits.  
      */  
   
     if (linePtr->splineSteps < 1) {  
         linePtr->splineSteps = 1;  
     } else if (linePtr->splineSteps > 100) {  
         linePtr->splineSteps = 100;  
     }  
   
     if ((!linePtr->numPoints) || (state==TK_STATE_HIDDEN)) {  
         ComputeLineBbox(canvas, linePtr);  
         return TCL_OK;  
     }  
   
     /*  
      * Setup arrowheads, if needed.  If arrowheads are turned off,  
      * restore the line's endpoints (they were shortened when the  
      * arrowheads were added).  
      */  
   
     if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != ARROWS_FIRST)  
             && (linePtr->arrow != ARROWS_BOTH)) {  
         linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];  
         linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];  
         ckfree((char *) linePtr->firstArrowPtr);  
         linePtr->firstArrowPtr = NULL;  
     }  
     if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != ARROWS_LAST)  
             && (linePtr->arrow != ARROWS_BOTH)) {  
         int i;  
   
         i = 2*(linePtr->numPoints-1);  
         linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];  
         linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];  
         ckfree((char *) linePtr->lastArrowPtr);  
         linePtr->lastArrowPtr = NULL;  
     }  
     if (linePtr->arrow != ARROWS_NONE) {  
         ConfigureArrows(canvas, linePtr);  
     }  
   
     /*  
      * Recompute bounding box for line.  
      */  
   
     ComputeLineBbox(canvas, linePtr);  
   
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * DeleteLine --  
  *  
  *      This procedure is called to clean up the data structure  
  *      associated with a line item.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Resources associated with itemPtr are released.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 DeleteLine(canvas, itemPtr, display)  
     Tk_Canvas canvas;                   /* Info about overall canvas widget. */  
     Tk_Item *itemPtr;                   /* Item that is being deleted. */  
     Display *display;                   /* Display containing window for  
                                          * canvas. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
   
     Tk_DeleteOutline(display, &(linePtr->outline));  
     if (linePtr->coordPtr != NULL) {  
         ckfree((char *) linePtr->coordPtr);  
     }  
     if (linePtr->arrowGC != None) {  
         Tk_FreeGC(display, linePtr->arrowGC);  
     }  
     if (linePtr->firstArrowPtr != NULL) {  
         ckfree((char *) linePtr->firstArrowPtr);  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         ckfree((char *) linePtr->lastArrowPtr);  
     }  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ComputeLineBbox --  
  *  
  *      This procedure is invoked to compute the bounding box of  
  *      all the pixels that may be drawn as part of a line.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The fields x1, y1, x2, and y2 are updated in the header  
  *      for itemPtr.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 ComputeLineBbox(canvas, linePtr)  
     Tk_Canvas canvas;                   /* Canvas that contains item. */  
     LineItem *linePtr;                  /* Item whose bbos is to be  
                                          * recomputed. */  
 {  
     double *coordPtr;  
     int i, intWidth;  
     double width;  
     Tk_State state = linePtr->header.state;  
     Tk_TSOffset *tsoffset;  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     if (!(linePtr->numPoints) || (state==TK_STATE_HIDDEN)) {  
         linePtr->header.x1 = -1;  
         linePtr->header.x2 = -1;  
         linePtr->header.y1 = -1;  
         linePtr->header.y2 = -1;  
         return;  
     }  
   
     width = linePtr->outline.width;  
     if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) {  
         if (linePtr->outline.activeWidth>width) {  
             width = linePtr->outline.activeWidth;  
         }  
     } else if (state==TK_STATE_DISABLED) {  
         if (linePtr->outline.disabledWidth>0) {  
             width = linePtr->outline.disabledWidth;  
         }  
     }  
   
     coordPtr = linePtr->coordPtr;  
     linePtr->header.x1 = linePtr->header.x2 = (int) *coordPtr;  
     linePtr->header.y1 = linePtr->header.y2 = (int) coordPtr[1];  
   
     /*  
      * Compute the bounding box of all the points in the line,  
      * then expand in all directions by the line's width to take  
      * care of butting or rounded corners and projecting or  
      * rounded caps.  This expansion is an overestimate (worst-case  
      * is square root of two over two) but it's simple.  Don't do  
      * anything special for curves.  This causes an additional  
      * overestimate in the bounding box, but is faster.  
      */  
   
     for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;  
             i++, coordPtr += 2) {  
         TkIncludePoint((Tk_Item *) linePtr, coordPtr);  
     }  
     width = linePtr->outline.width;  
     if (width < 1.0) {  
         width = 1.0;  
     }  
     if (linePtr->arrow != ARROWS_NONE) {  
         if (linePtr->arrow != ARROWS_LAST) {  
             TkIncludePoint((Tk_Item *) linePtr, linePtr->firstArrowPtr);  
         }  
         if (linePtr->arrow != ARROWS_FIRST) {  
             TkIncludePoint((Tk_Item *) linePtr, linePtr->lastArrowPtr);  
         }  
     }  
   
     tsoffset = &linePtr->outline.tsoffset;  
     if (tsoffset->flags & TK_OFFSET_INDEX) {  
         double *coordPtr = linePtr->coordPtr + (tsoffset->flags & ~TK_OFFSET_INDEX);  
         if (tsoffset->flags <= 0) {  
             coordPtr = linePtr->coordPtr;  
             if ((linePtr->arrow == ARROWS_FIRST) || (linePtr->arrow == ARROWS_BOTH)) {  
                 coordPtr = linePtr->firstArrowPtr;  
             }  
         }  
         if (tsoffset->flags > (linePtr->numPoints * 2)) {  
             coordPtr = linePtr->coordPtr + (linePtr->numPoints * 2);  
             if ((linePtr->arrow == ARROWS_LAST) || (linePtr->arrow == ARROWS_BOTH)) {  
                 coordPtr = linePtr->lastArrowPtr;  
             }  
         }  
         tsoffset->xoffset = (int) (coordPtr[0] + 0.5);  
         tsoffset->yoffset = (int) (coordPtr[1] + 0.5);  
     } else {  
         if (tsoffset->flags & TK_OFFSET_LEFT) {  
             tsoffset->xoffset = linePtr->header.x1;  
         } else if (tsoffset->flags & TK_OFFSET_CENTER) {  
             tsoffset->xoffset = (linePtr->header.x1 + linePtr->header.x2)/2;  
         } else if (tsoffset->flags & TK_OFFSET_RIGHT) {  
             tsoffset->xoffset = linePtr->header.x2;  
         }  
         if (tsoffset->flags & TK_OFFSET_TOP) {  
             tsoffset->yoffset = linePtr->header.y1;  
         } else if (tsoffset->flags & TK_OFFSET_MIDDLE) {  
             tsoffset->yoffset = (linePtr->header.y1 + linePtr->header.y2)/2;  
         } else if (tsoffset->flags & TK_OFFSET_BOTTOM) {  
             tsoffset->yoffset = linePtr->header.y2;  
         }  
     }  
   
     intWidth = (int) (width + 0.5);  
     linePtr->header.x1 -= intWidth;  
     linePtr->header.x2 += intWidth;  
     linePtr->header.y1 -= intWidth;  
     linePtr->header.y2 += intWidth;  
   
     if (linePtr->numPoints==1) {  
         linePtr->header.x1 -= 1;  
         linePtr->header.x2 += 1;  
         linePtr->header.y1 -= 1;  
         linePtr->header.y2 += 1;  
         return;  
     }  
   
     /*  
      * For mitered lines, make a second pass through all the points.  
      * Compute the locations of the two miter vertex points and add  
      * those into the bounding box.  
      */  
   
     if (linePtr->joinStyle == JoinMiter) {  
         for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;  
                 i--, coordPtr += 2) {  
             double miter[4];  
             int j;  
       
             if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,  
                     width, miter, miter+2)) {  
                 for (j = 0; j < 4; j += 2) {  
                     TkIncludePoint((Tk_Item *) linePtr, miter+j);  
                 }  
             }  
         }  
     }  
   
     /*  
      * Add in the sizes of arrowheads, if any.  
      */  
   
     if (linePtr->arrow != ARROWS_NONE) {  
         if (linePtr->arrow != ARROWS_LAST) {  
             for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint((Tk_Item *) linePtr, coordPtr);  
             }  
         }  
         if (linePtr->arrow != ARROWS_FIRST) {  
             for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint((Tk_Item *) linePtr, coordPtr);  
             }  
         }  
     }  
   
     /*  
      * Add one more pixel of fudge factor just to be safe (e.g.  
      * X may round differently than we do).  
      */  
   
     linePtr->header.x1 -= 1;  
     linePtr->header.x2 += 1;  
     linePtr->header.y1 -= 1;  
     linePtr->header.y2 += 1;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * DisplayLine --  
  *  
  *      This procedure is invoked to draw a line item in a given  
  *      drawable.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      ItemPtr is drawn in drawable using the transformation  
  *      information in canvas.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 DisplayLine(canvas, itemPtr, display, drawable, x, y, width, height)  
     Tk_Canvas canvas;                   /* Canvas that contains item. */  
     Tk_Item *itemPtr;                   /* Item to be displayed. */  
     Display *display;                   /* Display on which to draw item. */  
     Drawable drawable;                  /* Pixmap or window in which to draw  
                                          * item. */  
     int x, y, width, height;            /* Describes region of canvas that  
                                          * must be redisplayed (not used). */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     XPoint staticPoints[MAX_STATIC_POINTS];  
     XPoint *pointPtr;  
     XPoint *pPtr;  
     double *coordPtr, linewidth;  
     int i, numPoints;  
     Tk_State state = itemPtr->state;  
     Pixmap stipple = linePtr->outline.stipple;  
   
     if ((!linePtr->numPoints)||(linePtr->outline.gc==None)) {  
         return;  
     }  
   
     if (state == TK_STATE_NULL) {  
             state = ((TkCanvas *)canvas)->canvas_state;  
     }  
     linewidth = linePtr->outline.width;  
     if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {  
         if (linePtr->outline.activeStipple!=None) {  
             stipple = linePtr->outline.activeStipple;  
         }  
         if (linePtr->outline.activeWidth>linewidth) {  
             linewidth = linePtr->outline.activeWidth;  
         }  
     } else if (state==TK_STATE_DISABLED) {  
         if (linePtr->outline.disabledStipple!=None) {  
             stipple = linePtr->outline.disabledStipple;  
         }  
         if (linePtr->outline.disabledWidth>linewidth) {  
             linewidth = linePtr->outline.activeWidth;  
         }  
     }  
     /*  
      * Build up an array of points in screen coordinates.  Use a  
      * static array unless the line has an enormous number of points;  
      * in this case, dynamically allocate an array.  For smoothed lines,  
      * generate the curve points on each redisplay.  
      */  
   
     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {  
         numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL,  
                 linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,  
                 (double *) NULL);  
     } else {  
         numPoints = linePtr->numPoints;  
     }  
   
     if (numPoints <= MAX_STATIC_POINTS) {  
         pointPtr = staticPoints;  
     } else {  
         pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));  
     }  
   
     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {  
         numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,  
                 linePtr->numPoints, linePtr->splineSteps, pointPtr,  
                 (double *) NULL);  
     } else {  
         for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr;  
                 i < linePtr->numPoints;  i += 1, coordPtr += 2, pPtr++) {  
             Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1],  
                     &pPtr->x, &pPtr->y);  
         }  
     }  
   
     /*  
      * Display line, the free up line storage if it was dynamically  
      * allocated.  If we're stippling, then modify the stipple offset  
      * in the GC.  Be sure to reset the offset when done, since the  
      * GC is supposed to be read-only.  
      */  
   
     if (Tk_ChangeOutlineGC(canvas, itemPtr, &(linePtr->outline))) {  
         Tk_CanvasSetOffset(canvas, linePtr->arrowGC, &linePtr->outline.tsoffset);  
     }  
     if (numPoints>1) {  
         XDrawLines(display, drawable, linePtr->outline.gc, pointPtr, numPoints,  
             CoordModeOrigin);  
     } else {  
         int intwidth = (int) (linewidth + 0.5);  
         if (intwidth<1) {  
             intwidth=1;  
         }  
         XFillArc(display, drawable, linePtr->outline.gc,  
                 pointPtr->x - intwidth/2, pointPtr->y - intwidth/2,  
                 (unsigned int)intwidth+1, (unsigned int)intwidth+1, 0, 64*360);  
     }  
     if (pointPtr != staticPoints) {  
         ckfree((char *) pointPtr);  
     }  
   
     /*  
      * Display arrowheads, if they are wanted.  
      */  
   
     if (linePtr->firstArrowPtr != NULL) {  
         TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW,  
                 display, drawable, linePtr->arrowGC, NULL);  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW,  
                 display, drawable, linePtr->arrowGC, NULL);  
     }  
     if (Tk_ResetOutlineGC(canvas, itemPtr, &(linePtr->outline))) {  
         XSetTSOrigin(display, linePtr->arrowGC, 0, 0);  
     }  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * LineInsert --  
  *  
  *      Insert coords into a line item at a given index.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The coords in the given item is modified.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 LineInsert(canvas, itemPtr, beforeThis, obj)  
     Tk_Canvas canvas;           /* Canvas containing text item. */  
     Tk_Item *itemPtr;           /* Line item to be modified. */  
     int beforeThis;             /* Index before which new coordinates  
                                  * are to be inserted. */  
     Tcl_Obj *obj;               /* New coordinates to be inserted. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     int length, argc, i;  
     double *new, *coordPtr;  
     Tk_State state = itemPtr->state;  
     Tcl_Obj **objv;  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     if (!obj || (Tcl_ListObjGetElements((Tcl_Interp *) NULL, obj, &argc, &objv) != TCL_OK)  
             || !argc || argc&1) {  
         return;  
     }  
     length = 2*linePtr->numPoints;  
     if (beforeThis < 0) {  
         beforeThis = 0;  
     }  
     if (beforeThis > length) {  
         beforeThis = length;  
     }  
     if (linePtr->firstArrowPtr != NULL) {  
         linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];  
         linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0];  
         linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1];  
     }  
     new = (double *) ckalloc((unsigned)(sizeof(double) * (length + argc)));  
     for(i=0; i<beforeThis; i++) {  
         new[i] = linePtr->coordPtr[i];  
     }  
     for(i=0; i<argc; i++) {  
         if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,objv[i],  
                 new+(i+beforeThis))!=TCL_OK) {  
             Tcl_ResetResult(((TkCanvas *)canvas)->interp);  
             ckfree((char *) new);  
             return;  
         }  
     }  
   
     for(i=beforeThis; i<length; i++) {  
         new[i+argc] = linePtr->coordPtr[i];  
     }  
     if(linePtr->coordPtr) ckfree((char *)linePtr->coordPtr);  
     linePtr->coordPtr = new;  
     linePtr->numPoints = (length + argc)/2;  
   
     if ((length>3) && (state != TK_STATE_HIDDEN)) {  
         /*  
          * This is some optimizing code that will result that only the part  
          * of the polygon that changed (and the objects that are overlapping  
          * with that part) need to be redrawn. A special flag is set that  
          * instructs the general canvas code not to redraw the whole  
          * object. If this flag is not set, the canvas will do the redrawing,  
          * otherwise I have to do it here.  
          */  
         itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW;  
   
         if (beforeThis>0) {beforeThis -= 2; argc+=2; }  
         if ((beforeThis+argc)<length) argc+=2;  
         if (linePtr->smooth) {  
             if(beforeThis>0) {  
                 beforeThis-=2; argc+=2;  
             }  
             if((beforeThis+argc+2)<length) {  
                 argc+=2;  
             }  
         }  
         itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[beforeThis];  
         itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[beforeThis+1];  
         if ((linePtr->firstArrowPtr != NULL) && (beforeThis<1)) {  
             /* include old first arrow */  
             for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)>=length)) {  
                 /* include old last arrow */  
             for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         coordPtr = linePtr->coordPtr+beforeThis+2;  
         for(i=2; i<argc; i+=2) {  
             TkIncludePoint(itemPtr, coordPtr);  
                 coordPtr+=2;  
         }  
     }  
     if (linePtr->firstArrowPtr != NULL) {  
         ckfree((char *) linePtr->firstArrowPtr);  
         linePtr->firstArrowPtr = NULL;  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         ckfree((char *) linePtr->lastArrowPtr);  
         linePtr->lastArrowPtr = NULL;  
     }  
     if (linePtr->arrow != ARROWS_NONE) {  
             ConfigureArrows(canvas, linePtr);  
     }  
   
     if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) {  
         double width;  
         int intWidth;  
         if ((linePtr->firstArrowPtr != NULL) && (beforeThis>2)) {  
             /* include new first arrow */  
             for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)<(length-2))) {  
             /* include new right arrow */  
             for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         width = linePtr->outline.width;  
         if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {  
                 if (linePtr->outline.activeWidth>width) {  
                     width = linePtr->outline.activeWidth;  
                 }  
         } else if (state==TK_STATE_DISABLED) {  
                 if (linePtr->outline.disabledWidth>0) {  
                     width = linePtr->outline.disabledWidth;  
                 }  
         }  
         intWidth = (int) (width + 0.5);  
         if (intWidth < 1) {  
             intWidth = 1;  
         }  
         itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth;  
         itemPtr->x2 += intWidth; itemPtr->y2 += intWidth;  
         Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1,  
                 itemPtr->x2, itemPtr->y2);  
     }  
   
     ComputeLineBbox(canvas, linePtr);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * LineDeleteCoords --  
  *  
  *      Delete one or more coordinates from a line item.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Characters between "first" and "last", inclusive, get  
  *      deleted from itemPtr.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 LineDeleteCoords(canvas, itemPtr, first, last)  
     Tk_Canvas canvas;           /* Canvas containing itemPtr. */  
     Tk_Item *itemPtr;           /* Item in which to delete characters. */  
     int first;                  /* Index of first character to delete. */  
     int last;                   /* Index of last character to delete. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     int count, i, first1, last1;  
     int length = 2*linePtr->numPoints;  
     double *coordPtr;  
     Tk_State state = itemPtr->state;  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     first &= -2;  
     last &= -2;  
   
     if (first < 0) {  
         first = 0;  
     }  
     if (last >= length) {  
         last = length-2;  
     }  
     if (first > last) {  
         return;  
     }  
     if (linePtr->firstArrowPtr != NULL) {  
         linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];  
         linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0];  
         linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1];  
     }  
     first1 = first; last1 = last;  
     if(first1>0) first1 -= 2;  
     if(last1<length-2) last1 += 2;  
     if (linePtr->smooth) {  
         if(first1>0) first1 -= 2;  
         if(last1<length-2) last1 += 2;  
     }  
   
     if((first1<2) && (last1 >= length-2)) {  
         /*  
          * This is some optimizing code that will result that only the part  
          * of the line that changed (and the objects that are overlapping  
          * with that part) need to be redrawn. A special flag is set that  
          * instructs the general canvas code not to redraw the whole  
          * object. If this flag is set, the redrawing has to be done here,  
          * otherwise the general Canvas code will take care of it.  
          */  
   
         itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW;  
         itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[first1];  
         itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[first1+1];  
         if ((linePtr->firstArrowPtr != NULL) && (first1<2)) {  
             /* include old first arrow */  
             for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         if ((linePtr->lastArrowPtr != NULL) && (last1>=length-2)) {  
                 /* include old last arrow */  
             for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         coordPtr = linePtr->coordPtr+first1+2;  
         for (i=first1+2; i<=last1; i+=2) {  
             TkIncludePoint(itemPtr, coordPtr);  
             coordPtr+=2;  
         }  
     }  
   
     count = last + 2 - first;  
     for (i=last+2; i<length; i++) {  
         linePtr->coordPtr[i-count] = linePtr->coordPtr[i];  
     }  
     linePtr->numPoints -= count/2;  
     if (linePtr->firstArrowPtr != NULL) {  
         ckfree((char *) linePtr->firstArrowPtr);  
         linePtr->firstArrowPtr = NULL;  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         ckfree((char *) linePtr->lastArrowPtr);  
         linePtr->lastArrowPtr = NULL;  
     }  
     if (linePtr->arrow != ARROWS_NONE) {  
             ConfigureArrows(canvas, linePtr);  
     }  
     if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) {  
         double width;  
         int intWidth;  
         if ((linePtr->firstArrowPtr != NULL) && (first1<4)) {  
             /* include new first arrow */  
             for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         if ((linePtr->lastArrowPtr != NULL) && (last1>(length-4))) {  
             /* include new right arrow */  
             for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;  
                     i++, coordPtr += 2) {  
                 TkIncludePoint(itemPtr, coordPtr);  
             }  
         }  
         width = linePtr->outline.width;  
         if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {  
                 if (linePtr->outline.activeWidth>width) {  
                     width = linePtr->outline.activeWidth;  
                 }  
         } else if (state==TK_STATE_DISABLED) {  
                 if (linePtr->outline.disabledWidth>0) {  
                     width = linePtr->outline.disabledWidth;  
                 }  
         }  
         intWidth = (int) (width + 0.5);  
         if (intWidth < 1) {  
             intWidth = 1;  
         }  
         itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth;  
         itemPtr->x2 += intWidth; itemPtr->y2 += intWidth;  
         Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1,  
                 itemPtr->x2, itemPtr->y2);  
     }  
     ComputeLineBbox(canvas, linePtr);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * LineToPoint --  
  *  
  *      Computes the distance from a given point to a given  
  *      line, in canvas units.  
  *  
  * Results:  
  *      The return value is 0 if the point whose x and y coordinates  
  *      are pointPtr[0] and pointPtr[1] is inside the line.  If the  
  *      point isn't inside the line then the return value is the  
  *      distance from the point to the line.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
         /* ARGSUSED */  
 static double  
 LineToPoint(canvas, itemPtr, pointPtr)  
     Tk_Canvas canvas;           /* Canvas containing item. */  
     Tk_Item *itemPtr;           /* Item to check against point. */  
     double *pointPtr;           /* Pointer to x and y coordinates. */  
 {  
     Tk_State state = itemPtr->state;  
     LineItem *linePtr = (LineItem *) itemPtr;  
     double *coordPtr, *linePoints;  
     double staticSpace[2*MAX_STATIC_POINTS];  
     double poly[10];  
     double bestDist, dist, width;  
     int numPoints, count;  
     int changedMiterToBevel;    /* Non-zero means that a mitered corner  
                                  * had to be treated as beveled after all  
                                  * because the angle was < 11 degrees. */  
   
     bestDist = 1.0e36;  
   
     /*  
      * Handle smoothed lines by generating an expanded set of points  
      * against which to do the check.  
      */  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     width = linePtr->outline.width;  
     if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {  
         if (linePtr->outline.activeWidth>width) {  
             width = linePtr->outline.activeWidth;  
         }  
     } else if (state==TK_STATE_DISABLED) {  
         if (linePtr->outline.disabledWidth>0) {  
             width = linePtr->outline.disabledWidth;  
         }  
     }  
   
     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {  
         numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL,  
                 linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,  
                 (double *) NULL);  
         if (numPoints <= MAX_STATIC_POINTS) {  
             linePoints = staticSpace;  
         } else {  
             linePoints = (double *) ckalloc((unsigned)  
                     (2*numPoints*sizeof(double)));  
         }  
         numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,  
                 linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,  
                 linePoints);  
     } else {  
         numPoints = linePtr->numPoints;  
         linePoints = linePtr->coordPtr;  
     }  
   
     if (width < 1.0) {  
         width = 1.0;  
     }  
   
     if (!numPoints || itemPtr->state==TK_STATE_HIDDEN) {  
         return bestDist;  
     } else if (numPoints == 1) {  
         bestDist = hypot(linePoints[0] - pointPtr[0], linePoints[1] - pointPtr[1])  
                     - width/2.0;  
         if (bestDist < 0) bestDist = 0;  
         return bestDist;  
     }  
   
     /*  
      * The overall idea is to iterate through all of the edges of  
      * the line, computing a polygon for each edge and testing the  
      * point against that polygon.  In addition, there are additional  
      * tests to deal with rounded joints and caps.  
      */  
   
     changedMiterToBevel = 0;  
     for (count = numPoints, coordPtr = linePoints; count >= 2;  
             count--, coordPtr += 2) {  
   
         /*  
          * If rounding is done around the first point then compute  
          * the distance between the point and the point.  
          */  
   
         if (((linePtr->capStyle == CapRound) && (count == numPoints))  
                 || ((linePtr->joinStyle == JoinRound)  
                         && (count != numPoints))) {  
             dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])  
                     - width/2.0;  
             if (dist <= 0.0) {  
                 bestDist = 0.0;  
                 goto done;  
             } else if (dist < bestDist) {  
                 bestDist = dist;  
             }  
         }  
   
         /*  
          * Compute the polygonal shape corresponding to this edge,  
          * consisting of two points for the first point of the edge  
          * and two points for the last point of the edge.  
          */  
   
         if (count == numPoints) {  
             TkGetButtPoints(coordPtr+2, coordPtr, width,  
                     linePtr->capStyle == CapProjecting, poly, poly+2);  
         } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {  
             poly[0] = poly[6];  
             poly[1] = poly[7];  
             poly[2] = poly[4];  
             poly[3] = poly[5];  
         } else {  
             TkGetButtPoints(coordPtr+2, coordPtr, width, 0,  
                     poly, poly+2);  
   
             /*  
              * If this line uses beveled joints, then check the distance  
              * to a polygon comprising the last two points of the previous  
              * polygon and the first two from this polygon;  this checks  
              * the wedges that fill the mitered joint.  
              */  
   
             if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {  
                 poly[8] = poly[0];  
                 poly[9] = poly[1];  
                 dist = TkPolygonToPoint(poly, 5, pointPtr);  
                 if (dist <= 0.0) {  
                     bestDist = 0.0;  
                     goto done;  
                 } else if (dist < bestDist) {  
                     bestDist = dist;  
                 }  
                 changedMiterToBevel = 0;  
             }  
         }  
         if (count == 2) {  
             TkGetButtPoints(coordPtr, coordPtr+2, width,  
                     linePtr->capStyle == CapProjecting, poly+4, poly+6);  
         } else if (linePtr->joinStyle == JoinMiter) {  
             if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,  
                     width, poly+4, poly+6) == 0) {  
                 changedMiterToBevel = 1;  
                 TkGetButtPoints(coordPtr, coordPtr+2, width,  
                         0, poly+4, poly+6);  
             }  
         } else {  
             TkGetButtPoints(coordPtr, coordPtr+2, width, 0,  
                     poly+4, poly+6);  
         }  
         poly[8] = poly[0];  
         poly[9] = poly[1];  
         dist = TkPolygonToPoint(poly, 5, pointPtr);  
         if (dist <= 0.0) {  
             bestDist = 0.0;  
             goto done;  
         } else if (dist < bestDist) {  
             bestDist = dist;  
         }  
     }  
   
     /*  
      * If caps are rounded, check the distance to the cap around the  
      * final end point of the line.  
      */  
   
     if (linePtr->capStyle == CapRound) {  
         dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])  
                 - width/2.0;  
         if (dist <= 0.0) {  
             bestDist = 0.0;  
             goto done;  
         } else if (dist < bestDist) {  
             bestDist = dist;  
         }  
     }  
   
     /*  
      * If there are arrowheads, check the distance to the arrowheads.  
      */  
   
     if (linePtr->arrow != ARROWS_NONE) {  
         if (linePtr->arrow != ARROWS_LAST) {  
             dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,  
                     pointPtr);  
             if (dist <= 0.0) {  
                 bestDist = 0.0;  
                 goto done;  
             } else if (dist < bestDist) {  
                 bestDist = dist;  
             }  
         }  
         if (linePtr->arrow != ARROWS_FIRST) {  
             dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,  
                     pointPtr);  
             if (dist <= 0.0) {  
                 bestDist = 0.0;  
                 goto done;  
             } else if (dist < bestDist) {  
                 bestDist = dist;  
             }  
         }  
     }  
   
     done:  
     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {  
         ckfree((char *) linePoints);  
     }  
     return bestDist;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * LineToArea --  
  *  
  *      This procedure is called to determine whether an item  
  *      lies entirely inside, entirely outside, or overlapping  
  *      a given rectangular area.  
  *  
  * Results:  
  *      -1 is returned if the item is entirely outside the  
  *      area, 0 if it overlaps, and 1 if it is entirely  
  *      inside the given area.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
         /* ARGSUSED */  
 static int  
 LineToArea(canvas, itemPtr, rectPtr)  
     Tk_Canvas canvas;           /* Canvas containing item. */  
     Tk_Item *itemPtr;           /* Item to check against line. */  
     double *rectPtr;  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     double staticSpace[2*MAX_STATIC_POINTS];  
     double *linePoints;  
     int numPoints, result;  
     double radius, width;  
     Tk_State state = itemPtr->state;  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
     width = linePtr->outline.width;  
     if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {  
         if (linePtr->outline.activeWidth>width) {  
             width = linePtr->outline.activeWidth;  
         }  
     } else if (state==TK_STATE_DISABLED) {  
         if (linePtr->outline.disabledWidth>0) {  
             width = linePtr->outline.disabledWidth;  
         }  
     }  
   
     radius = (width+1.0)/2.0;  
   
     if ((state==TK_STATE_HIDDEN) || !linePtr->numPoints) {  
         return -1;  
     } else if (linePtr->numPoints == 1) {  
         double oval[4];  
         oval[0] = linePtr->coordPtr[0]-radius;  
         oval[1] = linePtr->coordPtr[1]-radius;  
         oval[2] = linePtr->coordPtr[0]+radius;  
         oval[3] = linePtr->coordPtr[1]+radius;  
         return TkOvalToArea(oval, rectPtr);  
     }  
   
     /*  
      * Handle smoothed lines by generating an expanded set of points  
      * against which to do the check.  
      */  
   
     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {  
         numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL,  
                 linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,  
                 (double *) NULL);  
         if (numPoints <= MAX_STATIC_POINTS) {  
             linePoints = staticSpace;  
         } else {  
             linePoints = (double *) ckalloc((unsigned)  
                     (2*numPoints*sizeof(double)));  
         }  
         numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,  
                 linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,  
                 linePoints);  
     } else {  
         numPoints = linePtr->numPoints;  
         linePoints = linePtr->coordPtr;  
     }  
   
     /*  
      * Check the segments of the line.  
      */  
   
      if (width < 1.0) {  
         width = 1.0;  
     }  
   
     result = TkThickPolyLineToArea(linePoints, numPoints,  
             width, linePtr->capStyle, linePtr->joinStyle,  
             rectPtr);  
     if (result == 0) {  
         goto done;  
     }  
   
     /*  
      * Check arrowheads, if any.  
      */  
   
     if (linePtr->arrow != ARROWS_NONE) {  
         if (linePtr->arrow != ARROWS_LAST) {  
             if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,  
                     rectPtr) != result) {  
                 result = 0;  
                 goto done;  
             }  
         }  
         if (linePtr->arrow != ARROWS_FIRST) {  
             if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,  
                     rectPtr) != result) {  
                 result = 0;  
                 goto done;  
             }  
         }  
     }  
   
     done:  
     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {  
         ckfree((char *) linePoints);  
     }  
     return result;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ScaleLine --  
  *  
  *      This procedure is invoked to rescale a line item.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The line referred to by itemPtr is rescaled so that the  
  *      following transformation is applied to all point  
  *      coordinates:  
  *              x' = originX + scaleX*(x-originX)  
  *              y' = originY + scaleY*(y-originY)  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 ScaleLine(canvas, itemPtr, originX, originY, scaleX, scaleY)  
     Tk_Canvas canvas;                   /* Canvas containing line. */  
     Tk_Item *itemPtr;                   /* Line to be scaled. */  
     double originX, originY;            /* Origin about which to scale rect. */  
     double scaleX;                      /* Amount to scale in X direction. */  
     double scaleY;                      /* Amount to scale in Y direction. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     double *coordPtr;  
     int i;  
   
     /*  
      * Delete any arrowheads before scaling all the points (so that  
      * the end-points of the line get restored).  
      */  
   
     if (linePtr->firstArrowPtr != NULL) {  
         linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];  
         linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];  
         ckfree((char *) linePtr->firstArrowPtr);  
         linePtr->firstArrowPtr = NULL;  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         int i;  
   
         i = 2*(linePtr->numPoints-1);  
         linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];  
         linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];  
         ckfree((char *) linePtr->lastArrowPtr);  
         linePtr->lastArrowPtr = NULL;  
     }  
     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;  
             i++, coordPtr += 2) {  
         coordPtr[0] = originX + scaleX*(*coordPtr - originX);  
         coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);  
     }  
     if (linePtr->arrow != ARROWS_NONE) {  
         ConfigureArrows(canvas, linePtr);  
     }  
     ComputeLineBbox(canvas, linePtr);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * GetLineIndex --  
  *  
  *      Parse an index into a line item and return either its value  
  *      or an error.  
  *  
  * Results:  
  *      A standard Tcl result.  If all went well, then *indexPtr is  
  *      filled in with the index (into itemPtr) corresponding to  
  *      string.  Otherwise an error message is left in  
  *      interp->result.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 GetLineIndex(interp, canvas, itemPtr, obj, indexPtr)  
     Tcl_Interp *interp;         /* Used for error reporting. */  
     Tk_Canvas canvas;           /* Canvas containing item. */  
     Tk_Item *itemPtr;           /* Item for which the index is being  
                                  * specified. */  
     Tcl_Obj *obj;               /* Specification of a particular coord  
                                  * in itemPtr's line. */  
     int *indexPtr;              /* Where to store converted index. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     size_t length;  
     char *string = Tcl_GetStringFromObj(obj, (int *) &length);  
   
     if (string[0] == 'e') {  
         if (strncmp(string, "end", length) == 0) {  
             *indexPtr = 2*linePtr->numPoints;  
         } else {  
             badIndex:  
   
             /*  
              * Some of the paths here leave messages in interp->result,  
              * so we have to clear it out before storing our own message.  
              */  
   
             Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);  
             Tcl_AppendResult(interp, "bad index \"", string, "\"",  
                     (char *) NULL);  
             return TCL_ERROR;  
         }  
     } else if (string[0] == '@') {  
         int i;  
         double x ,y, bestDist, dist, *coordPtr;  
         char *end, *p;  
   
         p = string+1;  
         x = strtod(p, &end);  
         if ((end == p) || (*end != ',')) {  
             goto badIndex;  
         }  
         p = end+1;  
         y = strtod(p, &end);  
         if ((end == p) || (*end != 0)) {  
             goto badIndex;  
         }  
         bestDist = 1.0e36;  
         coordPtr = linePtr->coordPtr;  
         *indexPtr = 0;  
         for(i=0; i<linePtr->numPoints; i++) {  
             dist = hypot(coordPtr[0] - x, coordPtr[1] - y);  
             if (dist<bestDist) {  
                 bestDist = dist;  
                 *indexPtr = 2*i;  
             }  
             coordPtr += 2;  
         }  
     } else {  
         if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) {  
             goto badIndex;  
         }  
         *indexPtr &= -2; /* if index is odd, make it even */  
         if (*indexPtr < 0){  
             *indexPtr = 0;  
         } else if (*indexPtr > (2*linePtr->numPoints)) {  
             *indexPtr = (2*linePtr->numPoints);  
         }  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * TranslateLine --  
  *  
  *      This procedure is called to move a line by a given amount.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The position of the line is offset by (xDelta, yDelta), and  
  *      the bounding box is updated in the generic part of the item  
  *      structure.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 TranslateLine(canvas, itemPtr, deltaX, deltaY)  
     Tk_Canvas canvas;                   /* Canvas containing item. */  
     Tk_Item *itemPtr;                   /* Item that is being moved. */  
     double deltaX, deltaY;              /* Amount by which item is to be  
                                          * moved. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     double *coordPtr;  
     int i;  
   
     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;  
             i++, coordPtr += 2) {  
         coordPtr[0] += deltaX;  
         coordPtr[1] += deltaY;  
     }  
     if (linePtr->firstArrowPtr != NULL) {  
         for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;  
                 i++, coordPtr += 2) {  
             coordPtr[0] += deltaX;  
             coordPtr[1] += deltaY;  
         }  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;  
                 i++, coordPtr += 2) {  
             coordPtr[0] += deltaX;  
             coordPtr[1] += deltaY;  
         }  
     }  
     ComputeLineBbox(canvas, linePtr);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ParseArrowShape --  
  *  
  *      This procedure is called back during option parsing to  
  *      parse arrow shape information.  
  *  
  * Results:  
  *      The return value is a standard Tcl result:  TCL_OK means  
  *      that the arrow shape information was parsed ok, and  
  *      TCL_ERROR means it couldn't be parsed.  
  *  
  * Side effects:  
  *      Arrow information in recordPtr is updated.  
  *  
  *--------------------------------------------------------------  
  */  
   
         /* ARGSUSED */  
 static int  
 ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset)  
     ClientData clientData;      /* Not used. */  
     Tcl_Interp *interp;         /* Used for error reporting. */  
     Tk_Window tkwin;            /* Not used. */  
     CONST char *value;          /* Textual specification of arrow shape. */  
     char *recordPtr;            /* Pointer to item record in which to  
                                  * store arrow information. */  
     int offset;                 /* Offset of shape information in widget  
                                  * record. */  
 {  
     LineItem *linePtr = (LineItem *) recordPtr;  
     double a, b, c;  
     int argc;  
     char **argv = NULL;  
   
     if (offset != Tk_Offset(LineItem, arrowShapeA)) {  
         panic("ParseArrowShape received bogus offset");  
     }  
   
     if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) {  
         syntaxError:  
         Tcl_ResetResult(interp);  
         Tcl_AppendResult(interp, "bad arrow shape \"", value,  
                 "\": must be list with three numbers", (char *) NULL);  
         if (argv != NULL) {  
             ckfree((char *) argv);  
         }  
         return TCL_ERROR;  
     }  
     if (argc != 3) {  
         goto syntaxError;  
     }  
     if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK)  
             || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[1], &b)  
                 != TCL_OK)  
             || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[2], &c)  
                 != TCL_OK)) {  
         goto syntaxError;  
     }  
     linePtr->arrowShapeA = (float)a;  
     linePtr->arrowShapeB = (float)b;  
     linePtr->arrowShapeC = (float)c;  
     ckfree((char *) argv);  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * PrintArrowShape --  
  *  
  *      This procedure is a callback invoked by the configuration  
  *      code to return a printable value describing an arrow shape.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
     /* ARGSUSED */  
 static char *  
 PrintArrowShape(clientData, tkwin, recordPtr, offset, freeProcPtr)  
     ClientData clientData;      /* Not used. */  
     Tk_Window tkwin;            /* Window associated with linePtr's widget. */  
     char *recordPtr;            /* Pointer to item record containing current  
                                  * shape information. */  
     int offset;                 /* Offset of arrow information in record. */  
     Tcl_FreeProc **freeProcPtr; /* Store address of procedure to call to  
                                  * free string here. */  
 {  
     LineItem *linePtr = (LineItem *) recordPtr;  
     char *buffer;  
   
     buffer = (char *) ckalloc(120);  
     sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA,  
             linePtr->arrowShapeB, linePtr->arrowShapeC);  
     *freeProcPtr = TCL_DYNAMIC;  
     return buffer;  
 }  
   
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ArrowParseProc --  
  *  
  *      This procedure is invoked during option processing to handle  
  *      the "-arrow" option.  
  *  
  * Results:  
  *      A standard Tcl return value.  
  *  
  * Side effects:  
  *      The arrow for a given item gets replaced by the arrow  
  *      indicated in the value argument.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 ArrowParseProc(clientData, interp, tkwin, value, widgRec, offset)  
     ClientData clientData;              /* some flags.*/  
     Tcl_Interp *interp;                 /* Used for reporting errors. */  
     Tk_Window tkwin;                    /* Window containing canvas widget. */  
     CONST char *value;                  /* Value of option. */  
     char *widgRec;                      /* Pointer to record for item. */  
     int offset;                         /* Offset into item. */  
 {  
     int c;  
     size_t length;  
   
     register Arrows *arrowPtr = (Arrows *) (widgRec + offset);  
   
     if(value == NULL || *value == 0) {  
         *arrowPtr = ARROWS_NONE;  
         return TCL_OK;  
     }  
   
     c = value[0];  
     length = strlen(value);  
   
     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {  
         *arrowPtr = ARROWS_NONE;  
         return TCL_OK;  
     }  
     if ((c == 'f') && (strncmp(value, "first", length) == 0)) {  
         *arrowPtr = ARROWS_FIRST;  
         return TCL_OK;  
     }  
     if ((c == 'l') && (strncmp(value, "last", length) == 0)) {  
         *arrowPtr = ARROWS_LAST;  
         return TCL_OK;  
     }  
     if ((c == 'b') && (strncmp(value, "both", length) == 0)) {  
         *arrowPtr = ARROWS_BOTH;  
         return TCL_OK;  
     }  
   
     Tcl_AppendResult(interp, "bad arrow spec \"", value,  
             "\": must be none, first, last, or both",  
             (char *) NULL);  
     *arrowPtr = ARROWS_NONE;  
     return TCL_ERROR;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ArrowPrintProc --  
  *  
  *      This procedure is invoked by the Tk configuration code  
  *      to produce a printable string for the "-arrow"  
  *      configuration option.  
  *  
  * Results:  
  *      The return value is a string describing the arrows for  
  *      the item referred to by "widgRec".  In addition, *freeProcPtr  
  *      is filled in with the address of a procedure to call to free  
  *      the result string when it's no longer needed (or NULL to  
  *      indicate that the string doesn't need to be freed).  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static char *  
 ArrowPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)  
     ClientData clientData;              /* Ignored. */  
     Tk_Window tkwin;                    /* Window containing canvas widget. */  
     char *widgRec;                      /* Pointer to record for item. */  
     int offset;                         /* Offset into item. */  
     Tcl_FreeProc **freeProcPtr;         /* Pointer to variable to fill in with  
                                          * information about how to reclaim  
                                          * storage for return string. */  
 {  
     register Arrows *arrowPtr = (Arrows *) (widgRec + offset);  
   
     switch (*arrowPtr) {  
       case ARROWS_FIRST:  
         return "first";  
       case ARROWS_LAST:  
         return "last";  
       case ARROWS_BOTH:  
         return "both";  
       default:  
         return "none";  
     }  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ConfigureArrows --  
  *  
  *      If arrowheads have been requested for a line, this  
  *      procedure makes arrangements for the arrowheads.  
  *  
  * Results:  
  *      Always returns TCL_OK.  
  *  
  * Side effects:  
  *      Information in linePtr is set up for one or two arrowheads.  
  *      the firstArrowPtr and lastArrowPtr polygons are allocated  
  *      and initialized, if need be, and the end points of the line  
  *      are adjusted so that a thick line doesn't stick out past  
  *      the arrowheads.  
  *  
  *--------------------------------------------------------------  
  */  
   
         /* ARGSUSED */  
 static int  
 ConfigureArrows(canvas, linePtr)  
     Tk_Canvas canvas;                   /* Canvas in which arrows will be  
                                          * displayed (interp and tkwin  
                                          * fields are needed). */  
     LineItem *linePtr;                  /* Item to configure for arrows. */  
 {  
     double *poly, *coordPtr;  
     double dx, dy, length, sinTheta, cosTheta, temp;  
     double fracHeight;                  /* Line width as fraction of  
                                          * arrowhead width. */  
     double backup;                      /* Distance to backup end points  
                                          * so the line ends in the middle  
                                          * of the arrowhead. */  
     double vertX, vertY;                /* Position of arrowhead vertex. */  
     double shapeA, shapeB, shapeC;      /* Adjusted coordinates (see  
                                          * explanation below). */  
     double width;  
     Tk_State state = linePtr->header.state;  
   
     if (linePtr->numPoints <2) {  
         return TCL_OK;  
     }  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     width = linePtr->outline.width;  
     if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) {  
         if (linePtr->outline.activeWidth>width) {  
             width = linePtr->outline.activeWidth;  
         }  
     } else if (state==TK_STATE_DISABLED) {  
         if (linePtr->outline.disabledWidth>0) {  
             width = linePtr->outline.disabledWidth;  
         }  
     }  
   
     /*  
      * The code below makes a tiny increase in the shape parameters  
      * for the line.  This is a bit of a hack, but it seems to result  
      * in displays that more closely approximate the specified parameters.  
      * Without the adjustment, the arrows come out smaller than expected.  
      */  
   
     shapeA = linePtr->arrowShapeA + 0.001;  
     shapeB = linePtr->arrowShapeB + 0.001;  
     shapeC = linePtr->arrowShapeC + width/2.0 + 0.001;  
   
     /*  
      * If there's an arrowhead on the first point of the line, compute  
      * its polygon and adjust the first point of the line so that the  
      * line doesn't stick out past the leading edge of the arrowhead.  
      */  
   
     fracHeight = (width/2.0)/shapeC;  
     backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0;  
     if (linePtr->arrow != ARROWS_LAST) {  
         poly = linePtr->firstArrowPtr;  
         if (poly == NULL) {  
             poly = (double *) ckalloc((unsigned)  
                     (2*PTS_IN_ARROW*sizeof(double)));  
             poly[0] = poly[10] = linePtr->coordPtr[0];  
             poly[1] = poly[11] = linePtr->coordPtr[1];  
             linePtr->firstArrowPtr = poly;  
         }  
         dx = poly[0] - linePtr->coordPtr[2];  
         dy = poly[1] - linePtr->coordPtr[3];  
         length = hypot(dx, dy);  
         if (length == 0) {  
             sinTheta = cosTheta = 0.0;  
         } else {  
             sinTheta = dy/length;  
             cosTheta = dx/length;  
         }  
         vertX = poly[0] - shapeA*cosTheta;  
         vertY = poly[1] - shapeA*sinTheta;  
         temp = shapeC*sinTheta;  
         poly[2] = poly[0] - shapeB*cosTheta + temp;  
         poly[8] = poly[2] - 2*temp;  
         temp = shapeC*cosTheta;  
         poly[3] = poly[1] - shapeB*sinTheta - temp;  
         poly[9] = poly[3] + 2*temp;  
         poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);  
         poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);  
         poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);  
         poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);  
   
         /*  
          * Polygon done.  Now move the first point towards the second so  
          * that the corners at the end of the line are inside the  
          * arrowhead.  
          */  
   
         linePtr->coordPtr[0] = poly[0] - backup*cosTheta;  
         linePtr->coordPtr[1] = poly[1] - backup*sinTheta;  
     }  
   
     /*  
      * Similar arrowhead calculation for the last point of the line.  
      */  
   
     if (linePtr->arrow != ARROWS_FIRST) {  
         coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);  
         poly = linePtr->lastArrowPtr;  
         if (poly == NULL) {  
             poly = (double *) ckalloc((unsigned)  
                     (2*PTS_IN_ARROW*sizeof(double)));  
             poly[0] = poly[10] = coordPtr[2];  
             poly[1] = poly[11] = coordPtr[3];  
             linePtr->lastArrowPtr = poly;  
         }  
         dx = poly[0] - coordPtr[0];  
         dy = poly[1] - coordPtr[1];  
         length = hypot(dx, dy);  
         if (length == 0) {  
             sinTheta = cosTheta = 0.0;  
         } else {  
             sinTheta = dy/length;  
             cosTheta = dx/length;  
         }  
         vertX = poly[0] - shapeA*cosTheta;  
         vertY = poly[1] - shapeA*sinTheta;  
         temp = shapeC*sinTheta;  
         poly[2] = poly[0] - shapeB*cosTheta + temp;  
         poly[8] = poly[2] - 2*temp;  
         temp = shapeC*cosTheta;  
         poly[3] = poly[1] - shapeB*sinTheta - temp;  
         poly[9] = poly[3] + 2*temp;  
         poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);  
         poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);  
         poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);  
         poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);  
         coordPtr[2] = poly[0] - backup*cosTheta;  
         coordPtr[3] = poly[1] - backup*sinTheta;  
     }  
   
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * LineToPostscript --  
  *  
  *      This procedure is called to generate Postscript for  
  *      line items.  
  *  
  * Results:  
  *      The return value is a standard Tcl result.  If an error  
  *      occurs in generating Postscript then an error message is  
  *      left in the interp's result, replacing whatever used  
  *      to be there.  If no error occurs, then Postscript for the  
  *      item is appended to the result.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 LineToPostscript(interp, canvas, itemPtr, prepass)  
     Tcl_Interp *interp;                 /* Leave Postscript or error message  
                                          * here. */  
     Tk_Canvas canvas;                   /* Information about overall canvas. */  
     Tk_Item *itemPtr;                   /* Item for which Postscript is  
                                          * wanted. */  
     int prepass;                        /* 1 means this is a prepass to  
                                          * collect font information;  0 means  
                                          * final Postscript is being created. */  
 {  
     LineItem *linePtr = (LineItem *) itemPtr;  
     char buffer[64 + TCL_INTEGER_SPACE];  
     char *style;  
   
     double width;  
     XColor *color;  
     Pixmap stipple;  
     Tk_State state = itemPtr->state;  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     width = linePtr->outline.width;  
     color = linePtr->outline.color;  
     stipple = linePtr->outline.stipple;  
     if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {  
         if (linePtr->outline.activeWidth>width) {  
             width = linePtr->outline.activeWidth;  
         }  
         if (linePtr->outline.activeColor!=NULL) {  
             color = linePtr->outline.activeColor;  
         }  
         if (linePtr->outline.activeStipple!=None) {  
             stipple = linePtr->outline.activeStipple;  
         }  
     } else if (state==TK_STATE_DISABLED) {  
         if (linePtr->outline.disabledWidth>0) {  
             width = linePtr->outline.disabledWidth;  
         }  
         if (linePtr->outline.disabledColor!=NULL) {  
             color = linePtr->outline.disabledColor;  
         }  
         if (linePtr->outline.disabledStipple!=None) {  
             stipple = linePtr->outline.disabledStipple;  
         }  
     }  
   
     if (color == NULL || linePtr->numPoints<1 || linePtr->coordPtr==NULL) {  
         return TCL_OK;  
     }  
   
     if (linePtr->numPoints==1) {  
         sprintf(buffer, "%.15g %.15g translate %.15g %.15g",  
                 linePtr->coordPtr[0], Tk_CanvasPsY(canvas, linePtr->coordPtr[1]),  
                 width/2.0, width/2.0);  
         Tcl_AppendResult(interp, "matrix currentmatrix\n",buffer,  
                 " scale 1 0 moveto 0 0 1 0 360 arc\nsetmatrix\n", (char *) NULL);  
         if (Tk_CanvasPsColor(interp, canvas, color)  
                 != TCL_OK) {  
             return TCL_ERROR;  
         }  
         if (stipple != None) {  
             Tcl_AppendResult(interp, "clip ", (char *) NULL);  
             if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {  
                 return TCL_ERROR;  
             }  
         } else {  
             Tcl_AppendResult(interp, "fill\n", (char *) NULL);  
         }  
         return TCL_OK;  
     }  
     /*  
      * Generate a path for the line's center-line (do this differently  
      * for straight lines and smoothed lines).  
      */  
   
     if ((!linePtr->smooth) || (linePtr->numPoints < 3)) {  
         Tk_CanvasPsPath(interp, canvas, linePtr->coordPtr, linePtr->numPoints);  
     } else {  
         if ((stipple == None) && linePtr->smooth->postscriptProc) {  
             linePtr->smooth->postscriptProc(interp, canvas,  
                     linePtr->coordPtr, linePtr->numPoints, linePtr->splineSteps);  
         } else {  
             /*  
              * Special hack: Postscript printers don't appear to be able  
              * to turn a path drawn with "curveto"s into a clipping path  
              * without exceeding resource limits, so TkMakeBezierPostscript  
              * won't work for stippled curves.  Instead, generate all of  
              * the intermediate points here and output them into the  
              * Postscript file with "lineto"s instead.  
              */  
   
             double staticPoints[2*MAX_STATIC_POINTS];  
             double *pointPtr;  
             int numPoints;  
   
             numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL,  
                     linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,  
                     (double *) NULL);  
             pointPtr = staticPoints;  
             if (numPoints > MAX_STATIC_POINTS) {  
                 pointPtr = (double *) ckalloc((unsigned)  
                         (numPoints * 2 * sizeof(double)));  
             }  
             numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,  
                     linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,  
                     pointPtr);  
             Tk_CanvasPsPath(interp, canvas, pointPtr, numPoints);  
             if (pointPtr != staticPoints) {  
                 ckfree((char *) pointPtr);  
             }  
         }  
     }  
   
     /*  
      * Set other line-drawing parameters and stroke out the line.  
      */  
   
     style = "0 setlinecap\n";  
     if (linePtr->capStyle == CapRound) {  
         style = "1 setlinecap\n";  
     } else if (linePtr->capStyle == CapProjecting) {  
         style = "2 setlinecap\n";  
     }  
     Tcl_AppendResult(interp, style, (char *) NULL);  
     style = "0 setlinejoin\n";  
     if (linePtr->joinStyle == JoinRound) {  
         style = "1 setlinejoin\n";  
     } else if (linePtr->joinStyle == JoinBevel) {  
         style = "2 setlinejoin\n";  
     }  
     Tcl_AppendResult(interp, style, (char *) NULL);  
   
     if (Tk_CanvasPsOutline(canvas, itemPtr,  
             &(linePtr->outline)) != TCL_OK) {  
         return TCL_ERROR;  
     }  
   
     /*  
      * Output polygons for the arrowheads, if there are any.  
      */  
   
     if (linePtr->firstArrowPtr != NULL) {  
         if (stipple != None) {  
             Tcl_AppendResult(interp, "grestore gsave\n",  
                     (char *) NULL);  
         }  
         if (ArrowheadPostscript(interp, canvas, linePtr,  
                 linePtr->firstArrowPtr) != TCL_OK) {  
             return TCL_ERROR;  
         }  
     }  
     if (linePtr->lastArrowPtr != NULL) {  
         if (stipple != None) {  
             Tcl_AppendResult(interp, "grestore gsave\n", (char *) NULL);  
         }  
         if (ArrowheadPostscript(interp, canvas, linePtr,  
                 linePtr->lastArrowPtr) != TCL_OK) {  
             return TCL_ERROR;  
         }  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ArrowheadPostscript --  
  *  
  *      This procedure is called to generate Postscript for  
  *      an arrowhead for a line item.  
  *  
  * Results:  
  *      The return value is a standard Tcl result.  If an error  
  *      occurs in generating Postscript then an error message is  
  *      left in the interp's result, replacing whatever used  
  *      to be there.  If no error occurs, then Postscript for the  
  *      arrowhead is appended to the result.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 ArrowheadPostscript(interp, canvas, linePtr, arrowPtr)  
     Tcl_Interp *interp;                 /* Leave Postscript or error message  
                                          * here. */  
     Tk_Canvas canvas;                   /* Information about overall canvas. */  
     LineItem *linePtr;                  /* Line item for which Postscript is  
                                          * being generated. */  
     double *arrowPtr;                   /* Pointer to first of five points  
                                          * describing arrowhead polygon. */  
 {  
     Pixmap stipple;  
     Tk_State state = linePtr->header.state;  
   
     if(state == TK_STATE_NULL) {  
         state = ((TkCanvas *)canvas)->canvas_state;  
     }  
   
     stipple = linePtr->outline.stipple;  
     if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) {  
         if (linePtr->outline.activeStipple!=None) {  
             stipple = linePtr->outline.activeStipple;  
         }  
     } else if (state==TK_STATE_DISABLED) {  
         if (linePtr->outline.activeStipple!=None) {  
             stipple = linePtr->outline.disabledStipple;  
         }  
     }  
   
     Tk_CanvasPsPath(interp, canvas, arrowPtr, PTS_IN_ARROW);  
     if (stipple != None) {  
         Tcl_AppendResult(interp, "clip ", (char *) NULL);  
         if (Tk_CanvasPsStipple(interp, canvas, stipple)  
                 != TCL_OK) {  
             return TCL_ERROR;  
         }  
     } else {  
         Tcl_AppendResult(interp, "fill\n", (char *) NULL);  
     }  
     return TCL_OK;  
 }  
   
   
 /* $History: tkCanvLine.c $  
  *  
  * *****************  Version 1  *****************  
  * User: Dtashley     Date: 1/02/01    Time: 2:35a  
  * Created in $/IjuScripter, IjuConsole/Source/Tk Base  
  * Initial check-in.  
  */  
   
 /* End of TKCANVLINE.C */  
1    /* $Header$ */
2    
3    /*
4     * tkCanvLine.c --
5     *
6     *      This file implements line items for canvas widgets.
7     *
8     * Copyright (c) 1991-1994 The Regents of the University of California.
9     * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10     * Copyright (c) 1998-1999 by Scriptics Corporation.
11     *
12     * See the file "license.terms" for information on usage and redistribution
13     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14     *
15     * RCS: @(#) $Id: tkcanvline.c,v 1.1.1.1 2001/06/13 04:56:46 dtashley Exp $
16     */
17    
18    #include <stdio.h>
19    #include "tkInt.h"
20    #include "tkPort.h"
21    #include "tkCanvas.h"
22    
23    /*
24     * The structure below defines the record for each line item.
25     */
26    
27    typedef enum {
28        ARROWS_NONE, ARROWS_FIRST, ARROWS_LAST, ARROWS_BOTH
29    } Arrows;
30    
31    typedef struct LineItem  {
32        Tk_Item header;             /* Generic stuff that's the same for all
33                                     * types.  MUST BE FIRST IN STRUCTURE. */
34        Tk_Outline outline;         /* Outline structure */
35        Tk_Canvas canvas;           /* Canvas containing item.  Needed for
36                                     * parsing arrow shapes. */
37        int numPoints;              /* Number of points in line (always >= 0). */
38        double *coordPtr;           /* Pointer to malloc-ed array containing
39                                     * x- and y-coords of all points in line.
40                                     * X-coords are even-valued indices, y-coords
41                                     * are corresponding odd-valued indices. If
42                                     * the line has arrowheads then the first
43                                     * and last points have been adjusted to refer
44                                     * to the necks of the arrowheads rather than
45                                     * their tips.  The actual endpoints are
46                                     * stored in the *firstArrowPtr and
47                                     * *lastArrowPtr, if they exist. */
48        int capStyle;               /* Cap style for line. */
49        int joinStyle;              /* Join style for line. */
50        GC arrowGC;                 /* Graphics context for drawing arrowheads. */
51        Arrows arrow;               /* Indicates whether or not to draw arrowheads:
52                                     * "none", "first", "last", or "both". */
53        float arrowShapeA;          /* Distance from tip of arrowhead to center. */
54        float arrowShapeB;          /* Distance from tip of arrowhead to trailing
55                                     * point, measured along shaft. */
56        float arrowShapeC;          /* Distance of trailing points from outside
57                                     * edge of shaft. */
58        double *firstArrowPtr;      /* Points to array of PTS_IN_ARROW points
59                                     * describing polygon for arrowhead at first
60                                     * point in line.  First point of arrowhead
61                                     * is tip.  Malloc'ed.  NULL means no arrowhead
62                                     * at first point. */
63        double *lastArrowPtr;       /* Points to polygon for arrowhead at last
64                                     * point in line (PTS_IN_ARROW points, first
65                                     * of which is tip).  Malloc'ed.  NULL means
66                                     * no arrowhead at last point. */
67        Tk_SmoothMethod *smooth;    /* Non-zero means draw line smoothed (i.e.
68                                     * with Bezier splines). */
69        int splineSteps;            /* Number of steps in each spline segment. */
70    } LineItem;
71    
72    /*
73     * Number of points in an arrowHead:
74     */
75    
76    #define PTS_IN_ARROW 6
77    
78    /*
79     * Prototypes for procedures defined in this file:
80     */
81    
82    static int              ArrowheadPostscript _ANSI_ARGS_((Tcl_Interp *interp,
83                                Tk_Canvas canvas, LineItem *linePtr,
84                                double *arrowPtr));
85    static void             ComputeLineBbox _ANSI_ARGS_((Tk_Canvas canvas,
86                                LineItem *linePtr));
87    static int              ConfigureLine _ANSI_ARGS_((Tcl_Interp *interp,
88                                Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
89                                Tcl_Obj *CONST argv[], int flags));
90    static int              ConfigureArrows _ANSI_ARGS_((Tk_Canvas canvas,
91                                LineItem *linePtr));
92    static int              CreateLine _ANSI_ARGS_((Tcl_Interp *interp,
93                                Tk_Canvas canvas, struct Tk_Item *itemPtr,
94                                int argc, Tcl_Obj *CONST argv[]));
95    static void             DeleteLine _ANSI_ARGS_((Tk_Canvas canvas,
96                                Tk_Item *itemPtr, Display *display));
97    static void             DisplayLine _ANSI_ARGS_((Tk_Canvas canvas,
98                                Tk_Item *itemPtr, Display *display, Drawable dst,
99                                int x, int y, int width, int height));
100    static int              GetLineIndex _ANSI_ARGS_((Tcl_Interp *interp,
101                                Tk_Canvas canvas, Tk_Item *itemPtr,
102                                Tcl_Obj *obj, int *indexPtr));
103    static int              LineCoords _ANSI_ARGS_((Tcl_Interp *interp,
104                                Tk_Canvas canvas, Tk_Item *itemPtr,
105                                int argc, Tcl_Obj *CONST argv[]));
106    static void             LineDeleteCoords _ANSI_ARGS_((Tk_Canvas canvas,
107                                Tk_Item *itemPtr, int first, int last));
108    static void             LineInsert _ANSI_ARGS_((Tk_Canvas canvas,
109                                Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj));
110    static int              LineToArea _ANSI_ARGS_((Tk_Canvas canvas,
111                                Tk_Item *itemPtr, double *rectPtr));
112    static double           LineToPoint _ANSI_ARGS_((Tk_Canvas canvas,
113                                Tk_Item *itemPtr, double *coordPtr));
114    static int              LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
115                                Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
116    static int              ArrowParseProc _ANSI_ARGS_((ClientData clientData,
117                                Tcl_Interp *interp, Tk_Window tkwin,
118                                CONST char *value, char *recordPtr, int offset));
119    static char *           ArrowPrintProc _ANSI_ARGS_((ClientData clientData,
120                                Tk_Window tkwin, char *recordPtr, int offset,
121                                Tcl_FreeProc **freeProcPtr));
122    static int              ParseArrowShape _ANSI_ARGS_((ClientData clientData,
123                                Tcl_Interp *interp, Tk_Window tkwin,
124                                CONST char *value, char *recordPtr, int offset));
125    static char *           PrintArrowShape _ANSI_ARGS_((ClientData clientData,
126                                Tk_Window tkwin, char *recordPtr, int offset,
127                                Tcl_FreeProc **freeProcPtr));
128    static void             ScaleLine _ANSI_ARGS_((Tk_Canvas canvas,
129                                Tk_Item *itemPtr, double originX, double originY,
130                                double scaleX, double scaleY));
131    static void             TranslateLine _ANSI_ARGS_((Tk_Canvas canvas,
132                                Tk_Item *itemPtr, double deltaX, double deltaY));
133    
134    /*
135     * Information used for parsing configuration specs.  If you change any
136     * of the default strings, be sure to change the corresponding default
137     * values in CreateLine.
138     */
139    
140    static Tk_CustomOption arrowShapeOption = {
141        (Tk_OptionParseProc *) ParseArrowShape,
142        PrintArrowShape, (ClientData) NULL
143    };
144    static Tk_CustomOption arrowOption = {
145        (Tk_OptionParseProc *) ArrowParseProc,
146        ArrowPrintProc, (ClientData) NULL
147    };
148    static Tk_CustomOption smoothOption = {
149        (Tk_OptionParseProc *) TkSmoothParseProc,
150        TkSmoothPrintProc, (ClientData) NULL
151    };
152    static Tk_CustomOption stateOption = {
153        (Tk_OptionParseProc *) TkStateParseProc,
154        TkStatePrintProc, (ClientData) 2
155    };
156    static Tk_CustomOption tagsOption = {
157        (Tk_OptionParseProc *) Tk_CanvasTagsParseProc,
158        Tk_CanvasTagsPrintProc, (ClientData) NULL
159    };
160    static Tk_CustomOption dashOption = {
161        (Tk_OptionParseProc *) TkCanvasDashParseProc,
162        TkCanvasDashPrintProc, (ClientData) NULL
163    };
164    static Tk_CustomOption offsetOption = {
165        (Tk_OptionParseProc *) TkOffsetParseProc,
166        TkOffsetPrintProc,
167        (ClientData) (TK_OFFSET_RELATIVE|TK_OFFSET_INDEX)
168    };
169    static Tk_CustomOption pixelOption = {
170        (Tk_OptionParseProc *) TkPixelParseProc,
171        TkPixelPrintProc, (ClientData) NULL
172    };
173    
174    static Tk_ConfigSpec configSpecs[] = {
175        {TK_CONFIG_CUSTOM, "-activedash", (char *) NULL, (char *) NULL,
176            (char *) NULL, Tk_Offset(LineItem, outline.activeDash),
177            TK_CONFIG_NULL_OK, &dashOption},
178        {TK_CONFIG_COLOR, "-activefill", (char *) NULL, (char *) NULL,
179            (char *) NULL, Tk_Offset(LineItem, outline.activeColor),
180            TK_CONFIG_NULL_OK},
181        {TK_CONFIG_BITMAP, "-activestipple", (char *) NULL, (char *) NULL,
182            (char *) NULL, Tk_Offset(LineItem, outline.activeStipple),
183            TK_CONFIG_NULL_OK},
184        {TK_CONFIG_CUSTOM, "-activewidth", (char *) NULL, (char *) NULL,
185            "0.0", Tk_Offset(LineItem, outline.activeWidth),
186            TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
187        {TK_CONFIG_CUSTOM, "-arrow", (char *) NULL, (char *) NULL,
188            "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT, &arrowOption},
189        {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL,
190            "8 10 3", Tk_Offset(LineItem, arrowShapeA),
191            TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},
192        {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL,
193            "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT},
194        {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
195            "black", Tk_Offset(LineItem, outline.color), TK_CONFIG_NULL_OK},
196        {TK_CONFIG_CUSTOM, "-dash", (char *) NULL, (char *) NULL,
197            (char *) NULL, Tk_Offset(LineItem, outline.dash),
198            TK_CONFIG_NULL_OK, &dashOption},
199        {TK_CONFIG_PIXELS, "-dashoffset", (char *) NULL, (char *) NULL,
200            "0", Tk_Offset(LineItem, outline.offset),
201            TK_CONFIG_DONT_SET_DEFAULT},
202        {TK_CONFIG_CUSTOM, "-disableddash", (char *) NULL, (char *) NULL,
203            (char *) NULL, Tk_Offset(LineItem, outline.disabledDash),
204            TK_CONFIG_NULL_OK, &dashOption},
205        {TK_CONFIG_COLOR, "-disabledfill", (char *) NULL, (char *) NULL,
206            (char *) NULL, Tk_Offset(LineItem, outline.disabledColor),
207            TK_CONFIG_NULL_OK},
208        {TK_CONFIG_BITMAP, "-disabledstipple", (char *) NULL, (char *) NULL,
209            (char *) NULL, Tk_Offset(LineItem, outline.disabledStipple),
210            TK_CONFIG_NULL_OK},
211        {TK_CONFIG_CUSTOM, "-disabledwidth", (char *) NULL, (char *) NULL,
212            "0.0", Tk_Offset(LineItem, outline.disabledWidth),
213            TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
214        {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,
215            "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},
216        {TK_CONFIG_CUSTOM, "-offset", (char *) NULL, (char *) NULL,
217            "0,0", Tk_Offset(LineItem, outline.tsoffset),
218            TK_CONFIG_DONT_SET_DEFAULT, &offsetOption},
219        {TK_CONFIG_CUSTOM, "-smooth", (char *) NULL, (char *) NULL,
220            "0", Tk_Offset(LineItem, smooth),
221            TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
222        {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,
223            "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},
224        {TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL,
225            (char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK,
226            &stateOption},
227        {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
228            (char *) NULL, Tk_Offset(LineItem, outline.stipple),
229            TK_CONFIG_NULL_OK},
230        {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
231            (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
232        {TK_CONFIG_CUSTOM, "-width", (char *) NULL, (char *) NULL,
233            "1.0", Tk_Offset(LineItem, outline.width),
234            TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
235        {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
236            (char *) NULL, 0, 0}
237    };
238    
239    /*
240     * The structures below defines the line item type by means
241     * of procedures that can be invoked by generic item code.
242     */
243    
244    Tk_ItemType tkLineType = {
245        "line",                             /* name */
246        sizeof(LineItem),                   /* itemSize */
247        CreateLine,                         /* createProc */
248        configSpecs,                        /* configSpecs */
249        ConfigureLine,                      /* configureProc */
250        LineCoords,                         /* coordProc */
251        DeleteLine,                         /* deleteProc */
252        DisplayLine,                        /* displayProc */
253        TK_CONFIG_OBJS,                     /* flags */
254        LineToPoint,                        /* pointProc */
255        LineToArea,                         /* areaProc */
256        LineToPostscript,                   /* postscriptProc */
257        ScaleLine,                          /* scaleProc */
258        TranslateLine,                      /* translateProc */
259        (Tk_ItemIndexProc *) GetLineIndex,  /* indexProc */
260        (Tk_ItemCursorProc *) NULL,         /* icursorProc */
261        (Tk_ItemSelectionProc *) NULL,      /* selectionProc */
262        (Tk_ItemInsertProc *) LineInsert,   /* insertProc */
263        LineDeleteCoords,                   /* dTextProc */
264        (Tk_ItemType *) NULL,               /* nextPtr */
265    };
266    
267    /*
268     * The definition below determines how large are static arrays
269     * used to hold spline points (splines larger than this have to
270     * have their arrays malloc-ed).
271     */
272    
273    #define MAX_STATIC_POINTS 200
274    
275    /*
276     *--------------------------------------------------------------
277     *
278     * CreateLine --
279     *
280     *      This procedure is invoked to create a new line item in
281     *      a canvas.
282     *
283     * Results:
284     *      A standard Tcl return value.  If an error occurred in
285     *      creating the item, then an error message is left in
286     *      the interp's result;  in this case itemPtr is left uninitialized,
287     *      so it can be safely freed by the caller.
288     *
289     * Side effects:
290     *      A new line item is created.
291     *
292     *--------------------------------------------------------------
293     */
294    
295    static int
296    CreateLine(interp, canvas, itemPtr, argc, argv)
297        Tcl_Interp *interp;                 /* Interpreter for error reporting. */
298        Tk_Canvas canvas;                   /* Canvas to hold new item. */
299        Tk_Item *itemPtr;                   /* Record to hold new item;  header
300                                             * has been initialized by caller. */
301        int argc;                           /* Number of arguments in argv. */
302        Tcl_Obj *CONST argv[];              /* Arguments describing line. */
303    {
304        LineItem *linePtr = (LineItem *) itemPtr;
305        int i;
306    
307        /*
308         * Carry out initialization that is needed to set defaults and to
309         * allow proper cleanup after errors during the the remainder of
310         * this procedure.
311         */
312    
313        Tk_CreateOutline(&(linePtr->outline));
314        linePtr->canvas = canvas;
315        linePtr->numPoints = 0;
316        linePtr->coordPtr = NULL;
317        linePtr->capStyle = CapButt;
318        linePtr->joinStyle = JoinRound;
319        linePtr->arrowGC = None;
320        linePtr->arrow = ARROWS_NONE;
321        linePtr->arrowShapeA = (float)8.0;
322        linePtr->arrowShapeB = (float)10.0;
323        linePtr->arrowShapeC = (float)3.0;
324        linePtr->firstArrowPtr = NULL;
325        linePtr->lastArrowPtr = NULL;
326        linePtr->smooth = (Tk_SmoothMethod *) NULL;
327        linePtr->splineSteps = 12;
328    
329        /*
330         * Count the number of points and then parse them into a point
331         * array.  Leading arguments are assumed to be points if they
332         * start with a digit or a minus sign followed by a digit.
333         */
334    
335        for (i = 0; i < argc; i++) {
336            char *arg = Tcl_GetStringFromObj(argv[i], NULL);
337            if ((arg[0] == '-') && (arg[1] >= 'a')
338                    && (arg[1] <= 'z')) {
339                break;
340            }
341        }
342        if (i && (LineCoords(interp, canvas, itemPtr, i, argv) != TCL_OK)) {
343            goto error;
344        }
345        if (ConfigureLine(interp, canvas, itemPtr, argc-i, argv+i, 0) == TCL_OK) {
346            return TCL_OK;
347        }
348    
349        error:
350        DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
351        return TCL_ERROR;
352    }
353    
354    /*
355     *--------------------------------------------------------------
356     *
357     * LineCoords --
358     *
359     *      This procedure is invoked to process the "coords" widget
360     *      command on lines.  See the user documentation for details
361     *      on what it does.
362     *
363     * Results:
364     *      Returns TCL_OK or TCL_ERROR, and sets the interp's result.
365     *
366     * Side effects:
367     *      The coordinates for the given item may be changed.
368     *
369     *--------------------------------------------------------------
370     */
371    
372    static int
373    LineCoords(interp, canvas, itemPtr, argc, argv)
374        Tcl_Interp *interp;                 /* Used for error reporting. */
375        Tk_Canvas canvas;                   /* Canvas containing item. */
376        Tk_Item *itemPtr;                   /* Item whose coordinates are to be
377                                             * read or modified. */
378        int argc;                           /* Number of coordinates supplied in
379                                             * argv. */
380        Tcl_Obj *CONST argv[];              /* Array of coordinates: x1, y1,
381                                             * x2, y2, ... */
382    {
383        LineItem *linePtr = (LineItem *) itemPtr;
384        int i, numPoints;
385        double *coordPtr;
386    
387        if (argc == 0) {
388            int numCoords;
389            Tcl_Obj *subobj, *obj = Tcl_NewObj();
390    
391            numCoords = 2*linePtr->numPoints;
392            if (linePtr->firstArrowPtr != NULL) {
393                coordPtr = linePtr->firstArrowPtr;
394            } else {
395                coordPtr = linePtr->coordPtr;
396            }
397            for (i = 0; i < numCoords; i++, coordPtr++) {
398                if (i == 2) {
399                    coordPtr = linePtr->coordPtr+2;
400                }
401                if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) {
402                    coordPtr = linePtr->lastArrowPtr;
403                }
404                subobj = Tcl_NewDoubleObj(*coordPtr);
405                Tcl_ListObjAppendElement(interp, obj, subobj);
406            }
407            Tcl_SetObjResult(interp, obj);
408            return TCL_OK;
409        }
410        if (argc == 1) {
411            if (Tcl_ListObjGetElements(interp, argv[0], &argc,
412                    (Tcl_Obj ***) &argv) != TCL_OK) {
413                return TCL_ERROR;
414            }
415        }
416        if (argc & 1) {
417            Tcl_AppendResult(interp,
418                    "odd number of coordinates specified for line",
419                    (char *) NULL);
420            return TCL_ERROR;
421        } else if (argc < 4) {
422            Tcl_AppendResult(interp,
423                    "too few coordinates specified for line",
424                    (char *) NULL);
425            return TCL_ERROR;
426        } else {
427            numPoints = argc/2;
428            if (linePtr->numPoints != numPoints) {
429                coordPtr = (double *) ckalloc((unsigned)
430                        (sizeof(double) * argc));
431                if (linePtr->coordPtr != NULL) {
432                    ckfree((char *) linePtr->coordPtr);
433                }
434                linePtr->coordPtr = coordPtr;
435                linePtr->numPoints = numPoints;
436            }
437            coordPtr = linePtr->coordPtr;
438            for (i = 0; i <argc; i++) {
439                if (Tk_CanvasGetCoordFromObj(interp, canvas, argv[i],
440                        coordPtr++) != TCL_OK) {
441                    return TCL_ERROR;
442                }
443            }
444    
445            /*
446             * Update arrowheads by throwing away any existing arrow-head
447             * information and calling ConfigureArrows to recompute it.
448             */
449    
450            if (linePtr->firstArrowPtr != NULL) {
451                ckfree((char *) linePtr->firstArrowPtr);
452                linePtr->firstArrowPtr = NULL;
453            }
454            if (linePtr->lastArrowPtr != NULL) {
455                ckfree((char *) linePtr->lastArrowPtr);
456                linePtr->lastArrowPtr = NULL;
457            }
458            if (linePtr->arrow != ARROWS_NONE) {
459                ConfigureArrows(canvas, linePtr);
460            }
461            ComputeLineBbox(canvas, linePtr);
462        }
463        return TCL_OK;
464    }
465    
466    /*
467     *--------------------------------------------------------------
468     *
469     * ConfigureLine --
470     *
471     *      This procedure is invoked to configure various aspects
472     *      of a line item such as its background color.
473     *
474     * Results:
475     *      A standard Tcl result code.  If an error occurs, then
476     *      an error message is left in the interp's result.
477     *
478     * Side effects:
479     *      Configuration information, such as colors and stipple
480     *      patterns, may be set for itemPtr.
481     *
482     *--------------------------------------------------------------
483     */
484    
485    static int
486    ConfigureLine(interp, canvas, itemPtr, argc, argv, flags)
487        Tcl_Interp *interp;         /* Used for error reporting. */
488        Tk_Canvas canvas;           /* Canvas containing itemPtr. */
489        Tk_Item *itemPtr;           /* Line item to reconfigure. */
490        int argc;                   /* Number of elements in argv.  */
491        Tcl_Obj *CONST argv[];      /* Arguments describing things to configure. */
492        int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
493    {
494        LineItem *linePtr = (LineItem *) itemPtr;
495        XGCValues gcValues;
496        GC newGC, arrowGC;
497        unsigned long mask;
498        Tk_Window tkwin;
499        Tk_State state;
500    
501        tkwin = Tk_CanvasTkwin(canvas);
502        if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, (char **) argv,
503                (char *) linePtr, flags|TK_CONFIG_OBJS) != TCL_OK) {
504            return TCL_ERROR;
505        }
506    
507        /*
508         * A few of the options require additional processing, such as
509         * graphics contexts.
510         */
511    
512        state = itemPtr->state;
513    
514        if(state == TK_STATE_NULL) {
515            state = ((TkCanvas *)canvas)->canvas_state;
516        }
517    
518        if (linePtr->outline.activeWidth > linePtr->outline.width ||
519                linePtr->outline.activeDash.number != 0 ||
520                linePtr->outline.activeColor != NULL ||
521                linePtr->outline.activeStipple != None) {
522            itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;
523        } else {
524            itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
525        }
526        mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr,
527                &(linePtr->outline));
528        if (mask) {
529            if (linePtr->arrow == ARROWS_NONE) {
530                gcValues.cap_style = linePtr->capStyle;
531                mask |= GCCapStyle;
532            }
533            gcValues.join_style = linePtr->joinStyle;
534            mask |= GCJoinStyle;
535            newGC = Tk_GetGC(tkwin, mask, &gcValues);
536            gcValues.line_width = 0;
537            arrowGC = Tk_GetGC(tkwin, mask, &gcValues);
538        } else {
539            newGC = arrowGC = None;
540        }
541        if (linePtr->outline.gc != None) {
542            Tk_FreeGC(Tk_Display(tkwin), linePtr->outline.gc);
543        }
544        if (linePtr->arrowGC != None) {
545            Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC);
546        }
547        linePtr->outline.gc = newGC;
548        linePtr->arrowGC = arrowGC;
549    
550        /*
551         * Keep spline parameters within reasonable limits.
552         */
553    
554        if (linePtr->splineSteps < 1) {
555            linePtr->splineSteps = 1;
556        } else if (linePtr->splineSteps > 100) {
557            linePtr->splineSteps = 100;
558        }
559    
560        if ((!linePtr->numPoints) || (state==TK_STATE_HIDDEN)) {
561            ComputeLineBbox(canvas, linePtr);
562            return TCL_OK;
563        }
564    
565        /*
566         * Setup arrowheads, if needed.  If arrowheads are turned off,
567         * restore the line's endpoints (they were shortened when the
568         * arrowheads were added).
569         */
570    
571        if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != ARROWS_FIRST)
572                && (linePtr->arrow != ARROWS_BOTH)) {
573            linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
574            linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
575            ckfree((char *) linePtr->firstArrowPtr);
576            linePtr->firstArrowPtr = NULL;
577        }
578        if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != ARROWS_LAST)
579                && (linePtr->arrow != ARROWS_BOTH)) {
580            int i;
581    
582            i = 2*(linePtr->numPoints-1);
583            linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
584            linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
585            ckfree((char *) linePtr->lastArrowPtr);
586            linePtr->lastArrowPtr = NULL;
587        }
588        if (linePtr->arrow != ARROWS_NONE) {
589            ConfigureArrows(canvas, linePtr);
590        }
591    
592        /*
593         * Recompute bounding box for line.
594         */
595    
596        ComputeLineBbox(canvas, linePtr);
597    
598        return TCL_OK;
599    }
600    
601    /*
602     *--------------------------------------------------------------
603     *
604     * DeleteLine --
605     *
606     *      This procedure is called to clean up the data structure
607     *      associated with a line item.
608     *
609     * Results:
610     *      None.
611     *
612     * Side effects:
613     *      Resources associated with itemPtr are released.
614     *
615     *--------------------------------------------------------------
616     */
617    
618    static void
619    DeleteLine(canvas, itemPtr, display)
620        Tk_Canvas canvas;                   /* Info about overall canvas widget. */
621        Tk_Item *itemPtr;                   /* Item that is being deleted. */
622        Display *display;                   /* Display containing window for
623                                             * canvas. */
624    {
625        LineItem *linePtr = (LineItem *) itemPtr;
626    
627        Tk_DeleteOutline(display, &(linePtr->outline));
628        if (linePtr->coordPtr != NULL) {
629            ckfree((char *) linePtr->coordPtr);
630        }
631        if (linePtr->arrowGC != None) {
632            Tk_FreeGC(display, linePtr->arrowGC);
633        }
634        if (linePtr->firstArrowPtr != NULL) {
635            ckfree((char *) linePtr->firstArrowPtr);
636        }
637        if (linePtr->lastArrowPtr != NULL) {
638            ckfree((char *) linePtr->lastArrowPtr);
639        }
640    }
641    
642    /*
643     *--------------------------------------------------------------
644     *
645     * ComputeLineBbox --
646     *
647     *      This procedure is invoked to compute the bounding box of
648     *      all the pixels that may be drawn as part of a line.
649     *
650     * Results:
651     *      None.
652     *
653     * Side effects:
654     *      The fields x1, y1, x2, and y2 are updated in the header
655     *      for itemPtr.
656     *
657     *--------------------------------------------------------------
658     */
659    
660    static void
661    ComputeLineBbox(canvas, linePtr)
662        Tk_Canvas canvas;                   /* Canvas that contains item. */
663        LineItem *linePtr;                  /* Item whose bbos is to be
664                                             * recomputed. */
665    {
666        double *coordPtr;
667        int i, intWidth;
668        double width;
669        Tk_State state = linePtr->header.state;
670        Tk_TSOffset *tsoffset;
671    
672        if(state == TK_STATE_NULL) {
673            state = ((TkCanvas *)canvas)->canvas_state;
674        }
675    
676        if (!(linePtr->numPoints) || (state==TK_STATE_HIDDEN)) {
677            linePtr->header.x1 = -1;
678            linePtr->header.x2 = -1;
679            linePtr->header.y1 = -1;
680            linePtr->header.y2 = -1;
681            return;
682        }
683    
684        width = linePtr->outline.width;
685        if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) {
686            if (linePtr->outline.activeWidth>width) {
687                width = linePtr->outline.activeWidth;
688            }
689        } else if (state==TK_STATE_DISABLED) {
690            if (linePtr->outline.disabledWidth>0) {
691                width = linePtr->outline.disabledWidth;
692            }
693        }
694    
695        coordPtr = linePtr->coordPtr;
696        linePtr->header.x1 = linePtr->header.x2 = (int) *coordPtr;
697        linePtr->header.y1 = linePtr->header.y2 = (int) coordPtr[1];
698    
699        /*
700         * Compute the bounding box of all the points in the line,
701         * then expand in all directions by the line's width to take
702         * care of butting or rounded corners and projecting or
703         * rounded caps.  This expansion is an overestimate (worst-case
704         * is square root of two over two) but it's simple.  Don't do
705         * anything special for curves.  This causes an additional
706         * overestimate in the bounding box, but is faster.
707         */
708    
709        for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;
710                i++, coordPtr += 2) {
711            TkIncludePoint((Tk_Item *) linePtr, coordPtr);
712        }
713        width = linePtr->outline.width;
714        if (width < 1.0) {
715            width = 1.0;
716        }
717        if (linePtr->arrow != ARROWS_NONE) {
718            if (linePtr->arrow != ARROWS_LAST) {
719                TkIncludePoint((Tk_Item *) linePtr, linePtr->firstArrowPtr);
720            }
721            if (linePtr->arrow != ARROWS_FIRST) {
722                TkIncludePoint((Tk_Item *) linePtr, linePtr->lastArrowPtr);
723            }
724        }
725    
726        tsoffset = &linePtr->outline.tsoffset;
727        if (tsoffset->flags & TK_OFFSET_INDEX) {
728            double *coordPtr = linePtr->coordPtr + (tsoffset->flags & ~TK_OFFSET_INDEX);
729            if (tsoffset->flags <= 0) {
730                coordPtr = linePtr->coordPtr;
731                if ((linePtr->arrow == ARROWS_FIRST) || (linePtr->arrow == ARROWS_BOTH)) {
732                    coordPtr = linePtr->firstArrowPtr;
733                }
734            }
735            if (tsoffset->flags > (linePtr->numPoints * 2)) {
736                coordPtr = linePtr->coordPtr + (linePtr->numPoints * 2);
737                if ((linePtr->arrow == ARROWS_LAST) || (linePtr->arrow == ARROWS_BOTH)) {
738                    coordPtr = linePtr->lastArrowPtr;
739                }
740            }
741            tsoffset->xoffset = (int) (coordPtr[0] + 0.5);
742            tsoffset->yoffset = (int) (coordPtr[1] + 0.5);
743        } else {
744            if (tsoffset->flags & TK_OFFSET_LEFT) {
745                tsoffset->xoffset = linePtr->header.x1;
746            } else if (tsoffset->flags & TK_OFFSET_CENTER) {
747                tsoffset->xoffset = (linePtr->header.x1 + linePtr->header.x2)/2;
748            } else if (tsoffset->flags & TK_OFFSET_RIGHT) {
749                tsoffset->xoffset = linePtr->header.x2;
750            }
751            if (tsoffset->flags & TK_OFFSET_TOP) {
752                tsoffset->yoffset = linePtr->header.y1;
753            } else if (tsoffset->flags & TK_OFFSET_MIDDLE) {
754                tsoffset->yoffset = (linePtr->header.y1 + linePtr->header.y2)/2;
755            } else if (tsoffset->flags & TK_OFFSET_BOTTOM) {
756                tsoffset->yoffset = linePtr->header.y2;
757            }
758        }
759    
760        intWidth = (int) (width + 0.5);
761        linePtr->header.x1 -= intWidth;
762        linePtr->header.x2 += intWidth;
763        linePtr->header.y1 -= intWidth;
764        linePtr->header.y2 += intWidth;
765    
766        if (linePtr->numPoints==1) {
767            linePtr->header.x1 -= 1;
768            linePtr->header.x2 += 1;
769            linePtr->header.y1 -= 1;
770            linePtr->header.y2 += 1;
771            return;
772        }
773    
774        /*
775         * For mitered lines, make a second pass through all the points.
776         * Compute the locations of the two miter vertex points and add
777         * those into the bounding box.
778         */
779    
780        if (linePtr->joinStyle == JoinMiter) {
781            for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;
782                    i--, coordPtr += 2) {
783                double miter[4];
784                int j;
785        
786                if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
787                        width, miter, miter+2)) {
788                    for (j = 0; j < 4; j += 2) {
789                        TkIncludePoint((Tk_Item *) linePtr, miter+j);
790                    }
791                }
792            }
793        }
794    
795        /*
796         * Add in the sizes of arrowheads, if any.
797         */
798    
799        if (linePtr->arrow != ARROWS_NONE) {
800            if (linePtr->arrow != ARROWS_LAST) {
801                for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
802                        i++, coordPtr += 2) {
803                    TkIncludePoint((Tk_Item *) linePtr, coordPtr);
804                }
805            }
806            if (linePtr->arrow != ARROWS_FIRST) {
807                for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
808                        i++, coordPtr += 2) {
809                    TkIncludePoint((Tk_Item *) linePtr, coordPtr);
810                }
811            }
812        }
813    
814        /*
815         * Add one more pixel of fudge factor just to be safe (e.g.
816         * X may round differently than we do).
817         */
818    
819        linePtr->header.x1 -= 1;
820        linePtr->header.x2 += 1;
821        linePtr->header.y1 -= 1;
822        linePtr->header.y2 += 1;
823    }
824    
825    /*
826     *--------------------------------------------------------------
827     *
828     * DisplayLine --
829     *
830     *      This procedure is invoked to draw a line item in a given
831     *      drawable.
832     *
833     * Results:
834     *      None.
835     *
836     * Side effects:
837     *      ItemPtr is drawn in drawable using the transformation
838     *      information in canvas.
839     *
840     *--------------------------------------------------------------
841     */
842    
843    static void
844    DisplayLine(canvas, itemPtr, display, drawable, x, y, width, height)
845        Tk_Canvas canvas;                   /* Canvas that contains item. */
846        Tk_Item *itemPtr;                   /* Item to be displayed. */
847        Display *display;                   /* Display on which to draw item. */
848        Drawable drawable;                  /* Pixmap or window in which to draw
849                                             * item. */
850        int x, y, width, height;            /* Describes region of canvas that
851                                             * must be redisplayed (not used). */
852    {
853        LineItem *linePtr = (LineItem *) itemPtr;
854        XPoint staticPoints[MAX_STATIC_POINTS];
855        XPoint *pointPtr;
856        XPoint *pPtr;
857        double *coordPtr, linewidth;
858        int i, numPoints;
859        Tk_State state = itemPtr->state;
860        Pixmap stipple = linePtr->outline.stipple;
861    
862        if ((!linePtr->numPoints)||(linePtr->outline.gc==None)) {
863            return;
864        }
865    
866        if (state == TK_STATE_NULL) {
867                state = ((TkCanvas *)canvas)->canvas_state;
868        }
869        linewidth = linePtr->outline.width;
870        if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
871            if (linePtr->outline.activeStipple!=None) {
872                stipple = linePtr->outline.activeStipple;
873            }
874            if (linePtr->outline.activeWidth>linewidth) {
875                linewidth = linePtr->outline.activeWidth;
876            }
877        } else if (state==TK_STATE_DISABLED) {
878            if (linePtr->outline.disabledStipple!=None) {
879                stipple = linePtr->outline.disabledStipple;
880            }
881            if (linePtr->outline.disabledWidth>linewidth) {
882                linewidth = linePtr->outline.activeWidth;
883            }
884        }
885        /*
886         * Build up an array of points in screen coordinates.  Use a
887         * static array unless the line has an enormous number of points;
888         * in this case, dynamically allocate an array.  For smoothed lines,
889         * generate the curve points on each redisplay.
890         */
891    
892        if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
893            numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL,
894                    linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
895                    (double *) NULL);
896        } else {
897            numPoints = linePtr->numPoints;
898        }
899    
900        if (numPoints <= MAX_STATIC_POINTS) {
901            pointPtr = staticPoints;
902        } else {
903            pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));
904        }
905    
906        if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
907            numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,
908                    linePtr->numPoints, linePtr->splineSteps, pointPtr,
909                    (double *) NULL);
910        } else {
911            for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr;
912                    i < linePtr->numPoints;  i += 1, coordPtr += 2, pPtr++) {
913                Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1],
914                        &pPtr->x, &pPtr->y);
915            }
916        }
917    
918        /*
919         * Display line, the free up line storage if it was dynamically
920         * allocated.  If we're stippling, then modify the stipple offset
921         * in the GC.  Be sure to reset the offset when done, since the
922         * GC is supposed to be read-only.
923         */
924    
925        if (Tk_ChangeOutlineGC(canvas, itemPtr, &(linePtr->outline))) {
926            Tk_CanvasSetOffset(canvas, linePtr->arrowGC, &linePtr->outline.tsoffset);
927        }
928        if (numPoints>1) {
929            XDrawLines(display, drawable, linePtr->outline.gc, pointPtr, numPoints,
930                CoordModeOrigin);
931        } else {
932            int intwidth = (int) (linewidth + 0.5);
933            if (intwidth<1) {
934                intwidth=1;
935            }
936            XFillArc(display, drawable, linePtr->outline.gc,
937                    pointPtr->x - intwidth/2, pointPtr->y - intwidth/2,
938                    (unsigned int)intwidth+1, (unsigned int)intwidth+1, 0, 64*360);
939        }
940        if (pointPtr != staticPoints) {
941            ckfree((char *) pointPtr);
942        }
943    
944        /*
945         * Display arrowheads, if they are wanted.
946         */
947    
948        if (linePtr->firstArrowPtr != NULL) {
949            TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW,
950                    display, drawable, linePtr->arrowGC, NULL);
951        }
952        if (linePtr->lastArrowPtr != NULL) {
953            TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW,
954                    display, drawable, linePtr->arrowGC, NULL);
955        }
956        if (Tk_ResetOutlineGC(canvas, itemPtr, &(linePtr->outline))) {
957            XSetTSOrigin(display, linePtr->arrowGC, 0, 0);
958        }
959    }
960    
961    /*
962     *--------------------------------------------------------------
963     *
964     * LineInsert --
965     *
966     *      Insert coords into a line item at a given index.
967     *
968     * Results:
969     *      None.
970     *
971     * Side effects:
972     *      The coords in the given item is modified.
973     *
974     *--------------------------------------------------------------
975     */
976    
977    static void
978    LineInsert(canvas, itemPtr, beforeThis, obj)
979        Tk_Canvas canvas;           /* Canvas containing text item. */
980        Tk_Item *itemPtr;           /* Line item to be modified. */
981        int beforeThis;             /* Index before which new coordinates
982                                     * are to be inserted. */
983        Tcl_Obj *obj;               /* New coordinates to be inserted. */
984    {
985        LineItem *linePtr = (LineItem *) itemPtr;
986        int length, argc, i;
987        double *new, *coordPtr;
988        Tk_State state = itemPtr->state;
989        Tcl_Obj **objv;
990    
991        if(state == TK_STATE_NULL) {
992            state = ((TkCanvas *)canvas)->canvas_state;
993        }
994    
995        if (!obj || (Tcl_ListObjGetElements((Tcl_Interp *) NULL, obj, &argc, &objv) != TCL_OK)
996                || !argc || argc&1) {
997            return;
998        }
999        length = 2*linePtr->numPoints;
1000        if (beforeThis < 0) {
1001            beforeThis = 0;
1002        }
1003        if (beforeThis > length) {
1004            beforeThis = length;
1005        }
1006        if (linePtr->firstArrowPtr != NULL) {
1007            linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
1008            linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
1009        }
1010        if (linePtr->lastArrowPtr != NULL) {
1011            linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0];
1012            linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1];
1013        }
1014        new = (double *) ckalloc((unsigned)(sizeof(double) * (length + argc)));
1015        for(i=0; i<beforeThis; i++) {
1016            new[i] = linePtr->coordPtr[i];
1017        }
1018        for(i=0; i<argc; i++) {
1019            if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,objv[i],
1020                    new+(i+beforeThis))!=TCL_OK) {
1021                Tcl_ResetResult(((TkCanvas *)canvas)->interp);
1022                ckfree((char *) new);
1023                return;
1024            }
1025        }
1026    
1027        for(i=beforeThis; i<length; i++) {
1028            new[i+argc] = linePtr->coordPtr[i];
1029        }
1030        if(linePtr->coordPtr) ckfree((char *)linePtr->coordPtr);
1031        linePtr->coordPtr = new;
1032        linePtr->numPoints = (length + argc)/2;
1033    
1034        if ((length>3) && (state != TK_STATE_HIDDEN)) {
1035            /*
1036             * This is some optimizing code that will result that only the part
1037             * of the polygon that changed (and the objects that are overlapping
1038             * with that part) need to be redrawn. A special flag is set that
1039             * instructs the general canvas code not to redraw the whole
1040             * object. If this flag is not set, the canvas will do the redrawing,
1041             * otherwise I have to do it here.
1042             */
1043            itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW;
1044    
1045            if (beforeThis>0) {beforeThis -= 2; argc+=2; }
1046            if ((beforeThis+argc)<length) argc+=2;
1047            if (linePtr->smooth) {
1048                if(beforeThis>0) {
1049                    beforeThis-=2; argc+=2;
1050                }
1051                if((beforeThis+argc+2)<length) {
1052                    argc+=2;
1053                }
1054            }
1055            itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[beforeThis];
1056            itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[beforeThis+1];
1057            if ((linePtr->firstArrowPtr != NULL) && (beforeThis<1)) {
1058                /* include old first arrow */
1059                for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1060                        i++, coordPtr += 2) {
1061                    TkIncludePoint(itemPtr, coordPtr);
1062                }
1063            }
1064            if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)>=length)) {
1065                    /* include old last arrow */
1066                for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1067                        i++, coordPtr += 2) {
1068                    TkIncludePoint(itemPtr, coordPtr);
1069                }
1070            }
1071            coordPtr = linePtr->coordPtr+beforeThis+2;
1072            for(i=2; i<argc; i+=2) {
1073                TkIncludePoint(itemPtr, coordPtr);
1074                    coordPtr+=2;
1075            }
1076        }
1077        if (linePtr->firstArrowPtr != NULL) {
1078            ckfree((char *) linePtr->firstArrowPtr);
1079            linePtr->firstArrowPtr = NULL;
1080        }
1081        if (linePtr->lastArrowPtr != NULL) {
1082            ckfree((char *) linePtr->lastArrowPtr);
1083            linePtr->lastArrowPtr = NULL;
1084        }
1085        if (linePtr->arrow != ARROWS_NONE) {
1086                ConfigureArrows(canvas, linePtr);
1087        }
1088    
1089        if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) {
1090            double width;
1091            int intWidth;
1092            if ((linePtr->firstArrowPtr != NULL) && (beforeThis>2)) {
1093                /* include new first arrow */
1094                for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1095                        i++, coordPtr += 2) {
1096                    TkIncludePoint(itemPtr, coordPtr);
1097                }
1098            }
1099            if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)<(length-2))) {
1100                /* include new right arrow */
1101                for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1102                        i++, coordPtr += 2) {
1103                    TkIncludePoint(itemPtr, coordPtr);
1104                }
1105            }
1106            width = linePtr->outline.width;
1107            if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1108                    if (linePtr->outline.activeWidth>width) {
1109                        width = linePtr->outline.activeWidth;
1110                    }
1111            } else if (state==TK_STATE_DISABLED) {
1112                    if (linePtr->outline.disabledWidth>0) {
1113                        width = linePtr->outline.disabledWidth;
1114                    }
1115            }
1116            intWidth = (int) (width + 0.5);
1117            if (intWidth < 1) {
1118                intWidth = 1;
1119            }
1120            itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth;
1121            itemPtr->x2 += intWidth; itemPtr->y2 += intWidth;
1122            Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1,
1123                    itemPtr->x2, itemPtr->y2);
1124        }
1125    
1126        ComputeLineBbox(canvas, linePtr);
1127    }
1128    
1129    /*
1130     *--------------------------------------------------------------
1131     *
1132     * LineDeleteCoords --
1133     *
1134     *      Delete one or more coordinates from a line item.
1135     *
1136     * Results:
1137     *      None.
1138     *
1139     * Side effects:
1140     *      Characters between "first" and "last", inclusive, get
1141     *      deleted from itemPtr.
1142     *
1143     *--------------------------------------------------------------
1144     */
1145    
1146    static void
1147    LineDeleteCoords(canvas, itemPtr, first, last)
1148        Tk_Canvas canvas;           /* Canvas containing itemPtr. */
1149        Tk_Item *itemPtr;           /* Item in which to delete characters. */
1150        int first;                  /* Index of first character to delete. */
1151        int last;                   /* Index of last character to delete. */
1152    {
1153        LineItem *linePtr = (LineItem *) itemPtr;
1154        int count, i, first1, last1;
1155        int length = 2*linePtr->numPoints;
1156        double *coordPtr;
1157        Tk_State state = itemPtr->state;
1158    
1159        if(state == TK_STATE_NULL) {
1160            state = ((TkCanvas *)canvas)->canvas_state;
1161        }
1162    
1163        first &= -2;
1164        last &= -2;
1165    
1166        if (first < 0) {
1167            first = 0;
1168        }
1169        if (last >= length) {
1170            last = length-2;
1171        }
1172        if (first > last) {
1173            return;
1174        }
1175        if (linePtr->firstArrowPtr != NULL) {
1176            linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
1177            linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
1178        }
1179        if (linePtr->lastArrowPtr != NULL) {
1180            linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0];
1181            linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1];
1182        }
1183        first1 = first; last1 = last;
1184        if(first1>0) first1 -= 2;
1185        if(last1<length-2) last1 += 2;
1186        if (linePtr->smooth) {
1187            if(first1>0) first1 -= 2;
1188            if(last1<length-2) last1 += 2;
1189        }
1190    
1191        if((first1<2) && (last1 >= length-2)) {
1192            /*
1193             * This is some optimizing code that will result that only the part
1194             * of the line that changed (and the objects that are overlapping
1195             * with that part) need to be redrawn. A special flag is set that
1196             * instructs the general canvas code not to redraw the whole
1197             * object. If this flag is set, the redrawing has to be done here,
1198             * otherwise the general Canvas code will take care of it.
1199             */
1200    
1201            itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW;
1202            itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[first1];
1203            itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[first1+1];
1204            if ((linePtr->firstArrowPtr != NULL) && (first1<2)) {
1205                /* include old first arrow */
1206                for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1207                        i++, coordPtr += 2) {
1208                    TkIncludePoint(itemPtr, coordPtr);
1209                }
1210            }
1211            if ((linePtr->lastArrowPtr != NULL) && (last1>=length-2)) {
1212                    /* include old last arrow */
1213                for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1214                        i++, coordPtr += 2) {
1215                    TkIncludePoint(itemPtr, coordPtr);
1216                }
1217            }
1218            coordPtr = linePtr->coordPtr+first1+2;
1219            for (i=first1+2; i<=last1; i+=2) {
1220                TkIncludePoint(itemPtr, coordPtr);
1221                coordPtr+=2;
1222            }
1223        }
1224    
1225        count = last + 2 - first;
1226        for (i=last+2; i<length; i++) {
1227            linePtr->coordPtr[i-count] = linePtr->coordPtr[i];
1228        }
1229        linePtr->numPoints -= count/2;
1230        if (linePtr->firstArrowPtr != NULL) {
1231            ckfree((char *) linePtr->firstArrowPtr);
1232            linePtr->firstArrowPtr = NULL;
1233        }
1234        if (linePtr->lastArrowPtr != NULL) {
1235            ckfree((char *) linePtr->lastArrowPtr);
1236            linePtr->lastArrowPtr = NULL;
1237        }
1238        if (linePtr->arrow != ARROWS_NONE) {
1239                ConfigureArrows(canvas, linePtr);
1240        }
1241        if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) {
1242            double width;
1243            int intWidth;
1244            if ((linePtr->firstArrowPtr != NULL) && (first1<4)) {
1245                /* include new first arrow */
1246                for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1247                        i++, coordPtr += 2) {
1248                    TkIncludePoint(itemPtr, coordPtr);
1249                }
1250            }
1251            if ((linePtr->lastArrowPtr != NULL) && (last1>(length-4))) {
1252                /* include new right arrow */
1253                for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1254                        i++, coordPtr += 2) {
1255                    TkIncludePoint(itemPtr, coordPtr);
1256                }
1257            }
1258            width = linePtr->outline.width;
1259            if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1260                    if (linePtr->outline.activeWidth>width) {
1261                        width = linePtr->outline.activeWidth;
1262                    }
1263            } else if (state==TK_STATE_DISABLED) {
1264                    if (linePtr->outline.disabledWidth>0) {
1265                        width = linePtr->outline.disabledWidth;
1266                    }
1267            }
1268            intWidth = (int) (width + 0.5);
1269            if (intWidth < 1) {
1270                intWidth = 1;
1271            }
1272            itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth;
1273            itemPtr->x2 += intWidth; itemPtr->y2 += intWidth;
1274            Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1,
1275                    itemPtr->x2, itemPtr->y2);
1276        }
1277        ComputeLineBbox(canvas, linePtr);
1278    }
1279    
1280    /*
1281     *--------------------------------------------------------------
1282     *
1283     * LineToPoint --
1284     *
1285     *      Computes the distance from a given point to a given
1286     *      line, in canvas units.
1287     *
1288     * Results:
1289     *      The return value is 0 if the point whose x and y coordinates
1290     *      are pointPtr[0] and pointPtr[1] is inside the line.  If the
1291     *      point isn't inside the line then the return value is the
1292     *      distance from the point to the line.
1293     *
1294     * Side effects:
1295     *      None.
1296     *
1297     *--------------------------------------------------------------
1298     */
1299    
1300            /* ARGSUSED */
1301    static double
1302    LineToPoint(canvas, itemPtr, pointPtr)
1303        Tk_Canvas canvas;           /* Canvas containing item. */
1304        Tk_Item *itemPtr;           /* Item to check against point. */
1305        double *pointPtr;           /* Pointer to x and y coordinates. */
1306    {
1307        Tk_State state = itemPtr->state;
1308        LineItem *linePtr = (LineItem *) itemPtr;
1309        double *coordPtr, *linePoints;
1310        double staticSpace[2*MAX_STATIC_POINTS];
1311        double poly[10];
1312        double bestDist, dist, width;
1313        int numPoints, count;
1314        int changedMiterToBevel;    /* Non-zero means that a mitered corner
1315                                     * had to be treated as beveled after all
1316                                     * because the angle was < 11 degrees. */
1317    
1318        bestDist = 1.0e36;
1319    
1320        /*
1321         * Handle smoothed lines by generating an expanded set of points
1322         * against which to do the check.
1323         */
1324    
1325        if(state == TK_STATE_NULL) {
1326            state = ((TkCanvas *)canvas)->canvas_state;
1327        }
1328    
1329        width = linePtr->outline.width;
1330        if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1331            if (linePtr->outline.activeWidth>width) {
1332                width = linePtr->outline.activeWidth;
1333            }
1334        } else if (state==TK_STATE_DISABLED) {
1335            if (linePtr->outline.disabledWidth>0) {
1336                width = linePtr->outline.disabledWidth;
1337            }
1338        }
1339    
1340        if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
1341            numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL,
1342                    linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
1343                    (double *) NULL);
1344            if (numPoints <= MAX_STATIC_POINTS) {
1345                linePoints = staticSpace;
1346            } else {
1347                linePoints = (double *) ckalloc((unsigned)
1348                        (2*numPoints*sizeof(double)));
1349            }
1350            numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,
1351                    linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
1352                    linePoints);
1353        } else {
1354            numPoints = linePtr->numPoints;
1355            linePoints = linePtr->coordPtr;
1356        }
1357    
1358        if (width < 1.0) {
1359            width = 1.0;
1360        }
1361    
1362        if (!numPoints || itemPtr->state==TK_STATE_HIDDEN) {
1363            return bestDist;
1364        } else if (numPoints == 1) {
1365            bestDist = hypot(linePoints[0] - pointPtr[0], linePoints[1] - pointPtr[1])
1366                        - width/2.0;
1367            if (bestDist < 0) bestDist = 0;
1368            return bestDist;
1369        }
1370    
1371        /*
1372         * The overall idea is to iterate through all of the edges of
1373         * the line, computing a polygon for each edge and testing the
1374         * point against that polygon.  In addition, there are additional
1375         * tests to deal with rounded joints and caps.
1376         */
1377    
1378        changedMiterToBevel = 0;
1379        for (count = numPoints, coordPtr = linePoints; count >= 2;
1380                count--, coordPtr += 2) {
1381    
1382            /*
1383             * If rounding is done around the first point then compute
1384             * the distance between the point and the point.
1385             */
1386    
1387            if (((linePtr->capStyle == CapRound) && (count == numPoints))
1388                    || ((linePtr->joinStyle == JoinRound)
1389                            && (count != numPoints))) {
1390                dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
1391                        - width/2.0;
1392                if (dist <= 0.0) {
1393                    bestDist = 0.0;
1394                    goto done;
1395                } else if (dist < bestDist) {
1396                    bestDist = dist;
1397                }
1398            }
1399    
1400            /*
1401             * Compute the polygonal shape corresponding to this edge,
1402             * consisting of two points for the first point of the edge
1403             * and two points for the last point of the edge.
1404             */
1405    
1406            if (count == numPoints) {
1407                TkGetButtPoints(coordPtr+2, coordPtr, width,
1408                        linePtr->capStyle == CapProjecting, poly, poly+2);
1409            } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
1410                poly[0] = poly[6];
1411                poly[1] = poly[7];
1412                poly[2] = poly[4];
1413                poly[3] = poly[5];
1414            } else {
1415                TkGetButtPoints(coordPtr+2, coordPtr, width, 0,
1416                        poly, poly+2);
1417    
1418                /*
1419                 * If this line uses beveled joints, then check the distance
1420                 * to a polygon comprising the last two points of the previous
1421                 * polygon and the first two from this polygon;  this checks
1422                 * the wedges that fill the mitered joint.
1423                 */
1424    
1425                if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
1426                    poly[8] = poly[0];
1427                    poly[9] = poly[1];
1428                    dist = TkPolygonToPoint(poly, 5, pointPtr);
1429                    if (dist <= 0.0) {
1430                        bestDist = 0.0;
1431                        goto done;
1432                    } else if (dist < bestDist) {
1433                        bestDist = dist;
1434                    }
1435                    changedMiterToBevel = 0;
1436                }
1437            }
1438            if (count == 2) {
1439                TkGetButtPoints(coordPtr, coordPtr+2, width,
1440                        linePtr->capStyle == CapProjecting, poly+4, poly+6);
1441            } else if (linePtr->joinStyle == JoinMiter) {
1442                if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
1443                        width, poly+4, poly+6) == 0) {
1444                    changedMiterToBevel = 1;
1445                    TkGetButtPoints(coordPtr, coordPtr+2, width,
1446                            0, poly+4, poly+6);
1447                }
1448            } else {
1449                TkGetButtPoints(coordPtr, coordPtr+2, width, 0,
1450                        poly+4, poly+6);
1451            }
1452            poly[8] = poly[0];
1453            poly[9] = poly[1];
1454            dist = TkPolygonToPoint(poly, 5, pointPtr);
1455            if (dist <= 0.0) {
1456                bestDist = 0.0;
1457                goto done;
1458            } else if (dist < bestDist) {
1459                bestDist = dist;
1460            }
1461        }
1462    
1463        /*
1464         * If caps are rounded, check the distance to the cap around the
1465         * final end point of the line.
1466         */
1467    
1468        if (linePtr->capStyle == CapRound) {
1469            dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
1470                    - width/2.0;
1471            if (dist <= 0.0) {
1472                bestDist = 0.0;
1473                goto done;
1474            } else if (dist < bestDist) {
1475                bestDist = dist;
1476            }
1477        }
1478    
1479        /*
1480         * If there are arrowheads, check the distance to the arrowheads.
1481         */
1482    
1483        if (linePtr->arrow != ARROWS_NONE) {
1484            if (linePtr->arrow != ARROWS_LAST) {
1485                dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,
1486                        pointPtr);
1487                if (dist <= 0.0) {
1488                    bestDist = 0.0;
1489                    goto done;
1490                } else if (dist < bestDist) {
1491                    bestDist = dist;
1492                }
1493            }
1494            if (linePtr->arrow != ARROWS_FIRST) {
1495                dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,
1496                        pointPtr);
1497                if (dist <= 0.0) {
1498                    bestDist = 0.0;
1499                    goto done;
1500                } else if (dist < bestDist) {
1501                    bestDist = dist;
1502                }
1503            }
1504        }
1505    
1506        done:
1507        if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
1508            ckfree((char *) linePoints);
1509        }
1510        return bestDist;
1511    }
1512    
1513    /*
1514     *--------------------------------------------------------------
1515     *
1516     * LineToArea --
1517     *
1518     *      This procedure is called to determine whether an item
1519     *      lies entirely inside, entirely outside, or overlapping
1520     *      a given rectangular area.
1521     *
1522     * Results:
1523     *      -1 is returned if the item is entirely outside the
1524     *      area, 0 if it overlaps, and 1 if it is entirely
1525     *      inside the given area.
1526     *
1527     * Side effects:
1528     *      None.
1529     *
1530     *--------------------------------------------------------------
1531     */
1532    
1533            /* ARGSUSED */
1534    static int
1535    LineToArea(canvas, itemPtr, rectPtr)
1536        Tk_Canvas canvas;           /* Canvas containing item. */
1537        Tk_Item *itemPtr;           /* Item to check against line. */
1538        double *rectPtr;
1539    {
1540        LineItem *linePtr = (LineItem *) itemPtr;
1541        double staticSpace[2*MAX_STATIC_POINTS];
1542        double *linePoints;
1543        int numPoints, result;
1544        double radius, width;
1545        Tk_State state = itemPtr->state;
1546    
1547        if(state == TK_STATE_NULL) {
1548            state = ((TkCanvas *)canvas)->canvas_state;
1549        }
1550        width = linePtr->outline.width;
1551        if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1552            if (linePtr->outline.activeWidth>width) {
1553                width = linePtr->outline.activeWidth;
1554            }
1555        } else if (state==TK_STATE_DISABLED) {
1556            if (linePtr->outline.disabledWidth>0) {
1557                width = linePtr->outline.disabledWidth;
1558            }
1559        }
1560    
1561        radius = (width+1.0)/2.0;
1562    
1563        if ((state==TK_STATE_HIDDEN) || !linePtr->numPoints) {
1564            return -1;
1565        } else if (linePtr->numPoints == 1) {
1566            double oval[4];
1567            oval[0] = linePtr->coordPtr[0]-radius;
1568            oval[1] = linePtr->coordPtr[1]-radius;
1569            oval[2] = linePtr->coordPtr[0]+radius;
1570            oval[3] = linePtr->coordPtr[1]+radius;
1571            return TkOvalToArea(oval, rectPtr);
1572        }
1573    
1574        /*
1575         * Handle smoothed lines by generating an expanded set of points
1576         * against which to do the check.
1577         */
1578    
1579        if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
1580            numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL,
1581                    linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
1582                    (double *) NULL);
1583            if (numPoints <= MAX_STATIC_POINTS) {
1584                linePoints = staticSpace;
1585            } else {
1586                linePoints = (double *) ckalloc((unsigned)
1587                        (2*numPoints*sizeof(double)));
1588            }
1589            numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,
1590                    linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
1591                    linePoints);
1592        } else {
1593            numPoints = linePtr->numPoints;
1594            linePoints = linePtr->coordPtr;
1595        }
1596    
1597        /*
1598         * Check the segments of the line.
1599         */
1600    
1601         if (width < 1.0) {
1602            width = 1.0;
1603        }
1604    
1605        result = TkThickPolyLineToArea(linePoints, numPoints,
1606                width, linePtr->capStyle, linePtr->joinStyle,
1607                rectPtr);
1608        if (result == 0) {
1609            goto done;
1610        }
1611    
1612        /*
1613         * Check arrowheads, if any.
1614         */
1615    
1616        if (linePtr->arrow != ARROWS_NONE) {
1617            if (linePtr->arrow != ARROWS_LAST) {
1618                if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,
1619                        rectPtr) != result) {
1620                    result = 0;
1621                    goto done;
1622                }
1623            }
1624            if (linePtr->arrow != ARROWS_FIRST) {
1625                if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,
1626                        rectPtr) != result) {
1627                    result = 0;
1628                    goto done;
1629                }
1630            }
1631        }
1632    
1633        done:
1634        if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
1635            ckfree((char *) linePoints);
1636        }
1637        return result;
1638    }
1639    
1640    /*
1641     *--------------------------------------------------------------
1642     *
1643     * ScaleLine --
1644     *
1645     *      This procedure is invoked to rescale a line item.
1646     *
1647     * Results:
1648     *      None.
1649     *
1650     * Side effects:
1651     *      The line referred to by itemPtr is rescaled so that the
1652     *      following transformation is applied to all point
1653     *      coordinates:
1654     *              x' = originX + scaleX*(x-originX)
1655     *              y' = originY + scaleY*(y-originY)
1656     *
1657     *--------------------------------------------------------------
1658     */
1659    
1660    static void
1661    ScaleLine(canvas, itemPtr, originX, originY, scaleX, scaleY)
1662        Tk_Canvas canvas;                   /* Canvas containing line. */
1663        Tk_Item *itemPtr;                   /* Line to be scaled. */
1664        double originX, originY;            /* Origin about which to scale rect. */
1665        double scaleX;                      /* Amount to scale in X direction. */
1666        double scaleY;                      /* Amount to scale in Y direction. */
1667    {
1668        LineItem *linePtr = (LineItem *) itemPtr;
1669        double *coordPtr;
1670        int i;
1671    
1672        /*
1673         * Delete any arrowheads before scaling all the points (so that
1674         * the end-points of the line get restored).
1675         */
1676    
1677        if (linePtr->firstArrowPtr != NULL) {
1678            linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
1679            linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
1680            ckfree((char *) linePtr->firstArrowPtr);
1681            linePtr->firstArrowPtr = NULL;
1682        }
1683        if (linePtr->lastArrowPtr != NULL) {
1684            int i;
1685    
1686            i = 2*(linePtr->numPoints-1);
1687            linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
1688            linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
1689            ckfree((char *) linePtr->lastArrowPtr);
1690            linePtr->lastArrowPtr = NULL;
1691        }
1692        for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
1693                i++, coordPtr += 2) {
1694            coordPtr[0] = originX + scaleX*(*coordPtr - originX);
1695            coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
1696        }
1697        if (linePtr->arrow != ARROWS_NONE) {
1698            ConfigureArrows(canvas, linePtr);
1699        }
1700        ComputeLineBbox(canvas, linePtr);
1701    }
1702    
1703    /*
1704     *--------------------------------------------------------------
1705     *
1706     * GetLineIndex --
1707     *
1708     *      Parse an index into a line item and return either its value
1709     *      or an error.
1710     *
1711     * Results:
1712     *      A standard Tcl result.  If all went well, then *indexPtr is
1713     *      filled in with the index (into itemPtr) corresponding to
1714     *      string.  Otherwise an error message is left in
1715     *      interp->result.
1716     *
1717     * Side effects:
1718     *      None.
1719     *
1720     *--------------------------------------------------------------
1721     */
1722    
1723    static int
1724    GetLineIndex(interp, canvas, itemPtr, obj, indexPtr)
1725        Tcl_Interp *interp;         /* Used for error reporting. */
1726        Tk_Canvas canvas;           /* Canvas containing item. */
1727        Tk_Item *itemPtr;           /* Item for which the index is being
1728                                     * specified. */
1729        Tcl_Obj *obj;               /* Specification of a particular coord
1730                                     * in itemPtr's line. */
1731        int *indexPtr;              /* Where to store converted index. */
1732    {
1733        LineItem *linePtr = (LineItem *) itemPtr;
1734        size_t length;
1735        char *string = Tcl_GetStringFromObj(obj, (int *) &length);
1736    
1737        if (string[0] == 'e') {
1738            if (strncmp(string, "end", length) == 0) {
1739                *indexPtr = 2*linePtr->numPoints;
1740            } else {
1741                badIndex:
1742    
1743                /*
1744                 * Some of the paths here leave messages in interp->result,
1745                 * so we have to clear it out before storing our own message.
1746                 */
1747    
1748                Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1749                Tcl_AppendResult(interp, "bad index \"", string, "\"",
1750                        (char *) NULL);
1751                return TCL_ERROR;
1752            }
1753        } else if (string[0] == '@') {
1754            int i;
1755            double x ,y, bestDist, dist, *coordPtr;
1756            char *end, *p;
1757    
1758            p = string+1;
1759            x = strtod(p, &end);
1760            if ((end == p) || (*end != ',')) {
1761                goto badIndex;
1762            }
1763            p = end+1;
1764            y = strtod(p, &end);
1765            if ((end == p) || (*end != 0)) {
1766                goto badIndex;
1767            }
1768            bestDist = 1.0e36;
1769            coordPtr = linePtr->coordPtr;
1770            *indexPtr = 0;
1771            for(i=0; i<linePtr->numPoints; i++) {
1772                dist = hypot(coordPtr[0] - x, coordPtr[1] - y);
1773                if (dist<bestDist) {
1774                    bestDist = dist;
1775                    *indexPtr = 2*i;
1776                }
1777                coordPtr += 2;
1778            }
1779        } else {
1780            if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) {
1781                goto badIndex;
1782            }
1783            *indexPtr &= -2; /* if index is odd, make it even */
1784            if (*indexPtr < 0){
1785                *indexPtr = 0;
1786            } else if (*indexPtr > (2*linePtr->numPoints)) {
1787                *indexPtr = (2*linePtr->numPoints);
1788            }
1789        }
1790        return TCL_OK;
1791    }
1792    
1793    /*
1794     *--------------------------------------------------------------
1795     *
1796     * TranslateLine --
1797     *
1798     *      This procedure is called to move a line by a given amount.
1799     *
1800     * Results:
1801     *      None.
1802     *
1803     * Side effects:
1804     *      The position of the line is offset by (xDelta, yDelta), and
1805     *      the bounding box is updated in the generic part of the item
1806     *      structure.
1807     *
1808     *--------------------------------------------------------------
1809     */
1810    
1811    static void
1812    TranslateLine(canvas, itemPtr, deltaX, deltaY)
1813        Tk_Canvas canvas;                   /* Canvas containing item. */
1814        Tk_Item *itemPtr;                   /* Item that is being moved. */
1815        double deltaX, deltaY;              /* Amount by which item is to be
1816                                             * moved. */
1817    {
1818        LineItem *linePtr = (LineItem *) itemPtr;
1819        double *coordPtr;
1820        int i;
1821    
1822        for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
1823                i++, coordPtr += 2) {
1824            coordPtr[0] += deltaX;
1825            coordPtr[1] += deltaY;
1826        }
1827        if (linePtr->firstArrowPtr != NULL) {
1828            for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1829                    i++, coordPtr += 2) {
1830                coordPtr[0] += deltaX;
1831                coordPtr[1] += deltaY;
1832            }
1833        }
1834        if (linePtr->lastArrowPtr != NULL) {
1835            for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1836                    i++, coordPtr += 2) {
1837                coordPtr[0] += deltaX;
1838                coordPtr[1] += deltaY;
1839            }
1840        }
1841        ComputeLineBbox(canvas, linePtr);
1842    }
1843    
1844    /*
1845     *--------------------------------------------------------------
1846     *
1847     * ParseArrowShape --
1848     *
1849     *      This procedure is called back during option parsing to
1850     *      parse arrow shape information.
1851     *
1852     * Results:
1853     *      The return value is a standard Tcl result:  TCL_OK means
1854     *      that the arrow shape information was parsed ok, and
1855     *      TCL_ERROR means it couldn't be parsed.
1856     *
1857     * Side effects:
1858     *      Arrow information in recordPtr is updated.
1859     *
1860     *--------------------------------------------------------------
1861     */
1862    
1863            /* ARGSUSED */
1864    static int
1865    ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset)
1866        ClientData clientData;      /* Not used. */
1867        Tcl_Interp *interp;         /* Used for error reporting. */
1868        Tk_Window tkwin;            /* Not used. */
1869        CONST char *value;          /* Textual specification of arrow shape. */
1870        char *recordPtr;            /* Pointer to item record in which to
1871                                     * store arrow information. */
1872        int offset;                 /* Offset of shape information in widget
1873                                     * record. */
1874    {
1875        LineItem *linePtr = (LineItem *) recordPtr;
1876        double a, b, c;
1877        int argc;
1878        char **argv = NULL;
1879    
1880        if (offset != Tk_Offset(LineItem, arrowShapeA)) {
1881            panic("ParseArrowShape received bogus offset");
1882        }
1883    
1884        if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) {
1885            syntaxError:
1886            Tcl_ResetResult(interp);
1887            Tcl_AppendResult(interp, "bad arrow shape \"", value,
1888                    "\": must be list with three numbers", (char *) NULL);
1889            if (argv != NULL) {
1890                ckfree((char *) argv);
1891            }
1892            return TCL_ERROR;
1893        }
1894        if (argc != 3) {
1895            goto syntaxError;
1896        }
1897        if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK)