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