source: trunk/kitgen/8.x/blt/generic/bltGrLegd.c@ 175

Last change on this file since 175 was 175, checked in by demin, 12 years ago

initial commit

File size: 44.4 KB
RevLine 
[175]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 */
42struct 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
127static Tk_OptionParseProc StringToPosition;
128static Tk_OptionPrintProc PositionToString;
129static Tk_CustomOption legendPositionOption =
130{
131 StringToPosition, PositionToString, (ClientData)0
132};
133extern Tk_CustomOption bltDistanceOption;
134extern Tk_CustomOption bltPadOption;
135extern Tk_CustomOption bltShadowOption;
136extern Tk_CustomOption bltCountOption;
137
138static 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
220static Tcl_IdleProc DisplayLegend;
221static Blt_BindPickProc PickLegendEntry;
222static Tk_EventProc LegendEventProc;
223
224extern 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 */
242static void
243EventuallyRedrawLegend(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 */
270static void
271LegendEventProc(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
306static int
307CreateLegendWindow(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*/
350static int
351StringToPosition(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*/
431static char *
432PositionToString(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
470static void
471SetLegendOrigin(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*/
544static ClientData
545PickLegendEntry(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 */
624void
625Blt_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
780void
781Blt_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 (graphPtr->tile != NULL) {
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 */
928void
929Blt_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 */
1020static void
1021DisplayLegend(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 */
1063static void
1064ConfigureLegend(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, "-*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 *
1102 * Blt_DestroyLegend --
1103 *
1104 * Results:
1105 * None.
1106 *
1107 * Side effects:
1108 * Resources associated with the legend are freed.
1109 *
1110 *----------------------------------------------------------------------
1111 */
1112void
1113Blt_DestroyLegend(graphPtr)
1114 Graph *graphPtr;
1115{
1116 Legend *legendPtr = graphPtr->legend;
1117
1118 Tk_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
1119 Blt_FreeTextStyle(graphPtr->display, &(legendPtr->style));
1120 Blt_DestroyBindingTable(legendPtr->bindTable);
1121 if (legendPtr->tkwin != graphPtr->tkwin) {
1122 Tk_Window tkwin;
1123
1124 /* The graph may be in the process of being torn down */
1125 if (legendPtr->cmdToken != NULL) {
1126 Tcl_DeleteCommandFromToken(graphPtr->interp, legendPtr->cmdToken);
1127 }
1128 if (legendPtr->flags & REDRAW_PENDING) {
1129 Tcl_CancelIdleCall(DisplayLegend, legendPtr);
1130 legendPtr->flags &= ~REDRAW_PENDING;
1131 }
1132 tkwin = legendPtr->tkwin;
1133 legendPtr->tkwin = NULL;
1134 if (tkwin != NULL) {
1135 Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask,
1136 LegendEventProc, legendPtr);
1137 Blt_DeleteWindowInstanceData(tkwin);
1138 Tk_DestroyWindow(tkwin);
1139 }
1140 }
1141 Blt_Free(legendPtr);
1142}
1143
1144/*
1145 *----------------------------------------------------------------------
1146 *
1147 * Blt_CreateLegend --
1148 *
1149 * Creates and initializes a legend structure with default settings
1150 *
1151 * Results:
1152 * A standard Tcl result.
1153 *
1154 *----------------------------------------------------------------------
1155 */
1156/*ARGSUSED*/
1157int
1158Blt_CreateLegend(graphPtr)
1159 Graph *graphPtr;
1160{
1161 Legend *legendPtr;
1162
1163 legendPtr = Blt_Calloc(1, sizeof(Legend));
1164 assert(legendPtr);
1165 graphPtr->legend = legendPtr;
1166 legendPtr->graphPtr = graphPtr;
1167 legendPtr->tkwin = graphPtr->tkwin;
1168 legendPtr->hidden = FALSE;
1169 legendPtr->anchorPos.x = legendPtr->anchorPos.y = -SHRT_MAX;
1170 legendPtr->relief = TK_RELIEF_SUNKEN;
1171 legendPtr->activeRelief = TK_RELIEF_FLAT;
1172 legendPtr->entryBorderWidth = legendPtr->borderWidth = 2;
1173 legendPtr->ipadX.side1 = legendPtr->ipadX.side2 = 1;
1174 legendPtr->ipadY.side1 = legendPtr->ipadY.side2 = 1;
1175 legendPtr->padX.side1 = legendPtr->padX.side2 = 1;
1176 legendPtr->padY.side1 = legendPtr->padY.side2 = 1;
1177 legendPtr->anchor = TK_ANCHOR_N;
1178 legendPtr->site = LEGEND_RIGHT;
1179 Blt_InitTextStyle(&(legendPtr->style));
1180 legendPtr->style.justify = TK_JUSTIFY_LEFT;
1181 legendPtr->style.anchor = TK_ANCHOR_NW;
1182 legendPtr->bindTable = Blt_CreateBindingTable(graphPtr->interp,
1183 graphPtr->tkwin, graphPtr, PickLegendEntry, Blt_GraphTags);
1184
1185 if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
1186 "legend", "Legend", configSpecs, 0, (char **)NULL,
1187 (char *)legendPtr, 0) != TCL_OK) {
1188 return TCL_ERROR;
1189 }
1190 ConfigureLegend(graphPtr, legendPtr);
1191 return TCL_OK;
1192}
1193
1194/*
1195 *----------------------------------------------------------------------
1196 *
1197 * GetOp --
1198 *
1199 * Find the legend entry from the given argument. The argument
1200 * can be either a screen position "@x,y" or the name of an
1201 * element.
1202 *
1203 * I don't know how useful it is to test with the name of an
1204 * element.
1205 *
1206 * Results:
1207 * A standard Tcl result.
1208 *
1209 * Side Effects:
1210 * Graph will be redrawn to reflect the new legend attributes.
1211 *
1212 *----------------------------------------------------------------------
1213 */
1214/*ARGSUSED*/
1215static int
1216GetOp(graphPtr, interp, argc, argv)
1217 Graph *graphPtr;
1218 Tcl_Interp *interp;
1219 int argc; /* Not used. */
1220 char *argv[];
1221{
1222 register Element *elemPtr;
1223 Legend *legendPtr = graphPtr->legend;
1224 int x, y;
1225 char c;
1226
1227 if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
1228 return TCL_OK;
1229 }
1230 elemPtr = NULL;
1231 c = argv[3][0];
1232 if ((c == 'c') && (strcmp(argv[3], "current") == 0)) {
1233 elemPtr = (Element *)Blt_GetCurrentItem(legendPtr->bindTable);
1234 } else if ((c == '@') &&
1235 (Blt_GetXY(interp, graphPtr->tkwin, argv[3], &x, &y) == TCL_OK)) {
1236 elemPtr = (Element *)PickLegendEntry(graphPtr, x, y, NULL);
1237 }
1238 if (elemPtr != NULL) {
1239 Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
1240 }
1241 return TCL_OK;
1242}
1243
1244/*
1245 *----------------------------------------------------------------------
1246 *
1247 * ActivateOp --
1248 *
1249 * Activates a particular label in the legend.
1250 *
1251 * Results:
1252 * A standard Tcl result.
1253 *
1254 * Side Effects:
1255 * Graph will be redrawn to reflect the new legend attributes.
1256 *
1257 *----------------------------------------------------------------------
1258 */
1259static int
1260ActivateOp(graphPtr, interp, argc, argv)
1261 Graph *graphPtr;
1262 Tcl_Interp *interp;
1263 int argc;
1264 char *argv[];
1265{
1266 Legend *legendPtr = graphPtr->legend;
1267 Element *elemPtr;
1268 unsigned int active, redraw;
1269 Blt_HashEntry *hPtr;
1270 Blt_HashSearch cursor;
1271 register int i;
1272
1273 active = (argv[2][0] == 'a') ? LABEL_ACTIVE : 0;
1274 redraw = 0;
1275 for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
1276 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1277 elemPtr = Blt_GetHashValue(hPtr);
1278 for (i = 3; i < argc; i++) {
1279 if (Tcl_StringMatch(elemPtr->name, argv[i])) {
1280 break;
1281 }
1282 }
1283 if ((i < argc) && (active != (elemPtr->flags & LABEL_ACTIVE))) {
1284 elemPtr->flags ^= LABEL_ACTIVE;
1285 if (elemPtr->label != NULL) {
1286 redraw++;
1287 }
1288 }
1289 }
1290 if ((redraw) && (!legendPtr->hidden)) {
1291 /*
1292 * See if how much we need to draw. If the graph is already
1293 * schedule for a redraw, just make sure the right flags are
1294 * set. Otherwise redraw only the legend: it's either in an
1295 * external window or it's the only thing that need updating.
1296 */
1297 if (graphPtr->flags & REDRAW_PENDING) {
1298 if (legendPtr->site & LEGEND_IN_PLOT) {
1299 graphPtr->flags |= REDRAW_BACKING_STORE;
1300 }
1301 graphPtr->flags |= REDRAW_WORLD; /* Redraw entire graph. */
1302 } else {
1303 EventuallyRedrawLegend(legendPtr);
1304 }
1305 }
1306 /* Return the names of all the active legend entries */
1307 for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
1308 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1309 elemPtr = Blt_GetHashValue(hPtr);
1310 if (elemPtr->flags & LABEL_ACTIVE) {
1311 Tcl_AppendElement(interp, elemPtr->name);
1312 }
1313 }
1314 return TCL_OK;
1315}
1316
1317/*
1318 *----------------------------------------------------------------------
1319 *
1320 * BindOp --
1321 *
1322 * .t bind index sequence command
1323 *
1324 *----------------------------------------------------------------------
1325 */
1326/*ARGSUSED*/
1327static int
1328BindOp(graphPtr, interp, argc, argv)
1329 Graph *graphPtr;
1330 Tcl_Interp *interp;
1331 int argc;
1332 char **argv;
1333{
1334 if (argc == 3) {
1335 Blt_HashEntry *hPtr;
1336 Blt_HashSearch cursor;
1337 char *tagName;
1338
1339 for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.tagTable), &cursor);
1340 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1341 tagName = Blt_GetHashKey(&(graphPtr->elements.tagTable), hPtr);
1342 Tcl_AppendElement(interp, tagName);
1343 }
1344 return TCL_OK;
1345 }
1346 return Blt_ConfigureBindings(interp, graphPtr->legend->bindTable,
1347 Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
1348}
1349
1350/*
1351 *----------------------------------------------------------------------
1352 *
1353 * CgetOp --
1354 *
1355 * Queries or resets options for the legend.
1356 *
1357 * Results:
1358 * A standard Tcl result.
1359 *
1360 * Side Effects:
1361 * Graph will be redrawn to reflect the new legend attributes.
1362 *
1363 *----------------------------------------------------------------------
1364 */
1365/* ARGSUSED */
1366static int
1367CgetOp(graphPtr, interp, argc, argv)
1368 Graph *graphPtr;
1369 Tcl_Interp *interp;
1370 int argc;
1371 char **argv;
1372{
1373 return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
1374 (char *)graphPtr->legend, argv[3], 0);
1375}
1376
1377/*
1378 *----------------------------------------------------------------------
1379 *
1380 * ConfigureOp --
1381 *
1382 * Queries or resets options for the legend.
1383 *
1384 * Results:
1385 * A standard Tcl result.
1386 *
1387 * Side Effects:
1388 * Graph will be redrawn to reflect the new legend attributes.
1389 *
1390 *----------------------------------------------------------------------
1391 */
1392static int
1393ConfigureOp(graphPtr, interp, argc, argv)
1394 Graph *graphPtr;
1395 Tcl_Interp *interp;
1396 int argc;
1397 char **argv;
1398{
1399 int flags = TK_CONFIG_ARGV_ONLY;
1400 Legend *legendPtr;
1401
1402 legendPtr = graphPtr->legend;
1403 if (argc == 3) {
1404 return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1405 (char *)legendPtr, (char *)NULL, flags);
1406 } else if (argc == 4) {
1407 return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1408 (char *)legendPtr, argv[3], flags);
1409 }
1410 if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
1411 argv + 3, (char *)legendPtr, flags) != TCL_OK) {
1412 return TCL_ERROR;
1413 }
1414 ConfigureLegend(graphPtr, legendPtr);
1415 return TCL_OK;
1416}
1417
1418/*
1419 *----------------------------------------------------------------------
1420 *
1421 * Blt_LegendOp --
1422 *
1423 * Results:
1424 * A standard Tcl result.
1425 *
1426 * Side Effects:
1427 * Legend is possibly redrawn.
1428 *
1429 *----------------------------------------------------------------------
1430 */
1431
1432static Blt_OpSpec legendOps[] =
1433{
1434 {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
1435 {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
1436 {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
1437 {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
1438 {"deactivate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
1439 {"get", 1, (Blt_Op)GetOp, 4, 4, "index",},
1440};
1441static int nLegendOps = sizeof(legendOps) / sizeof(Blt_OpSpec);
1442
1443int
1444Blt_LegendOp(graphPtr, interp, argc, argv)
1445 Graph *graphPtr;
1446 Tcl_Interp *interp;
1447 int argc;
1448 char **argv;
1449{
1450 Blt_Op proc;
1451 int result;
1452
1453 proc = Blt_GetOp(interp, nLegendOps, legendOps, BLT_OP_ARG2, argc, argv,0);
1454 if (proc == NULL) {
1455 return TCL_ERROR;
1456 }
1457 result = (*proc) (graphPtr, interp, argc, argv);
1458 return result;
1459}
1460
1461int
1462Blt_LegendSite(legendPtr)
1463 Legend *legendPtr;
1464{
1465 return legendPtr->site;
1466}
1467
1468int
1469Blt_LegendWidth(legendPtr)
1470 Legend *legendPtr;
1471{
1472 return legendPtr->width;
1473}
1474
1475int
1476Blt_LegendHeight(legendPtr)
1477 Legend *legendPtr;
1478{
1479 return legendPtr->height;
1480}
1481
1482int
1483Blt_LegendIsHidden(legendPtr)
1484 Legend *legendPtr;
1485{
1486 return legendPtr->hidden;
1487}
1488
1489int
1490Blt_LegendIsRaised(legendPtr)
1491 Legend *legendPtr;
1492{
1493 return legendPtr->raised;
1494}
1495
1496int
1497Blt_LegendX(legendPtr)
1498 Legend *legendPtr;
1499{
1500 return legendPtr->x;
1501}
1502
1503int
1504Blt_LegendY(legendPtr)
1505 Legend *legendPtr;
1506{
1507 return legendPtr->y;
1508}
1509
1510void
1511Blt_LegendRemoveElement(legendPtr, elemPtr)
1512 Legend *legendPtr;
1513 Element *elemPtr;
1514{
1515 Blt_DeleteBindings(legendPtr->bindTable, elemPtr);
1516}
Note: See TracBrowser for help on using the repository browser.