summaryrefslogtreecommitdiff
path: root/blt/src/bltGrLegd.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltGrLegd.c')
-rw-r--r--blt/src/bltGrLegd.c1488
1 files changed, 1488 insertions, 0 deletions
diff --git a/blt/src/bltGrLegd.c b/blt/src/bltGrLegd.c
new file mode 100644
index 00000000000..f46c2a87d2d
--- /dev/null
+++ b/blt/src/bltGrLegd.c
@@ -0,0 +1,1488 @@
+
+/*
+ * bltGrLegd.c --
+ *
+ * This module implements the legend for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltGrElem.h"
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Legend --
+ *
+ * Contains information specific to how the legend will be
+ * displayed.
+ *
+ *
+ * -------------------------------------------------------------------
+ */
+struct LegendStruct {
+ unsigned int flags;
+ Tk_Uid classUid; /* Type: Element or Marker. */
+
+ int hidden; /* If non-zero, don't display the legend. */
+
+ int raised; /* If non-zero, draw the legend last, above
+ * everything else. */
+
+ int nEntries; /* Number of element entries in table. */
+
+ short int width, height; /* Dimensions of the legend */
+
+ short int nColumns, nRows; /* Number of columns and rows in legend */
+
+ int site;
+ Point2D anchorPos; /* Says how to position the legend. Indicates
+ * the site and/or x-y screen coordinates of
+ * the legend. Used in conjunction with the
+ * anchor to determine its location. */
+
+ Tk_Anchor anchor; /* Anchor of legend. Used to interpret the
+ * positioning point of the legend in the
+ * graph*/
+
+ int x, y; /* Computed origin of legend. */
+
+ Graph *graphPtr;
+ Tcl_Command cmdToken; /* Token for graph's widget command. */
+ int reqColumns, reqRows;
+
+ Blt_Pad ipadX, ipadY; /* # of pixels padding around legend entries */
+ Blt_Pad padX, padY; /* # of pixels padding to exterior of legend */
+
+ Tk_Window tkwin; /* Optional external window to draw legend. */
+
+ TextStyle style;
+
+ int maxSymSize; /* Size of largest symbol to be displayed.
+ * Used to calculate size of legend */
+
+ Tk_3DBorder activeBorder; /* Active legend entry background color. */
+ int activeRelief; /* 3-D effect on active entry. */
+ int entryBW; /* Border width around each entry in legend. */
+
+ Tk_3DBorder border; /* 3-D effect of legend. */
+ int borderWidth; /* Width of legend 3-D border */
+ int relief; /* 3-d effect of border around the legend:
+ * TK_RELIEF_RAISED etc. */
+
+ Blt_BindTable bindTable;
+};
+
+#define padLeft padX.side1
+#define padRight padX.side2
+#define padTop padY.side1
+#define padBottom padY.side2
+#define PADDING(x) ((x).side1 + (x).side2)
+
+#define DEF_LEGEND_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG
+#define DEF_LEGEND_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_LEGEND_ACTIVE_BORDER_WIDTH "2"
+#define DEF_LEGEND_ACTIVE_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_LEGEND_ACTIVE_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_LEGEND_ACTIVE_RELIEF "flat"
+#define DEF_LEGEND_ANCHOR "n"
+#define DEF_LEGEND_BG_COLOR (char *)NULL
+#define DEF_LEGEND_BG_MONO (char *)NULL
+#define DEF_LEGEND_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_LEGEND_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_LEGEND_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_LEGEND_FONT STD_FONT_SMALL
+#define DEF_LEGEND_HIDE "no"
+#define DEF_LEGEND_IPAD_X "1"
+#define DEF_LEGEND_IPAD_Y "1"
+#define DEF_LEGEND_PAD_X "1"
+#define DEF_LEGEND_PAD_Y "1"
+#define DEF_LEGEND_POSITION "rightmargin"
+#define DEF_LEGEND_RAISED "no"
+#define DEF_LEGEND_RELIEF "sunken"
+#define DEF_LEGEND_SHADOW_COLOR (char *)NULL
+#define DEF_LEGEND_SHADOW_MONO (char *)NULL
+#define DEF_LEGEND_ROWS "0"
+#define DEF_LEGEND_COLUMNS "0"
+
+static Tk_OptionParseProc StringToPosition;
+static Tk_OptionPrintProc PositionToString;
+static Tk_CustomOption legendPositionOption =
+{
+ StringToPosition, PositionToString, (ClientData)0
+};
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltCountOption;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_LEGEND_ACTIVE_BG_COLOR,
+ Tk_Offset(Legend, activeBorder), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_LEGEND_ACTIVE_BG_MONO,
+ Tk_Offset(Legend, activeBorder), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-activeborderwidth", "activeBorderWidth",
+ "BorderWidth", DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, entryBW),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_LEGEND_ACTIVE_FG_COLOR,
+ Tk_Offset(Legend, style.activeColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_LEGEND_ACTIVE_FG_MONO,
+ Tk_Offset(Legend, style.activeColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
+ DEF_LEGEND_ACTIVE_RELIEF, Tk_Offset(Legend, activeRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_LEGEND_ANCHOR, Tk_Offset(Legend, anchor),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_LEGEND_BG_MONO, Tk_Offset(Legend, border),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_LEGEND_BG_COLOR, Tk_Offset(Legend, border),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-columns", "columns", "columns",
+ DEF_LEGEND_COLUMNS, Tk_Offset(Legend, reqColumns),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_LEGEND_FONT, Tk_Offset(Legend, style.font), 0},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_LEGEND_FG_COLOR, Tk_Offset(Legend, style.color),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_LEGEND_FG_MONO, Tk_Offset(Legend, style.color),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_LEGEND_HIDE, Tk_Offset(Legend, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "Pad",
+ DEF_LEGEND_IPAD_X, Tk_Offset(Legend, ipadX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "Pad",
+ DEF_LEGEND_IPAD_Y, Tk_Offset(Legend, ipadY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "Pad",
+ DEF_LEGEND_PAD_X, Tk_Offset(Legend, padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "Pad",
+ DEF_LEGEND_PAD_Y, Tk_Offset(Legend, padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-position", "position", "Position",
+ DEF_LEGEND_POSITION, 0,
+ TK_CONFIG_DONT_SET_DEFAULT, &legendPositionOption},
+ {TK_CONFIG_BOOLEAN, "-raised", "raised", "Raised",
+ DEF_LEGEND_RAISED, Tk_Offset(Legend, raised),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_LEGEND_RELIEF, Tk_Offset(Legend, relief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-rows", "rows", "rows",
+ DEF_LEGEND_ROWS, Tk_Offset(Legend, reqRows),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_LEGEND_SHADOW_COLOR, Tk_Offset(Legend, style.shadow),
+ TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_LEGEND_SHADOW_MONO, Tk_Offset(Legend, style.shadow),
+ TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+#ifdef __STDC__
+static Tcl_IdleProc DisplayLegend;
+static Blt_BindPickProc PickLegendEntry;
+static Tk_EventProc LegendEventProc;
+#endif
+
+extern Tcl_CmdProc Blt_GraphInstCmdProc;
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EventuallyRedrawLegend --
+ *
+ * Tells the Tk dispatcher to call the graph display routine at
+ * the next idle point. This request is made only if the window
+ * is displayed and no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ * The window is eventually redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+EventuallyRedrawLegend(legendPtr)
+ Legend *legendPtr; /* Legend record */
+{
+ if ((legendPtr->tkwin != NULL) && !(legendPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayLegend, legendPtr);
+ legendPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * LegendEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on graphs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, the graph is eventually
+ * redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+LegendEventProc(clientData, eventPtr)
+ ClientData clientData; /* Legend record */
+ register XEvent *eventPtr; /* Event which triggered call to routine */
+{
+ Legend *legendPtr = clientData;
+
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedrawLegend(legendPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ Graph *graphPtr = legendPtr->graphPtr;
+
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ Blt_DeleteWindowInstanceData(legendPtr->tkwin);
+ if (legendPtr->cmdToken != NULL) {
+ Tcl_DeleteCommandFromToken(graphPtr->interp,
+ legendPtr->cmdToken);
+ legendPtr->cmdToken = NULL;
+ }
+ legendPtr->tkwin = graphPtr->tkwin;
+ }
+ if (legendPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+ legendPtr->flags &= ~REDRAW_PENDING;
+ }
+ legendPtr->site = LEGEND_RIGHT;
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_MoveBindingTable(legendPtr->bindTable, graphPtr->tkwin);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ } else if (eventPtr->type == ConfigureNotify) {
+ EventuallyRedrawLegend(legendPtr);
+ }
+}
+
+static int
+CreateLegendWindow(interp, legendPtr, pathName)
+ Tcl_Interp *interp;
+ Legend *legendPtr;
+ char *pathName;
+{
+ Tk_Window tkwin;
+
+ tkwin = Tk_MainWindow(interp);
+ tkwin = Tk_CreateWindowFromPath(interp, tkwin, pathName, NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_SetWindowInstanceData(tkwin, legendPtr);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ LegendEventProc, legendPtr);
+ /* Move the legend's binding table to the new window. */
+ Blt_MoveBindingTable(legendPtr->bindTable, tkwin);
+ if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
+ Tk_DestroyWindow(legendPtr->tkwin);
+ }
+ legendPtr->cmdToken = Tcl_CreateCommand(interp, pathName,
+ Blt_GraphInstCmdProc, legendPtr->graphPtr, NULL);
+ legendPtr->tkwin = tkwin;
+ legendPtr->site = LEGEND_WINDOW;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPosition --
+ *
+ * Convert the string representation of a legend XY position into
+ * window coordinates. The form of the string must be "@x,y" or
+ * none.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The symbol type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPosition(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New legend position string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to XPoint structure */
+{
+ Legend *legendPtr = (Legend *)widgRec;
+ char c;
+ unsigned int length;
+
+ c = string[0];
+ length = strlen(string);
+
+ if ((string == NULL) || (*string == '\0')) {
+ legendPtr->site = LEGEND_RIGHT;
+ } else if ((c == 'l') && (strncmp(string, "leftmargin", length) == 0)) {
+ legendPtr->site = LEGEND_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "rightmargin", length) == 0)) {
+ legendPtr->site = LEGEND_RIGHT;
+ } else if ((c == 't') && (strncmp(string, "topmargin", length) == 0)) {
+ legendPtr->site = LEGEND_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottommargin", length) == 0)) {
+ legendPtr->site = LEGEND_BOTTOM;
+ } else if ((c == 'p') && (strncmp(string, "plotarea", length) == 0)) {
+ legendPtr->site = LEGEND_PLOT;
+ } else if (c == '@') {
+ char *comma;
+ long x, y;
+ int result;
+
+ comma = strchr(string + 1, ',');
+ if (comma == NULL) {
+ Tcl_AppendResult(interp, "bad screen position \"", string,
+ "\": should be @x,y", (char *)NULL);
+ return TCL_ERROR;
+ }
+ x = y = 0;
+ *comma = '\0';
+ result = ((Tcl_ExprLong(interp, string + 1, &x) == TCL_OK) &&
+ (Tcl_ExprLong(interp, comma + 1, &y) == TCL_OK));
+ *comma = ',';
+ if (!result) {
+ return TCL_ERROR;
+ }
+ legendPtr->anchorPos.x = (int)x;
+ legendPtr->anchorPos.y = (int)y;
+ legendPtr->site = LEGEND_XY;
+ } else if (c == '.') {
+ if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
+ Tk_DestroyWindow(legendPtr->tkwin);
+ legendPtr->tkwin = legendPtr->graphPtr->tkwin;
+ }
+ if (CreateLegendWindow(interp, legendPtr, string) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ legendPtr->site = LEGEND_WINDOW;
+ } else {
+ Tcl_AppendResult(interp, "bad position \"", string, "\": should be \
+\"leftmargin\", \"rightmargin\", \"topmargin\", \"bottommargin\", \
+\"plotarea\", .window or @x,y", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PositionToString --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PositionToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of XPoint in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Legend *legendPtr = (Legend *)widgRec;
+
+ switch (legendPtr->site) {
+ case LEGEND_LEFT:
+ return "leftmargin";
+ case LEGEND_RIGHT:
+ return "rightmargin";
+ case LEGEND_TOP:
+ return "topmargin";
+ case LEGEND_BOTTOM:
+ return "bottommargin";
+ case LEGEND_PLOT:
+ return "plotarea";
+ case LEGEND_WINDOW:
+ return Tk_PathName(legendPtr->tkwin);
+ case LEGEND_XY:
+ {
+ char string[200];
+ char *result;
+
+ sprintf(string, "@%d,%d", (int)legendPtr->anchorPos.x,
+ (int)legendPtr->anchorPos.y);
+ result = Blt_Strdup(string);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+ }
+ default:
+ return "unknown legend position";
+ }
+}
+
+static void
+SetLegendOrigin(legendPtr)
+ Legend *legendPtr;
+{
+ Graph *graphPtr;
+ int x, y, width, height;
+
+ graphPtr = legendPtr->graphPtr;
+ x = y = width = height = 0; /* Suppress compiler warning. */
+ switch (legendPtr->site) {
+ case LEGEND_RIGHT:
+ width = graphPtr->rightMargin.width - graphPtr->rightMargin.axesOffset;
+ height = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->width - (width + graphPtr->inset);
+ y = graphPtr->top;
+ break;
+ case LEGEND_LEFT:
+ width = graphPtr->leftMargin.width - graphPtr->leftMargin.axesOffset;
+ height = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->inset;
+ y = graphPtr->top;
+ break;
+ case LEGEND_TOP:
+ width = graphPtr->right - graphPtr->left;
+ height = graphPtr->topMargin.height - graphPtr->topMargin.axesOffset;
+ if (graphPtr->titleText != NULL) {
+ height -= graphPtr->titleStyle.height;
+ }
+ x = graphPtr->left;
+ y = graphPtr->inset;
+ if (graphPtr->titleText != NULL) {
+ y += graphPtr->titleStyle.height;
+ }
+ break;
+ case LEGEND_BOTTOM:
+ width = graphPtr->right - graphPtr->left;
+ height = graphPtr->bottomMargin.height -
+ graphPtr->bottomMargin.axesOffset;
+ x = graphPtr->left;
+ y = graphPtr->height - (height + graphPtr->inset);
+ break;
+ case LEGEND_PLOT:
+ width = graphPtr->right - graphPtr->left;
+ height = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->left;
+ y = graphPtr->top;
+ break;
+ case LEGEND_XY:
+ width = legendPtr->width;
+ height = legendPtr->height;
+ x = (int)legendPtr->anchorPos.x;
+ y = (int)legendPtr->anchorPos.y;
+ if (x < 0) {
+ x += graphPtr->width;
+ }
+ if (y < 0) {
+ y += graphPtr->height;
+ }
+ break;
+ case LEGEND_WINDOW:
+ legendPtr->anchor = TK_ANCHOR_NW;
+ legendPtr->x = legendPtr->y = 0;
+ return;
+ }
+ width = legendPtr->width - width;
+ height = legendPtr->height - height;
+ Blt_TranslateAnchor(x, y, width, height, legendPtr->anchor, &x, &y);
+
+ legendPtr->x = x + legendPtr->padLeft;
+ legendPtr->y = y + legendPtr->padTop;
+}
+
+
+static ClientData
+PickLegendEntry(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Point to be tested */
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+ int width, height;
+
+ legendPtr = graphPtr->legend;
+ width = legendPtr->width;
+ height = legendPtr->height;
+
+ x -= legendPtr->x + legendPtr->borderWidth;
+ y -= legendPtr->y + legendPtr->borderWidth;
+ width -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padX);
+ height -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padY);
+
+ if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
+ int row, column;
+ int n;
+
+ /*
+ * It's in the bounding box, so compute the index.
+ */
+ row = y / legendPtr->style.height;
+ column = x / legendPtr->style.width;
+ n = (column * legendPtr->nRows) + row;
+ if (n < legendPtr->nEntries) {
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+ int count;
+
+ /* FIXME: The entry is not in numeric order since not all
+ * entries may be visible. */
+ /* Legend entries are stored in reverse. */
+ count = 0;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label != NULL) {
+ if (count == n) {
+ return elemPtr;
+ }
+ count++;
+ }
+ }
+ if (linkPtr != NULL) {
+ return Blt_ChainGetValue(linkPtr);
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_MapLegend --
+ *
+ * Calculates the dimensions (width and height) needed for
+ * the legend. Also determines the number of rows and columns
+ * necessary to list all the valid element labels.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The following fields of the legend are calculated and set.
+ *
+ * nEntries - number of valid labels of elements in the
+ * display list.
+ * nRows - number of rows of entries
+ * nColumns - number of columns of entries
+ * style.height - height of each entry
+ * style.width - width of each entry
+ * height - width of legend (includes borders and padding)
+ * width - height of legend (includes borders and padding)
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_MapLegend(legendPtr, plotWidth, plotHeight)
+ Legend *legendPtr;
+ int plotWidth; /* Maximum width available in window
+ * to draw the legend. Will calculate number
+ * of columns from this. */
+ int plotHeight; /* Maximum height available in window
+ * to draw the legend. Will calculate number
+ * of rows from this. */
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+ int nRows, nColumns;
+ int nEntries;
+ int width, height;
+ int legendWidth, legendHeight;
+ int symbolWidth;
+ int outer, inner;
+ Tk_FontMetrics fontMetrics;
+
+ /* Initialize legend values to default (no legend displayed) */
+
+ legendPtr->style.width = legendPtr->style.height = 0;
+ legendPtr->nRows = legendPtr->nColumns = 0;
+ legendPtr->nEntries = 0;
+ legendPtr->height = legendPtr->width = 0;
+
+ if (legendPtr->site == LEGEND_WINDOW) {
+ if (Tk_Width(legendPtr->tkwin) > 1) {
+ plotWidth = Tk_Width(legendPtr->tkwin);
+ }
+ if (Tk_Height(legendPtr->tkwin) > 1) {
+ plotHeight = Tk_Height(legendPtr->tkwin);
+ }
+ }
+ if ((legendPtr->hidden) || (plotWidth < 1) || (plotHeight < 1)) {
+ return; /* Legend is not being displayed */
+ }
+
+ /* Determine the number of labels and the widest label */
+
+ nEntries = 0;
+ legendWidth = legendHeight = 0;
+ for (linkPtr = Blt_ChainLastLink(legendPtr->graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this label */
+ }
+ Blt_GetTextExtents(&legendPtr->style, elemPtr->label, &width, &height);
+ if (legendWidth < width) {
+ legendWidth = width;
+ }
+ if (legendHeight < height) {
+ legendHeight = height;
+ }
+ nEntries++;
+ }
+
+ if (nEntries == 0) {
+ return; /* No labels to display in legend */
+ }
+
+ /*
+ * Calculate the space need to for the legend based upon the size
+ * of a label entry and the number of rows and columns needed.
+ * Bound the height of the area by *maxHeight* which is the size
+ * of the plotting area.
+ */
+
+ Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolWidth = 2 * fontMetrics.ascent;
+ legendPtr->nEntries = nEntries;
+
+ outer = 2 * legendPtr->borderWidth;
+ inner = 2 * legendPtr->entryBW;
+
+ legendPtr->style.height = legendHeight + inner + PADDING(legendPtr->ipadY);
+ legendPtr->style.width = legendWidth + inner + PADDING(legendPtr->ipadX) +
+ 5 + symbolWidth;
+
+ width = plotWidth - (outer + PADDING(legendPtr->padX));
+ height = plotHeight - (outer + PADDING(legendPtr->padY));
+ if (legendPtr->reqRows > 0) {
+ nRows = legendPtr->reqRows;
+ } else {
+ nRows = height / legendPtr->style.height;
+ }
+ if (legendPtr->reqColumns > 0) {
+ nColumns = legendPtr->reqColumns;
+ } else {
+ nColumns = width / legendPtr->style.width;
+ }
+ if (nRows < 1) {
+ nRows = 1;
+ }
+ if (nColumns < 1) {
+ nColumns = 1;
+ }
+ if ((legendPtr->site == LEGEND_TOP) || (legendPtr->site == LEGEND_BOTTOM)) {
+ if (nColumns > 0) {
+ nRows = ((nEntries - 1) / nColumns) + 1;
+ if (nColumns > nEntries) {
+ nColumns = nEntries;
+ } else {
+ nColumns = ((nEntries - 1) / nRows) + 1;
+ }
+ }
+ } else {
+ if (nRows > 0) {
+ nColumns = ((nEntries - 1) / nRows) + 1;
+ if (nRows > nEntries) {
+ nRows = nEntries;
+ }
+ }
+ }
+ legendPtr->height = outer + PADDING(legendPtr->padY) +
+ (nRows * legendPtr->style.height);
+ legendPtr->width = outer + PADDING(legendPtr->padX) +
+ (nColumns * legendPtr->style.width);
+ legendPtr->nRows = nRows;
+ legendPtr->nColumns = nColumns;
+ if ((legendPtr->tkwin != legendPtr->graphPtr->tkwin) &&
+ ((Tk_ReqWidth(legendPtr->tkwin) != legendPtr->width) ||
+ (Tk_ReqHeight(legendPtr->tkwin) != legendPtr->height))) {
+ Tk_GeometryRequest(legendPtr->tkwin, legendPtr->width,
+ legendPtr->height);
+ }
+}
+
+void
+Blt_DrawLegend(legendPtr, drawable)
+ Legend *legendPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Graph *graphPtr;
+ Blt_ChainLink *linkPtr;
+ Pixmap pixmap;
+ Tk_3DBorder border;
+ Tk_FontMetrics fontMetrics;
+ Tk_Window tkwin;
+ int count;
+ int labelX, startY, symbolX, symbolY;
+ int symbolSize, midX, midY;
+ int width, height;
+ int x, y;
+ register Element *elemPtr;
+
+ graphPtr = legendPtr->graphPtr;
+ graphPtr->flags &= ~DRAW_LEGEND;
+ if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+ SetLegendOrigin(legendPtr);
+
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ tkwin = legendPtr->tkwin;
+ width = Tk_Width(tkwin);
+ if (width < 1) {
+ width = legendPtr->width;
+ }
+ height = Tk_Height(tkwin);
+ if (height < 1) {
+ height = legendPtr->height;
+ }
+ } else {
+ tkwin = graphPtr->tkwin;
+ width = legendPtr->width;
+ height = legendPtr->height;
+ }
+ Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+
+ symbolSize = fontMetrics.ascent;
+ midX = symbolSize + 1 + legendPtr->entryBW;
+ midY = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ labelX = 2 * symbolSize + legendPtr->entryBW + legendPtr->ipadX.side1 + 5;
+ symbolY = midY + legendPtr->ipadY.side1;
+ symbolX = midX + legendPtr->ipadX.side1;
+
+ pixmap = Tk_GetPixmap(graphPtr->display, Tk_WindowId(legendPtr->tkwin),
+ width, height, Tk_Depth(legendPtr->tkwin));
+
+ if (legendPtr->border != NULL) {
+ /* Background color and relief. */
+ Tk_Fill3DRectangle(legendPtr->tkwin, pixmap, legendPtr->border, 0, 0,
+ width, height, 0, TK_RELIEF_FLAT);
+ } else if (legendPtr->site & LEGEND_IN_PLOT) {
+ /*
+ * Legend background is transparent and is positioned over the
+ * the plot area. Either copy the part of the background from
+ * the backing store pixmap or (if no backing store exists)
+ * just fill it with the background color of the plot.
+ */
+ if (graphPtr->backPixmap != None) {
+ XCopyArea(graphPtr->display, graphPtr->backPixmap, pixmap,
+ graphPtr->drawGC, legendPtr->x, legendPtr->y, width, height,
+ 0, 0);
+ } else {
+ XFillRectangle(graphPtr->display, pixmap, graphPtr->plotFillGC,
+ 0, 0, width, height);
+ }
+ } else {
+ /*
+ * The legend is positioned in one of the margins or the
+ * external window. Draw either the solid or tiled background
+ * with the the border.
+ */
+ if (graphPtr->tile != NULL) {
+ Blt_SetTileOrigin(legendPtr->tkwin, graphPtr->tile, legendPtr->x,
+ legendPtr->y);
+ Blt_TileRectangle(legendPtr->tkwin, pixmap, graphPtr->tile, 0, 0,
+ width, height);
+ } else {
+ XFillRectangle(graphPtr->display, pixmap, graphPtr->fillGC, 0, 0,
+ width, height);
+ }
+ }
+ x = legendPtr->padLeft + legendPtr->borderWidth;
+ y = legendPtr->padTop + legendPtr->borderWidth;
+ count = 0;
+ startY = y;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this entry */
+ }
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ legendPtr->style.state |= STATE_ACTIVE;
+ Tk_Fill3DRectangle(legendPtr->tkwin, pixmap,
+ legendPtr->activeBorder, x, y,
+ legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, legendPtr->activeRelief);
+ } else {
+ legendPtr->style.state &= ~STATE_ACTIVE;
+ if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(legendPtr->tkwin, pixmap, graphPtr->border,
+ x, y, legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, elemPtr->labelRelief);
+ }
+ }
+ (*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr,
+ x + symbolX, y + symbolY, symbolSize);
+ Blt_DrawText(legendPtr->tkwin, pixmap, elemPtr->label,
+ &legendPtr->style,
+ x + labelX, y + legendPtr->entryBW + legendPtr->ipadY.side1);
+ count++;
+
+ /* Check when to move to the next column */
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->style.height;
+ } else {
+ x += legendPtr->style.width;
+ y = startY;
+ }
+ }
+ /*
+ * Draw the border and/or background of the legend.
+ */
+ border = legendPtr->border;
+ if (border == NULL) {
+ border = graphPtr->border;
+ }
+ Tk_Draw3DRectangle(legendPtr->tkwin, pixmap, border, 0, 0, width, height,
+ legendPtr->borderWidth, legendPtr->relief);
+
+ XCopyArea(graphPtr->display, pixmap, drawable, graphPtr->drawGC, 0, 0,
+ width, height, legendPtr->x, legendPtr->y);
+ Tk_FreePixmap(graphPtr->display, pixmap);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_LegendToPostScript --
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_LegendToPostScript(legendPtr, psToken)
+ Legend *legendPtr;
+ PsToken psToken;
+{
+ Graph *graphPtr;
+ double x, y, startY;
+ Element *elemPtr;
+ int labelX, symbolX, symbolY;
+ int count;
+ Blt_ChainLink *linkPtr;
+ int symbolSize, midX, midY;
+ int width, height;
+ Tk_FontMetrics fontMetrics;
+
+ if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+ SetLegendOrigin(legendPtr);
+
+ x = legendPtr->x, y = legendPtr->y;
+ width = legendPtr->width - PADDING(legendPtr->padX);
+ height = legendPtr->height - PADDING(legendPtr->padY);
+
+ graphPtr = legendPtr->graphPtr;
+ if (graphPtr->postscript->decorations) {
+ if (legendPtr->border != NULL) {
+ Blt_Fill3DRectangleToPostScript(psToken, legendPtr->border, x, y,
+ width, height, legendPtr->borderWidth, legendPtr->relief);
+ } else {
+ Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border, x, y,
+ width, height, legendPtr->borderWidth, legendPtr->relief);
+ }
+ } else {
+ Blt_ClearBackgroundToPostScript(psToken);
+ Blt_RectangleToPostScript(psToken, x, y, width, height);
+ }
+ x += legendPtr->borderWidth;
+ y += legendPtr->borderWidth;
+
+ Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolSize = fontMetrics.ascent;
+ midX = symbolSize + 1 + legendPtr->entryBW;
+ midY = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ labelX = 2 * symbolSize + legendPtr->entryBW + legendPtr->ipadX.side1 + 5;
+ symbolY = midY + legendPtr->ipadY.side1;
+ symbolX = midX + legendPtr->ipadX.side1;
+
+ count = 0;
+ startY = y;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this label */
+ }
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ legendPtr->style.state |= STATE_ACTIVE;
+ Blt_Fill3DRectangleToPostScript(psToken, legendPtr->activeBorder,
+ x, y, legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, legendPtr->activeRelief);
+ } else {
+ legendPtr->style.state &= ~STATE_ACTIVE;
+ if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
+ Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border,
+ x, y, legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, elemPtr->labelRelief);
+ }
+ }
+ (*elemPtr->procsPtr->printSymbolProc) (graphPtr, psToken, elemPtr,
+ x + symbolX, y + symbolY, symbolSize);
+ Blt_TextToPostScript(psToken, elemPtr->label, &(legendPtr->style),
+ x + labelX, y + legendPtr->entryBW + legendPtr->ipadY.side1);
+ count++;
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->style.height;
+ } else {
+ x += legendPtr->style.width;
+ y = startY;
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DisplayLegend --
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DisplayLegend(clientData)
+ ClientData clientData;
+{
+ Legend *legendPtr = clientData;
+ int width, height;
+
+ legendPtr->flags &= ~REDRAW_PENDING;
+
+ if (legendPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ if (legendPtr->site == LEGEND_WINDOW) {
+ width = Tk_Width(legendPtr->tkwin);
+ height = Tk_Height(legendPtr->tkwin);
+ if ((width <= 1) || (height <= 1)) {
+ return;
+ }
+ if ((width != legendPtr->width) || (height != legendPtr->height)) {
+ Blt_MapLegend(legendPtr, width, height);
+ }
+ }
+ if (!Tk_IsMapped(legendPtr->tkwin)) {
+ return;
+ }
+ Blt_DrawLegend(legendPtr, Tk_WindowId(legendPtr->tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureLegend --
+ *
+ * Routine to configure the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureLegend(graphPtr, legendPtr)
+ Graph *graphPtr;
+ Legend *legendPtr;
+{
+ Blt_ResetTextStyle(graphPtr->tkwin, &(legendPtr->style));
+
+ if (legendPtr->site == LEGEND_WINDOW) {
+ EventuallyRedrawLegend(legendPtr);
+ } else {
+ /*
+ * Update the layout of the graph (and redraw the elements) if
+ * any of the following legend options (all of which affect the
+ * size of the legend) have changed.
+ *
+ * -activeborderwidth, -borderwidth
+ * -border
+ * -font
+ * -hide
+ * -ipadx, -ipady, -padx, -pady
+ * -rows
+ *
+ * If the position of the legend changed to/from the default
+ * position, also indicate that a new layout is needed.
+ *
+ */
+ if (Blt_ConfigModified(configSpecs, "-*border*", "-*pad?",
+ "-position", "-hide", "-font", "-rows", (char *)NULL)) {
+ graphPtr->flags |= MAP_WORLD;
+ }
+ graphPtr->flags |= (REDRAW_WORLD | REDRAW_BACKING_STORE);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyLegend --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resources associated with the legend are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyLegend(graphPtr)
+ Graph *graphPtr;
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ Tk_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
+ Blt_FreeTextStyle(graphPtr->display, &(legendPtr->style));
+ Blt_DestroyBindingTable(legendPtr->bindTable);
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ Tk_Window tkwin;
+
+ /* The graph may be in the process of being torn down */
+ if (legendPtr->cmdToken != NULL) {
+ Tcl_DeleteCommandFromToken(graphPtr->interp, legendPtr->cmdToken);
+ }
+ if (legendPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+ legendPtr->flags &= ~REDRAW_PENDING;
+ }
+ tkwin = legendPtr->tkwin;
+ legendPtr->tkwin = NULL;
+ if (tkwin != NULL) {
+ Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ LegendEventProc, legendPtr);
+ Blt_DeleteWindowInstanceData(tkwin);
+ Tk_DestroyWindow(tkwin);
+ }
+ }
+ Blt_Free(legendPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateLegend --
+ *
+ * Creates and initializes a legend structure with default settings
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_CreateLegend(graphPtr)
+ Graph *graphPtr;
+{
+ Legend *legendPtr;
+
+ legendPtr = Blt_Calloc(1, sizeof(Legend));
+ assert(legendPtr);
+ graphPtr->legend = legendPtr;
+ legendPtr->graphPtr = graphPtr;
+ legendPtr->tkwin = graphPtr->tkwin;
+ legendPtr->hidden = FALSE;
+ legendPtr->anchorPos.x = legendPtr->anchorPos.y = -SHRT_MAX;
+ legendPtr->relief = TK_RELIEF_SUNKEN;
+ legendPtr->activeRelief = TK_RELIEF_FLAT;
+ legendPtr->entryBW = legendPtr->borderWidth = 2;
+ legendPtr->ipadX.side1 = legendPtr->ipadX.side2 = 1;
+ legendPtr->ipadY.side1 = legendPtr->ipadY.side2 = 1;
+ legendPtr->padX.side1 = legendPtr->padX.side2 = 1;
+ legendPtr->padY.side1 = legendPtr->padY.side2 = 1;
+ legendPtr->anchor = TK_ANCHOR_N;
+ legendPtr->site = LEGEND_RIGHT;
+ Blt_InitTextStyle(&(legendPtr->style));
+ legendPtr->style.justify = TK_JUSTIFY_LEFT;
+ legendPtr->style.anchor = TK_ANCHOR_NW;
+ legendPtr->bindTable = Blt_CreateBindingTable(graphPtr->interp,
+ graphPtr->tkwin, graphPtr, PickLegendEntry, Blt_GraphTags);
+
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ "legend", "Legend", configSpecs, 0, (char **)NULL,
+ (char *)legendPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureLegend(graphPtr, legendPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Find the legend entry from the given argument. The argument
+ * can be either a screen position "@x,y" or the name of an
+ * element.
+ *
+ * I don't know how useful it is to test with the name of an
+ * element.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ register Element *elemPtr;
+ Legend *legendPtr = graphPtr->legend;
+ int x, y;
+ char c;
+
+ if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+ return TCL_OK;
+ }
+ elemPtr = NULL;
+ c = argv[3][0];
+ if ((c == 'c') && (strcmp(argv[3], "current") == 0)) {
+ elemPtr = (Element *)Blt_GetCurrentItem(legendPtr->bindTable);
+ } else if ((c == '@') &&
+ (Blt_GetXY(interp, graphPtr->tkwin, argv[3], &x, &y) == TCL_OK)) {
+ elemPtr = (Element *)PickLegendEntry(graphPtr, x, y);
+ }
+ if (elemPtr != NULL) {
+ Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Activates a particular label in the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ActivateOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+ unsigned int active, redraw;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ register int i;
+
+ active = (argv[2][0] == 'a') ? LABEL_ACTIVE : 0;
+ redraw = 0;
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = Blt_GetHashValue(hPtr);
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(elemPtr->name, argv[i])) {
+ break;
+ }
+ }
+ if ((i < argc) && (active != (elemPtr->flags & LABEL_ACTIVE))) {
+ elemPtr->flags ^= LABEL_ACTIVE;
+ if (elemPtr->label != NULL) {
+ redraw++;
+ }
+ }
+ }
+ if ((redraw) && (!legendPtr->hidden)) {
+ /*
+ * See if how much we need to draw. If the graph is already
+ * schedule for a redraw, just make sure the right flags are
+ * set. Otherwise redraw only the legend: it's either in an
+ * external window or it's the only thing that need updating.
+ */
+ if (graphPtr->flags & REDRAW_PENDING) {
+ if (legendPtr->site & LEGEND_IN_PLOT) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ graphPtr->flags |= REDRAW_WORLD; /* Redraw entire graph. */
+ } else {
+ EventuallyRedrawLegend(legendPtr);
+ }
+ }
+ /* Return the names of all the active legend entries */
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = Blt_GetHashValue(hPtr);
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ Tcl_AppendElement(interp, elemPtr->name);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind index sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tagName;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.tagTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagName = Blt_GetHashKey(&(graphPtr->elements.tagTable), hPtr);
+ Tcl_AppendElement(interp, tagName);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, graphPtr->legend->bindTable,
+ Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries or resets options for the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr->legend, argv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets options for the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int flags = TK_CONFIG_ARGV_ONLY;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, (char *)NULL, flags);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, argv[3], flags);
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)legendPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureLegend(graphPtr, legendPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LegendOp --
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Legend is possibly redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec legendOps[] =
+{
+ {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
+ {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+ {"deactivate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
+ {"get", 1, (Blt_Op)GetOp, 4, 4, "index",},
+};
+static int nLegendOps = sizeof(legendOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_LegendOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nLegendOps, legendOps, BLT_OP_ARG2, argc, argv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, argc, argv);
+ return result;
+}
+
+int
+Blt_LegendSite(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->site;
+}
+
+int
+Blt_LegendWidth(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->width;
+}
+
+int
+Blt_LegendHeight(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->height;
+}
+
+int
+Blt_LegendIsHidden(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->hidden;
+}
+
+int
+Blt_LegendIsRaised(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->raised;
+}
+
+int
+Blt_LegendX(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->x;
+}
+
+int
+Blt_LegendY(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->y;
+}
+
+void
+Blt_LegendRemoveElement(legendPtr, elemPtr)
+ Legend *legendPtr;
+ Element *elemPtr;
+{
+ Blt_DeleteBindings(legendPtr->bindTable, elemPtr);
+}