OSDN Git Service

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