1 
2 /*
3  * bltGrLegd.c --
4  *
5  *	This module implements the legend 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 "bltGrElem.h"
30 
31 /*
32  * -------------------------------------------------------------------
33  *
34  * Legend --
35  *
36  * 	Contains information specific to how the legend will be
37  *	displayed.
38  *
39  *
40  * -------------------------------------------------------------------
41  */
42 struct LegendStruct {
43     unsigned int flags;
44     Blt_Uid classUid;		/* Type: Element or Marker. */
45 
46     int hidden;			/* If non-zero, don't display the legend. */
47 
48     int raised;			/* If non-zero, draw the legend last, above
49 				 * everything else. */
50 
51     int nEntries;		/* Number of element entries in table. */
52 
53     short int width, height;	/* Dimensions of the legend */
54 
55     short int nColumns, nRows;	/* Number of columns and rows in legend */
56 
57     int site;
58     Point2D anchorPos;		/* Says how to position the legend. Indicates
59 				 * the site and/or x-y screen coordinates of
60 				 * the legend.  Used in conjunction with the
61 				 * anchor to determine its location. */
62 
63     Tk_Anchor anchor;		/* Anchor of legend. Used to interpret the
64 				 * positioning point of the legend in the
65 				 * graph*/
66 
67     int x, y;			/* Computed origin of legend. */
68 
69     Graph *graphPtr;
70     Tcl_Command cmdToken;	/* Token for graph's widget command. */
71     int reqColumns, reqRows;
72 
73     Blt_Pad ipadX, ipadY;	/* # of pixels padding around legend entries */
74     Blt_Pad padX, padY;		/* # of pixels padding to exterior of legend */
75 
76     Tk_Window tkwin;		/* Optional external window to draw legend. */
77 
78     TextStyle style;
79 
80     int maxSymSize;		/* Size of largest symbol to be displayed.
81 				 * Used to calculate size of legend */
82 
83     Tk_3DBorder activeBorder;	/* Active legend entry background color. */
84     int activeRelief;		/* 3-D effect on active entry. */
85     int entryBorderWidth;	/* Border width around each entry in legend. */
86 
87     Tk_3DBorder border;		/* 3-D effect of legend. */
88     int borderWidth;		/* Width of legend 3-D border */
89     int relief;			/* 3-d effect of border around the legend:
90 				 * TK_RELIEF_RAISED etc. */
91 
92     Blt_BindTable bindTable;
93 };
94 
95 #define padLeft  	padX.side1
96 #define padRight  	padX.side2
97 #define padTop		padY.side1
98 #define padBottom	padY.side2
99 #define PADDING(x)	((x).side1 + (x).side2)
100 
101 #define DEF_LEGEND_ACTIVE_BACKGROUND 	STD_ACTIVE_BACKGROUND
102 #define DEF_LEGEND_ACTIVE_BG_MONO	STD_ACTIVE_BG_MONO
103 #define DEF_LEGEND_ACTIVE_BORDERWIDTH  "2"
104 #define DEF_LEGEND_ACTIVE_FOREGROUND	STD_ACTIVE_FOREGROUND
105 #define DEF_LEGEND_ACTIVE_FG_MONO	STD_ACTIVE_FG_MONO
106 #define DEF_LEGEND_ACTIVE_RELIEF	"flat"
107 #define DEF_LEGEND_ANCHOR	   	"n"
108 #define DEF_LEGEND_BACKGROUND	   	(char *)NULL
109 #define DEF_LEGEND_BG_MONO		(char *)NULL
110 #define DEF_LEGEND_BORDERWIDTH 	STD_BORDERWIDTH
111 #define DEF_LEGEND_FOREGROUND		STD_NORMAL_FOREGROUND
112 #define DEF_LEGEND_FG_MONO		STD_NORMAL_FG_MONO
113 #define DEF_LEGEND_FONT			STD_FONT_SMALL
114 #define DEF_LEGEND_HIDE			"no"
115 #define DEF_LEGEND_IPAD_X		"1"
116 #define DEF_LEGEND_IPAD_Y		"1"
117 #define DEF_LEGEND_PAD_X		"1"
118 #define DEF_LEGEND_PAD_Y		"1"
119 #define DEF_LEGEND_POSITION		"rightmargin"
120 #define DEF_LEGEND_RAISED       	"no"
121 #define DEF_LEGEND_RELIEF		"sunken"
122 #define DEF_LEGEND_SHADOW_COLOR		(char *)NULL
123 #define DEF_LEGEND_SHADOW_MONO		(char *)NULL
124 #define DEF_LEGEND_ROWS			"0"
125 #define DEF_LEGEND_COLUMNS		"0"
126 
127 static Tk_OptionParseProc StringToPosition;
128 static Tk_OptionPrintProc PositionToString;
129 static Tk_CustomOption legendPositionOption =
130 {
131     StringToPosition, PositionToString, (ClientData)0
132 };
133 extern Tk_CustomOption bltDistanceOption;
134 extern Tk_CustomOption bltPadOption;
135 extern Tk_CustomOption bltShadowOption;
136 extern Tk_CustomOption bltCountOption;
137 
138 static Tk_ConfigSpec configSpecs[] =
139 {
140     {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
141 	"ActiveBackground", DEF_LEGEND_ACTIVE_BACKGROUND,
142 	Tk_Offset(Legend, activeBorder), TK_CONFIG_COLOR_ONLY},
143     {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
144 	"ActiveBackground", DEF_LEGEND_ACTIVE_BG_MONO,
145 	Tk_Offset(Legend, activeBorder), TK_CONFIG_MONO_ONLY},
146     {TK_CONFIG_CUSTOM, "-activeborderwidth", "activeBorderWidth",
147 	"BorderWidth", DEF_LEGEND_BORDERWIDTH,
148 	Tk_Offset(Legend, entryBorderWidth),
149 	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
150     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
151 	"ActiveForeground", DEF_LEGEND_ACTIVE_FOREGROUND,
152 	Tk_Offset(Legend, style.activeColor), TK_CONFIG_COLOR_ONLY},
153     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
154 	"ActiveForeground", DEF_LEGEND_ACTIVE_FG_MONO,
155 	Tk_Offset(Legend, style.activeColor), TK_CONFIG_MONO_ONLY},
156     {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
157 	DEF_LEGEND_ACTIVE_RELIEF, Tk_Offset(Legend, activeRelief),
158 	TK_CONFIG_DONT_SET_DEFAULT},
159     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
160 	DEF_LEGEND_ANCHOR, Tk_Offset(Legend, anchor),
161 	TK_CONFIG_DONT_SET_DEFAULT},
162     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
163     {TK_CONFIG_BORDER, "-background", "background", "Background",
164 	DEF_LEGEND_BG_MONO, Tk_Offset(Legend, border),
165 	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
166     {TK_CONFIG_BORDER, "-background", "background", "Background",
167 	DEF_LEGEND_BACKGROUND, Tk_Offset(Legend, border),
168 	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
169     {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
170 	DEF_LEGEND_BORDERWIDTH, Tk_Offset(Legend, borderWidth),
171 	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
172     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
173     {TK_CONFIG_CUSTOM, "-columns", "columns", "columns",
174 	DEF_LEGEND_COLUMNS, Tk_Offset(Legend, reqColumns),
175 	TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
176     {TK_CONFIG_FONT, "-font", "font", "Font",
177 	DEF_LEGEND_FONT, Tk_Offset(Legend, style.font), 0},
178     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
179     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
180 	DEF_LEGEND_FOREGROUND, Tk_Offset(Legend, style.color),
181 	TK_CONFIG_COLOR_ONLY},
182     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
183 	DEF_LEGEND_FG_MONO, Tk_Offset(Legend, style.color),
184 	TK_CONFIG_MONO_ONLY},
185     {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
186 	DEF_LEGEND_HIDE, Tk_Offset(Legend, hidden), TK_CONFIG_DONT_SET_DEFAULT},
187     {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "Pad",
188 	DEF_LEGEND_IPAD_X, Tk_Offset(Legend, ipadX),
189 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
190     {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "Pad",
191 	DEF_LEGEND_IPAD_Y, Tk_Offset(Legend, ipadY),
192 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
193     {TK_CONFIG_CUSTOM, "-padx", "padX", "Pad",
194 	DEF_LEGEND_PAD_X, Tk_Offset(Legend, padX),
195 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
196     {TK_CONFIG_CUSTOM, "-pady", "padY", "Pad",
197 	DEF_LEGEND_PAD_Y, Tk_Offset(Legend, padY),
198 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
199     {TK_CONFIG_CUSTOM, "-position", "position", "Position",
200 	DEF_LEGEND_POSITION, 0,
201         TK_CONFIG_DONT_SET_DEFAULT, &legendPositionOption},
202     {TK_CONFIG_BOOLEAN, "-raised", "raised", "Raised",
203 	DEF_LEGEND_RAISED, Tk_Offset(Legend, raised),
204 	TK_CONFIG_DONT_SET_DEFAULT},
205     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
206 	DEF_LEGEND_RELIEF, Tk_Offset(Legend, relief),
207 	TK_CONFIG_DONT_SET_DEFAULT},
208     {TK_CONFIG_CUSTOM, "-rows", "rows", "rows",
209 	DEF_LEGEND_ROWS, Tk_Offset(Legend, reqRows),
210 	TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
211     {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
212 	DEF_LEGEND_SHADOW_COLOR, Tk_Offset(Legend, style.shadow),
213 	TK_CONFIG_COLOR_ONLY, &bltShadowOption},
214     {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
215 	DEF_LEGEND_SHADOW_MONO, Tk_Offset(Legend, style.shadow),
216 	TK_CONFIG_MONO_ONLY, &bltShadowOption},
217     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
218 };
219 
220 static Tcl_IdleProc DisplayLegend;
221 static Blt_BindPickProc PickLegendEntry;
222 static Tk_EventProc LegendEventProc;
223 
224 extern Tcl_CmdProc Blt_GraphInstCmdProc;
225 
226 /*
227  *--------------------------------------------------------------
228  *
229  * EventuallyRedrawLegend --
230  *
231  *	Tells the Tk dispatcher to call the graph display routine at
232  *	the next idle point.  This request is made only if the window
233  *	is displayed and no other redraw request is pending.
234  *
235  * Results: None.
236  *
237  * Side effects:
238  *	The window is eventually redisplayed.
239  *
240  *--------------------------------------------------------------
241  */
242 static void
EventuallyRedrawLegend(legendPtr)243 EventuallyRedrawLegend(legendPtr)
244     Legend *legendPtr;		/* Legend record */
245 {
246     if ((legendPtr->tkwin != NULL) && !(legendPtr->flags & REDRAW_PENDING)) {
247 	Tcl_DoWhenIdle(DisplayLegend, legendPtr);
248 	legendPtr->flags |= REDRAW_PENDING;
249     }
250 }
251 
252 /*
253  *--------------------------------------------------------------
254  *
255  * LegendEventProc --
256  *
257  *	This procedure is invoked by the Tk dispatcher for various
258  *	events on graphs.
259  *
260  * Results:
261  *	None.
262  *
263  * Side effects:
264  *	When the window gets deleted, internal structures get
265  *	cleaned up.  When it gets exposed, the graph is eventually
266  *	redisplayed.
267  *
268  *--------------------------------------------------------------
269  */
270 static void
LegendEventProc(clientData,eventPtr)271 LegendEventProc(clientData, eventPtr)
272     ClientData clientData;	/* Legend record */
273     register XEvent *eventPtr;	/* Event which triggered call to routine */
274 {
275     Legend *legendPtr = clientData;
276 
277     if (eventPtr->type == Expose) {
278 	if (eventPtr->xexpose.count == 0) {
279 	    EventuallyRedrawLegend(legendPtr);
280 	}
281     } else if (eventPtr->type == DestroyNotify) {
282 	Graph *graphPtr = legendPtr->graphPtr;
283 
284 	if (legendPtr->tkwin != graphPtr->tkwin) {
285 	    Blt_DeleteWindowInstanceData(legendPtr->tkwin);
286 	    if (legendPtr->cmdToken != NULL) {
287 		Tcl_DeleteCommandFromToken(graphPtr->interp,
288 					   legendPtr->cmdToken);
289 		legendPtr->cmdToken = NULL;
290 	    }
291 	    legendPtr->tkwin = graphPtr->tkwin;
292 	}
293 	if (legendPtr->flags & REDRAW_PENDING) {
294 	    Tcl_CancelIdleCall(DisplayLegend, legendPtr);
295 	    legendPtr->flags &= ~REDRAW_PENDING;
296 	}
297 	legendPtr->site = LEGEND_RIGHT;
298 	graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
299 	Blt_MoveBindingTable(legendPtr->bindTable, graphPtr->tkwin);
300 	Blt_EventuallyRedrawGraph(graphPtr);
301     } else if (eventPtr->type == ConfigureNotify) {
302 	EventuallyRedrawLegend(legendPtr);
303     }
304 }
305 
306 static int
CreateLegendWindow(interp,legendPtr,pathName)307 CreateLegendWindow(interp, legendPtr, pathName)
308     Tcl_Interp *interp;
309     Legend *legendPtr;
310     char *pathName;
311 {
312     Tk_Window tkwin;
313 
314     tkwin = Tk_MainWindow(interp);
315     tkwin = Tk_CreateWindowFromPath(interp, tkwin, pathName, NULL);
316     if (tkwin == NULL) {
317 	return TCL_ERROR;
318     }
319     Blt_SetWindowInstanceData(tkwin, legendPtr);
320     Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
321 	  LegendEventProc, legendPtr);
322     /* Move the legend's binding table to the new window. */
323     Blt_MoveBindingTable(legendPtr->bindTable, tkwin);
324     if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
325 	Tk_DestroyWindow(legendPtr->tkwin);
326     }
327     legendPtr->cmdToken = Tcl_CreateCommand(interp, pathName,
328 	Blt_GraphInstCmdProc, legendPtr->graphPtr, NULL);
329     legendPtr->tkwin = tkwin;
330     legendPtr->site = LEGEND_WINDOW;
331     return TCL_OK;
332 }
333 
334 /*
335  *----------------------------------------------------------------------
336  *
337  * StringToPosition --
338  *
339  *	Convert the string representation of a legend XY position into
340  *	window coordinates.  The form of the string must be "@x,y" or
341  *	none.
342  *
343  * Results:
344  *	The return value is a standard Tcl result.  The symbol type is
345  *	written into the widget record.
346  *
347  *----------------------------------------------------------------------
348  */
349 /*ARGSUSED*/
350 static int
StringToPosition(clientData,interp,tkwin,string,widgRec,offset)351 StringToPosition(clientData, interp, tkwin, string, widgRec, offset)
352     ClientData clientData;	/* Not used. */
353     Tcl_Interp *interp;		/* Interpreter to send results back to */
354     Tk_Window tkwin;		/* Not used. */
355     char *string;		/* New legend position string */
356     char *widgRec;		/* Widget record */
357     int offset;			/* offset to XPoint structure */
358 {
359     Legend *legendPtr = (Legend *)widgRec;
360     char c;
361     unsigned int length;
362 
363     c = string[0];
364     length = strlen(string);
365 
366     if ((string == NULL) || (*string == '\0')) {
367 	legendPtr->site = LEGEND_RIGHT;
368     } else if ((c == 'l') && (strncmp(string, "leftmargin", length) == 0)) {
369 	legendPtr->site = LEGEND_LEFT;
370     } else if ((c == 'r') && (strncmp(string, "rightmargin", length) == 0)) {
371 	legendPtr->site = LEGEND_RIGHT;
372     } else if ((c == 't') && (strncmp(string, "topmargin", length) == 0)) {
373 	legendPtr->site = LEGEND_TOP;
374     } else if ((c == 'b') && (strncmp(string, "bottommargin", length) == 0)) {
375 	legendPtr->site = LEGEND_BOTTOM;
376     } else if ((c == 'p') && (strncmp(string, "plotarea", length) == 0)) {
377 	legendPtr->site = LEGEND_PLOT;
378     } else if (c == '@') {
379 	char *comma;
380 	long x, y;
381 	int result;
382 
383 	comma = strchr(string + 1, ',');
384 	if (comma == NULL) {
385 	    Tcl_AppendResult(interp, "bad screen position \"", string,
386 			     "\": should be @x,y", (char *)NULL);
387 	    return TCL_ERROR;
388 	}
389 	x = y = 0;
390 	*comma = '\0';
391 	result = ((Tcl_ExprLong(interp, string + 1, &x) == TCL_OK) &&
392 		  (Tcl_ExprLong(interp, comma + 1, &y) == TCL_OK));
393 	*comma = ',';
394 	if (!result) {
395 	    return TCL_ERROR;
396 	}
397 	legendPtr->anchorPos.x = (int)x;
398 	legendPtr->anchorPos.y = (int)y;
399 	legendPtr->site = LEGEND_XY;
400     } else if (c == '.') {
401 	if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
402 	    Tk_DestroyWindow(legendPtr->tkwin);
403 	    legendPtr->tkwin = legendPtr->graphPtr->tkwin;
404 	}
405 	if (CreateLegendWindow(interp, legendPtr, string) != TCL_OK) {
406 	    return TCL_ERROR;
407 	}
408 	legendPtr->site = LEGEND_WINDOW;
409     } else {
410 	Tcl_AppendResult(interp, "bad position \"", string, "\": should be  \
411 \"leftmargin\", \"rightmargin\", \"topmargin\", \"bottommargin\", \
412 \"plotarea\", .window or @x,y", (char *)NULL);
413 	return TCL_ERROR;
414     }
415     return TCL_OK;
416 }
417 
418 /*
419  *----------------------------------------------------------------------
420  *
421  * PositionToString --
422  *
423  *	Convert the window coordinates into a string.
424  *
425  * Results:
426  *	The string representing the coordinate position is returned.
427  *
428  *----------------------------------------------------------------------
429  */
430 /*ARGSUSED*/
431 static char *
PositionToString(clientData,tkwin,widgRec,offset,freeProcPtr)432 PositionToString(clientData, tkwin, widgRec, offset, freeProcPtr)
433     ClientData clientData;	/* Not used. */
434     Tk_Window tkwin;		/* Not used. */
435     char *widgRec;		/* Widget record */
436     int offset;			/* offset of XPoint in record */
437     Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
438 {
439     Legend *legendPtr = (Legend *)widgRec;
440 
441     switch (legendPtr->site) {
442     case LEGEND_LEFT:
443 	return "leftmargin";
444     case LEGEND_RIGHT:
445 	return "rightmargin";
446     case LEGEND_TOP:
447 	return "topmargin";
448     case LEGEND_BOTTOM:
449 	return "bottommargin";
450     case LEGEND_PLOT:
451 	return "plotarea";
452     case LEGEND_WINDOW:
453 	return Tk_PathName(legendPtr->tkwin);
454     case LEGEND_XY:
455 	{
456 	    char string[200];
457 	    char *result;
458 
459 	    sprintf(string, "@%d,%d", (int)legendPtr->anchorPos.x,
460 		    (int)legendPtr->anchorPos.y);
461 	    result = Blt_Strdup(string);
462 	    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
463 	    return result;
464 	}
465     default:
466 	return "unknown legend position";
467     }
468 }
469 
470 static void
SetLegendOrigin(legendPtr)471 SetLegendOrigin(legendPtr)
472     Legend *legendPtr;
473 {
474     Graph *graphPtr;
475     int x, y, width, height;
476 
477     graphPtr = legendPtr->graphPtr;
478     x = y = width = height = 0;		/* Suppress compiler warning. */
479     switch (legendPtr->site) {
480     case LEGEND_RIGHT:
481 	width = graphPtr->rightMargin.width - graphPtr->rightMargin.axesOffset;
482 	height = graphPtr->bottom - graphPtr->top;
483 	x = graphPtr->width - (width + graphPtr->inset);
484 	y = graphPtr->top;
485 	break;
486     case LEGEND_LEFT:
487 	width = graphPtr->leftMargin.width - graphPtr->leftMargin.axesOffset;
488 	height = graphPtr->bottom - graphPtr->top;
489 	x = graphPtr->inset;
490 	y = graphPtr->top;
491 	break;
492     case LEGEND_TOP:
493 	width = graphPtr->right - graphPtr->left;
494 	height = graphPtr->topMargin.height - graphPtr->topMargin.axesOffset;
495 	if (graphPtr->title != NULL) {
496 	    height -= graphPtr->titleTextStyle.height;
497 	}
498 	x = graphPtr->left;
499 	y = graphPtr->inset;
500 	if (graphPtr->title != NULL) {
501 	    y += graphPtr->titleTextStyle.height;
502 	}
503 	break;
504     case LEGEND_BOTTOM:
505 	width = graphPtr->right - graphPtr->left;
506 	height = graphPtr->bottomMargin.height -
507 	    graphPtr->bottomMargin.axesOffset;
508 	x = graphPtr->left;
509 	y = graphPtr->height - (height + graphPtr->inset);
510 	break;
511     case LEGEND_PLOT:
512 	width = graphPtr->right - graphPtr->left;
513 	height = graphPtr->bottom - graphPtr->top;
514 	x = graphPtr->left;
515 	y = graphPtr->top;
516 	break;
517     case LEGEND_XY:
518 	width = legendPtr->width;
519 	height = legendPtr->height;
520 	x = (int)legendPtr->anchorPos.x;
521 	y = (int)legendPtr->anchorPos.y;
522 	if (x < 0) {
523 	    x += graphPtr->width;
524 	}
525 	if (y < 0) {
526 	    y += graphPtr->height;
527 	}
528 	break;
529     case LEGEND_WINDOW:
530 	legendPtr->anchor = TK_ANCHOR_NW;
531 	legendPtr->x = legendPtr->y = 0;
532 	return;
533     }
534     width = legendPtr->width - width;
535     height = legendPtr->height - height;
536     Blt_TranslateAnchor(x, y, width, height, legendPtr->anchor, &x, &y);
537 
538     legendPtr->x = x + legendPtr->padLeft;
539     legendPtr->y = y + legendPtr->padTop;
540 }
541 
542 
543 /*ARGSUSED*/
544 static ClientData
PickLegendEntry(clientData,x,y,contextPtr)545 PickLegendEntry(clientData, x, y, contextPtr)
546     ClientData clientData;
547     int x, y;			/* Point to be tested */
548     ClientData *contextPtr;	/* Not used. */
549 {
550     Graph *graphPtr = clientData;
551     Legend *legendPtr;
552     int width, height;
553 
554     legendPtr = graphPtr->legend;
555     width = legendPtr->width;
556     height = legendPtr->height;
557 
558     x -= legendPtr->x + legendPtr->borderWidth;
559     y -= legendPtr->y + legendPtr->borderWidth;
560     width -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padX);
561     height -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padY);
562 
563     if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
564 	int row, column;
565 	int n;
566 
567 	/*
568 	 * It's in the bounding box, so compute the index.
569 	 */
570 	row = y / legendPtr->style.height;
571 	column = x / legendPtr->style.width;
572 	n = (column * legendPtr->nRows) + row;
573 	if (n < legendPtr->nEntries) {
574 	    Blt_ChainLink *linkPtr;
575 	    Element *elemPtr;
576 	    int count;
577 
578 	    /* Legend entries are stored in reverse. */
579 	    count = 0;
580 	    for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
581 		 linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
582 		elemPtr = Blt_ChainGetValue(linkPtr);
583 		if (elemPtr->label != NULL) {
584 		    if (count == n) {
585 			return elemPtr;
586 		    }
587 		    count++;
588 		}
589 	    }
590 	    if (linkPtr != NULL) {
591 		return Blt_ChainGetValue(linkPtr);
592 	    }
593 	}
594     }
595     return NULL;
596 }
597 
598 /*
599  * -----------------------------------------------------------------
600  *
601  * Blt_MapLegend --
602  *
603  * 	Calculates the dimensions (width and height) needed for
604  *	the legend.  Also determines the number of rows and columns
605  *	necessary to list all the valid element labels.
606  *
607  * Results:
608  *      None.
609  *
610  * Side effects:
611  *   	The following fields of the legend are calculated and set.
612  *
613  * 	nEntries   - number of valid labels of elements in the
614  *		      display list.
615  * 	nRows	    - number of rows of entries
616  * 	nColumns    - number of columns of entries
617  * 	style.height - height of each entry
618  * 	style.width  - width of each entry
619  * 	height	    - width of legend (includes borders and padding)
620  * 	width	    - height of legend (includes borders and padding)
621  *
622  * -----------------------------------------------------------------
623  */
624 void
Blt_MapLegend(legendPtr,plotWidth,plotHeight)625 Blt_MapLegend(legendPtr, plotWidth, plotHeight)
626     Legend *legendPtr;
627     int plotWidth;		/* Maximum width available in window
628 				 * to draw the legend. Will calculate number
629 				 * of columns from this. */
630     int plotHeight;		/* Maximum height available in window
631 				 * to draw the legend. Will calculate number
632 				 * of rows from this. */
633 {
634     Blt_ChainLink *linkPtr;
635     Element *elemPtr;
636     int nRows, nColumns, nEntries;
637     int legendWidth, legendHeight;
638     int entryWidth, entryHeight;
639     int symbolWidth;
640     Tk_FontMetrics fontMetrics;
641 
642     /* Initialize legend values to default (no legend displayed) */
643 
644     legendPtr->style.width = legendPtr->style.height = 0;
645     legendPtr->nRows = legendPtr->nColumns = 0;
646     legendPtr->nEntries = 0;
647     legendPtr->height = legendPtr->width = 0;
648 
649     if (legendPtr->site == LEGEND_WINDOW) {
650 	if (Tk_Width(legendPtr->tkwin) > 1) {
651 	    plotWidth = Tk_Width(legendPtr->tkwin);
652 	}
653 	if (Tk_Height(legendPtr->tkwin) > 1) {
654 	    plotHeight = Tk_Height(legendPtr->tkwin);
655 	}
656     }
657     if ((legendPtr->hidden) || (plotWidth < 1) || (plotHeight < 1)) {
658 	return;			/* Legend is not being displayed */
659     }
660 
661     /*
662      * Count the number of legend entries and determine the widest and
663      * tallest label.  The number of entries would normally be the
664      * number of elements, but 1) elements can be hidden and 2)
665      * elements can have no legend entry (-label "").
666      */
667     nEntries = 0;
668     entryWidth = entryHeight = 0;
669     for (linkPtr = Blt_ChainLastLink(legendPtr->graphPtr->elements.displayList);
670 	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
671 	int width, height;
672 
673 	elemPtr = Blt_ChainGetValue(linkPtr);
674 	if (elemPtr->label == NULL) {
675 	    continue;		/* Element has no legend entry. */
676 	}
677 	Blt_GetTextExtents(&legendPtr->style, elemPtr->label, &width, &height);
678 	if (entryWidth < width) {
679 	    entryWidth = width;
680 	}
681 	if (entryHeight < height) {
682 	    entryHeight = height;
683 	}
684 	nEntries++;
685     }
686 
687     if (nEntries == 0) {
688 	return;			/* No legend entries. */
689     }
690 
691 
692     Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
693     symbolWidth = 2 * fontMetrics.ascent;
694 
695     entryWidth += 2 * legendPtr->entryBorderWidth + PADDING(legendPtr->ipadX) +
696 	5 + symbolWidth;
697     entryHeight += 2 * legendPtr->entryBorderWidth + PADDING(legendPtr->ipadY);
698 
699     legendWidth = plotWidth - 2 * legendPtr->borderWidth -
700 	PADDING(legendPtr->padX);
701     legendHeight = plotHeight - 2 * legendPtr->borderWidth -
702 	PADDING(legendPtr->padY);
703 
704     /*
705      * The number of rows and columns is computed as one of the following:
706      *
707      *	both options set		User defined.
708      *  -rows				Compute columns from rows.
709      *  -columns			Compute rows from columns.
710      *	neither set			Compute rows and columns from
711      *					size of plot.
712      */
713     if (legendPtr->reqRows > 0) {
714 	nRows = legendPtr->reqRows;
715 	if (nRows > nEntries) {
716 	    nRows = nEntries;
717 	}
718 	if (legendPtr->reqColumns > 0) {
719 	    nColumns = legendPtr->reqColumns;
720 	    if (nColumns > nEntries) {
721 		nColumns = nEntries; /* Both -rows, -columns set. */
722 	    }
723 	} else {
724 	    nColumns = ((nEntries - 1) / nRows) + 1; /* Only -rows. */
725 	}
726     } else if (legendPtr->reqColumns > 0) { /* Only -columns. */
727 	nColumns = legendPtr->reqColumns;
728 	if (nColumns > nEntries) {
729 	    nColumns = nEntries;
730 	}
731 	nRows = ((nEntries - 1) / nColumns) + 1;
732     } else {
733 	/* Compute # of rows and columns from the legend size. */
734 	nRows = legendHeight / entryHeight;
735 	nColumns = legendWidth / entryWidth;
736 
737 	if (nRows > nEntries) {
738 	    nRows = nEntries;
739 	} else if (nRows < 1) {
740 	    nRows = 1;
741 	}
742 	if (nColumns > nEntries) {
743 	    nColumns = nEntries;
744 	} else if (nColumns < 1) {
745 	    nColumns = 1;
746 	}
747 	if ((legendPtr->site == LEGEND_TOP) ||
748 	    (legendPtr->site == LEGEND_BOTTOM)) {
749 	    nRows = ((nEntries - 1) / nColumns) + 1;
750 	} else {
751 	    nColumns = ((nEntries - 1) / nRows) + 1;
752 	}
753     }
754     if (nRows < 1) {
755 	nRows = 1;
756     }
757     if (nColumns < 1) {
758 	nColumns = 1;
759     }
760     legendWidth = 2 * legendPtr->borderWidth + PADDING(legendPtr->padX);
761     legendHeight = 2 * legendPtr->borderWidth + PADDING(legendPtr->padY);
762     legendHeight += nRows * entryHeight;
763     legendWidth += nColumns * entryWidth;
764 
765     legendPtr->height = legendHeight;
766     legendPtr->width = legendWidth;
767     legendPtr->nRows = nRows;
768     legendPtr->nColumns = nColumns;
769     legendPtr->nEntries = nEntries;
770     legendPtr->style.height = entryHeight;
771     legendPtr->style.width = entryWidth;
772 
773     if ((legendPtr->tkwin != legendPtr->graphPtr->tkwin) &&
774 	((Tk_ReqWidth(legendPtr->tkwin) != legendWidth) ||
775 	 (Tk_ReqHeight(legendPtr->tkwin) != legendHeight))) {
776 	Tk_GeometryRequest(legendPtr->tkwin, legendWidth, legendHeight);
777     }
778 }
779 
780 void
Blt_DrawLegend(legendPtr,drawable)781 Blt_DrawLegend(legendPtr, drawable)
782     Legend *legendPtr;
783     Drawable drawable;		/* Pixmap or window to draw into */
784 {
785     Graph *graphPtr;
786     Blt_ChainLink *linkPtr;
787     Pixmap pixmap;
788     Tk_3DBorder border;
789     Tk_FontMetrics fontMetrics;
790     Tk_Window tkwin;
791     int count;
792     int labelX, startY, symbolX, symbolY;
793     int symbolSize, midX, midY;
794     int width, height;
795     int x, y;
796     register Element *elemPtr;
797 
798     graphPtr = legendPtr->graphPtr;
799     graphPtr->flags &= ~DRAW_LEGEND;
800     if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
801 	return;
802     }
803     SetLegendOrigin(legendPtr);
804 
805     if (legendPtr->tkwin != graphPtr->tkwin) {
806 	tkwin = legendPtr->tkwin;
807 	width = Tk_Width(tkwin);
808 	if (width < 1) {
809 	    width = legendPtr->width;
810 	}
811 	height = Tk_Height(tkwin);
812 	if (height < 1) {
813 	    height = legendPtr->height;
814 	}
815     } else {
816 	width = legendPtr->width;
817 	height = legendPtr->height;
818     }
819     Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
820 
821     symbolSize = fontMetrics.ascent;
822     midX = symbolSize + 1 + legendPtr->entryBorderWidth;
823     midY = (symbolSize / 2) + 1 + legendPtr->entryBorderWidth;
824     labelX = 2 * symbolSize + legendPtr->entryBorderWidth +
825 	legendPtr->ipadX.side1 + 5;
826     symbolY = midY + legendPtr->ipadY.side1;
827     symbolX = midX + legendPtr->ipadX.side1;
828 
829     pixmap = Tk_GetPixmap(graphPtr->display, Tk_WindowId(legendPtr->tkwin),
830 	width, height, Tk_Depth(legendPtr->tkwin));
831 
832     if (legendPtr->border != NULL) {
833 	/* Background color and relief. */
834 	Blt_Fill3DRectangle(legendPtr->tkwin, pixmap, legendPtr->border, 0, 0,
835 		width, height, 0, TK_RELIEF_FLAT);
836     } else if (legendPtr->site & LEGEND_IN_PLOT) {
837 	/*
838 	 * Legend background is transparent and is positioned over the
839 	 * the plot area.  Either copy the part of the background from
840 	 * the backing store pixmap or (if no backing store exists)
841 	 * just fill it with the background color of the plot.
842 	 */
843 	if (graphPtr->backPixmap != None) {
844 	    XCopyArea(graphPtr->display, graphPtr->backPixmap, pixmap,
845 		graphPtr->drawGC, legendPtr->x, legendPtr->y, width, height,
846 		0, 0);
847         } else {
848 	    XFillRectangle(graphPtr->display, pixmap, graphPtr->plotFillGC,
849 		0, 0, width, height);
850  	}
851     } else {
852 	/*
853 	 * The legend is positioned in one of the margins or the
854 	 * external window.  Draw either the solid or tiled background
855 	 * with the the border.
856 	 */
857 	if (Blt_HasTile(graphPtr->tile)) {
858 	    Blt_SetTileOrigin(legendPtr->tkwin, graphPtr->tile, legendPtr->x,
859 			      legendPtr->y);
860 	    Blt_TileRectangle(legendPtr->tkwin, pixmap, graphPtr->tile, 0, 0,
861 				width, height);
862 	} else {
863 	    XFillRectangle(graphPtr->display, pixmap, graphPtr->fillGC, 0, 0,
864 			   width, height);
865  	}
866     }
867     x = legendPtr->padLeft + legendPtr->borderWidth;
868     y = legendPtr->padTop + legendPtr->borderWidth;
869     count = 0;
870     startY = y;
871     for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
872 	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
873 	elemPtr = Blt_ChainGetValue(linkPtr);
874 	if (elemPtr->label == NULL) {
875 	    continue;		/* Skip this entry */
876 	}
877 	if (elemPtr->flags & LABEL_ACTIVE) {
878 	    legendPtr->style.state |= STATE_ACTIVE;
879 	    Blt_Fill3DRectangle(legendPtr->tkwin, pixmap,
880 		legendPtr->activeBorder, x, y,
881 		legendPtr->style.width, legendPtr->style.height,
882 		legendPtr->entryBorderWidth, legendPtr->activeRelief);
883 	} else {
884 	    legendPtr->style.state &= ~STATE_ACTIVE;
885 	    if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
886 		Blt_Draw3DRectangle(legendPtr->tkwin, pixmap, graphPtr->border,
887 		    x, y, legendPtr->style.width, legendPtr->style.height,
888 		    legendPtr->entryBorderWidth, elemPtr->labelRelief);
889 	    }
890 	}
891 	(*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr,
892 		x + symbolX, y + symbolY, symbolSize);
893 	Blt_DrawText(legendPtr->tkwin, pixmap, elemPtr->label,
894 		&legendPtr->style, x + labelX,
895 		y + legendPtr->entryBorderWidth + legendPtr->ipadY.side1);
896 	count++;
897 
898 	/* Check when to move to the next column */
899 	if ((count % legendPtr->nRows) > 0) {
900 	    y += legendPtr->style.height;
901 	} else {
902 	    x += legendPtr->style.width;
903 	    y = startY;
904 	}
905     }
906     /*
907      * Draw the border and/or background of the legend.
908      */
909     border = legendPtr->border;
910     if (border == NULL) {
911 	border = graphPtr->border;
912     }
913     Blt_Draw3DRectangle(legendPtr->tkwin, pixmap, border, 0, 0, width, height,
914 	legendPtr->borderWidth, legendPtr->relief);
915 
916     XCopyArea(graphPtr->display, pixmap, drawable, graphPtr->drawGC, 0, 0,
917 	width, height, legendPtr->x, legendPtr->y);
918     Tk_FreePixmap(graphPtr->display, pixmap);
919 }
920 
921 /*
922  * -----------------------------------------------------------------
923  *
924  * Blt_LegendToPostScript --
925  *
926  * -----------------------------------------------------------------
927  */
928 void
Blt_LegendToPostScript(legendPtr,psToken)929 Blt_LegendToPostScript(legendPtr, psToken)
930     Legend *legendPtr;
931     PsToken psToken;
932 {
933     Graph *graphPtr;
934     double x, y, startY;
935     Element *elemPtr;
936     int labelX, symbolX, symbolY;
937     int count;
938     Blt_ChainLink *linkPtr;
939     int symbolSize, midX, midY;
940     int width, height;
941     Tk_FontMetrics fontMetrics;
942 
943     if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
944 	return;
945     }
946     SetLegendOrigin(legendPtr);
947 
948     x = legendPtr->x, y = legendPtr->y;
949     width = legendPtr->width - PADDING(legendPtr->padX);
950     height = legendPtr->height - PADDING(legendPtr->padY);
951 
952     graphPtr = legendPtr->graphPtr;
953     if (graphPtr->postscript->decorations) {
954 	if (legendPtr->border != NULL) {
955 	    Blt_Fill3DRectangleToPostScript(psToken, legendPtr->border, x, y,
956 		width, height, legendPtr->borderWidth, legendPtr->relief);
957 	} else {
958 	    Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border, x, y,
959 		width, height, legendPtr->borderWidth, legendPtr->relief);
960 	}
961     } else {
962 	Blt_ClearBackgroundToPostScript(psToken);
963 	Blt_RectangleToPostScript(psToken, x, y, width, height);
964     }
965     x += legendPtr->borderWidth;
966     y += legendPtr->borderWidth;
967 
968     Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
969     symbolSize = fontMetrics.ascent;
970     midX = symbolSize + 1 + legendPtr->entryBorderWidth;
971     midY = (symbolSize / 2) + 1 + legendPtr->entryBorderWidth;
972     labelX = 2 * symbolSize + legendPtr->entryBorderWidth +
973 	legendPtr->ipadX.side1 + 5;
974     symbolY = midY + legendPtr->ipadY.side1;
975     symbolX = midX + legendPtr->ipadX.side1;
976 
977     count = 0;
978     startY = y;
979     for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
980 	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
981 	elemPtr = Blt_ChainGetValue(linkPtr);
982 	if (elemPtr->label == NULL) {
983 	    continue;		/* Skip this label */
984 	}
985 	if (elemPtr->flags & LABEL_ACTIVE) {
986 	    legendPtr->style.state |= STATE_ACTIVE;
987 	    Blt_Fill3DRectangleToPostScript(psToken, legendPtr->activeBorder,
988 		    x, y, legendPtr->style.width, legendPtr->style.height,
989 		    legendPtr->entryBorderWidth, legendPtr->activeRelief);
990 	} else {
991 	    legendPtr->style.state &= ~STATE_ACTIVE;
992 	    if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
993 		Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border,
994 		    x, y, legendPtr->style.width, legendPtr->style.height,
995 		    legendPtr->entryBorderWidth, elemPtr->labelRelief);
996 	    }
997 	}
998 	(*elemPtr->procsPtr->printSymbolProc) (graphPtr, psToken, elemPtr,
999 	    x + symbolX, y + symbolY, symbolSize);
1000 	Blt_TextToPostScript(psToken, elemPtr->label, &(legendPtr->style),
1001 		x + labelX,
1002 		y + legendPtr->entryBorderWidth + legendPtr->ipadY.side1);
1003 	count++;
1004 	if ((count % legendPtr->nRows) > 0) {
1005 	    y += legendPtr->style.height;
1006 	} else {
1007 	    x += legendPtr->style.width;
1008 	    y = startY;
1009 	}
1010     }
1011 }
1012 
1013 /*
1014  * -----------------------------------------------------------------
1015  *
1016  * DisplayLegend --
1017  *
1018  * -----------------------------------------------------------------
1019  */
1020 static void
DisplayLegend(clientData)1021 DisplayLegend(clientData)
1022     ClientData clientData;
1023 {
1024     Legend *legendPtr = clientData;
1025     int width, height;
1026 
1027     legendPtr->flags &= ~REDRAW_PENDING;
1028 
1029     if (legendPtr->tkwin == NULL) {
1030 	return;			/* Window has been destroyed. */
1031     }
1032     if (legendPtr->site == LEGEND_WINDOW) {
1033 	width = Tk_Width(legendPtr->tkwin);
1034 	height = Tk_Height(legendPtr->tkwin);
1035 	if ((width <= 1) || (height <= 1)) {
1036 	    return;
1037 	}
1038 	if ((width != legendPtr->width) || (height != legendPtr->height)) {
1039 	    Blt_MapLegend(legendPtr, width, height);
1040 	}
1041     }
1042     if (!Tk_IsMapped(legendPtr->tkwin)) {
1043 	return;
1044     }
1045     Blt_DrawLegend(legendPtr, Tk_WindowId(legendPtr->tkwin));
1046 }
1047 
1048 /*
1049  *----------------------------------------------------------------------
1050  *
1051  * ConfigureLegend --
1052  *
1053  * 	Routine to configure the legend.
1054  *
1055  * Results:
1056  *	A standard Tcl result.
1057  *
1058  * Side Effects:
1059  *	Graph will be redrawn to reflect the new legend attributes.
1060  *
1061  *----------------------------------------------------------------------
1062  */
1063 static void
ConfigureLegend(graphPtr,legendPtr)1064 ConfigureLegend(graphPtr, legendPtr)
1065     Graph *graphPtr;
1066     Legend *legendPtr;
1067 {
1068     Blt_ResetTextStyle(graphPtr->tkwin, &(legendPtr->style));
1069 
1070     if (legendPtr->site == LEGEND_WINDOW) {
1071 	EventuallyRedrawLegend(legendPtr);
1072     } else {
1073 	/*
1074 	 *  Update the layout of the graph (and redraw the elements) if
1075 	 *  any of the following legend options (all of which affect the
1076 	 *	size of the legend) have changed.
1077 	 *
1078 	 *		-activeborderwidth, -borderwidth
1079 	 *		-border
1080 	 *		-font
1081 	 *		-hide
1082 	 *		-ipadx, -ipady, -padx, -pady
1083 	 *		-rows
1084 	 *
1085 	 *  If the position of the legend changed to/from the default
1086 	 *  position, also indicate that a new layout is needed.
1087 	 *
1088 	 */
1089 	if (Blt_ConfigModified(configSpecs, graphPtr->interp, "-*border*", "-*pad?",
1090 		"-position", "-hide", "-font", "-rows", (char *)NULL)) {
1091 	    graphPtr->flags |= MAP_WORLD;
1092 	}
1093 	graphPtr->flags |= (REDRAW_WORLD | REDRAW_BACKING_STORE);
1094 	Blt_EventuallyRedrawGraph(graphPtr);
1095     }
1096 }
1097 
1098 /*
1099  *----------------------------------------------------------------------
1100  *
1101  * Blt_DestroyLegend --
1102  *
1103  * Results:
1104  *	None.
1105  *
1106  * Side effects:
1107  *	Resources associated with the legend are freed.
1108  *
1109  *----------------------------------------------------------------------
1110  */
1111 void
Blt_DestroyLegend(graphPtr)1112 Blt_DestroyLegend(graphPtr)
1113     Graph *graphPtr;
1114 {
1115     Legend *legendPtr = graphPtr->legend;
1116 
1117     Tk_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
1118     Blt_FreeTextStyle(graphPtr->display, &(legendPtr->style));
1119     Blt_DestroyBindingTable(legendPtr->bindTable);
1120     if (legendPtr->tkwin != graphPtr->tkwin) {
1121 	Tk_Window tkwin;
1122 
1123 	/* The graph may be in the process of being torn down */
1124 	if (legendPtr->cmdToken != NULL) {
1125 	    Tcl_DeleteCommandFromToken(graphPtr->interp, legendPtr->cmdToken);
1126 	}
1127 	if (legendPtr->flags & REDRAW_PENDING) {
1128 	    Tcl_CancelIdleCall(DisplayLegend, legendPtr);
1129 	    legendPtr->flags &= ~REDRAW_PENDING;
1130 	}
1131 	tkwin = legendPtr->tkwin;
1132 	legendPtr->tkwin = NULL;
1133 	if (tkwin != NULL) {
1134 	    Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask,
1135 				  LegendEventProc, legendPtr);
1136 	    Blt_DeleteWindowInstanceData(tkwin);
1137 	    Tk_DestroyWindow(tkwin);
1138 	}
1139     }
1140     Blt_Free(legendPtr);
1141 }
1142 
1143 /*
1144  *----------------------------------------------------------------------
1145  *
1146  * Blt_CreateLegend --
1147  *
1148  * 	Creates and initializes a legend structure with default settings
1149  *
1150  * Results:
1151  *	A standard Tcl result.
1152  *
1153  *----------------------------------------------------------------------
1154  */
1155 /*ARGSUSED*/
1156 int
Blt_CreateLegend(graphPtr)1157 Blt_CreateLegend(graphPtr)
1158     Graph *graphPtr;
1159 {
1160     Legend *legendPtr;
1161 
1162     legendPtr = Blt_Calloc(1, sizeof(Legend));
1163     assert(legendPtr);
1164     graphPtr->legend = legendPtr;
1165     legendPtr->graphPtr = graphPtr;
1166     legendPtr->tkwin = graphPtr->tkwin;
1167     legendPtr->hidden = FALSE;
1168     legendPtr->anchorPos.x = legendPtr->anchorPos.y = -SHRT_MAX;
1169     legendPtr->relief = TK_RELIEF_SUNKEN;
1170     legendPtr->activeRelief = TK_RELIEF_FLAT;
1171     legendPtr->entryBorderWidth = legendPtr->borderWidth = 2;
1172     legendPtr->ipadX.side1 = legendPtr->ipadX.side2 = 1;
1173     legendPtr->ipadY.side1 = legendPtr->ipadY.side2 = 1;
1174     legendPtr->padX.side1 = legendPtr->padX.side2 = 1;
1175     legendPtr->padY.side1 = legendPtr->padY.side2 = 1;
1176     legendPtr->anchor = TK_ANCHOR_N;
1177     legendPtr->site = LEGEND_RIGHT;
1178     Blt_InitTextStyle(&(legendPtr->style));
1179     legendPtr->style.justify = TK_JUSTIFY_LEFT;
1180     legendPtr->style.anchor = TK_ANCHOR_NW;
1181     legendPtr->bindTable = Blt_CreateBindingTable(graphPtr->interp,
1182 	graphPtr->tkwin, graphPtr, PickLegendEntry, Blt_GraphTags);
1183 
1184     if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
1185 	    "legend", "Legend", configSpecs, 0, (char **)NULL,
1186 	    (char *)legendPtr, 0) != TCL_OK) {
1187 	return TCL_ERROR;
1188     }
1189     ConfigureLegend(graphPtr, legendPtr);
1190     return TCL_OK;
1191 }
1192 
1193 /*
1194  *----------------------------------------------------------------------
1195  *
1196  * GetOp --
1197  *
1198  * 	Find the legend entry from the given argument.  The argument
1199  *	can be either a screen position "@x,y" or the name of an
1200  *	element.
1201  *
1202  *	I don't know how useful it is to test with the name of an
1203  *	element.
1204  *
1205  * Results:
1206  *	A standard Tcl result.
1207  *
1208  * Side Effects:
1209  *	Graph will be redrawn to reflect the new legend attributes.
1210  *
1211  *----------------------------------------------------------------------
1212  */
1213 /*ARGSUSED*/
1214 static int
GetOp(graphPtr,interp,argc,argv)1215 GetOp(graphPtr, interp, argc, argv)
1216     Graph *graphPtr;
1217     Tcl_Interp *interp;
1218     int argc;			/* Not used. */
1219     char *argv[];
1220 {
1221     register Element *elemPtr;
1222     Legend *legendPtr = graphPtr->legend;
1223     int x, y;
1224     char c;
1225 
1226     if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
1227 	return TCL_OK;
1228     }
1229     elemPtr = NULL;
1230     c = argv[3][0];
1231     if ((c == 'c') && (strcmp(argv[3], "current") == 0)) {
1232 	elemPtr = (Element *)Blt_GetCurrentItem(legendPtr->bindTable);
1233     } else if ((c == '@') &&
1234        (Blt_GetXY(interp, graphPtr->tkwin, argv[3], &x, &y) == TCL_OK)) {
1235 	elemPtr = (Element *)PickLegendEntry(graphPtr, x, y, NULL);
1236     }
1237     if (elemPtr != NULL) {
1238 	Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
1239     }
1240     return TCL_OK;
1241 }
1242 
1243 /*
1244  *----------------------------------------------------------------------
1245  *
1246  * ActivateOp --
1247  *
1248  * 	Activates a particular label in the legend.
1249  *
1250  * Results:
1251  *	A standard Tcl result.
1252  *
1253  * Side Effects:
1254  *	Graph will be redrawn to reflect the new legend attributes.
1255  *
1256  *----------------------------------------------------------------------
1257  */
1258 static int
ActivateOp(graphPtr,interp,argc,argv)1259 ActivateOp(graphPtr, interp, argc, argv)
1260     Graph *graphPtr;
1261     Tcl_Interp *interp;
1262     int argc;
1263     char *argv[];
1264 {
1265     Legend *legendPtr = graphPtr->legend;
1266     Element *elemPtr;
1267     unsigned int active, redraw;
1268     Blt_HashEntry *hPtr;
1269     Blt_HashSearch cursor;
1270     register int i;
1271 
1272     active = (argv[2][0] == 'a') ? LABEL_ACTIVE : 0;
1273     redraw = 0;
1274     for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
1275 	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1276 	elemPtr = Blt_GetHashValue(hPtr);
1277 	for (i = 3; i < argc; i++) {
1278 	    if (Tcl_StringMatch(elemPtr->name, argv[i])) {
1279 		break;
1280 	    }
1281 	}
1282 	if ((i < argc) && (active != (elemPtr->flags & LABEL_ACTIVE))) {
1283 	    elemPtr->flags ^= LABEL_ACTIVE;
1284 	    if (elemPtr->label != NULL) {
1285 		redraw++;
1286 	    }
1287 	}
1288     }
1289     if ((redraw) && (!legendPtr->hidden)) {
1290 	/*
1291 	 * See if how much we need to draw. If the graph is already
1292 	 * schedule for a redraw, just make sure the right flags are
1293 	 * set.  Otherwise redraw only the legend: it's either in an
1294 	 * external window or it's the only thing that need updating.
1295 	 */
1296 	if (graphPtr->flags & REDRAW_PENDING) {
1297 	    if (legendPtr->site & LEGEND_IN_PLOT) {
1298 		graphPtr->flags |= REDRAW_BACKING_STORE;
1299 	    }
1300 	    graphPtr->flags |= REDRAW_WORLD; /* Redraw entire graph. */
1301 	} else {
1302 	    EventuallyRedrawLegend(legendPtr);
1303 	}
1304     }
1305     /* Return the names of all the active legend entries */
1306     for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
1307 	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1308 	elemPtr = Blt_GetHashValue(hPtr);
1309 	if (elemPtr->flags & LABEL_ACTIVE) {
1310 	    Tcl_AppendElement(interp, elemPtr->name);
1311 	}
1312     }
1313     return TCL_OK;
1314 }
1315 
1316 /*
1317  *----------------------------------------------------------------------
1318  *
1319  * BindOp --
1320  *
1321  *	  .t bind index sequence command
1322  *
1323  *----------------------------------------------------------------------
1324  */
1325 /*ARGSUSED*/
1326 static int
BindOp(graphPtr,interp,argc,argv)1327 BindOp(graphPtr, interp, argc, argv)
1328     Graph *graphPtr;
1329     Tcl_Interp *interp;
1330     int argc;
1331     char **argv;
1332 {
1333     if (argc == 3) {
1334 	Blt_HashEntry *hPtr;
1335 	Blt_HashSearch cursor;
1336 	char *tagName;
1337 
1338 	for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.tagTable), &cursor);
1339 	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1340 	    tagName = Blt_GetHashKey(&(graphPtr->elements.tagTable), hPtr);
1341 	    Tcl_AppendElement(interp, tagName);
1342 	}
1343 	return TCL_OK;
1344     }
1345     return Blt_ConfigureBindings(interp, graphPtr->legend->bindTable,
1346 	Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
1347 }
1348 
1349 /*
1350  *----------------------------------------------------------------------
1351  *
1352  * CgetOp --
1353  *
1354  * 	Queries or resets options for the legend.
1355  *
1356  * Results:
1357  *	A standard Tcl result.
1358  *
1359  * Side Effects:
1360  *	Graph will be redrawn to reflect the new legend attributes.
1361  *
1362  *----------------------------------------------------------------------
1363  */
1364 /* ARGSUSED */
1365 static int
CgetOp(graphPtr,interp,argc,argv)1366 CgetOp(graphPtr, interp, argc, argv)
1367     Graph *graphPtr;
1368     Tcl_Interp *interp;
1369     int argc;
1370     char **argv;
1371 {
1372     return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
1373 	    (char *)graphPtr->legend, argv[3], 0);
1374 }
1375 
1376 /*
1377  *----------------------------------------------------------------------
1378  *
1379  * ConfigureOp --
1380  *
1381  * 	Queries or resets options for the legend.
1382  *
1383  * Results:
1384  *	A standard Tcl result.
1385  *
1386  * Side Effects:
1387  *	Graph will be redrawn to reflect the new legend attributes.
1388  *
1389  *----------------------------------------------------------------------
1390  */
1391 static int
ConfigureOp(graphPtr,interp,argc,argv)1392 ConfigureOp(graphPtr, interp, argc, argv)
1393     Graph *graphPtr;
1394     Tcl_Interp *interp;
1395     int argc;
1396     CONST char **argv;
1397 {
1398     int flags = TK_CONFIG_ARGV_ONLY;
1399     Legend *legendPtr;
1400 
1401     legendPtr = graphPtr->legend;
1402     if (argc == 3) {
1403 	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1404 		(char *)legendPtr, (char *)NULL, flags);
1405     } else if (argc == 4) {
1406 	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1407 		(char *)legendPtr, argv[3], flags);
1408     }
1409     if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
1410 	    argv + 3, (char *)legendPtr, flags) != TCL_OK) {
1411 	return TCL_ERROR;
1412     }
1413     ConfigureLegend(graphPtr, legendPtr);
1414     return TCL_OK;
1415 }
1416 
1417 /*
1418  *----------------------------------------------------------------------
1419  *
1420  * Blt_LegendOp --
1421  *
1422  * Results:
1423  *	A standard Tcl result.
1424  *
1425  * Side Effects:
1426  *	Legend is possibly redrawn.
1427  *
1428  *----------------------------------------------------------------------
1429  */
1430 
1431 static Blt_OpSpec legendOps[] =
1432 {
1433     {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
1434     {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
1435     {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
1436     {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
1437     {"deactivate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
1438     {"get", 1, (Blt_Op)GetOp, 4, 4, "index",},
1439 };
1440 static int nLegendOps = sizeof(legendOps) / sizeof(Blt_OpSpec);
1441 
1442 int
Blt_LegendOp(graphPtr,interp,argc,argv)1443 Blt_LegendOp(graphPtr, interp, argc, argv)
1444     Graph *graphPtr;
1445     Tcl_Interp *interp;
1446     int argc;
1447     char **argv;
1448 {
1449     Blt_Op proc;
1450     int result;
1451 
1452     proc = Blt_GetOp(interp, nLegendOps, legendOps, BLT_OP_ARG2, argc, argv,0);
1453     if (proc == NULL) {
1454 	return TCL_ERROR;
1455     }
1456     result = (*proc) (graphPtr, interp, argc, argv);
1457     return result;
1458 }
1459 
1460 int
Blt_LegendSite(legendPtr)1461 Blt_LegendSite(legendPtr)
1462     Legend *legendPtr;
1463 {
1464     return legendPtr->site;
1465 }
1466 
1467 int
Blt_LegendWidth(legendPtr)1468 Blt_LegendWidth(legendPtr)
1469     Legend *legendPtr;
1470 {
1471     return legendPtr->width;
1472 }
1473 
1474 int
Blt_LegendHeight(legendPtr)1475 Blt_LegendHeight(legendPtr)
1476     Legend *legendPtr;
1477 {
1478     return legendPtr->height;
1479 }
1480 
1481 int
Blt_LegendIsHidden(legendPtr)1482 Blt_LegendIsHidden(legendPtr)
1483     Legend *legendPtr;
1484 {
1485     return legendPtr->hidden;
1486 }
1487 
1488 int
Blt_LegendIsRaised(legendPtr)1489 Blt_LegendIsRaised(legendPtr)
1490     Legend *legendPtr;
1491 {
1492     return legendPtr->raised;
1493 }
1494 
1495 int
Blt_LegendX(legendPtr)1496 Blt_LegendX(legendPtr)
1497     Legend *legendPtr;
1498 {
1499     return legendPtr->x;
1500 }
1501 
1502 int
Blt_LegendY(legendPtr)1503 Blt_LegendY(legendPtr)
1504     Legend *legendPtr;
1505 {
1506     return legendPtr->y;
1507 }
1508 
1509 void
Blt_LegendRemoveElement(legendPtr,elemPtr)1510 Blt_LegendRemoveElement(legendPtr, elemPtr)
1511     Legend *legendPtr;
1512     Element *elemPtr;
1513 {
1514     Blt_DeleteBindings(legendPtr->bindTable, elemPtr);
1515 }
1516