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