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