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