1/*
2 * tkCanvUtil.c --
3 *
4 *	This file contains a collection of utility functions used by the
5 *	implementations of various canvas item types.
6 *
7 * Copyright (c) 1994 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id$
13 */
14
15#include "tkInt.h"
16#include "tkCanvas.h"
17#include <assert.h>
18
19/*
20 * Structures defined only in this file.
21 */
22
23typedef struct SmoothAssocData {
24    struct SmoothAssocData *nextPtr;
25				/* Pointer to next SmoothAssocData. */
26    Tk_SmoothMethod smooth;	/* Name and functions associated with this
27				 * option. */
28} SmoothAssocData;
29
30Tk_SmoothMethod tkBezierSmoothMethod = {
31    "true",
32    TkMakeBezierCurve,
33    (void (*) (Tcl_Interp *interp, Tk_Canvas canvas, double *coordPtr,
34	    int numPoints, int numSteps)) TkMakeBezierPostscript,
35};
36static Tk_SmoothMethod tkRawSmoothMethod = {
37    "raw",
38    TkMakeRawCurve,
39    (void (*) (Tcl_Interp *interp, Tk_Canvas canvas, double *coordPtr,
40	    int numPoints, int numSteps)) TkMakeRawCurvePostscript,
41};
42
43/*
44 * Function forward-declarations.
45 */
46
47static void		SmoothMethodCleanupProc(ClientData clientData,
48			    Tcl_Interp *interp);
49static SmoothAssocData *InitSmoothMethods(Tcl_Interp *interp);
50static int		DashConvert(char *l, CONST char *p, int n,
51			    double width);
52static void		TranslateAndAppendCoords(TkCanvas *canvPtr,
53			    double x, double y, XPoint *outArr, int numOut);
54
55#define ABS(a) ((a>=0)?(a):(-(a)))
56
57/*
58 *----------------------------------------------------------------------
59 *
60 * Tk_CanvasTkwin --
61 *
62 *	Given a token for a canvas, this function returns the widget that
63 *	represents the canvas.
64 *
65 * Results:
66 *	The return value is a handle for the widget.
67 *
68 * Side effects:
69 *	None.
70 *
71 *----------------------------------------------------------------------
72 */
73
74Tk_Window
75Tk_CanvasTkwin(
76    Tk_Canvas canvas)		/* Token for the canvas. */
77{
78    TkCanvas *canvasPtr = (TkCanvas *) canvas;
79    return canvasPtr->tkwin;
80}
81
82/*
83 *----------------------------------------------------------------------
84 *
85 * Tk_CanvasDrawableCoords --
86 *
87 *	Given an (x,y) coordinate pair within a canvas, this function
88 *	returns the corresponding coordinates at which the point should
89 *	be drawn in the drawable used for display.
90 *
91 * Results:
92 *	There is no return value. The values at *drawableXPtr and
93 *	*drawableYPtr are filled in with the coordinates at which x and y
94 *	should be drawn. These coordinates are clipped to fit within a
95 *	"short", since this is what X uses in most cases for drawing.
96 *
97 * Side effects:
98 *	None.
99 *
100 *----------------------------------------------------------------------
101 */
102
103void
104Tk_CanvasDrawableCoords(
105    Tk_Canvas canvas,		/* Token for the canvas. */
106    double x,			/* Coordinates in canvas space. */
107    double y,
108    short *drawableXPtr,	/* Screen coordinates are stored here. */
109    short *drawableYPtr)
110{
111    TkCanvas *canvasPtr = (TkCanvas *) canvas;
112    double tmp;
113
114    tmp = x - canvasPtr->drawableXOrigin;
115    if (tmp > 0) {
116	tmp += 0.5;
117    } else {
118	tmp -= 0.5;
119    }
120    if (tmp > 32767) {
121	*drawableXPtr = 32767;
122    } else if (tmp < -32768) {
123	*drawableXPtr = -32768;
124    } else {
125	*drawableXPtr = (short) tmp;
126    }
127
128    tmp = y - canvasPtr->drawableYOrigin;
129    if (tmp > 0) {
130	tmp += 0.5;
131    } else {
132	tmp -= 0.5;
133    }
134    if (tmp > 32767) {
135	*drawableYPtr = 32767;
136    } else if (tmp < -32768) {
137	*drawableYPtr = -32768;
138    } else {
139	*drawableYPtr = (short) tmp;
140    }
141}
142
143/*
144 *----------------------------------------------------------------------
145 *
146 * Tk_CanvasWindowCoords --
147 *
148 *	Given an (x,y) coordinate pair within a canvas, this function returns
149 *	the corresponding coordinates in the canvas's window.
150 *
151 * Results:
152 *	There is no return value. The values at *screenXPtr and *screenYPtr
153 *	are filled in with the coordinates at which (x,y) appears in the
154 *	canvas's window. These coordinates are clipped to fit within a
155 *	"short", since this is what X uses in most cases for drawing.
156 *
157 * Side effects:
158 *	None.
159 *
160 *----------------------------------------------------------------------
161 */
162
163void
164Tk_CanvasWindowCoords(
165    Tk_Canvas canvas,		/* Token for the canvas. */
166    double x,			/* Coordinates in canvas space. */
167    double y,
168    short *screenXPtr,		/* Screen coordinates are stored here. */
169    short *screenYPtr)
170{
171    TkCanvas *canvasPtr = (TkCanvas *) canvas;
172    double tmp;
173
174    tmp = x - canvasPtr->xOrigin;
175    if (tmp > 0) {
176	tmp += 0.5;
177    } else {
178	tmp -= 0.5;
179    }
180    if (tmp > 32767) {
181	*screenXPtr = 32767;
182    } else if (tmp < -32768) {
183	*screenXPtr = -32768;
184    } else {
185	*screenXPtr = (short) tmp;
186    }
187
188    tmp = y - canvasPtr->yOrigin;
189    if (tmp > 0) {
190	tmp += 0.5;
191    } else {
192	tmp -= 0.5;
193    }
194    if (tmp > 32767) {
195	*screenYPtr = 32767;
196    } else if (tmp < -32768) {
197	*screenYPtr = -32768;
198    } else {
199	*screenYPtr = (short) tmp;
200    }
201}
202
203/*
204 *--------------------------------------------------------------
205 *
206 * Tk_CanvasGetCoord --
207 *
208 *	Given a string, returns a floating-point canvas coordinate
209 *	corresponding to that string.
210 *
211 * Results:
212 *	The return value is a standard Tcl return result. If TCL_OK is
213 *	returned, then everything went well and the canvas coordinate is
214 *	stored at *doublePtr; otherwise TCL_ERROR is returned and an error
215 *	message is left in the interp's result.
216 *
217 * Side effects:
218 *	None.
219 *
220 *--------------------------------------------------------------
221 */
222
223int
224Tk_CanvasGetCoord(
225    Tcl_Interp *interp,		/* Interpreter for error reporting. */
226    Tk_Canvas canvas,		/* Canvas to which coordinate applies. */
227    CONST char *string,		/* Describes coordinate (any screen coordinate
228				 * form may be used here). */
229    double *doublePtr)		/* Place to store converted coordinate. */
230{
231    TkCanvas *canvasPtr = (TkCanvas *) canvas;
232
233    if (Tk_GetScreenMM(canvasPtr->interp, canvasPtr->tkwin, string,
234	    doublePtr) != TCL_OK) {
235	return TCL_ERROR;
236    }
237    *doublePtr *= canvasPtr->pixelsPerMM;
238    return TCL_OK;
239}
240
241/*
242 *--------------------------------------------------------------
243 *
244 * Tk_CanvasGetCoordFromObj --
245 *
246 *	Given a string, returns a floating-point canvas coordinate
247 *	corresponding to that string.
248 *
249 * Results:
250 *	The return value is a standard Tcl return result. If TCL_OK is
251 *	returned, then everything went well and the canvas coordinate is
252 *	stored at *doublePtr; otherwise TCL_ERROR is returned and an error
253 *	message is left in interp->result.
254 *
255 * Side effects:
256 *	None.
257 *
258 *--------------------------------------------------------------
259 */
260
261int
262Tk_CanvasGetCoordFromObj(
263    Tcl_Interp *interp,		/* Interpreter for error reporting. */
264    Tk_Canvas canvas,		/* Canvas to which coordinate applies. */
265    Tcl_Obj *obj,		/* Describes coordinate (any screen coordinate
266				 * form may be used here). */
267    double *doublePtr)		/* Place to store converted coordinate. */
268{
269    TkCanvas *canvasPtr = (TkCanvas *) canvas;
270
271    return Tk_GetDoublePixelsFromObj(canvasPtr->interp, canvasPtr->tkwin, obj, doublePtr);
272}
273
274/*
275 *----------------------------------------------------------------------
276 *
277 * Tk_CanvasSetStippleOrigin --
278 *
279 *	This function sets the stipple origin in a graphics context so that
280 *	stipples drawn with the GC will line up with other stipples previously
281 *	drawn in the canvas.
282 *
283 * Results:
284 *	None.
285 *
286 * Side effects:
287 *	The graphics context is modified.
288 *
289 *----------------------------------------------------------------------
290 */
291
292void
293Tk_CanvasSetStippleOrigin(
294    Tk_Canvas canvas,		/* Token for a canvas. */
295    GC gc)			/* Graphics context that is about to be used
296				 * to draw a stippled pattern as part of
297				 * redisplaying the canvas. */
298{
299    TkCanvas *canvasPtr = (TkCanvas *) canvas;
300
301    XSetTSOrigin(canvasPtr->display, gc, -canvasPtr->drawableXOrigin,
302	    -canvasPtr->drawableYOrigin);
303}
304
305/*
306 *----------------------------------------------------------------------
307 *
308 * Tk_CanvasSetOffset--
309 *
310 *	This function sets the stipple offset in a graphics context so that
311 *	stipples drawn with the GC will line up with other stipples with the
312 *	same offset.
313 *
314 * Results:
315 *	None.
316 *
317 * Side effects:
318 *	The graphics context is modified.
319 *
320 *----------------------------------------------------------------------
321 */
322
323void
324Tk_CanvasSetOffset(
325    Tk_Canvas canvas,		/* Token for a canvas. */
326    GC gc,			/* Graphics context that is about to be used
327				 * to draw a stippled pattern as part of
328				 * redisplaying the canvas. */
329    Tk_TSOffset *offset)	/* Offset (may be NULL pointer)*/
330{
331    TkCanvas *canvasPtr = (TkCanvas *) canvas;
332    int flags = 0;
333    int x = - canvasPtr->drawableXOrigin;
334    int y = - canvasPtr->drawableYOrigin;
335
336    if (offset != NULL) {
337	flags = offset->flags;
338	x += offset->xoffset;
339	y += offset->yoffset;
340    }
341    if ((flags & TK_OFFSET_RELATIVE) && !(flags & TK_OFFSET_INDEX)) {
342	Tk_SetTSOrigin(canvasPtr->tkwin, gc, x - canvasPtr->xOrigin,
343		y - canvasPtr->yOrigin);
344    } else {
345	XSetTSOrigin(canvasPtr->display, gc, x, y);
346    }
347}
348
349/*
350 *----------------------------------------------------------------------
351 *
352 * Tk_CanvasGetTextInfo --
353 *
354 *	This function returns a pointer to a structure containing information
355 *	about the selection and insertion cursor for a canvas widget. Items
356 *	such as text items save the pointer and use it to share access to the
357 *	information with the generic canvas code.
358 *
359 * Results:
360 *	The return value is a pointer to the structure holding text
361 *	information for the canvas. Most of the fields should not be modified
362 *	outside the generic canvas code; see the user documentation for
363 *	details.
364 *
365 * Side effects:
366 *	None.
367 *
368 *----------------------------------------------------------------------
369 */
370
371Tk_CanvasTextInfo *
372Tk_CanvasGetTextInfo(
373    Tk_Canvas canvas)		/* Token for the canvas widget. */
374{
375    return &((TkCanvas *) canvas)->textInfo;
376}
377
378/*
379 *--------------------------------------------------------------
380 *
381 * Tk_CanvasTagsParseProc --
382 *
383 *	This function is invoked during option processing to handle "-tags"
384 *	options for canvas items.
385 *
386 * Results:
387 *	A standard Tcl return value.
388 *
389 * Side effects:
390 *	The tags for a given item get replaced by those indicated in the value
391 *	argument.
392 *
393 *--------------------------------------------------------------
394 */
395
396int
397Tk_CanvasTagsParseProc(
398    ClientData clientData,	/* Not used.*/
399    Tcl_Interp *interp,		/* Used for reporting errors. */
400    Tk_Window tkwin,		/* Window containing canvas widget. */
401    CONST char *value,		/* Value of option (list of tag names). */
402    char *widgRec,		/* Pointer to record for item. */
403    int offset)			/* Offset into item (ignored). */
404{
405    register Tk_Item *itemPtr = (Tk_Item *) widgRec;
406    int argc, i;
407    CONST char **argv;
408    Tk_Uid *newPtr;
409
410    /*
411     * Break the value up into the individual tag names.
412     */
413
414    if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
415	return TCL_ERROR;
416    }
417
418    /*
419     * Make sure that there's enough space in the item to hold the tag names.
420     */
421
422    if (itemPtr->tagSpace < argc) {
423	newPtr = (Tk_Uid *) ckalloc((unsigned) (argc * sizeof(Tk_Uid)));
424	for (i = itemPtr->numTags-1; i >= 0; i--) {
425	    newPtr[i] = itemPtr->tagPtr[i];
426	}
427	if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
428	    ckfree((char *) itemPtr->tagPtr);
429	}
430	itemPtr->tagPtr = newPtr;
431	itemPtr->tagSpace = argc;
432    }
433    itemPtr->numTags = argc;
434    for (i = 0; i < argc; i++) {
435	itemPtr->tagPtr[i] = Tk_GetUid(argv[i]);
436    }
437    ckfree((char *) argv);
438    return TCL_OK;
439}
440
441/*
442 *--------------------------------------------------------------
443 *
444 * Tk_CanvasTagsPrintProc --
445 *
446 *	This function is invoked by the Tk configuration code to produce a
447 *	printable string for the "-tags" configuration option for canvas
448 *	items.
449 *
450 * Results:
451 *	The return value is a string describing all the tags for the item
452 *	referred to by "widgRec". In addition, *freeProcPtr is filled in with
453 *	the address of a function to call to free the result string when it's
454 *	no longer needed (or NULL to indicate that the string doesn't need to
455 *	be freed).
456 *
457 * Side effects:
458 *	None.
459 *
460 *--------------------------------------------------------------
461 */
462
463char *
464Tk_CanvasTagsPrintProc(
465    ClientData clientData,	/* Ignored. */
466    Tk_Window tkwin,		/* Window containing canvas widget. */
467    char *widgRec,		/* Pointer to record for item. */
468    int offset,			/* Ignored. */
469    Tcl_FreeProc **freeProcPtr)	/* Pointer to variable to fill in with
470				 * information about how to reclaim storage
471				 * for return string. */
472{
473    register Tk_Item *itemPtr = (Tk_Item *) widgRec;
474
475    if (itemPtr->numTags == 0) {
476	*freeProcPtr = NULL;
477	return "";
478    }
479    if (itemPtr->numTags == 1) {
480	*freeProcPtr = NULL;
481	return (char *) itemPtr->tagPtr[0];
482    }
483    *freeProcPtr = TCL_DYNAMIC;
484    return Tcl_Merge(itemPtr->numTags, (CONST char **) itemPtr->tagPtr);
485}
486
487/*
488 *--------------------------------------------------------------
489 *
490 * TkCanvasDashParseProc --
491 *
492 *	This function is invoked during option processing to handle "-dash",
493 *	"-activedash" and "-disableddash" options for canvas objects.
494 *
495 * Results:
496 *	A standard Tcl return value.
497 *
498 * Side effects:
499 *	The dash list for a given canvas object gets replaced by those
500 *	indicated in the value argument.
501 *
502 *--------------------------------------------------------------
503 */
504
505int
506TkCanvasDashParseProc(
507    ClientData clientData,	/* Not used.*/
508    Tcl_Interp *interp,		/* Used for reporting errors. */
509    Tk_Window tkwin,		/* Window containing canvas widget. */
510    CONST char *value,		/* Value of option. */
511    char *widgRec,		/* Pointer to record for item. */
512    int offset)			/* Offset into item. */
513{
514    return Tk_GetDash(interp, value, (Tk_Dash *)(widgRec+offset));
515}
516
517/*
518 *--------------------------------------------------------------
519 *
520 * TkCanvasDashPrintProc --
521 *
522 *	This function is invoked by the Tk configuration code to produce a
523 *	printable string for the "-dash", "-activedash" and "-disableddash"
524 *	configuration options for canvas items.
525 *
526 * Results:
527 *	The return value is a string describing all the dash list for the item
528 *	referred to by "widgRec"and "offset". In addition, *freeProcPtr is
529 *	filled in with the address of a function to call to free the result
530 *	string when it's no longer needed (or NULL to indicate that the string
531 *	doesn't need to be freed).
532 *
533 * Side effects:
534 *	None.
535 *
536 *--------------------------------------------------------------
537 */
538
539char *
540TkCanvasDashPrintProc(
541    ClientData clientData,	/* Ignored. */
542    Tk_Window tkwin,		/* Window containing canvas widget. */
543    char *widgRec,		/* Pointer to record for item. */
544    int offset,			/* Offset in record for item. */
545    Tcl_FreeProc **freeProcPtr)	/* Pointer to variable to fill in with
546				 * information about how to reclaim storage
547				 * for return string. */
548{
549    Tk_Dash *dash = (Tk_Dash *) (widgRec+offset);
550    char *buffer;
551    char *p;
552    int i = dash->number;
553
554    if (i < 0) {
555	i = -i;
556	*freeProcPtr = TCL_DYNAMIC;
557	buffer = (char *) ckalloc((unsigned int) (i+1));
558	p = (i > (int)sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
559	memcpy(buffer, p, (unsigned int) i);
560	buffer[i] = 0;
561	return buffer;
562    } else if (!i) {
563	*freeProcPtr = NULL;
564	return "";
565    }
566    buffer = (char *)ckalloc((unsigned int) (4*i));
567    *freeProcPtr = TCL_DYNAMIC;
568
569    p = (i > (int)sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
570    sprintf(buffer, "%d", *p++ & 0xff);
571    while(--i) {
572	sprintf(buffer+strlen(buffer), " %d", *p++ & 0xff);
573    }
574    return buffer;
575}
576
577/*
578 *--------------------------------------------------------------
579 *
580 * InitSmoothMethods --
581 *
582 *	This function is invoked to set up the initial state of the list of
583 *	"-smooth" methods. It should only be called when the list installed
584 *	in the interpreter is NULL.
585 *
586 * Results:
587 *	Pointer to the start of the list of default smooth methods.
588 *
589 * Side effects:
590 *	A linked list of smooth methods is created and attached to the
591 *	interpreter's association key "smoothMethod"
592 *
593 *--------------------------------------------------------------
594 */
595
596static SmoothAssocData *
597InitSmoothMethods(
598    Tcl_Interp *interp)
599{
600    SmoothAssocData *methods, *ptr;
601
602    methods = (SmoothAssocData *) ckalloc(sizeof(SmoothAssocData));
603    methods->smooth.name = tkRawSmoothMethod.name;
604    methods->smooth.coordProc = tkRawSmoothMethod.coordProc;
605    methods->smooth.postscriptProc = tkRawSmoothMethod.postscriptProc;
606
607    methods->nextPtr = (SmoothAssocData *) ckalloc(sizeof(SmoothAssocData));
608
609    ptr = methods->nextPtr;
610    ptr->smooth.name = tkBezierSmoothMethod.name;
611    ptr->smooth.coordProc = tkBezierSmoothMethod.coordProc;
612    ptr->smooth.postscriptProc = tkBezierSmoothMethod.postscriptProc;
613    ptr->nextPtr = NULL;
614
615    Tcl_SetAssocData(interp, "smoothMethod", SmoothMethodCleanupProc,
616	    (ClientData) methods);
617    return methods;
618}
619
620/*
621 *--------------------------------------------------------------
622 *
623 * Tk_CreateSmoothMethod --
624 *
625 *	This function is invoked to add additional values for the "-smooth"
626 *	option to the list.
627 *
628 * Results:
629 *	A standard Tcl return value.
630 *
631 * Side effects:
632 *	In the future "-smooth <name>" will be accepted as smooth method for
633 *	the line and polygon.
634 *
635 *--------------------------------------------------------------
636 */
637
638void
639Tk_CreateSmoothMethod(
640    Tcl_Interp *interp,
641    Tk_SmoothMethod *smooth)
642{
643    SmoothAssocData *methods, *typePtr2, *prevPtr, *ptr;
644    methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod",
645	    NULL);
646
647    /*
648     * Initialize if we were not previously initialized.
649     */
650
651    if (methods == NULL) {
652	methods = InitSmoothMethods(interp);
653    }
654
655    /*
656     * If there's already a smooth method with the given name, remove it.
657     */
658
659    for (typePtr2 = methods, prevPtr = NULL; typePtr2 != NULL;
660	    prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
661	if (!strcmp(typePtr2->smooth.name, smooth->name)) {
662	    if (prevPtr == NULL) {
663		methods = typePtr2->nextPtr;
664	    } else {
665		prevPtr->nextPtr = typePtr2->nextPtr;
666	    }
667	    ckfree((char *) typePtr2);
668	    break;
669	}
670    }
671    ptr = (SmoothAssocData *) ckalloc(sizeof(SmoothAssocData));
672    ptr->smooth.name = smooth->name;
673    ptr->smooth.coordProc = smooth->coordProc;
674    ptr->smooth.postscriptProc = smooth->postscriptProc;
675    ptr->nextPtr = methods;
676    Tcl_SetAssocData(interp, "smoothMethod", SmoothMethodCleanupProc,
677	    (ClientData) ptr);
678}
679
680/*
681 *----------------------------------------------------------------------
682 *
683 * SmoothMethodCleanupProc --
684 *
685 *	This function is invoked whenever an interpreter is deleted to
686 *	cleanup the smooth methods.
687 *
688 * Results:
689 *	None.
690 *
691 * Side effects:
692 *	Smooth methods are removed.
693 *
694 *----------------------------------------------------------------------
695 */
696
697static void
698SmoothMethodCleanupProc(
699    ClientData clientData,	/* Points to "smoothMethod" AssocData for the
700				 * interpreter. */
701    Tcl_Interp *interp)		/* Interpreter that is being deleted. */
702{
703    SmoothAssocData *ptr, *methods = (SmoothAssocData *) clientData;
704
705    while (methods != NULL) {
706	methods = (ptr = methods)->nextPtr;
707	ckfree((char *) ptr);
708    }
709}
710/*
711 *--------------------------------------------------------------
712 *
713 * TkSmoothParseProc --
714 *
715 *	This function is invoked during option processing to handle the
716 *	"-smooth" option.
717 *
718 * Results:
719 *	A standard Tcl return value.
720 *
721 * Side effects:
722 *	The smooth option for a given item gets replaced by the value
723 *	indicated in the value argument.
724 *
725 *--------------------------------------------------------------
726 */
727
728int
729TkSmoothParseProc(
730    ClientData clientData,	/* some flags.*/
731    Tcl_Interp *interp,		/* Used for reporting errors. */
732    Tk_Window tkwin,		/* Window containing canvas widget. */
733    CONST char *value,		/* Value of option. */
734    char *widgRec,		/* Pointer to record for item. */
735    int offset)			/* Offset into item. */
736{
737    register Tk_SmoothMethod **smoothPtr =
738	    (Tk_SmoothMethod **) (widgRec + offset);
739    Tk_SmoothMethod *smooth = NULL;
740    int b;
741    size_t length;
742    SmoothAssocData *methods;
743
744    if (value == NULL || *value == 0) {
745	*smoothPtr = NULL;
746	return TCL_OK;
747    }
748    length = strlen(value);
749    methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod",
750	    NULL);
751
752    /*
753     * Not initialized yet; fix that now.
754     */
755
756    if (methods == NULL) {
757	methods = InitSmoothMethods(interp);
758    }
759
760    /*
761     * Backward compatability hack.
762     */
763
764    if (strncmp(value, "bezier", length) == 0) {
765	smooth = &tkBezierSmoothMethod;
766    }
767
768    /*
769     * Search the list of installed smooth methods.
770     */
771
772    while (methods != NULL) {
773	if (strncmp(value, methods->smooth.name, length) == 0) {
774	    if (smooth != NULL) {
775		Tcl_AppendResult(interp, "ambiguous smooth method \"", value,
776			"\"", NULL);
777		return TCL_ERROR;
778	    }
779	    smooth = &methods->smooth;
780	}
781	methods = methods->nextPtr;
782    }
783    if (smooth) {
784	*smoothPtr = smooth;
785	return TCL_OK;
786    }
787
788    /*
789     * Did not find it. Try parsing as a boolean instead.
790     */
791
792    if (Tcl_GetBoolean(interp, (char *) value, &b) != TCL_OK) {
793	return TCL_ERROR;
794    }
795    *smoothPtr = b ? &tkBezierSmoothMethod : NULL;
796    return TCL_OK;
797}
798/*
799 *--------------------------------------------------------------
800 *
801 * TkSmoothPrintProc --
802 *
803 *	This function is invoked by the Tk configuration code to produce a
804 *	printable string for the "-smooth" configuration option.
805 *
806 * Results:
807 *	The return value is a string describing the smooth option for the item
808 *	referred to by "widgRec". In addition, *freeProcPtr is filled in with
809 *	the address of a function to call to free the result string when it's
810 *	no longer needed (or NULL to indicate that the string doesn't need to
811 *	be freed).
812 *
813 * Side effects:
814 *	None.
815 *
816 *--------------------------------------------------------------
817 */
818
819char *
820TkSmoothPrintProc(
821    ClientData clientData,	/* Ignored. */
822    Tk_Window tkwin,		/* Window containing canvas widget. */
823    char *widgRec,		/* Pointer to record for item. */
824    int offset,			/* Offset into item. */
825    Tcl_FreeProc **freeProcPtr)	/* Pointer to variable to fill in with
826				 * information about how to reclaim storage
827				 * for return string. */
828{
829    register Tk_SmoothMethod **smoothPtr =
830	    (Tk_SmoothMethod **) (widgRec + offset);
831
832    return (*smoothPtr) ? (*smoothPtr)->name : "0";
833}
834/*
835 *--------------------------------------------------------------
836 *
837 * Tk_GetDash
838 *
839 *	This function is used to parse a string, assuming it is dash
840 *	information.
841 *
842 * Results:
843 *	The return value is a standard Tcl result: TCL_OK means that the dash
844 *	information was parsed ok, and TCL_ERROR means it couldn't be parsed.
845 *
846 * Side effects:
847 *	Dash information in the dash structure is updated.
848 *
849 *--------------------------------------------------------------
850 */
851
852int
853Tk_GetDash(
854    Tcl_Interp *interp,		/* Used for error reporting. */
855    CONST char *value,		/* Textual specification of dash list. */
856    Tk_Dash *dash)		/* Pointer to record in which to store dash
857				 * information. */
858{
859    int argc, i;
860    CONST char **largv, **argv = NULL;
861    char *pt;
862
863    if ((value==NULL) || (*value==0) ) {
864	dash->number = 0;
865	return TCL_OK;
866    }
867
868    /*
869     * switch is usually compiled more efficiently than a chain of conditions.
870     */
871
872    switch (*value) {
873    case '.': case ',': case '-': case '_':
874	i = DashConvert(NULL, value, -1, 0.0);
875	if (i>0) {
876	    i = strlen(value);
877	} else {
878	    goto badDashList;
879	}
880	if (i > (int)sizeof(char *)) {
881	    dash->pattern.pt = pt = (char *) ckalloc(strlen(value));
882	} else {
883	    pt = dash->pattern.array;
884	}
885	memcpy(pt,value, (unsigned int) i);
886	dash->number = -i;
887	return TCL_OK;
888    }
889
890    if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) {
891	Tcl_ResetResult(interp);
892	goto badDashList;
893    }
894
895    if ((unsigned int)ABS(dash->number) > sizeof(char *)) {
896	ckfree((char *) dash->pattern.pt);
897    }
898    if (argc > (int)sizeof(char *)) {
899	dash->pattern.pt = pt = (char *) ckalloc((unsigned int) argc);
900    } else {
901	pt = dash->pattern.array;
902    }
903    dash->number = argc;
904
905    largv = argv;
906    while (argc>0) {
907	if (Tcl_GetInt(interp, *largv, &i) != TCL_OK || i < 1 || i>255) {
908	    Tcl_ResetResult(interp);
909	    Tcl_AppendResult(interp,
910		    "expected integer in the range 1..255 but got \"",
911		    *largv, "\"", NULL);
912	    goto syntaxError;
913	}
914	*pt++ = i;
915	argc--;
916	largv++;
917    }
918
919    if (argv != NULL) {
920	ckfree((char *) argv);
921    }
922    return TCL_OK;
923
924    /*
925     * Something went wrong. Generate error message, clean up and return.
926     */
927
928  badDashList:
929    Tcl_AppendResult(interp, "bad dash list \"", value,
930	    "\": must be a list of integers or a format like \"-..\"",
931	    NULL);
932  syntaxError:
933    if (argv != NULL) {
934	ckfree((char *) argv);
935    }
936    if ((unsigned int)ABS(dash->number) > sizeof(char *)) {
937	ckfree((char *) dash->pattern.pt);
938    }
939    dash->number = 0;
940    return TCL_ERROR;
941}
942
943/*
944 *--------------------------------------------------------------
945 *
946 * Tk_CreateOutline
947 *
948 *	This function initializes the Tk_Outline structure with default
949 *	values.
950 *
951 * Results:
952 *	None
953 *
954 * Side effects:
955 *	None
956 *
957 *--------------------------------------------------------------
958 */
959
960void
961Tk_CreateOutline(
962    Tk_Outline *outline)	/* Outline structure to be filled in. */
963{
964    outline->gc = None;
965    outline->width = 1.0;
966    outline->activeWidth = 0.0;
967    outline->disabledWidth = 0.0;
968    outline->offset = 0;
969    outline->dash.number = 0;
970    outline->activeDash.number = 0;
971    outline->disabledDash.number = 0;
972    outline->tsoffset.flags = 0;
973    outline->tsoffset.xoffset = 0;
974    outline->tsoffset.yoffset = 0;
975    outline->color = NULL;
976    outline->activeColor = NULL;
977    outline->disabledColor = NULL;
978    outline->stipple = None;
979    outline->activeStipple = None;
980    outline->disabledStipple = None;
981}
982
983/*
984 *--------------------------------------------------------------
985 *
986 * Tk_DeleteOutline
987 *
988 *	This function frees all memory that might be allocated and referenced
989 *	in the Tk_Outline structure.
990 *
991 * Results:
992 *	None
993 *
994 * Side effects:
995 *	None
996 *
997 *--------------------------------------------------------------
998 */
999
1000void
1001Tk_DeleteOutline(
1002    Display *display,		/* Display containing window. */
1003    Tk_Outline *outline)
1004{
1005    if (outline->gc != None) {
1006	Tk_FreeGC(display, outline->gc);
1007    }
1008    if ((unsigned int)ABS(outline->dash.number) > sizeof(char *)) {
1009	ckfree((char *) outline->dash.pattern.pt);
1010    }
1011    if ((unsigned int)ABS(outline->activeDash.number) > sizeof(char *)) {
1012	ckfree((char *) outline->activeDash.pattern.pt);
1013    }
1014    if ((unsigned int)ABS(outline->disabledDash.number) > sizeof(char *)) {
1015	ckfree((char *) outline->disabledDash.pattern.pt);
1016    }
1017    if (outline->color != NULL) {
1018	Tk_FreeColor(outline->color);
1019    }
1020    if (outline->activeColor != NULL) {
1021	Tk_FreeColor(outline->activeColor);
1022    }
1023    if (outline->disabledColor != NULL) {
1024	Tk_FreeColor(outline->disabledColor);
1025    }
1026    if (outline->stipple != None) {
1027	Tk_FreeBitmap(display, outline->stipple);
1028    }
1029    if (outline->activeStipple != None) {
1030	Tk_FreeBitmap(display, outline->activeStipple);
1031    }
1032    if (outline->disabledStipple != None) {
1033	Tk_FreeBitmap(display, outline->disabledStipple);
1034    }
1035}
1036
1037/*
1038 *--------------------------------------------------------------
1039 *
1040 * Tk_ConfigOutlineGC
1041 *
1042 *	This function should be called in the canvas object during the
1043 *	configure command. The graphics context description in gcValues is
1044 *	updated according to the information in the dash structure, as far as
1045 *	possible.
1046 *
1047 * Results:
1048 *	The return-value is a mask, indicating which elements of gcValues have
1049 *	been updated. 0 means there is no outline.
1050 *
1051 * Side effects:
1052 *	GC information in gcValues is updated.
1053 *
1054 *--------------------------------------------------------------
1055 */
1056
1057int
1058Tk_ConfigOutlineGC(
1059    XGCValues *gcValues,
1060    Tk_Canvas canvas,
1061    Tk_Item *item,
1062    Tk_Outline *outline)
1063{
1064    int mask = 0;
1065    double width;
1066    Tk_Dash *dash;
1067    XColor *color;
1068    Pixmap stipple;
1069    Tk_State state = item->state;
1070
1071    if (outline->width < 0.0) {
1072	outline->width = 0.0;
1073    }
1074    if (outline->activeWidth < 0.0) {
1075	outline->activeWidth = 0.0;
1076    }
1077    if (outline->disabledWidth < 0) {
1078	outline->disabledWidth = 0.0;
1079    }
1080    if (state==TK_STATE_HIDDEN) {
1081	return 0;
1082    }
1083
1084    width = outline->width;
1085    if (width < 1.0) {
1086	width = 1.0;
1087    }
1088    dash = &(outline->dash);
1089    color = outline->color;
1090    stipple = outline->stipple;
1091    if (state == TK_STATE_NULL) {
1092	state = ((TkCanvas *)canvas)->canvas_state;
1093    }
1094    if (((TkCanvas *)canvas)->currentItemPtr == item) {
1095	if (outline->activeWidth>width) {
1096	    width = outline->activeWidth;
1097	}
1098	if (outline->activeDash.number != 0) {
1099	    dash = &(outline->activeDash);
1100	}
1101	if (outline->activeColor!=NULL) {
1102	    color = outline->activeColor;
1103	}
1104	if (outline->activeStipple!=None) {
1105	    stipple = outline->activeStipple;
1106	}
1107    } else if (state == TK_STATE_DISABLED) {
1108	if (outline->disabledWidth>0) {
1109	    width = outline->disabledWidth;
1110	}
1111	if (outline->disabledDash.number != 0) {
1112	    dash = &(outline->disabledDash);
1113	}
1114	if (outline->disabledColor!=NULL) {
1115	    color = outline->disabledColor;
1116	}
1117	if (outline->disabledStipple!=None) {
1118	    stipple = outline->disabledStipple;
1119	}
1120    }
1121
1122    if (color==NULL) {
1123	return 0;
1124    }
1125
1126    gcValues->line_width = (int) (width + 0.5);
1127    if (color != NULL) {
1128	gcValues->foreground = color->pixel;
1129	mask = GCForeground|GCLineWidth;
1130	if (stipple != None) {
1131	    gcValues->stipple = stipple;
1132	    gcValues->fill_style = FillStippled;
1133	    mask |= GCStipple|GCFillStyle;
1134	}
1135    }
1136    if (mask && (dash->number != 0)) {
1137	gcValues->line_style = LineOnOffDash;
1138	gcValues->dash_offset = outline->offset;
1139	if (dash->number > 0) {
1140	    gcValues->dashes = dash->pattern.array[0];
1141	} else {
1142	    gcValues->dashes = (char) (4 * width + 0.5);
1143	}
1144	mask |= GCLineStyle|GCDashList|GCDashOffset;
1145    }
1146    return mask;
1147}
1148
1149/*
1150 *--------------------------------------------------------------
1151 *
1152 * Tk_ChangeOutlineGC
1153 *
1154 *	Updates the GC to represent the full information of the dash
1155 *	structure. Partly this is already done in Tk_ConfigOutlineGC(). This
1156 *	function should be called just before drawing the dashed item.
1157 *
1158 * Results:
1159 *	1 if there is a stipple pattern, and 0 otherwise.
1160 *
1161 * Side effects:
1162 *	GC is updated.
1163 *
1164 *--------------------------------------------------------------
1165 */
1166
1167int
1168Tk_ChangeOutlineGC(
1169    Tk_Canvas canvas,
1170    Tk_Item *item,
1171    Tk_Outline *outline)
1172{
1173    CONST char *p;
1174    double width;
1175    Tk_Dash *dash;
1176    XColor *color;
1177    Pixmap stipple;
1178    Tk_State state = item->state;
1179
1180    width = outline->width;
1181    if (width < 1.0) {
1182	width = 1.0;
1183    }
1184    dash = &(outline->dash);
1185    color = outline->color;
1186    stipple = outline->stipple;
1187    if (state == TK_STATE_NULL) {
1188	state = ((TkCanvas *)canvas)->canvas_state;
1189    }
1190    if (((TkCanvas *)canvas)->currentItemPtr == item) {
1191	if (outline->activeWidth > width) {
1192	    width = outline->activeWidth;
1193	}
1194	if (outline->activeDash.number != 0) {
1195	    dash = &(outline->activeDash);
1196	}
1197	if (outline->activeColor != NULL) {
1198	    color = outline->activeColor;
1199	}
1200	if (outline->activeStipple != None) {
1201	    stipple = outline->activeStipple;
1202	}
1203    } else if (state == TK_STATE_DISABLED) {
1204	if (outline->disabledWidth > width) {
1205	    width = outline->disabledWidth;
1206	}
1207	if (outline->disabledDash.number != 0) {
1208	    dash = &(outline->disabledDash);
1209	}
1210	if (outline->disabledColor != NULL) {
1211	    color = outline->disabledColor;
1212	}
1213	if (outline->disabledStipple != None) {
1214	    stipple = outline->disabledStipple;
1215	}
1216    }
1217    if (color==NULL) {
1218	return 0;
1219    }
1220
1221    if ((dash->number<-1) ||
1222	    ((dash->number == -1) && (dash->pattern.array[0] != ','))) {
1223	char *q;
1224	int i = -dash->number;
1225
1226	p = (i > (int)sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
1227	q = (char *) ckalloc(2*(unsigned int)i);
1228	i = DashConvert(q, p, i, width);
1229	XSetDashes(((TkCanvas *)canvas)->display, outline->gc,
1230		outline->offset, q, i);
1231	ckfree(q);
1232    } else if (dash->number>2 || (dash->number==2 &&
1233	    (dash->pattern.array[0]!=dash->pattern.array[1]))) {
1234	p = (dash->number > (int)sizeof(char *))
1235		? dash->pattern.pt : dash->pattern.array;
1236	XSetDashes(((TkCanvas *)canvas)->display, outline->gc,
1237		outline->offset, p, dash->number);
1238    }
1239    if (stipple!=None) {
1240	int w=0; int h=0;
1241	Tk_TSOffset *tsoffset = &outline->tsoffset;
1242	int flags = tsoffset->flags;
1243	if (!(flags & TK_OFFSET_INDEX) &&
1244		(flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE))) {
1245	    Tk_SizeOfBitmap(((TkCanvas *)canvas)->display, stipple, &w, &h);
1246	    if (flags & TK_OFFSET_CENTER) {
1247		w /= 2;
1248	    } else {
1249		w = 0;
1250	    }
1251	    if (flags & TK_OFFSET_MIDDLE) {
1252		h /= 2;
1253	    } else {
1254		h = 0;
1255	    }
1256	}
1257	tsoffset->xoffset -= w;
1258	tsoffset->yoffset -= h;
1259	Tk_CanvasSetOffset(canvas, outline->gc, tsoffset);
1260	tsoffset->xoffset += w;
1261	tsoffset->yoffset += h;
1262	return 1;
1263    }
1264    return 0;
1265}
1266
1267
1268/*
1269 *--------------------------------------------------------------
1270 *
1271 * Tk_ResetOutlineGC
1272 *
1273 *	Restores the GC to the situation before Tk_ChangeDashGC() was called.
1274 *	This function should be called just after the dashed item is drawn,
1275 *	because the GC is supposed to be read-only.
1276 *
1277 * Results:
1278 *	1 if there is a stipple pattern, and 0 otherwise.
1279 *
1280 * Side effects:
1281 *	GC is updated.
1282 *
1283 *--------------------------------------------------------------
1284 */
1285
1286int
1287Tk_ResetOutlineGC(
1288    Tk_Canvas canvas,
1289    Tk_Item *item,
1290    Tk_Outline *outline)
1291{
1292    char dashList;
1293    double width;
1294    Tk_Dash *dash;
1295    XColor *color;
1296    Pixmap stipple;
1297    Tk_State state = item->state;
1298
1299    width = outline->width;
1300    if (width < 1.0) {
1301	width = 1.0;
1302    }
1303    dash = &(outline->dash);
1304    color = outline->color;
1305    stipple = outline->stipple;
1306    if (state == TK_STATE_NULL) {
1307	state = ((TkCanvas *)canvas)->canvas_state;
1308    }
1309    if (((TkCanvas *)canvas)->currentItemPtr == item) {
1310	if (outline->activeWidth>width) {
1311	    width = outline->activeWidth;
1312	}
1313	if (outline->activeDash.number != 0) {
1314	    dash = &(outline->activeDash);
1315	}
1316	if (outline->activeColor!=NULL) {
1317	    color = outline->activeColor;
1318	}
1319	if (outline->activeStipple!=None) {
1320	    stipple = outline->activeStipple;
1321	}
1322    } else if (state == TK_STATE_DISABLED) {
1323	if (outline->disabledWidth>width) {
1324	    width = outline->disabledWidth;
1325	}
1326	if (outline->disabledDash.number != 0) {
1327	    dash = &(outline->disabledDash);
1328	}
1329	if (outline->disabledColor!=NULL) {
1330	    color = outline->disabledColor;
1331	}
1332	if (outline->disabledStipple!=None) {
1333	    stipple = outline->disabledStipple;
1334	}
1335    }
1336    if (color==NULL) {
1337	return 0;
1338    }
1339
1340    if ((dash->number > 2) || (dash->number < -1) || (dash->number==2 &&
1341	    (dash->pattern.array[0] != dash->pattern.array[1])) ||
1342	    ((dash->number == -1) && (dash->pattern.array[0] != ','))) {
1343	if (dash->number > 0) {
1344	    dashList = dash->pattern.array[0];
1345	} else {
1346	    dashList = (char) (4 * width + 0.5);
1347	}
1348	XSetDashes(((TkCanvas *)canvas)->display, outline->gc,
1349		outline->offset, &dashList , 1);
1350    }
1351    if (stipple != None) {
1352	XSetTSOrigin(((TkCanvas *)canvas)->display, outline->gc, 0, 0);
1353	return 1;
1354    }
1355    return 0;
1356}
1357
1358/*
1359 *--------------------------------------------------------------
1360 *
1361 * Tk_CanvasPsOutline
1362 *
1363 *	Creates the postscript command for the correct Outline-information
1364 *	(width, dash, color and stipple).
1365 *
1366 * Results:
1367 *	TCL_OK if succeeded, otherwise TCL_ERROR.
1368 *
1369 * Side effects:
1370 *	canvas->interp->result contains the postscript string, or an error
1371 *	message if the result was TCL_ERROR.
1372 *
1373 *--------------------------------------------------------------
1374 */
1375
1376int
1377Tk_CanvasPsOutline(
1378    Tk_Canvas canvas,
1379    Tk_Item *item,
1380    Tk_Outline *outline)
1381{
1382    char string[41];
1383    char pattern[11];
1384    int i;
1385    char *ptr;
1386    char *str = string;
1387    char *lptr = pattern;
1388    Tcl_Interp *interp = ((TkCanvas *)canvas)->interp;
1389    double width;
1390    Tk_Dash *dash;
1391    XColor *color;
1392    Pixmap stipple;
1393    Tk_State state = item->state;
1394
1395    width = outline->width;
1396    dash = &(outline->dash);
1397    color = outline->color;
1398    stipple = outline->stipple;
1399    if (state == TK_STATE_NULL) {
1400	state = ((TkCanvas *)canvas)->canvas_state;
1401    }
1402
1403    if (((TkCanvas *)canvas)->currentItemPtr == item) {
1404	if (outline->activeWidth > width) {
1405	    width = outline->activeWidth;
1406	}
1407	if (outline->activeDash.number > 0) {
1408	    dash = &(outline->activeDash);
1409	}
1410	if (outline->activeColor != NULL) {
1411	    color = outline->activeColor;
1412	}
1413	if (outline->activeStipple != None) {
1414	    stipple = outline->activeStipple;
1415	}
1416    } else if (state == TK_STATE_DISABLED) {
1417	if (outline->disabledWidth > 0) {
1418	    width = outline->disabledWidth;
1419	}
1420	if (outline->disabledDash.number > 0) {
1421	    dash = &(outline->disabledDash);
1422	}
1423	if (outline->disabledColor != NULL) {
1424	    color = outline->disabledColor;
1425	}
1426	if (outline->disabledStipple != None) {
1427	    stipple = outline->disabledStipple;
1428	}
1429    }
1430    sprintf(string, "%.15g setlinewidth\n", width);
1431    Tcl_AppendResult(interp, string, NULL);
1432
1433    if (dash->number > 10) {
1434	str = (char *)ckalloc((unsigned int) (1 + 4*dash->number));
1435    } else if (dash->number < -5) {
1436	str = (char *)ckalloc((unsigned int) (1 - 8*dash->number));
1437	lptr = (char *)ckalloc((unsigned int) (1 - 2*dash->number));
1438    }
1439    ptr = ((unsigned int)ABS(dash->number) > sizeof(char *)) ?
1440	    dash->pattern.pt : dash->pattern.array;
1441    if (dash->number > 0) {
1442	char *ptr0 = ptr;
1443
1444	sprintf(str, "[%d", *ptr++ & 0xff);
1445	i = dash->number-1;
1446	while (i--) {
1447	    sprintf(str+strlen(str), " %d", *ptr++ & 0xff);
1448	}
1449	Tcl_AppendResult(interp, str, NULL);
1450	if (dash->number&1) {
1451	    Tcl_AppendResult(interp, " ", str+1, NULL);
1452	}
1453	sprintf(str, "] %d setdash\n", outline->offset);
1454	Tcl_AppendResult(interp, str, NULL);
1455	ptr = ptr0;
1456    } else if (dash->number < 0) {
1457	if ((i = DashConvert(lptr, ptr, -dash->number, width)) != 0) {
1458	    char *lptr0 = lptr;
1459
1460	    sprintf(str, "[%d", *lptr++ & 0xff);
1461	    while (--i) {
1462		sprintf(str+strlen(str), " %d", *lptr++ & 0xff);
1463	    }
1464	    Tcl_AppendResult(interp, str, NULL);
1465	    sprintf(str, "] %d setdash\n", outline->offset);
1466	    Tcl_AppendResult(interp, str, NULL);
1467	    lptr = lptr0;
1468	} else {
1469	    Tcl_AppendResult(interp, "[] 0 setdash\n", NULL);
1470	}
1471    } else {
1472	Tcl_AppendResult(interp, "[] 0 setdash\n", NULL);
1473    }
1474    if (str != string) {
1475	ckfree(str);
1476    }
1477    if (lptr != pattern) {
1478	ckfree(lptr);
1479    }
1480    if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
1481	return TCL_ERROR;
1482    }
1483    if (stipple != None) {
1484	Tcl_AppendResult(interp, "StrokeClip ", NULL);
1485	if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {
1486	    return TCL_ERROR;
1487	}
1488    } else {
1489	Tcl_AppendResult(interp, "stroke\n", NULL);
1490    }
1491
1492    return TCL_OK;
1493}
1494
1495/*
1496 *--------------------------------------------------------------
1497 *
1498 * DashConvert
1499 *
1500 *	Converts a character-like dash-list (e.g. "-..") into an X11-style. l
1501 *	must point to a string that holds room to at least 2*n characters. If
1502 *	l == NULL, this function can be used for syntax checking only.
1503 *
1504 * Results:
1505 *	The length of the resulting X11 compatible dash-list. -1 if failed.
1506 *
1507 * Side effects:
1508 *	None
1509 *
1510 *--------------------------------------------------------------
1511 */
1512
1513static int
1514DashConvert(
1515    char *l,			/* Must be at least 2*n chars long, or NULL to
1516				 * indicate "just check syntax". */
1517    CONST char *p,		/* String to parse. */
1518    int n,			/* Length of string to parse, or -1 to
1519				 * indicate that strlen() should be used. */
1520    double width)		/* Width of line. */
1521{
1522    int result = 0;
1523    int size, intWidth;
1524
1525    if (n<0) {
1526	n = strlen(p);
1527    }
1528    intWidth = (int) (width + 0.5);
1529    if (intWidth < 1) {
1530	intWidth = 1;
1531    }
1532    while (n-- && *p) {
1533	switch (*p++) {
1534	case ' ':
1535	    if (result) {
1536		if (l) {
1537		    l[-1] += intWidth + 1;
1538		}
1539		continue;
1540	    }
1541	    return 0;
1542	case '_':
1543	    size = 8;
1544	    break;
1545	case '-':
1546	    size = 6;
1547	    break;
1548	case ',':
1549	    size = 4;
1550	    break;
1551	case '.':
1552	    size = 2;
1553	    break;
1554	default:
1555	    return -1;
1556	}
1557	if (l) {
1558	    *l++ = size * intWidth;
1559	    *l++ = 4 * intWidth;
1560	}
1561	result += 2;
1562    }
1563    return result;
1564}
1565
1566/*
1567 *----------------------------------------------------------------------
1568 *
1569 * TranslateAndAppendCoords --
1570 *
1571 *	This is a helper routine for TkCanvTranslatePath() below.
1572 *
1573 *	Given an (x,y) coordinate pair within a canvas, this function computes
1574 *	the corresponding coordinates at which the point should be drawn in
1575 *	the drawable used for display. Those coordinates are then written into
1576 *	outArr[numOut*2] and outArr[numOut*2+1].
1577 *
1578 * Results:
1579 *	There is no return value.
1580 *
1581 * Side effects:
1582 *	None.
1583 *
1584 *----------------------------------------------------------------------
1585 */
1586
1587static void
1588TranslateAndAppendCoords(
1589    TkCanvas *canvPtr,		/* The canvas. */
1590    double x,			/* Coordinates in canvas space. */
1591    double y,
1592    XPoint *outArr,		/* Write results into this array */
1593    int numOut)			/* Num of prior entries in outArr[] */
1594{
1595    double tmp;
1596
1597    tmp = x - canvPtr->drawableXOrigin;
1598    if (tmp > 0) {
1599	tmp += 0.5;
1600    } else {
1601	tmp -= 0.5;
1602    }
1603    outArr[numOut].x = (short) tmp;
1604
1605    tmp = y - canvPtr->drawableYOrigin;
1606    if (tmp > 0) {
1607	tmp += 0.5;
1608    } else {
1609	tmp -= 0.5;
1610    }
1611    outArr[numOut].y = (short) tmp;
1612}
1613
1614/*
1615 *--------------------------------------------------------------
1616 *
1617 * TkCanvTranslatePath
1618 *
1619 *	Translate a line or polygon path so that all vertices are within a
1620 *	rectangle that is 1000 pixels larger than the total size of the canvas
1621 *	window. This will prevent pixel coordinates from overflowing the
1622 *	16-bit integer size limitation imposed by most windowing systems.
1623 *
1624 *	coordPtr must point to an array of doubles, two doubles per vertex.
1625 *	There are a total of numVertex vertices, or 2*numVertex entries in
1626 *	coordPtr. The result vertices written into outArr have their
1627 *	coordinate origin shifted to canvPtr->drawableXOrigin by
1628 *	canvPtr->drawableYOrigin. There might be as many as 3 times more
1629 *	output vertices than there are input vertices. The calling function
1630 *	should allocate space accordingly.
1631 *
1632 *	This routine limits the width and height of a canvas window to 31767
1633 *	pixels. At the highest resolution display devices available today (210
1634 *	ppi in Jan 2003) that's a window that is over 13 feet wide and tall.
1635 *	Should be enough for the near future.
1636 *
1637 * Results:
1638 *	Clipped and translated path vertices are written into outArr[]. There
1639 *	might be as many as twice the vertices in outArr[] as there are in
1640 *	coordPtr[]. The return value is the number of vertices actually
1641 *	written into outArr[].
1642 *
1643 * Side effects:
1644 *	None
1645 *
1646 *--------------------------------------------------------------
1647 */
1648
1649int
1650TkCanvTranslatePath(
1651    TkCanvas *canvPtr,		/* The canvas */
1652    int numVertex,		/* Number of vertices specified by
1653				 * coordArr[] */
1654    double *coordArr,		/* X and Y coordinates for each vertex */
1655    int closedPath,		/* True if this is a closed polygon */
1656    XPoint *outArr)		/* Write results here, if not NULL */
1657{
1658    int numOutput = 0;		/* Number of output coordinates */
1659    double lft, rgh;		/* Left and right sides of the bounding box */
1660    double top, btm;		/* Top and bottom sizes of the bounding box */
1661    double *tempArr;		/* Temporary storage used by the clipper */
1662    double *a, *b, *t;		/* Pointers to parts of the temporary
1663				 * storage */
1664    int i, j;			/* Loop counters */
1665    int maxOutput;		/* Maximum number of outputs that we will
1666				 * allow */
1667    double limit[4];		/* Boundries at which clipping occurs */
1668    double staticSpace[480];	/* Temp space from the stack */
1669
1670    /*
1671     * Constrain all vertices of the path to be within a box that is no larger
1672     * than 32000 pixels wide or height. The top-left corner of this clipping
1673     * box is 1000 pixels above and to the left of the top left corner of the
1674     * window on which the canvas is displayed.
1675     *
1676     * This means that a canvas will not display properly on a canvas window
1677     * that is larger than 31000 pixels wide or high. That is not a problem
1678     * today, but might someday become a factor for ultra-high resolutions
1679     * displays.
1680     *
1681     * The X11 protocol allows us (in theory) to expand the size of the
1682     * clipping box to 32767 pixels. But we have found experimentally that
1683     * XFree86 sometimes fails to draw lines correctly if they are longer than
1684     * about 32500 pixels. So we have left a little margin in the size to mask
1685     * that bug.
1686     */
1687
1688    lft = canvPtr->xOrigin - 1000.0;
1689    top = canvPtr->yOrigin - 1000.0;
1690    rgh = lft + 32000.0;
1691    btm = top + 32000.0;
1692
1693    /*
1694     * Try the common case first - no clipping. Loop over the input
1695     * coordinates and translate them into appropriate output coordinates.
1696     * But if a vertex outside of the bounding box is seen, break out of the
1697     * loop.
1698     *
1699     * Most of the time, no clipping is needed, so this one loop is sufficient
1700     * to do the translation.
1701     */
1702
1703    for (i=0; i<numVertex; i++){
1704	double x, y;
1705
1706	x = coordArr[i*2];
1707	y = coordArr[i*2+1];
1708	if (x<lft || x>rgh || y<top || y>btm) {
1709	    break;
1710	}
1711	TranslateAndAppendCoords(canvPtr, x, y, outArr, numOutput++);
1712    }
1713    if (i == numVertex){
1714	assert(numOutput == numVertex);
1715	return numOutput;
1716    }
1717
1718    /*
1719     * If we reach this point, it means that some clipping is required. Begin
1720     * by allocating some working storage - at least 6 times as much space as
1721     * coordArr[] requires. Divide this space into two separate arrays a[] and
1722     * b[]. Initialize a[] to be equal to coordArr[].
1723     */
1724
1725    if (numVertex*12 <= (int)(sizeof(staticSpace)/sizeof(staticSpace[0]))) {
1726	tempArr = staticSpace;
1727    } else {
1728	tempArr = (double *)ckalloc(numVertex*12*sizeof(tempArr[0]));
1729    }
1730    for (i=0; i<numVertex*2; i++){
1731	tempArr[i] = coordArr[i];
1732    }
1733    a = tempArr;
1734    b = &tempArr[numVertex*6];
1735
1736    /*
1737     * We will make four passes through the input data. On each pass, we copy
1738     * the contents of a[] over into b[]. As we copy, we clip any line
1739     * segments that extend to the right past xClip then we rotate the
1740     * coordinate system 90 degrees clockwise. After each pass is complete, we
1741     * interchange a[] and b[] in preparation for the next pass.
1742     *
1743     * Each pass clips line segments that extend beyond a single side of the
1744     * bounding box, and four passes rotate the coordinate system back to its
1745     * original value. I'm not an expert on graphics algorithms, but I think
1746     * this is called Cohen-Sutherland polygon clipping.
1747     *
1748     * The limit[] array contains the xClip value used for each of the four
1749     * passes.
1750     */
1751
1752    limit[0] = rgh;
1753    limit[1] = -top;
1754    limit[2] = -lft;
1755    limit[3] = btm;
1756
1757    /*
1758     * This is the loop that makes the four passes through the data.
1759     */
1760
1761    maxOutput = numVertex*3;
1762    for (j=0; j<4; j++){
1763	double xClip = limit[j];
1764	int inside = a[0]<xClip;
1765	double priorY = a[1];
1766	numOutput = 0;
1767
1768	/*
1769	 * Clip everything to the right of xClip. Store the results in b[]
1770	 * rotated by 90 degrees clockwise.
1771	 */
1772
1773	for (i=0; i<numVertex; i++){
1774	    double x = a[i*2];
1775	    double y = a[i*2+1];
1776
1777	    if (x >= xClip) {
1778		/*
1779		 * The current vertex is to the right of xClip.
1780		 */
1781
1782		if (inside) {
1783		    /*
1784		     * If the current vertex is to the right of xClip but the
1785		     * previous vertex was left of xClip, then draw a line
1786		     * segment from the previous vertex to until it intersects
1787		     * the vertical at xClip.
1788		     */
1789
1790		    double x0, y0, yN;
1791
1792		    assert(i > 0);
1793		    x0 = a[i*2-2];
1794		    y0 = a[i*2-1];
1795		    yN = y0 + (y - y0)*(xClip-x0)/(x-x0);
1796		    b[numOutput*2] = -yN;
1797		    b[numOutput*2+1] = xClip;
1798		    numOutput++;
1799		    assert(numOutput <= maxOutput);
1800		    priorY = yN;
1801		    inside = 0;
1802		} else if (i == 0) {
1803		    /*
1804		     * If the first vertex is to the right of xClip, add a
1805		     * vertex that is the projection of the first vertex onto
1806		     * the vertical xClip line.
1807		     */
1808
1809		    b[0] = -y;
1810		    b[1] = xClip;
1811		    numOutput = 1;
1812		    priorY = y;
1813		}
1814	    } else {
1815		/*
1816		 * The current vertex is to the left of xClip
1817		 */
1818		if (!inside) {
1819		    /* If the current vertex is on the left of xClip and one
1820		     * or more prior vertices where to the right, then we have
1821		     * to draw a line segment along xClip that extends from
1822		     * the spot where we first crossed from left to right to
1823		     * the spot where we cross back from right to left.
1824		     */
1825
1826		    double x0, y0, yN;
1827
1828		    assert(i > 0);
1829		    x0 = a[i*2-2];
1830		    y0 = a[i*2-1];
1831		    yN = y0 + (y - y0)*(xClip-x0)/(x-x0);
1832		    if (yN != priorY) {
1833			b[numOutput*2] = -yN;
1834			b[numOutput*2+1] = xClip;
1835			numOutput++;
1836			assert(numOutput <= maxOutput);
1837		    }
1838		    inside = 1;
1839		}
1840		b[numOutput*2] = -y;
1841		b[numOutput*2+1] = x;
1842		numOutput++;
1843		assert(numOutput <= maxOutput);
1844	    }
1845	}
1846
1847	/*
1848	 * Interchange a[] and b[] in preparation for the next pass.
1849	 */
1850
1851	t = a;
1852	a = b;
1853	b = t;
1854	numVertex = numOutput;
1855    }
1856
1857    /*
1858     * All clipping is now finished. Convert the coordinates from doubles into
1859     * XPoints and translate the origin for the drawable.
1860     */
1861
1862    for (i=0; i<numVertex; i++){
1863	TranslateAndAppendCoords(canvPtr, a[i*2], a[i*2+1], outArr, i);
1864    }
1865    if (tempArr != staticSpace) {
1866	ckfree((char *) tempArr);
1867    }
1868    return numOutput;
1869}
1870
1871/*
1872 * Local Variables:
1873 * mode: c
1874 * c-basic-offset: 4
1875 * fill-column: 78
1876 * End:
1877 */
1878