OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / blt2.5 / generic / bltGrElem.c
1
2 /*
3  * bltGrElem.c --
4  *
5  *      This module implements generic elements for the BLT graph widget.
6  *
7  * Copyright 1993-1998 Lucent Technologies, Inc.
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that the copyright notice and warranty
13  * disclaimer appear in supporting documentation, and that the names
14  * of Lucent Technologies any of their entities not be used in
15  * advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission.
17  *
18  * Lucent Technologies disclaims all warranties with regard to this
19  * software, including all implied warranties of merchantability and
20  * fitness.  In no event shall Lucent Technologies be liable for any
21  * special, indirect or consequential damages or any damages
22  * whatsoever resulting from loss of use, data or profits, whether in
23  * an action of contract, negligence or other tortuous action, arising
24  * out of or in connection with the use or performance of this
25  * software.
26  */
27
28 #include "bltGraph.h"
29 #include "bltChain.h"
30 #include <X11/Xutil.h>
31
32
33 static Tk_OptionParseProc StringToData;
34 static Tk_OptionPrintProc DataToString;
35 static Tk_OptionParseProc StringToDataPairs;
36 static Tk_OptionPrintProc DataPairsToString;
37 static Tk_OptionParseProc StringToAlong;
38 static Tk_OptionPrintProc AlongToString;
39 static Tk_CustomOption alongOption =
40 {
41     StringToAlong, AlongToString, (ClientData)0
42 };
43 Tk_CustomOption bltDataOption =
44 {
45     StringToData, DataToString, (ClientData)0
46 };
47 Tk_CustomOption bltDataPairsOption =
48 {
49     StringToDataPairs, DataPairsToString, (ClientData)0
50 };
51 extern Tk_CustomOption bltDistanceOption;
52
53 \f
54 static int counter;
55
56 #include "bltGrElem.h"
57
58 extern Element *Blt_BarElement();
59 extern Element *Blt_LineElement();
60
61 static Blt_VectorChangedProc VectorChangedProc;
62
63 EXTERN int Blt_VectorExists2 _ANSI_ARGS_((Tcl_Interp *interp, char *vecName));
64
65 /*
66  * ----------------------------------------------------------------------
67  * Custom option parse and print procedures
68  * ----------------------------------------------------------------------
69  */
70 static int
71 GetPenStyle(graphPtr, string, type, stylePtr)
72     Graph *graphPtr;
73     char *string;
74     Blt_Uid type;
75     PenStyle *stylePtr;
76 {
77     Pen *penPtr;
78     Tcl_Interp *interp = graphPtr->interp;
79     char **elemArr;
80     int nElem;
81
82     elemArr = NULL;
83     if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
84         return TCL_ERROR;
85     }
86     if ((nElem != 1) && (nElem != 3)) {
87         Tcl_AppendResult(interp, "bad style \"", string, "\": should be ", 
88                  "\"penName\" or \"penName min max\"", (char *)NULL);
89         if (elemArr != NULL) {
90             Blt_Free(elemArr);
91         }
92         return TCL_ERROR;
93     }
94     if (Blt_GetPen(graphPtr, elemArr[0], type, &penPtr) != TCL_OK) {
95         Blt_Free(elemArr);
96         return TCL_ERROR;
97     }
98     if (nElem == 3) {
99         double min, max;
100
101         if ((Tcl_GetDouble(interp, elemArr[1], &min) != TCL_OK) ||
102             (Tcl_GetDouble(interp, elemArr[2], &max) != TCL_OK)) {
103             Blt_Free(elemArr);
104             return TCL_ERROR;
105         }
106         SetWeight(stylePtr->weight, min, max);
107     }
108     stylePtr->penPtr = penPtr;
109     Blt_Free(elemArr);
110     return TCL_OK;
111 }
112
113
114 static void
115 SyncElemVector(vPtr)
116     ElemVector *vPtr;
117 {
118     vPtr->nValues = Blt_VecLength(vPtr->vecPtr);
119     vPtr->valueArr = Blt_VecData(vPtr->vecPtr);
120     vPtr->min = Blt_VecMin(vPtr->vecPtr);
121     vPtr->max = Blt_VecMax(vPtr->vecPtr);
122 }
123
124 /*
125  *----------------------------------------------------------------------
126  *
127  * FindRange --
128  *
129  *      Find the minimum, positive minimum, and maximum values in a
130  *      given vector and store the results in the vector structure.
131  *
132  * Results:
133  *      None.
134  *
135  * Side Effects:
136  *      Minimum, positive minimum, and maximum values are stored in
137  *      the vector.
138  *
139  *----------------------------------------------------------------------
140  */
141 static void
142 FindRange(vPtr)
143     ElemVector *vPtr;
144 {
145     register int i;
146     register double *x;
147     register double min, max;
148
149     if ((vPtr->nValues < 1) || (vPtr->valueArr == NULL)) {
150         return;                 /* This shouldn't ever happen. */
151     }
152     x = vPtr->valueArr;
153
154     min = DBL_MAX, max = -DBL_MAX;
155     for(i = 0; i < vPtr->nValues; i++) {
156         if (FINITE(x[i])) {
157             min = max = x[i];
158             break;
159         }
160     }
161     /*  Initialize values to track the vector range */
162     for (/* empty */; i < vPtr->nValues; i++) {
163         if (FINITE(x[i])) {
164             if (x[i] < min) {
165                 min = x[i];
166             } else if (x[i] > max) {
167                 max = x[i];
168             }
169         }
170     }
171     vPtr->min = min, vPtr->max = max;
172 }
173
174 /*
175  *----------------------------------------------------------------------
176  *
177  * Blt_FindElemVectorMinimum --
178  *
179  *      Find the minimum, positive minimum, and maximum values in a
180  *      given vector and store the results in the vector structure.
181  *
182  * Results:
183  *      None.
184  *
185  * Side Effects:
186  *      Minimum, positive minimum, and maximum values are stored in
187  *      the vector.
188  *
189  *----------------------------------------------------------------------
190  */
191 double
192 Blt_FindElemVectorMinimum(vPtr, minLimit)
193     ElemVector *vPtr;
194     double minLimit;
195 {
196     register int i;
197     register double *arr;
198     register double min, x;
199
200     min = DBL_MAX;
201     arr = vPtr->valueArr;
202     for (i = 0; i < vPtr->nValues; i++) {
203         x = arr[i];
204         if (x < 0.0) {
205             /* What do you do about negative values when using log
206              * scale values seems like a grey area.  Mirror. */
207             x = -x;
208         }
209         if ((x > minLimit) && (min > x)) {
210             min = x;
211         }
212     }
213     if (min == DBL_MAX) {
214         min = minLimit;
215     }
216     return min;
217 }
218
219 static void
220 FreeDataVector(vPtr)
221     ElemVector *vPtr;
222 {
223     if (vPtr->clientId != NULL) {
224         Blt_FreeVectorId(vPtr->clientId);       /* Free the old vector */
225         vPtr->clientId = NULL;
226     } else if (vPtr->valueArr != NULL) {
227         Blt_Free(vPtr->valueArr);
228     }
229     vPtr->valueArr = NULL;
230     vPtr->nValues = 0;
231 }
232
233 /*
234  *----------------------------------------------------------------------
235  *
236  * VectorChangedProc --
237  *
238  *
239  * Results:
240  *      None.
241  *
242  * Side Effects:
243  *      Graph is redrawn.
244  *
245  *----------------------------------------------------------------------
246  */
247 static void
248 VectorChangedProc(interp, clientData, notify)
249     Tcl_Interp *interp;
250     ClientData clientData;
251     Blt_VectorNotify notify;
252 {
253     ElemVector *vPtr = clientData;
254     Element *elemPtr = vPtr->elemPtr;
255     Graph *graphPtr = elemPtr->graphPtr;
256
257     switch (notify) {
258     case BLT_VECTOR_NOTIFY_DESTROY:
259         vPtr->clientId = NULL;
260         vPtr->valueArr = NULL;
261         vPtr->nValues = 0;
262         break;
263
264     case BLT_VECTOR_NOTIFY_UPDATE:
265     default:
266         Blt_GetVectorById(interp, vPtr->clientId, &vPtr->vecPtr);
267         SyncElemVector(vPtr);
268         break;
269     }
270     graphPtr->flags |= RESET_AXES;
271     elemPtr->flags |= MAP_ITEM;
272     if (!elemPtr->hidden) {
273         graphPtr->flags |= REDRAW_BACKING_STORE;
274         Blt_EventuallyRedrawGraph(graphPtr);
275     }
276 }
277
278 static int
279 EvalExprList(interp, list, nElemPtr, arrayPtr)
280     Tcl_Interp *interp;
281     char *list;
282     int *nElemPtr;
283     double **arrayPtr;
284 {
285     int nElem;
286     char **elemArr;
287     double *array;
288     int result;
289
290     result = TCL_ERROR;
291     elemArr = NULL;
292     if (Tcl_SplitList(interp, list, &nElem, &elemArr) != TCL_OK) {
293         return TCL_ERROR;
294     }
295     array = NULL;
296     if (nElem > 0) {
297         register double *valuePtr;
298         register int i;
299
300         counter++;
301         array = Blt_Malloc(sizeof(double) * nElem);
302         if (array == NULL) {
303             Tcl_AppendResult(interp, "can't allocate new vector", (char *)NULL);
304             goto badList;
305         }
306         valuePtr = array;
307         for (i = 0; i < nElem; i++) {
308             if (Tcl_ExprDouble(interp, elemArr[i], valuePtr) != TCL_OK) {
309                 goto badList;
310             }
311             valuePtr++;
312         }
313     }
314     result = TCL_OK;
315
316   badList:
317     Blt_Free(elemArr);
318     *arrayPtr = array;
319     *nElemPtr = nElem;
320     if (result != TCL_OK) {
321         Blt_Free(array);
322     }
323     return result;
324 }
325
326 /*
327  *----------------------------------------------------------------------
328  *
329  * StringToData --
330  *
331  *      Given a Tcl list of numeric expression representing the element
332  *      values, convert into an array of double precision values. In
333  *      addition, the minimum and maximum values are saved.  Since
334  *      elastic values are allow (values which translate to the
335  *      min/max of the graph), we must try to get the non-elastic
336  *      minimum and maximum.
337  *
338  * Results:
339  *      The return value is a standard Tcl result.  The vector is passed
340  *      back via the vPtr.
341  *
342  *----------------------------------------------------------------------
343  */
344 /*ARGSUSED*/
345 static int
346 StringToData(clientData, interp, tkwin, string, widgRec, offset)
347     ClientData clientData;      /* Type of axis vector to fill */
348     Tcl_Interp *interp;         /* Interpreter to send results back to */
349     Tk_Window tkwin;            /* Not used. */
350     char *string;               /* Tcl list of expressions */
351     char *widgRec;              /* Element record */
352     int offset;                 /* Offset of vector in Element record */
353 {
354     Element *elemPtr = (Element *)(widgRec);
355     ElemVector *vPtr = (ElemVector *)(widgRec + offset);
356
357     FreeDataVector(vPtr);
358     if (Blt_VectorExists2(interp, string)) {
359         Blt_VectorId clientId;
360
361         clientId = Blt_AllocVectorId(interp, string);
362         if (Blt_GetVectorById(interp, clientId, &vPtr->vecPtr) != TCL_OK) {
363             return TCL_ERROR;
364         }
365         Blt_SetVectorChangedProc(clientId, VectorChangedProc, vPtr);
366         vPtr->elemPtr = elemPtr;
367         vPtr->clientId = clientId;
368         SyncElemVector(vPtr);
369         elemPtr->flags |= MAP_ITEM;
370     } else {
371         double *newArr;
372         int nValues;
373
374         if (EvalExprList(interp, string, &nValues, &newArr) != TCL_OK) {
375             return TCL_ERROR;
376         }
377         if (nValues > 0) {
378             vPtr->valueArr = newArr;
379         }
380         vPtr->nValues = nValues;
381         FindRange(vPtr);
382     }
383     return TCL_OK;
384 }
385
386 /*
387  *----------------------------------------------------------------------
388  *
389  * DataToString --
390  *
391  *      Convert the vector of floating point values into a Tcl list.
392  *
393  * Results:
394  *      The string representation of the vector is returned.
395  *
396  *----------------------------------------------------------------------
397  */
398 /*ARGSUSED*/
399 static char *
400 DataToString(clientData, tkwin, widgRec, offset, freeProcPtr)
401     ClientData clientData;      /* Type of axis vector to print */
402     Tk_Window tkwin;            /* Not used. */
403     char *widgRec;              /* Element record */
404     int offset;                 /* Offset of vector in Element record */
405     Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
406 {
407     ElemVector *vPtr = (ElemVector *)(widgRec + offset);
408     Element *elemPtr = (Element *)(widgRec);
409     Tcl_DString dString;
410     char *result;
411     char string[TCL_DOUBLE_SPACE + 1];
412     double *p, *endPtr; 
413
414     if (vPtr->clientId != NULL) {
415         return Blt_NameOfVectorId(vPtr->clientId);
416     }
417     if (vPtr->nValues == 0) {
418         return "";
419     }
420     Tcl_DStringInit(&dString);
421     endPtr = vPtr->valueArr + vPtr->nValues;
422     for (p = vPtr->valueArr; p < endPtr; p++) {
423         Tcl_PrintDouble(elemPtr->graphPtr->interp, *p, string);
424         Tcl_DStringAppendElement(&dString, string);
425     }
426     result = Tcl_DStringValue(&dString);
427
428     /*
429      * If memory wasn't allocated for the dynamic string, do it here (it's
430      * currently on the stack), so that Tcl can free it normally.
431      */
432     if (result == dString.staticSpace) {
433         result = Blt_Strdup(result);
434     }
435     *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
436     return result;
437 }
438
439 /*
440  *----------------------------------------------------------------------
441  *
442  * StringToDataPairs --
443  *
444  *      This procedure is like StringToData except that it interprets
445  *      the list of numeric expressions as X Y coordinate pairs.  The
446  *      minimum and maximum for both the X and Y vectors are
447  *      determined.
448  *
449  * Results:
450  *      The return value is a standard Tcl result.  The vectors are
451  *      passed back via the widget record (elemPtr).
452  *
453  *----------------------------------------------------------------------
454  */
455 /*ARGSUSED*/
456 static int
457 StringToDataPairs(clientData, interp, tkwin, string, widgRec, offset)
458     ClientData clientData;      /* Not used. */
459     Tcl_Interp *interp;         /* Interpreter to send results back to */
460     Tk_Window tkwin;            /* Not used. */
461     char *string;               /* Tcl list of numeric expressions */
462     char *widgRec;              /* Element record */
463     int offset;                 /* Not used. */
464 {
465     Element *elemPtr = (Element *)widgRec;
466     int nElem;
467     unsigned int newSize;
468     double *newArr;
469
470     if (EvalExprList(interp, string, &nElem, &newArr) != TCL_OK) {
471         return TCL_ERROR;
472     }
473     if (nElem & 1) {
474         Tcl_AppendResult(interp, "odd number of data points", (char *)NULL);
475         Blt_Free(newArr);
476         return TCL_ERROR;
477     }
478     nElem /= 2;
479     newSize = nElem * sizeof(double);
480
481     FreeDataVector(&elemPtr->x);
482     FreeDataVector(&elemPtr->y);
483
484     elemPtr->x.valueArr = Blt_Malloc(newSize);
485     elemPtr->y.valueArr = Blt_Malloc(newSize);
486     assert(elemPtr->x.valueArr && elemPtr->y.valueArr);
487     elemPtr->x.nValues = elemPtr->y.nValues = nElem;
488
489     if (newSize > 0) {
490         register double *dataPtr;
491         register int i;
492
493         for (dataPtr = newArr, i = 0; i < nElem; i++) {
494             elemPtr->x.valueArr[i] = *dataPtr++;
495             elemPtr->y.valueArr[i] = *dataPtr++;
496         }
497         Blt_Free(newArr);
498         FindRange(&elemPtr->x);
499         FindRange(&elemPtr->y);
500     }
501     return TCL_OK;
502 }
503
504 /*
505  *----------------------------------------------------------------------
506  *
507  * DataPairsToString --
508  *
509  *      Convert pairs of floating point values in the X and Y arrays
510  *      into a Tcl list.
511  *
512  * Results:
513  *      The return value is a string (Tcl list).
514  *
515  *----------------------------------------------------------------------
516  */
517 /*ARGSUSED*/
518 static char *
519 DataPairsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
520     ClientData clientData;      /* Not used. */
521     Tk_Window tkwin;            /* Not used. */
522     char *widgRec;              /* Element information record */
523     int offset;                 /* Not used. */
524     Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
525 {
526     Element *elemPtr = (Element *)widgRec;
527     Tcl_Interp *interp = elemPtr->graphPtr->interp;
528     int i;
529     int length;
530     char *result;
531     char string[TCL_DOUBLE_SPACE + 1];
532     Tcl_DString dString;
533
534     length = NumberOfPoints(elemPtr);
535     if (length < 1) {
536         return "";
537     }
538     Tcl_DStringInit(&dString);
539     for (i = 0; i < length; i++) {
540         Tcl_PrintDouble(interp, elemPtr->x.valueArr[i], string);
541         Tcl_DStringAppendElement(&dString, string);
542         Tcl_PrintDouble(interp, elemPtr->y.valueArr[i], string);
543         Tcl_DStringAppendElement(&dString, string);
544     }
545     result = Tcl_DStringValue(&dString);
546
547     /*
548      * If memory wasn't allocated for the dynamic string, do it here
549      * (it's currently on the stack), so that Tcl can free it
550      * normally.
551      */
552     if (result == dString.staticSpace) {
553         result = Blt_Strdup(result);
554     }
555     *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
556     return result;
557 }
558
559 /*
560  *----------------------------------------------------------------------
561  *
562  * StringToAlong --
563  *
564  *      Given a Tcl list of numeric expression representing the element
565  *      values, convert into an array of double precision values. In
566  *      addition, the minimum and maximum values are saved.  Since
567  *      elastic values are allow (values which translate to the
568  *      min/max of the graph), we must try to get the non-elastic
569  *      minimum and maximum.
570  *
571  * Results:
572  *      The return value is a standard Tcl result.  The vector is passed
573  *      back via the vPtr.
574  *
575  *----------------------------------------------------------------------
576  */
577 /*ARGSUSED*/
578 static int
579 StringToAlong(clientData, interp, tkwin, string, widgRec, offset)
580     ClientData clientData;      /* Not used. */
581     Tcl_Interp *interp;         /* Interpreter to send results back to */
582     Tk_Window tkwin;            /* Not used. */
583     char *string;               /* String representation of value. */
584     char *widgRec;              /* Widget record. */
585     int offset;                 /* Offset of field in widget record. */
586 {
587     int *intPtr = (int *)(widgRec + offset);
588
589     if ((string[0] == 'x') && (string[1] == '\0')) {
590         *intPtr = SEARCH_X;
591     } else if ((string[0] == 'y') && (string[1] == '\0')) { 
592         *intPtr = SEARCH_Y;
593     } else if ((string[0] == 'b') && (strcmp(string, "both") == 0)) {
594         *intPtr = SEARCH_BOTH;
595     } else {
596         Tcl_AppendResult(interp, "bad along value \"", string, "\"",
597                          (char *)NULL);
598         return TCL_ERROR;
599     }
600     return TCL_OK;
601 }
602
603 /*
604  *----------------------------------------------------------------------
605  *
606  * AlongToString --
607  *
608  *      Convert the vector of floating point values into a Tcl list.
609  *
610  * Results:
611  *      The string representation of the vector is returned.
612  *
613  *----------------------------------------------------------------------
614  */
615 /*ARGSUSED*/
616 static char *
617 AlongToString(clientData, tkwin, widgRec, offset, freeProcPtr)
618     ClientData clientData;      /* Not used. */
619     Tk_Window tkwin;            /* Not used. */
620     char *widgRec;              /* Widget record */
621     int offset;                 /* Offset of field in widget record */
622     Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
623 {
624     int along = *(int *)(widgRec + offset);
625
626     switch (along) {
627     case SEARCH_X:
628         return "x";
629     case SEARCH_Y:
630         return "y";
631     case SEARCH_BOTH:
632         return "both";
633     default:
634         return "unknown along value";
635     }
636 }
637
638 void
639 Blt_FreePalette(graphPtr, palette)
640     Graph *graphPtr;
641     Blt_Chain *palette;
642 {
643     Blt_ChainLink *linkPtr;
644
645     /* Skip the first slot. It contains the built-in "normal" pen of
646      * the element.  */
647     linkPtr = Blt_ChainFirstLink(palette);
648     if (linkPtr != NULL) {
649         register PenStyle *stylePtr;
650         Blt_ChainLink *nextPtr;
651
652         for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL; 
653              linkPtr = nextPtr) {
654             nextPtr =  Blt_ChainNextLink(linkPtr);
655             stylePtr = Blt_ChainGetValue(linkPtr);
656             Blt_FreePen(graphPtr, stylePtr->penPtr);
657             Blt_ChainDeleteLink(palette, linkPtr);
658         }
659     }
660 }
661
662 /*
663  *----------------------------------------------------------------------
664  *
665  * Blt_StringToStyles --
666  *
667  *      Parse the list of style names.
668  *
669  * Results:
670  *      The return value is a standard Tcl result.
671  *
672  *----------------------------------------------------------------------
673  */
674 /*ARGSUSED*/
675 int
676 Blt_StringToStyles(clientData, interp, tkwin, string, widgRec, offset)
677     ClientData clientData;      /* Not used. */
678     Tcl_Interp *interp;         /* Interpreter to send results back to */
679     Tk_Window tkwin;            /* Not used. */
680     char *string;               /* String representing style list */
681     char *widgRec;              /* Element information record */
682     int offset;                 /* Offset of symbol type field in record */
683 {
684     Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset);
685     Blt_ChainLink *linkPtr;
686     Element *elemPtr = (Element *)(widgRec);
687     PenStyle *stylePtr;
688     char **elemArr;
689     int nStyles;
690     register int i;
691     size_t size = (size_t)clientData;
692
693     elemArr = NULL;
694     Blt_FreePalette(elemPtr->graphPtr, palette);
695     if ((string == NULL) || (*string == '\0')) {
696         nStyles = 0;
697     } else if (Tcl_SplitList(interp, string, &nStyles, &elemArr) != TCL_OK) {
698         return TCL_ERROR;
699     }
700     /* Reserve the first entry for the "normal" pen. We'll set the
701      * style later */
702     linkPtr = Blt_ChainFirstLink(palette);
703     if (linkPtr == NULL) {
704         linkPtr = Blt_ChainAllocLink(size);
705         Blt_ChainLinkBefore(palette, linkPtr, NULL);
706     }
707     stylePtr = Blt_ChainGetValue(linkPtr);
708     stylePtr->penPtr = elemPtr->normalPenPtr;
709
710     for (i = 0; i < nStyles; i++) {
711         linkPtr = Blt_ChainAllocLink(size);
712         stylePtr = Blt_ChainGetValue(linkPtr);
713         stylePtr->weight.min = (double)i;
714         stylePtr->weight.max = (double)i + 1.0;
715         stylePtr->weight.range = 1.0;
716         if (GetPenStyle(elemPtr->graphPtr, elemArr[i], elemPtr->classUid,
717             (PenStyle *)stylePtr) != TCL_OK) {
718             Blt_Free(elemArr);
719             Blt_FreePalette(elemPtr->graphPtr, palette);
720             return TCL_ERROR;
721         }
722         Blt_ChainLinkBefore(palette, linkPtr, NULL);
723     }
724     if (elemArr != NULL) {
725         Blt_Free(elemArr);
726     }
727     return TCL_OK;
728 }
729
730 /*
731  *----------------------------------------------------------------------
732  *
733  * Blt_StylesToString --
734  *
735  *      Convert the style information into a string.
736  *
737  * Results:
738  *      The string representing the style information is returned.
739  *
740  *----------------------------------------------------------------------
741  */
742 /*ARGSUSED*/
743 char *
744 Blt_StylesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
745     ClientData clientData;      /* Not used. */
746     Tk_Window tkwin;            /* Not used. */
747     char *widgRec;              /* Element information record */
748     int offset;                 /* Not used. */
749     Tcl_FreeProc **freeProcPtr; /* Not used. */
750 {
751     Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset);
752     Tcl_DString dString;
753     char *result;
754     Blt_ChainLink *linkPtr;
755
756     Tcl_DStringInit(&dString);
757     linkPtr = Blt_ChainFirstLink(palette);
758     if (linkPtr != NULL) {
759         Element *elemPtr = (Element *)(widgRec);
760         char string[TCL_DOUBLE_SPACE];
761         Tcl_Interp *interp;
762         PenStyle *stylePtr;
763
764         interp = elemPtr->graphPtr->interp;
765         for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL;
766              linkPtr = Blt_ChainNextLink(linkPtr)) {
767             stylePtr = Blt_ChainGetValue(linkPtr);
768             Tcl_DStringStartSublist(&dString);
769             Tcl_DStringAppendElement(&dString, stylePtr->penPtr->name);
770             Tcl_PrintDouble(interp, stylePtr->weight.min, string);
771             Tcl_DStringAppendElement(&dString, string);
772             Tcl_PrintDouble(interp, stylePtr->weight.max, string);
773             Tcl_DStringAppendElement(&dString, string);
774             Tcl_DStringEndSublist(&dString);
775         }
776     }
777     result = Tcl_DStringValue(&dString);
778     if (result == dString.staticSpace) {
779         result = Blt_Strdup(result);
780     }
781     *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
782     return result;
783 }
784
785 /*
786  *----------------------------------------------------------------------
787  *
788  * Blt_StyleMap --
789  *
790  *      Creates an array of style indices and fills it based on the weight
791  *      of each data point.
792  *
793  * Results:
794  *      None.
795  *
796  * Side effects:
797  *      Memory is freed and allocated for the index array.
798  *
799  *----------------------------------------------------------------------
800  */
801
802 PenStyle **
803 Blt_StyleMap(elemPtr)
804     Element *elemPtr;
805 {
806     register int i;
807     int nWeights;               /* Number of weights to be examined.
808                                  * If there are more data points than
809                                  * weights, they will default to the
810                                  * normal pen. */
811
812     PenStyle **dataToStyle;     /* Directory of styles.  Each array
813                                  * element represents the style for
814                                  * the data point at that index */
815     Blt_ChainLink *linkPtr;
816     PenStyle *stylePtr;
817     double *w;                  /* Weight vector */
818     int nPoints;
819
820     nPoints = NumberOfPoints(elemPtr);
821     nWeights = MIN(elemPtr->w.nValues, nPoints);
822     w = elemPtr->w.valueArr;
823     linkPtr = Blt_ChainFirstLink(elemPtr->palette);
824     stylePtr = Blt_ChainGetValue(linkPtr);
825
826     /* 
827      * Create a style mapping array (data point index to style), 
828      * initialized to the default style.
829      */
830     dataToStyle = Blt_Malloc(nPoints * sizeof(PenStyle *));
831     assert(dataToStyle);
832     for (i = 0; i < nPoints; i++) {
833         dataToStyle[i] = stylePtr;
834     }
835
836     for (i = 0; i < nWeights; i++) {
837         for (linkPtr = Blt_ChainLastLink(elemPtr->palette); linkPtr != NULL;
838              linkPtr = Blt_ChainPrevLink(linkPtr)) {
839             stylePtr = Blt_ChainGetValue(linkPtr);
840
841             if (stylePtr->weight.range > 0.0) {
842                 double norm;
843
844                 norm = (w[i] - stylePtr->weight.min) / stylePtr->weight.range;
845                 if (((norm - 1.0) <= DBL_EPSILON) && 
846                     (((1.0 - norm) - 1.0) <= DBL_EPSILON)) {
847                     dataToStyle[i] = stylePtr;
848                     break;              /* Done: found range that matches. */
849                 }
850             }
851         }
852     }
853     return dataToStyle;
854 }
855
856
857 /*
858  *----------------------------------------------------------------------
859  *
860  * Blt_MapErrorBars --
861  *
862  *      Creates two arrays of points and pen indices, filled with
863  *      the screen coordinates of the visible
864  *
865  * Results:
866  *      None.
867  *
868  * Side effects:
869  *      Memory is freed and allocated for the index array.
870  *
871  *----------------------------------------------------------------------
872  */
873 void
874 Blt_MapErrorBars(graphPtr, elemPtr, dataToStyle)
875     Graph *graphPtr;
876     Element *elemPtr;
877     PenStyle **dataToStyle;
878 {
879     int n, nPoints;
880     Extents2D exts;
881     PenStyle *stylePtr;
882
883     Blt_GraphExtents(graphPtr, &exts);
884     nPoints = NumberOfPoints(elemPtr);
885     if (elemPtr->xError.nValues > 0) {
886         n = MIN(elemPtr->xError.nValues, nPoints);
887     } else {
888         n = MIN3(elemPtr->xHigh.nValues, elemPtr->xLow.nValues, nPoints);
889     }
890     if (n > 0) {
891         Segment2D *errorBars;
892         Segment2D *segPtr;
893         double high, low;
894         double x, y;
895         int *errorToData;
896         int *indexPtr;
897         register int i;
898                 
899         segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D));
900         indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int));
901         for (i = 0; i < n; i++) {
902             x = elemPtr->x.valueArr[i];
903             y = elemPtr->y.valueArr[i];
904             stylePtr = dataToStyle[i];
905             if ((FINITE(x)) && (FINITE(y))) {
906                 if (elemPtr->xError.nValues > 0) {
907                     high = x + elemPtr->xError.valueArr[i];
908                     low = x - elemPtr->xError.valueArr[i];
909                 } else {
910                     high = elemPtr->xHigh.valueArr[i];
911                     low = elemPtr->xLow.valueArr[i];
912                 }
913                 if ((FINITE(high)) && (FINITE(low)))  {
914                     Point2D p, q;
915
916                     p = Blt_Map2D(graphPtr, high, y, &elemPtr->axes);
917                     q = Blt_Map2D(graphPtr, low, y, &elemPtr->axes);
918                     segPtr->p = p;
919                     segPtr->q = q;
920                     if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
921                         segPtr++;
922                         *indexPtr++ = i;
923                     }
924                     /* Left cap */
925                     segPtr->p.x = segPtr->q.x = p.x;
926                     segPtr->p.y = p.y - stylePtr->errorBarCapWidth;
927                     segPtr->q.y = p.y + stylePtr->errorBarCapWidth;
928                     if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
929                         segPtr++;
930                         *indexPtr++ = i;
931                     }
932                     /* Right cap */
933                     segPtr->p.x = segPtr->q.x = q.x;
934                     segPtr->p.y = q.y - stylePtr->errorBarCapWidth;
935                     segPtr->q.y = q.y + stylePtr->errorBarCapWidth;
936                     if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
937                         segPtr++;
938                         *indexPtr++ = i;
939                     }
940                 }
941             }
942         }
943         elemPtr->xErrorBars = errorBars;
944         elemPtr->xErrorBarCnt = segPtr - errorBars;
945         elemPtr->xErrorToData = errorToData;
946     }
947     if (elemPtr->yError.nValues > 0) {
948         n = MIN(elemPtr->yError.nValues, nPoints);
949     } else {
950         n = MIN3(elemPtr->yHigh.nValues, elemPtr->yLow.nValues, nPoints);
951     }
952     if (n > 0) {
953         Segment2D *errorBars;
954         Segment2D *segPtr;
955         double high, low;
956         double x, y;
957         int *errorToData;
958         int *indexPtr;
959         register int i;
960                 
961         segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D));
962         indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int));
963         for (i = 0; i < n; i++) {
964             x = elemPtr->x.valueArr[i];
965             y = elemPtr->y.valueArr[i];
966             stylePtr = dataToStyle[i];
967             if ((FINITE(x)) && (FINITE(y))) {
968                 if (elemPtr->yError.nValues > 0) {
969                     high = y + elemPtr->yError.valueArr[i];
970                     low = y - elemPtr->yError.valueArr[i];
971                 } else {
972                     high = elemPtr->yHigh.valueArr[i];
973                     low = elemPtr->yLow.valueArr[i];
974                 }
975                 if ((FINITE(high)) && (FINITE(low)))  {
976                     Point2D p, q;
977                     
978                     p = Blt_Map2D(graphPtr, x, high, &elemPtr->axes);
979                     q = Blt_Map2D(graphPtr, x, low, &elemPtr->axes);
980                     segPtr->p = p;
981                     segPtr->q = q;
982                     if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
983                         segPtr++;
984                         *indexPtr++ = i;
985                     }
986                     /* Top cap. */
987                     segPtr->p.y = segPtr->q.y = p.y;
988                     segPtr->p.x = p.x - stylePtr->errorBarCapWidth;
989                     segPtr->q.x = p.x + stylePtr->errorBarCapWidth;
990                     if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
991                         segPtr++;
992                         *indexPtr++ = i;
993                     }
994                     /* Bottom cap. */
995                     segPtr->p.y = segPtr->q.y = q.y;
996                     segPtr->p.x = q.x - stylePtr->errorBarCapWidth;
997                     segPtr->q.x = q.x + stylePtr->errorBarCapWidth;
998                     if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
999                         segPtr++;
1000                         *indexPtr++ = i;
1001                     }
1002                 }
1003             }
1004         }
1005         elemPtr->yErrorBars = errorBars;
1006         elemPtr->yErrorBarCnt = segPtr - errorBars;
1007         elemPtr->yErrorToData = errorToData;
1008     }
1009 }
1010
1011
1012 /*
1013  *----------------------------------------------------------------------
1014  *
1015  * GetIndex --
1016  *
1017  *      Given a string representing the index of a pair of x,y
1018  *      coordinates, return the numeric index.
1019  *
1020  * Results:
1021  *      A standard TCL result.
1022  *
1023  *----------------------------------------------------------------------
1024  */
1025 static int
1026 GetIndex(interp, elemPtr, string, indexPtr)
1027     Tcl_Interp *interp;
1028     Element *elemPtr;
1029     char *string;
1030     int *indexPtr;
1031 {
1032     long ielem;
1033     int last;
1034
1035     last = NumberOfPoints(elemPtr) - 1;
1036     if ((*string == 'e') && (strcmp("end", string) == 0)) {
1037         ielem = last;
1038     } else if (Tcl_ExprLong(interp, string, &ielem) != TCL_OK) {
1039         return TCL_ERROR;
1040     }
1041     *indexPtr = (int)ielem;
1042     return TCL_OK;
1043 }
1044
1045 /*
1046  *----------------------------------------------------------------------
1047  *
1048  * NameToElement --
1049  *
1050  *      Find the element represented the given name,  returning
1051  *      a pointer to its data structure via elemPtrPtr.
1052  *
1053  * Results:
1054  *      A standard TCL result.
1055  *
1056  *----------------------------------------------------------------------
1057  */
1058 static int
1059 NameToElement(graphPtr, name, elemPtrPtr)
1060     Graph *graphPtr;
1061     char *name;
1062     Element **elemPtrPtr;
1063 {
1064     Blt_HashEntry *hPtr;
1065
1066     if (name == NULL) {
1067         return TCL_ERROR;
1068     }
1069     hPtr = Blt_FindHashEntry(&graphPtr->elements.table, name);
1070     if (hPtr == NULL) {
1071         Tcl_AppendResult(graphPtr->interp, "can't find element \"", name,
1072             "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
1073         return TCL_ERROR;
1074     }
1075     *elemPtrPtr = (Element *)Blt_GetHashValue(hPtr);
1076     return TCL_OK;
1077 }
1078
1079 /*
1080  *----------------------------------------------------------------------
1081  *
1082  * DestroyElement --
1083  *
1084  *      Add a new element to the graph.
1085  *
1086  * Results:
1087  *      The return value is a standard Tcl result.
1088  *
1089  *----------------------------------------------------------------------
1090  */
1091 static void
1092 DestroyElement(graphPtr, elemPtr)
1093     Graph *graphPtr;
1094     Element *elemPtr;
1095 {
1096     Blt_ChainLink *linkPtr;
1097
1098     Blt_DeleteBindings(graphPtr->bindTable, elemPtr);
1099     Blt_LegendRemoveElement(graphPtr->legend, elemPtr);
1100
1101     Tk_FreeOptions(elemPtr->specsPtr, (char *)elemPtr, graphPtr->display, 0);
1102     /*
1103      * Call the element's own destructor to release the memory and
1104      * resources allocated for it.
1105      */
1106     (*elemPtr->procsPtr->destroyProc) (graphPtr, elemPtr);
1107
1108     /* Remove it also from the element display list */
1109     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
1110         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1111         if (elemPtr == Blt_ChainGetValue(linkPtr)) {
1112             Blt_ChainDeleteLink(graphPtr->elements.displayList, linkPtr);
1113             if (!elemPtr->hidden) {
1114                 graphPtr->flags |= RESET_WORLD;
1115                 Blt_EventuallyRedrawGraph(graphPtr);
1116             }
1117             break;
1118         }
1119     }
1120     /* Remove the element for the graph's hash table of elements */
1121     if (elemPtr->hashPtr != NULL) {
1122         Blt_DeleteHashEntry(&graphPtr->elements.table, elemPtr->hashPtr);
1123     }
1124     if (elemPtr->name != NULL) {
1125         Blt_Free(elemPtr->name);
1126     }
1127     Blt_Free(elemPtr);
1128 }
1129
1130 /*
1131  *----------------------------------------------------------------------
1132  *
1133  * CreateElement --
1134  *
1135  *      Add a new element to the graph.
1136  *
1137  * Results:
1138  *      The return value is a standard Tcl result.
1139  *
1140  *----------------------------------------------------------------------
1141  */
1142 static int
1143 CreateElement(graphPtr, interp, argc, argv, classUid)
1144     Graph *graphPtr;
1145     Tcl_Interp *interp;
1146     int argc;
1147     char **argv;
1148     Blt_Uid classUid;
1149 {
1150     Element *elemPtr;
1151     Blt_HashEntry *hPtr;
1152     int isNew;
1153
1154     if (argv[3][0] == '-') {
1155         Tcl_AppendResult(graphPtr->interp, "name of element \"", argv[3], 
1156                          "\" can't start with a '-'", (char *)NULL);
1157         return TCL_ERROR;
1158     }
1159     hPtr = Blt_CreateHashEntry(&graphPtr->elements.table, argv[3], &isNew);
1160     if (!isNew) {
1161         Tcl_AppendResult(interp, "element \"", argv[3],
1162             "\" already exists in \"", argv[0], "\"", (char *)NULL);
1163         return TCL_ERROR;
1164     }
1165     if (classUid == bltBarElementUid) {
1166         elemPtr = Blt_BarElement(graphPtr, argv[3], classUid);
1167     } else { 
1168         /* Stripcharts are line graphs with some options enabled. */    
1169         elemPtr = Blt_LineElement(graphPtr, argv[3], classUid);
1170     }
1171     elemPtr->hashPtr = hPtr;
1172     Blt_SetHashValue(hPtr, elemPtr);
1173
1174     if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, elemPtr->name,
1175             "Element", elemPtr->specsPtr, argc - 4, argv + 4, 
1176                 (char *)elemPtr, 0) != TCL_OK) {
1177         DestroyElement(graphPtr, elemPtr);
1178         return TCL_ERROR;
1179     }
1180     (*elemPtr->procsPtr->configProc) (graphPtr, elemPtr);
1181     Blt_ChainPrepend(graphPtr->elements.displayList, elemPtr);
1182
1183     if (!elemPtr->hidden) {
1184         /* If the new element isn't hidden then redraw the graph.  */
1185         graphPtr->flags |= REDRAW_BACKING_STORE;
1186         Blt_EventuallyRedrawGraph(graphPtr);
1187     }
1188     elemPtr->flags |= MAP_ITEM;
1189     graphPtr->flags |= RESET_AXES;
1190     Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
1191     return TCL_OK;
1192 }
1193
1194 /*
1195  *----------------------------------------------------------------------
1196  *
1197  * RebuildDisplayList --
1198  *
1199  *      Given a Tcl list of element names, this procedure rebuilds the
1200  *      display list, ignoring invalid element names. This list describes
1201  *      not only only which elements to draw, but in what order.  This is
1202  *      only important for bar and pie charts.
1203  *
1204  * Results:
1205  *      The return value is a standard Tcl result.  Only if the Tcl list
1206  *      can not be split, a TCL_ERROR is returned and interp->result contains
1207  *      an error message.
1208  *
1209  * Side effects:
1210  *      The graph is eventually redrawn using the new display list.
1211  *
1212  *----------------------------------------------------------------------
1213  */
1214 static int
1215 RebuildDisplayList(graphPtr, newList)
1216     Graph *graphPtr;            /* Graph widget record */
1217     char *newList;              /* Tcl list of element names */
1218 {
1219     int nNames;                 /* Number of names found in Tcl name list */
1220     char **nameArr;             /* Broken out array of element names */
1221     register int i;
1222     Element *elemPtr;           /* Element information record */
1223
1224     if (Tcl_SplitList(graphPtr->interp, newList, &nNames, &nameArr) != TCL_OK) {
1225         Tcl_AppendResult(graphPtr->interp, "can't split name list \"", newList,
1226             "\"", (char *)NULL);
1227         return TCL_ERROR;
1228     }
1229     /* Clear the display list and mark all elements as hidden.  */
1230     Blt_ChainReset(graphPtr->elements.displayList);
1231
1232     /* Rebuild the display list, checking that each name it exists
1233      * (currently ignoring invalid element names).  */
1234     for (i = 0; i < nNames; i++) {
1235         if (NameToElement(graphPtr, nameArr[i], &elemPtr) == TCL_OK) {
1236             Blt_ChainAppend(graphPtr->elements.displayList, elemPtr);
1237         }
1238     }
1239     Blt_Free(nameArr);
1240     graphPtr->flags |= RESET_WORLD;
1241     Blt_EventuallyRedrawGraph(graphPtr);
1242     Tcl_ResetResult(graphPtr->interp);
1243     return TCL_OK;
1244 }
1245 \f
1246 /*
1247  *----------------------------------------------------------------------
1248  *
1249  * Blt_DestroyElements --
1250  *
1251  *      Removes all the graph's elements. This routine is called when
1252  *      the graph is destroyed.
1253  *
1254  * Results:
1255  *      None.
1256  *
1257  * Side effects:
1258  *      Memory allocated for the graph's elements is freed.
1259  *
1260  *----------------------------------------------------------------------
1261  */
1262 void
1263 Blt_DestroyElements(graphPtr)
1264     Graph *graphPtr;
1265 {
1266     Blt_HashEntry *hPtr;
1267     Blt_HashSearch cursor;
1268     Element *elemPtr;
1269
1270     for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor);
1271         hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1272         elemPtr = (Element *)Blt_GetHashValue(hPtr);
1273         elemPtr->hashPtr = NULL;
1274         DestroyElement(graphPtr, elemPtr);
1275     }
1276     Blt_DeleteHashTable(&graphPtr->elements.table);
1277     Blt_DeleteHashTable(&graphPtr->elements.tagTable);
1278     Blt_ChainDestroy(graphPtr->elements.displayList);
1279 }
1280
1281 void
1282 Blt_MapElements(graphPtr)
1283     Graph *graphPtr;
1284 {
1285     Element *elemPtr;
1286     Blt_ChainLink *linkPtr;
1287
1288     if (graphPtr->mode != MODE_INFRONT) {
1289         Blt_ResetStacks(graphPtr);
1290     }
1291     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
1292         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1293         elemPtr = Blt_ChainGetValue(linkPtr);
1294         if (elemPtr->hidden) {
1295             continue;
1296         }
1297         if ((graphPtr->flags & MAP_ALL) || (elemPtr->flags & MAP_ITEM)) {
1298             (*elemPtr->procsPtr->mapProc) (graphPtr, elemPtr);
1299             elemPtr->flags &= ~MAP_ITEM;
1300         }
1301     }
1302 }
1303
1304 /*
1305  * -----------------------------------------------------------------
1306  *
1307  * Blt_DrawElements --
1308  *
1309  *      Calls the individual element drawing routines for each
1310  *      element.
1311  *
1312  * Results:
1313  *      None
1314  *
1315  * Side Effects:
1316  *      Elements are drawn into the drawable (pixmap) which will
1317  *      eventually be displayed in the graph window.
1318  *
1319  * -----------------------------------------------------------------
1320  */
1321 void
1322 Blt_DrawElements(graphPtr, drawable)
1323     Graph *graphPtr;
1324     Drawable drawable;          /* Pixmap or window to draw into */
1325 {
1326     Blt_ChainLink *linkPtr;
1327     Element *elemPtr;
1328
1329     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
1330         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1331         elemPtr = Blt_ChainGetValue(linkPtr);
1332         if (!elemPtr->hidden) {
1333             (*elemPtr->procsPtr->drawNormalProc) (graphPtr, drawable, elemPtr);
1334         }
1335     }
1336 }
1337
1338 /*
1339  * -----------------------------------------------------------------
1340  *
1341  * Blt_DrawActiveElements --
1342  *
1343  *      Calls the individual element drawing routines to display
1344  *      the active colors for each element.
1345  *
1346  * Results:
1347  *      None
1348  *
1349  * Side Effects:
1350  *      Elements are drawn into the drawable (pixmap) which will
1351  *      eventually be displayed in the graph window.
1352  *
1353  * -----------------------------------------------------------------
1354  */
1355 void
1356 Blt_DrawActiveElements(graphPtr, drawable)
1357     Graph *graphPtr;
1358     Drawable drawable;          /* Pixmap or window to draw into */
1359 {
1360     Blt_ChainLink *linkPtr;
1361     Element *elemPtr;
1362
1363     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
1364         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1365         elemPtr = Blt_ChainGetValue(linkPtr);
1366         if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
1367             (*elemPtr->procsPtr->drawActiveProc) (graphPtr, drawable, elemPtr);
1368         }
1369     }
1370 }
1371
1372 /*
1373  * -----------------------------------------------------------------
1374  *
1375  * Blt_ElementsToPostScript --
1376  *
1377  *      Generates PostScript output for each graph element in the
1378  *      element display list.
1379  *
1380  * -----------------------------------------------------------------
1381  */
1382 void
1383 Blt_ElementsToPostScript(graphPtr, psToken)
1384     Graph *graphPtr;
1385     PsToken psToken;
1386 {
1387     Blt_ChainLink *linkPtr;
1388     Element *elemPtr;
1389
1390     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
1391         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1392         elemPtr = Blt_ChainGetValue(linkPtr);
1393         if (!elemPtr->hidden) {
1394             /* Comment the PostScript to indicate the start of the element */
1395             Blt_FormatToPostScript(psToken, "\n%% Element \"%s\"\n\n", 
1396                 elemPtr->name);
1397             (*elemPtr->procsPtr->printNormalProc) (graphPtr, psToken, elemPtr);
1398         }
1399     }
1400 }
1401
1402 /*
1403  * -----------------------------------------------------------------
1404  *
1405  * Blt_ActiveElementsToPostScript --
1406  *
1407  * -----------------------------------------------------------------
1408  */
1409 void
1410 Blt_ActiveElementsToPostScript(graphPtr, psToken)
1411     Graph *graphPtr;
1412     PsToken psToken;
1413 {
1414     Blt_ChainLink *linkPtr;
1415     Element *elemPtr;
1416
1417     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
1418         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1419         elemPtr = Blt_ChainGetValue(linkPtr);
1420         if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
1421             Blt_FormatToPostScript(psToken, "\n%% Active Element \"%s\"\n\n",
1422                 elemPtr->name);
1423             (*elemPtr->procsPtr->printActiveProc) (graphPtr, psToken, elemPtr);
1424         }
1425     }
1426 }
1427
1428 int
1429 Blt_GraphUpdateNeeded(graphPtr)
1430     Graph *graphPtr;
1431 {
1432     Blt_ChainLink *linkPtr;
1433     Element *elemPtr;
1434
1435     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
1436         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1437         elemPtr = Blt_ChainGetValue(linkPtr);
1438         if (elemPtr->hidden) {
1439             continue;
1440         }
1441         /* Check if the x or y vectors have notifications pending */
1442         if ((Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
1443             (Blt_VectorNotifyPending(elemPtr->y.clientId))) {
1444             return 1;
1445         }
1446     }
1447     return 0;
1448 }
1449
1450 \f
1451 /*
1452  *----------------------------------------------------------------------
1453  *
1454  * ActivateOp --
1455  *
1456  *      Marks data points of elements (given by their index) as active.
1457  *
1458  * Results:
1459  *      Returns TCL_OK if no errors occurred.
1460  *
1461  *----------------------------------------------------------------------
1462  */
1463 static int
1464 ActivateOp(graphPtr, interp, argc, argv)
1465     Graph *graphPtr;            /* Graph widget */
1466     Tcl_Interp *interp;         /* Interpreter to report errors to */
1467     int argc;                   /* Number of element names */
1468     char **argv;                /* List of element names */
1469 {
1470     Element *elemPtr;
1471     register int i;
1472     int *activeArr;
1473     int nActiveIndices;
1474
1475     if (argc == 3) {
1476         register Blt_HashEntry *hPtr;
1477         Blt_HashSearch cursor;
1478
1479         /* List all the currently active elements */
1480         for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor);
1481             hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1482             elemPtr = (Element *)Blt_GetHashValue(hPtr);
1483             if (elemPtr->flags & ELEM_ACTIVE) {
1484                 Tcl_AppendElement(graphPtr->interp, elemPtr->name);
1485             }
1486         }
1487         return TCL_OK;
1488     }
1489     if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
1490         return TCL_ERROR;       /* Can't find named element */
1491     }
1492     elemPtr->flags |= ELEM_ACTIVE | ACTIVE_PENDING;
1493
1494     activeArr = NULL;
1495     nActiveIndices = -1;
1496     if (argc > 4) {
1497         register int *activePtr;
1498
1499         nActiveIndices = argc - 4;
1500         activePtr = activeArr = Blt_Malloc(sizeof(int) * nActiveIndices);
1501         assert(activeArr);
1502         for (i = 4; i < argc; i++) {
1503             if (GetIndex(interp, elemPtr, argv[i], activePtr) != TCL_OK) {
1504                 return TCL_ERROR;
1505             }
1506             activePtr++;
1507         }
1508     }
1509     if (elemPtr->activeIndices != NULL) {
1510         Blt_Free(elemPtr->activeIndices);
1511     }
1512     elemPtr->nActiveIndices = nActiveIndices;
1513     elemPtr->activeIndices = activeArr;
1514     Blt_EventuallyRedrawGraph(graphPtr);
1515     return TCL_OK;
1516 }
1517
1518 ClientData
1519 Blt_MakeElementTag(graphPtr, tagName)
1520     Graph *graphPtr;
1521     char *tagName;
1522 {
1523     Blt_HashEntry *hPtr;
1524     int isNew;
1525
1526     hPtr = Blt_CreateHashEntry(&graphPtr->elements.tagTable, tagName, &isNew);
1527     assert(hPtr);
1528     return Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
1529 }
1530
1531 /*
1532  *----------------------------------------------------------------------
1533  *
1534  * BindOp --
1535  *
1536  *      .g element bind elemName sequence command
1537  *
1538  *----------------------------------------------------------------------
1539  */
1540 /*ARGSUSED*/
1541 static int
1542 BindOp(graphPtr, interp, argc, argv)
1543     Graph *graphPtr;
1544     Tcl_Interp *interp;
1545     int argc;
1546     char **argv;
1547 {
1548     if (argc == 3) {
1549         Blt_HashEntry *hPtr;
1550         Blt_HashSearch cursor;
1551         char *tagName;
1552
1553         for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.tagTable, &cursor);
1554             hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1555             tagName = Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
1556             Tcl_AppendElement(interp, tagName);
1557         }
1558         return TCL_OK;
1559     }
1560     return Blt_ConfigureBindings(interp, graphPtr->bindTable,
1561         Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
1562 }
1563
1564 /*
1565  *----------------------------------------------------------------------
1566  *
1567  * CreateOp --
1568  *
1569  *      Add a new element to the graph (using the default type of the
1570  *      graph).
1571  *
1572  * Results:
1573  *      The return value is a standard Tcl result.
1574  *
1575  *----------------------------------------------------------------------
1576  */
1577 static int
1578 CreateOp(graphPtr, interp, argc, argv, type)
1579     Graph *graphPtr;
1580     Tcl_Interp *interp;
1581     int argc;
1582     char **argv;
1583     Blt_Uid type;
1584 {
1585     return CreateElement(graphPtr, interp, argc, argv, type);
1586 }
1587
1588 /*
1589  *----------------------------------------------------------------------
1590  *
1591  * CgetOp --
1592  *
1593  *----------------------------------------------------------------------
1594  */
1595 /*ARGSUSED*/
1596 static int
1597 CgetOp(graphPtr, interp, argc, argv)
1598     Graph *graphPtr;
1599     Tcl_Interp *interp;
1600     int argc;
1601     char *argv[];
1602 {
1603     Element *elemPtr;
1604
1605     if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
1606         return TCL_ERROR;       /* Can't find named element */
1607     }
1608     if (Tk_ConfigureValue(interp, graphPtr->tkwin, elemPtr->specsPtr,
1609             (char *)elemPtr, argv[4], 0) != TCL_OK) {
1610         return TCL_ERROR;
1611     }
1612     return TCL_OK;
1613 }
1614
1615 /*
1616  *----------------------------------------------------------------------
1617  *
1618  * ClosestOp --
1619  *
1620  *      Find the element closest to the specified screen coordinates.
1621  *      Options:
1622  *      -halo           Consider points only with this maximum distance
1623  *                      from the picked coordinate.
1624  *      -interpolate    Find closest point along element traces, not just
1625  *                      data points.
1626  *      -along
1627  *
1628  * Results:
1629  *      A standard Tcl result. If an element could be found within
1630  *      the halo distance, the interpreter result is "1", otherwise
1631  *      "0".  If a closest element exists, the designated Tcl array
1632  *      variable will be set with the following information:
1633  *
1634  *      1) the element name,
1635  *      2) the index of the closest point,
1636  *      3) the distance (in screen coordinates) from the picked X-Y
1637  *         coordinate and the closest point,
1638  *      4) the X coordinate (graph coordinate) of the closest point,
1639  *      5) and the Y-coordinate.
1640  *
1641  *----------------------------------------------------------------------
1642  */
1643
1644 static Tk_ConfigSpec closestSpecs[] =
1645 {
1646     {TK_CONFIG_CUSTOM, "-halo", (char *)NULL, (char *)NULL,
1647         (char *)NULL, Tk_Offset(ClosestSearch, halo), 0, &bltDistanceOption},
1648     {TK_CONFIG_BOOLEAN, "-interpolate", (char *)NULL, (char *)NULL,
1649         (char *)NULL, Tk_Offset(ClosestSearch, mode), 0 }, 
1650     {TK_CONFIG_CUSTOM, "-along", (char *)NULL, (char *)NULL,
1651         (char *)NULL, Tk_Offset(ClosestSearch, along), 0, &alongOption},
1652     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
1653         (char *)NULL, 0, 0}
1654 };
1655
1656 static int
1657 ClosestOp(graphPtr, interp, argc, argv)
1658     Graph *graphPtr;            /* Graph widget */
1659     Tcl_Interp *interp;         /* Interpreter to report results to */
1660     int argc;                   /* Number of element names */
1661     char **argv;                /* List of element names */
1662 {
1663     Element *elemPtr;
1664     ClosestSearch search;
1665     int i, x, y;
1666     int flags = TCL_LEAVE_ERR_MSG;
1667
1668     if (graphPtr->flags & RESET_AXES) {
1669         Blt_ResetAxes(graphPtr);
1670     }
1671     if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &x) != TCL_OK) {
1672         Tcl_AppendResult(interp, ": bad window x-coordinate", (char *)NULL);
1673         return TCL_ERROR;
1674     }
1675     if (Tk_GetPixels(interp, graphPtr->tkwin, argv[4], &y) != TCL_OK) {
1676         Tcl_AppendResult(interp, ": bad window y-coordinate", (char *)NULL);
1677         return TCL_ERROR;
1678     }
1679     if (graphPtr->inverted) {
1680         int temp;
1681
1682         temp = x, x = y, y = temp;
1683     }
1684     for (i = 6; i < argc; i += 2) {     /* Count switches-value pairs */
1685         if ((argv[i][0] != '-') || 
1686             ((argv[i][1] == '-') && (argv[i][2] == '\0'))) {
1687             break;
1688         }
1689     }
1690     if (i > argc) {
1691         i = argc;
1692     }
1693
1694     search.mode = SEARCH_POINTS;
1695     search.halo = graphPtr->halo;
1696     search.index = -1;
1697     search.along = SEARCH_BOTH;
1698     search.x = x;
1699     search.y = y;
1700
1701     if (Tk_ConfigureWidget(interp, graphPtr->tkwin, closestSpecs, i - 6,
1702             argv + 6, (char *)&search, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
1703         return TCL_ERROR;       /* Error occurred processing an option. */
1704     }
1705     if ((i < argc) && (argv[i][0] == '-')) {
1706         i++;                    /* Skip "--" */
1707     }
1708     search.dist = (double)(search.halo + 1);
1709
1710     if (i < argc) {
1711
1712         for ( /* empty */ ; i < argc; i++) {
1713             if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
1714                 return TCL_ERROR;       /* Can't find named element */
1715             }
1716             if (elemPtr->hidden) {
1717                 Tcl_AppendResult(interp, "element \"", argv[i],
1718                         "\" is hidden", (char *)NULL);
1719                 return TCL_ERROR;       /* Element isn't visible */
1720             }
1721
1722             /* Check if the X or Y vectors have notifications pending */
1723             if ((elemPtr->flags & MAP_ITEM) ||
1724                 (Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
1725                 (Blt_VectorNotifyPending(elemPtr->y.clientId))) {
1726                 continue;
1727             }
1728             (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
1729         }
1730     } else {
1731         Blt_ChainLink *linkPtr;
1732
1733         /* 
1734          * Find the closest point from the set of displayed elements,
1735          * searching the display list from back to front.  That way if
1736          * the points from two different elements overlay each other
1737          * exactly, the last one picked will be the topmost.  
1738          */
1739         for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
1740             linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
1741             elemPtr = Blt_ChainGetValue(linkPtr);
1742             /* Check if the X or Y vectors have notifications pending */
1743             if ((elemPtr->hidden) || 
1744                 (elemPtr->flags & MAP_ITEM) ||
1745                 (Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
1746                 (Blt_VectorNotifyPending(elemPtr->y.clientId))) {
1747                 continue;
1748             }
1749             (*elemPtr->procsPtr->closestProc)(graphPtr, elemPtr, &search);
1750         }
1751
1752     }
1753     if (search.dist < (double)search.halo) {
1754         char string[200];
1755         /*
1756          *  Return an array of 5 elements
1757          */
1758         if (Tcl_SetVar2(interp, argv[5], "name",
1759                 search.elemPtr->name, flags) == NULL) {
1760             return TCL_ERROR;
1761         }
1762         sprintf(string, "%d", search.index);
1763         if (Tcl_SetVar2(interp, argv[5], "index", string, flags) == NULL) {
1764             return TCL_ERROR;
1765         }
1766         Tcl_PrintDouble(interp, search.point.x, string);
1767         if (Tcl_SetVar2(interp, argv[5], "x", string, flags) == NULL) {
1768             return TCL_ERROR;
1769         }
1770         Tcl_PrintDouble(interp, search.point.y, string);
1771         if (Tcl_SetVar2(interp, argv[5], "y", string, flags) == NULL) {
1772             return TCL_ERROR;
1773         }
1774         Tcl_PrintDouble(interp, search.dist, string);
1775         if (Tcl_SetVar2(interp, argv[5], "dist", string, flags) == NULL) {
1776             return TCL_ERROR;
1777         }
1778         Tcl_SetResult(interp, "1", TCL_STATIC);
1779     } else {
1780         if (Tcl_SetVar2(interp, argv[5], "name", "", flags) == NULL) {
1781             return TCL_ERROR;
1782         }
1783         Tcl_SetResult(interp, "0", TCL_STATIC);
1784     }
1785     return TCL_OK;
1786 }
1787
1788 /*
1789  *----------------------------------------------------------------------
1790  *
1791  * ConfigureOp --
1792  *
1793  *      Sets the element specifications by the given the command line
1794  *      arguments and calls the element specification configuration
1795  *      routine. If zero or one command line options are given, only
1796  *      information about the option(s) is returned in interp->result.
1797  *      If the element configuration has changed and the element is
1798  *      currently displayed, the axis limits are updated and
1799  *      recomputed.
1800  *
1801  * Results:
1802  *      The return value is a standard Tcl result.
1803  *
1804  * Side Effects:
1805  *      Graph will be redrawn to reflect the new display list.
1806  *
1807  *---------------------------------------------------------------------- 
1808  */
1809 static int
1810 ConfigureOp(graphPtr, interp, argc, argv)
1811     Graph *graphPtr;
1812     Tcl_Interp *interp;
1813     int argc;
1814     char *argv[];
1815 {
1816     Element *elemPtr;
1817     int flags;
1818     int numNames, numOpts;
1819     char **options;
1820     register int i;
1821
1822     /* Figure out where the option value pairs begin */
1823     argc -= 3;
1824     argv += 3;
1825     for (i = 0; i < argc; i++) {
1826         if (argv[i][0] == '-') {
1827             break;
1828         }
1829         if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
1830             return TCL_ERROR;   /* Can't find named element */
1831         }
1832     }
1833     numNames = i;               /* Number of element names specified */
1834     numOpts = argc - i;         /* Number of options specified */
1835     options = argv + numNames;  /* Start of options in argv  */
1836
1837     for (i = 0; i < numNames; i++) {
1838         NameToElement(graphPtr, argv[i], &elemPtr);
1839         flags = TK_CONFIG_ARGV_ONLY;
1840         if (numOpts == 0) {
1841             return Tk_ConfigureInfo(interp, graphPtr->tkwin, 
1842                 elemPtr->specsPtr, (char *)elemPtr, (char *)NULL, flags);
1843         } else if (numOpts == 1) {
1844             return Tk_ConfigureInfo(interp, graphPtr->tkwin, 
1845                 elemPtr->specsPtr, (char *)elemPtr, options[0], flags);
1846         }
1847         if (Tk_ConfigureWidget(interp, graphPtr->tkwin, elemPtr->specsPtr, 
1848                 numOpts, options, (char *)elemPtr, flags) != TCL_OK) {
1849             return TCL_ERROR;
1850         }
1851         if ((*elemPtr->procsPtr->configProc) (graphPtr, elemPtr) != TCL_OK) {
1852             return TCL_ERROR;   /* Failed to configure element */
1853         }
1854         if (Blt_ConfigModified(elemPtr->specsPtr, graphPtr->interp, "-hide", (char *)NULL)) {
1855             graphPtr->flags |= RESET_AXES;
1856             elemPtr->flags |= MAP_ITEM;
1857         }
1858         /* If data points or axes have changed, reset the axes (may
1859          * affect autoscaling) and recalculate the screen points of
1860          * the element. */
1861
1862         if (Blt_ConfigModified(elemPtr->specsPtr, graphPtr->interp, "-*data", "-map*", "-x",
1863                        "-y", (char *)NULL)) {
1864             graphPtr->flags |= RESET_WORLD;
1865             elemPtr->flags |= MAP_ITEM;
1866         }
1867         /* The new label may change the size of the legend */
1868         if (Blt_ConfigModified(elemPtr->specsPtr, graphPtr->interp, "-label", (char *)NULL)) {
1869             graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
1870         }
1871     }
1872     /* Update the pixmap if any configuration option changed */
1873     graphPtr->flags |= (REDRAW_BACKING_STORE | DRAW_MARGINS);
1874     Blt_EventuallyRedrawGraph(graphPtr);
1875     return TCL_OK;
1876 }
1877
1878 /*
1879  *----------------------------------------------------------------------
1880  *
1881  * DeactivateOp --
1882  *
1883  *      Clears the active bit for the named elements.
1884  *
1885  * Results:
1886  *      Returns TCL_OK if no errors occurred.
1887  *
1888  *----------------------------------------------------------------------
1889  */
1890 /*ARGSUSED*/
1891 static int
1892 DeactivateOp(graphPtr, interp, argc, argv)
1893     Graph *graphPtr;            /* Graph widget */
1894     Tcl_Interp *interp;         /* Not used. */
1895     int argc;                   /* Number of element names */
1896     char **argv;                /* List of element names */
1897 {
1898     Element *elemPtr;
1899     register int i;
1900
1901     for (i = 3; i < argc; i++) {
1902         if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
1903             return TCL_ERROR;   /* Can't find named element */
1904         }
1905         elemPtr->flags &= ~ELEM_ACTIVE;
1906         if (elemPtr->activeIndices != NULL) {
1907             Blt_Free(elemPtr->activeIndices);
1908             elemPtr->activeIndices = NULL;
1909         }
1910         elemPtr->nActiveIndices = 0;
1911     }
1912     Blt_EventuallyRedrawGraph(graphPtr);
1913     return TCL_OK;
1914 }
1915
1916 /*
1917  *----------------------------------------------------------------------
1918  *
1919  * DeleteOp --
1920  *
1921  *      Delete the named elements from the graph.
1922  *
1923  * Results:
1924  *      TCL_ERROR is returned if any of the named elements can not be
1925  *      found.  Otherwise TCL_OK is returned;
1926  *
1927  * Side Effects:
1928  *      If the element is currently displayed, the plotting area of
1929  *      the graph is redrawn. Memory and resources allocated by the
1930  *      elements are released.
1931  *
1932  *----------------------------------------------------------------------
1933  */
1934 /*ARGSUSED*/
1935 static int
1936 DeleteOp(graphPtr, interp, argc, argv)
1937     Graph *graphPtr;            /* Graph widget */
1938     Tcl_Interp *interp;         /* Not used. */
1939     int argc;                   /* Number of element names */
1940     char **argv;                /* List of element names */
1941 {
1942     Element *elemPtr;
1943     register int i;
1944
1945     for (i = 3; i < argc; i++) {
1946         if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
1947             return TCL_ERROR;   /* Can't find named element */
1948         }
1949         DestroyElement(graphPtr, elemPtr);
1950     }
1951     Blt_EventuallyRedrawGraph(graphPtr);
1952     return TCL_OK;
1953 }
1954
1955 /*
1956  *----------------------------------------------------------------------
1957  *
1958  * ExistsOp --
1959  *
1960  *      Indicates if the named element exists in the graph.
1961  *
1962  * Results:
1963  *      The return value is a standard Tcl result.  The interpreter
1964  *      result will contain "1" or "0".
1965  *
1966  *----------------------------------------------------------------------
1967  */
1968 /* ARGSUSED */
1969 static int
1970 ExistsOp(graphPtr, interp, argc, argv)
1971     Graph *graphPtr;
1972     Tcl_Interp *interp;
1973     int argc;                   /* Not used. */
1974     char **argv;
1975 {
1976     Blt_HashEntry *hPtr;
1977
1978     hPtr = Blt_FindHashEntry(&graphPtr->elements.table, argv[3]);
1979     Blt_SetBooleanResult(interp, (hPtr != NULL));
1980     return TCL_OK;
1981 }
1982
1983 /*
1984  *----------------------------------------------------------------------
1985  *
1986  * GetOp --
1987  *
1988  *      Returns the name of the picked element (using the element
1989  *      bind operation).  Right now, the only name accepted is
1990  *      "current".
1991  *
1992  * Results:
1993  *      A standard Tcl result.  The interpreter result will contain
1994  *      the name of the element.
1995  *
1996  *----------------------------------------------------------------------
1997  */
1998 /*ARGSUSED*/
1999 static int
2000 GetOp(graphPtr, interp, argc, argv)
2001     Graph *graphPtr;
2002     Tcl_Interp *interp;
2003     int argc;                   /* Not used. */
2004     char *argv[];
2005 {
2006     register Element *elemPtr;
2007
2008     if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) {
2009         elemPtr = (Element *)Blt_GetCurrentItem(graphPtr->bindTable);
2010         /* Report only on elements. */
2011         if ((elemPtr != NULL) && 
2012             ((elemPtr->classUid == bltBarElementUid) ||
2013             (elemPtr->classUid == bltLineElementUid) ||
2014             (elemPtr->classUid == bltStripElementUid))) {
2015             Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
2016         }
2017     }
2018     return TCL_OK;
2019 }
2020
2021 /*
2022  *----------------------------------------------------------------------
2023  *
2024  * NamesOp --
2025  *
2026  *      Returns the names of the elements is the graph matching
2027  *      one of more patterns provided.  If no pattern arguments
2028  *      are given, then all element names will be returned.
2029  *
2030  * Results:
2031  *      The return value is a standard Tcl result. The interpreter
2032  *      result will contain a Tcl list of the element names.
2033  *
2034  *----------------------------------------------------------------------
2035  */
2036 static int
2037 NamesOp(graphPtr, interp, argc, argv)
2038     Graph *graphPtr;
2039     Tcl_Interp *interp;
2040     int argc;
2041     char **argv;
2042 {
2043     Element *elemPtr;
2044     Blt_HashSearch cursor;
2045     register Blt_HashEntry *hPtr;
2046     register int i;
2047
2048     for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor);
2049         hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
2050         elemPtr = (Element *)Blt_GetHashValue(hPtr);
2051         if (argc == 3) {
2052             Tcl_AppendElement(graphPtr->interp, elemPtr->name);
2053             continue;
2054         }
2055         for (i = 3; i < argc; i++) {
2056             if (Tcl_StringMatch(elemPtr->name, argv[i])) {
2057                 Tcl_AppendElement(interp, elemPtr->name);
2058                 break;
2059             }
2060         }
2061     }
2062     return TCL_OK;
2063 }
2064
2065 /*
2066  *----------------------------------------------------------------------
2067  *
2068  * ShowOp --
2069  *
2070  *      Queries or resets the element display list.
2071  *
2072  * Results:
2073  *      The return value is a standard Tcl result. The interpreter
2074  *      result will contain the new display list of element names.
2075  *
2076  *----------------------------------------------------------------------
2077  */
2078 static int
2079 ShowOp(graphPtr, interp, argc, argv)
2080     Graph *graphPtr;
2081     Tcl_Interp *interp;
2082     int argc;
2083     char **argv;
2084 {
2085     Element *elemPtr;
2086     Blt_ChainLink *linkPtr;
2087
2088     if (argc == 4) {
2089         if (RebuildDisplayList(graphPtr, argv[3]) != TCL_OK) {
2090             return TCL_ERROR;
2091         }
2092     }
2093     for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
2094         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2095         elemPtr = Blt_ChainGetValue(linkPtr);
2096         Tcl_AppendElement(interp, elemPtr->name);
2097     }
2098     return TCL_OK;
2099 }
2100
2101 /*
2102  *----------------------------------------------------------------------
2103  *
2104  * TypeOp --
2105  *
2106  *      Returns the name of the type of the element given by some
2107  *      element name.
2108  *
2109  * Results:
2110  *      A standard Tcl result. Returns the type of the element in
2111  *      interp->result. If the identifier given doesn't represent an
2112  *      element, then an error message is left in interp->result.
2113  *
2114  *----------------------------------------------------------------------
2115  */
2116 /*ARGSUSED*/
2117 static int
2118 TypeOp(graphPtr, interp, argc, argv)
2119     Graph *graphPtr;            /* Graph widget */
2120     Tcl_Interp *interp;
2121     int argc;                   /* Not used. */
2122     char **argv;                /* Element name */
2123 {
2124     Element *elemPtr;
2125
2126     if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
2127         return TCL_ERROR;       /* Can't find named element */
2128     }
2129     Tcl_SetResult(interp, elemPtr->classUid, TCL_STATIC);
2130     return TCL_OK;
2131 }
2132
2133 /*
2134  * Global routines:
2135  */
2136 static Blt_OpSpec elemOps[] =
2137 {
2138     {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?elemName? ?index...?",},
2139     {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
2140     {"cget", 2, (Blt_Op)CgetOp, 5, 5, "elemName option",},
2141     {"closest", 2, (Blt_Op)ClosestOp, 6, 0,
2142         "x y varName ?option value?... ?elemName?...",},
2143     {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
2144         "elemName ?elemName?... ?option value?...",},
2145     {"create", 2, (Blt_Op)CreateOp, 4, 0, "elemName ?option value?...",},
2146     {"deactivate", 3, (Blt_Op)DeactivateOp, 3, 0, "?elemName?...",},
2147     {"delete", 3, (Blt_Op)DeleteOp, 3, 0, "?elemName?...",},
2148     {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "elemName",},
2149     {"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
2150     {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
2151     {"show", 1, (Blt_Op)ShowOp, 3, 4, "?elemList?",},
2152     {"type", 1, (Blt_Op)TypeOp, 4, 4, "elemName",},
2153 };
2154 static int numElemOps = sizeof(elemOps) / sizeof(Blt_OpSpec);
2155
2156
2157 /*
2158  * ----------------------------------------------------------------
2159  *
2160  * Blt_ElementOp --
2161  *
2162  *      This procedure is invoked to process the Tcl command that
2163  *      corresponds to a widget managed by this module.  See the user
2164  *      documentation for details on what it does.
2165  *
2166  * Results:
2167  *      A standard Tcl result.
2168  *
2169  * Side effects:
2170  *      See the user documentation.
2171  *
2172  * ----------------------------------------------------------------
2173  */
2174 int
2175 Blt_ElementOp(graphPtr, interp, argc, argv, type)
2176     Graph *graphPtr;            /* Graph widget record */
2177     Tcl_Interp *interp;
2178     int argc;                   /* # arguments */
2179     char **argv;                /* Argument list */
2180     Blt_Uid type;
2181 {
2182     Blt_Op proc;
2183     int result;
2184
2185     proc = Blt_GetOp(interp, numElemOps, elemOps, BLT_OP_ARG2, argc, argv, 0);
2186     if (proc == NULL) {
2187         return TCL_ERROR;
2188     }
2189     if (proc == CreateOp) {
2190         result = CreateOp(graphPtr, interp, argc, argv, type);
2191     } else {
2192         result = (*proc) (graphPtr, interp, argc, argv);
2193     }
2194     return result;
2195 }