summaryrefslogtreecommitdiffstats
path: root/src/bltGrLegd.C
diff options
context:
space:
mode:
Diffstat (limited to 'src/bltGrLegd.C')
-rw-r--r--src/bltGrLegd.C3047
1 files changed, 3047 insertions, 0 deletions
diff --git a/src/bltGrLegd.C b/src/bltGrLegd.C
new file mode 100644
index 0000000..9ed3169
--- /dev/null
+++ b/src/bltGrLegd.C
@@ -0,0 +1,3047 @@
+
+/*
+ * bltGrLegd.c --
+ *
+ * This module implements the legend for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+#include "bltGrElem.h"
+#include "bltPicture.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+/*
+ * Selection related flags:
+ *
+ * SELECT_EXPORT Export the selection to X11.
+ *
+ * SELECT_PENDING A "selection" command idle task is pending.
+ *
+ * SELECT_CLEAR Clear selection flag of entry.
+ *
+ * SELECT_SET Set selection flag of entry.
+ *
+ * SELECT_TOGGLE Toggle selection flag of entry.
+ *
+ * SELECT_MASK Mask of selection set/clear/toggle flags.
+ *
+ * SELECT_SORTED Indicates if the entries in the selection
+ * should be sorted or displayed in the order
+ * they were selected.
+ *
+ */
+
+#define SELECT_CLEAR (1<<16)
+#define SELECT_EXPORT (1<<17)
+#define SELECT_PENDING (1<<18)
+#define SELECT_SET (1<<19)
+#define SELECT_TOGGLE (SELECT_SET | SELECT_CLEAR)
+#define SELECT_MASK (SELECT_SET | SELECT_CLEAR)
+#define SELECT_SORTED (1<<20)
+
+#define RAISED (1<<21)
+
+#define SELECT_MODE_SINGLE (1<<0)
+#define SELECT_MODE_MULTIPLE (1<<1)
+
+typedef int (GraphLegendProc)(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+/*
+ * Legend --
+ *
+ * Contains information specific to how the legend will be displayed.
+ */
+struct _Legend {
+ unsigned int flags;
+ ClassId classId; /* Type: Element or Marker. */
+
+ int nEntries; /* Number of element entries in
+ * table. */
+ short int nColumns, nRows; /* Number of columns and rows in
+ * legend */
+ short int width, height; /* Dimensions of the legend */
+ short int entryWidth, entryHeight;
+
+ int site;
+ short int xReq, yReq; /* User-requested site of legend, not
+ * the final actual position. Used in
+ * conjunction with the anchor below
+ * to determine location of the
+ * legend. */
+
+ 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 ixPad, iyPad; /* # of pixels interior padding around
+ * legend entries */
+ Blt_Pad xPad, yPad; /* # of pixels padding to exterior of
+ * legend */
+ Tk_Window tkwin; /* If non-NULL, external window to draw
+ * legend. */
+ TextStyle style;
+
+ int maxSymSize; /* Size of largest symbol to be
+ * displayed. Used to calculate size
+ * of legend */
+ XColor *fgColor;
+ Blt_Background activeBg; /* Active legend entry background
+ * color. */
+ XColor *activeFgColor;
+ int activeRelief; /* 3-D effect on active entry. */
+ int entryBW; /* Border width around each entry in
+ * legend. */
+ Blt_Background normalBg; /* 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;
+
+ int selRelief;
+ int selBW;
+
+ XColor *selInFocusFgColor; /* Text color of a selected entry. */
+ XColor *selOutFocusFgColor;
+
+ Blt_Background selInFocusBg;
+ Blt_Background selOutFocusBg;
+
+ XColor *focusColor;
+ Blt_Dashes focusDashes; /* Dash on-off value. */
+ GC focusGC; /* Graphics context for the active
+ * label. */
+
+ const char *takeFocus;
+ int focus; /* Position of the focus entry. */
+
+ int cursorX, cursorY; /* Position of the insertion cursor in
+ * the textbox window. */
+ short int cursorWidth; /* Size of the insertion cursor
+ * symbol. */
+ short int cursorHeight;
+ Element *focusPtr; /* Element that currently has the
+ * focus. If NULL, no legend entry has
+ * the focus. */
+ Element *selAnchorPtr; /* Fixed end of selection. Used to
+ * extend the selection while
+ * maintaining the other end of the
+ * selection. */
+ Element *selMarkPtr;
+ Element *selFirstPtr; /* First element selected in current
+ * pick. */
+ Element *selLastPtr; /* Last element selected in current
+ * pick. */
+ int exportSelection;
+ int active;
+ int cursorOn; /* Indicates if the cursor is
+ * displayed. */
+ int onTime, offTime; /* Time in milliseconds to wait before
+ * changing the cursor from off-to-on
+ * and on-to-off. Setting offTime to 0
+ * makes the * cursor steady. */
+ Tcl_TimerToken timerToken; /* Handle for a timer event called
+ * periodically to blink the cursor. */
+ const char *selectCmd; /* TCL script that's invoked whenever
+ * the selection changes. */
+ int selectMode; /* Mode of selection: single or
+ * multiple. */
+ Blt_HashTable selectTable; /* Table of selected elements. Used to
+ * quickly determine whether an element
+ * is selected. */
+ Blt_Chain selected; /* List of selected elements. */
+
+ const char *title;
+ unsigned int titleWidth, titleHeight;
+ TextStyle titleStyle; /* Legend title attributes */
+};
+
+#define padLeft xPad.side1
+#define padRight xPad.side2
+#define padTop yPad.side1
+#define padBottom yPad.side2
+#define PADDING(x) ((x).side1 + (x).side2)
+#define LABEL_PAD 2
+
+#define DEF_LEGEND_ACTIVEBACKGROUND RGB_SKYBLUE4
+#define DEF_LEGEND_ACTIVEBORDERWIDTH "2"
+#define DEF_LEGEND_ACTIVEFOREGROUND RGB_WHITE
+#define DEF_LEGEND_ACTIVERELIEF "flat"
+#define DEF_LEGEND_ANCHOR "n"
+#define DEF_LEGEND_BACKGROUND (char *)NULL
+#define DEF_LEGEND_BORDERWIDTH STD_BORDERWIDTH
+#define DEF_LEGEND_COLUMNS "0"
+#define DEF_LEGEND_EXPORTSELECTION "no"
+#define DEF_LEGEND_FONT "{Sans Serif} 8"
+#define DEF_LEGEND_FOREGROUND STD_NORMAL_FOREGROUND
+#define DEF_LEGEND_HIDE "no"
+#define DEF_LEGEND_IPADX "1"
+#define DEF_LEGEND_IPADY "1"
+#define DEF_LEGEND_PADX "1"
+#define DEF_LEGEND_PADY "1"
+#define DEF_LEGEND_POSITION "rightmargin"
+#define DEF_LEGEND_RAISED "no"
+#define DEF_LEGEND_RELIEF "flat"
+#define DEF_LEGEND_ROWS "0"
+#define DEF_LEGEND_SELECTBACKGROUND RGB_SKYBLUE4
+#define DEF_LEGEND_SELECT_BG_MONO STD_SELECT_BG_MONO
+#define DEF_LEGEND_SELECTBORDERWIDTH "1"
+#define DEF_LEGEND_SELECTMODE "multiple"
+#define DEF_LEGEND_SELECT_FG_MONO STD_SELECT_FG_MONO
+#define DEF_LEGEND_SELECTFOREGROUND RGB_WHITE /*STD_SELECT_FOREGROUND*/
+#define DEF_LEGEND_SELECTRELIEF "flat"
+#define DEF_LEGEND_FOCUSDASHES "dot"
+#define DEF_LEGEND_FOCUSEDIT "no"
+#define DEF_LEGEND_FOCUSFOREGROUND STD_ACTIVE_FOREGROUND
+#define DEF_LEGEND_FOCUS_FG_MONO STD_ACTIVE_FG_MONO
+#define DEF_LEGEND_TAKEFOCUS "1"
+#define DEF_LEGEND_TITLE (char *)NULL
+#define DEF_LEGEND_TITLECOLOR STD_NORMAL_FOREGROUND
+#define DEF_LEGEND_TITLEFONT "{Sans Serif} 8 bold"
+
+static Blt_OptionParseProc ObjToPosition;
+static Blt_OptionPrintProc PositionToObj;
+static Blt_CustomOption legendPositionOption =
+{
+ ObjToPosition, PositionToObj, NULL, (ClientData)0
+};
+
+static Blt_OptionParseProc ObjToSelectmode;
+static Blt_OptionPrintProc SelectmodeToObj;
+static Blt_CustomOption selectmodeOption = {
+ ObjToSelectmode, SelectmodeToObj, NULL, NULL,
+};
+
+static Blt_ConfigSpec configSpecs[] =
+{
+ {BLT_CONFIG_BACKGROUND, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_LEGEND_ACTIVEBACKGROUND,
+ Blt_Offset(Legend, activeBg), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-activeborderwidth", "activeBorderWidth",
+ "BorderWidth", DEF_LEGEND_BORDERWIDTH,
+ Blt_Offset(Legend, entryBW), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_LEGEND_ACTIVEFOREGROUND,
+ Blt_Offset(Legend, activeFgColor), 0},
+ {BLT_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
+ DEF_LEGEND_ACTIVERELIEF, Blt_Offset(Legend, activeRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_LEGEND_ANCHOR,
+ Blt_Offset(Legend, anchor), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_BACKGROUND, "-background", "background", "Background",
+ DEF_LEGEND_BACKGROUND, Blt_Offset(Legend, normalBg),BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS_NNEG, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_LEGEND_BORDERWIDTH, Blt_Offset(Legend, borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,0},
+ {BLT_CONFIG_INT_NNEG, "-columns", "columns", "columns",
+ DEF_LEGEND_COLUMNS, Blt_Offset(Legend, reqColumns),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-exportselection", "exportSelection",
+ "ExportSelection", DEF_LEGEND_EXPORTSELECTION,
+ Blt_Offset(Legend, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)SELECT_EXPORT},
+ {BLT_CONFIG_DASHES, "-focusdashes", "focusDashes", "FocusDashes",
+ DEF_LEGEND_FOCUSDASHES, Blt_Offset(Legend, focusDashes),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-focusforeground", "focusForeground", "FocusForeground",
+ DEF_LEGEND_FOCUSFOREGROUND, Blt_Offset(Legend, focusColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-focusforeground", "focusForeground", "FocusForeground",
+ DEF_LEGEND_FOCUS_FG_MONO, Blt_Offset(Legend, focusColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_FONT, "-font", "font", "Font", DEF_LEGEND_FONT,
+ Blt_Offset(Legend, style.font), 0},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_LEGEND_FOREGROUND, Blt_Offset(Legend, fgColor), 0},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_LEGEND_HIDE,
+ Blt_Offset(Legend, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_PAD, "-ipadx", "iPadX", "Pad", DEF_LEGEND_IPADX,
+ Blt_Offset(Legend, ixPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-ipady", "iPadY", "Pad", DEF_LEGEND_IPADY,
+ Blt_Offset(Legend, iyPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BACKGROUND, "-nofocusselectbackground",
+ "noFocusSelectBackground", "NoFocusSelectBackground",
+ DEF_LEGEND_SELECTBACKGROUND, Blt_Offset(Legend, selOutFocusBg), 0},
+ {BLT_CONFIG_COLOR, "-nofocusselectforeground", "noFocusSelectForeground",
+ "NoFocusSelectForeground", DEF_LEGEND_SELECTFOREGROUND,
+ Blt_Offset(Legend, selOutFocusFgColor), 0},
+ {BLT_CONFIG_PAD, "-padx", "padX", "Pad", DEF_LEGEND_PADX,
+ Blt_Offset(Legend, xPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-pady", "padY", "Pad", DEF_LEGEND_PADY,
+ Blt_Offset(Legend, yPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-position", "position", "Position",
+ DEF_LEGEND_POSITION, 0, BLT_CONFIG_DONT_SET_DEFAULT,
+ &legendPositionOption},
+ {BLT_CONFIG_BITMASK, "-raised", "raised", "Raised", DEF_LEGEND_RAISED,
+ Blt_Offset(Legend, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)RAISED},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_LEGEND_RELIEF,
+ Blt_Offset(Legend, relief), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_INT_NNEG, "-rows", "rows", "rows", DEF_LEGEND_ROWS,
+ Blt_Offset(Legend, reqRows),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BACKGROUND, "-selectbackground", "selectBackground",
+ "Background", DEF_LEGEND_SELECTBACKGROUND,
+ Blt_Offset(Legend, selInFocusBg), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-selectborderwidth", "selectBorderWidth",
+ "BorderWidth", DEF_LEGEND_SELECTBORDERWIDTH,
+ Blt_Offset(Legend, selBW), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+ (char *)NULL, Blt_Offset(Legend, selectCmd), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_LEGEND_SELECT_FG_MONO, Blt_Offset(Legend, selInFocusFgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_LEGEND_SELECTFOREGROUND, Blt_Offset(Legend, selInFocusFgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_CUSTOM, "-selectmode", "selectMode", "SelectMode",
+ DEF_LEGEND_SELECTMODE, Blt_Offset(Legend, selectMode),
+ BLT_CONFIG_DONT_SET_DEFAULT, &selectmodeOption},
+ {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
+ DEF_LEGEND_SELECTRELIEF, Blt_Offset(Legend, selRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_LEGEND_TAKEFOCUS, Blt_Offset(Legend, takeFocus),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-title", "title", "Title", DEF_LEGEND_TITLE,
+ Blt_Offset(Legend, title), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-titlecolor", "titleColor", "Foreground",
+ DEF_LEGEND_TITLECOLOR, Blt_Offset(Legend, titleStyle.color), 0},
+ {BLT_CONFIG_FONT, "-titlefont", "titleFont", "Font",
+ DEF_LEGEND_TITLEFONT, Blt_Offset(Legend, titleStyle.font), 0},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tcl_IdleProc DisplayLegend;
+static Blt_BindPickProc PickEntryProc;
+static Tk_EventProc LegendEventProc;
+static Tcl_TimerProc BlinkCursorProc;
+static Tk_LostSelProc LostSelectionProc;
+static Tk_SelectionProc SelectionProc;
+
+BLT_EXTERN Tcl_ObjCmdProc Blt_GraphInstCmdProc;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Legend_EventuallyRedraw --
+ *
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Legend_EventuallyRedraw(Graph *graphPtr)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if ((legendPtr->tkwin != NULL) && !(legendPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayLegend, legendPtr);
+ legendPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectCmdProc --
+ *
+ * Invoked at the next idle point whenever the current selection changes.
+ * Executes some application-specific code in the -selectcommand option.
+ * This provides a way for applications to handle selection changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TCL code gets executed for some application-specific task.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+SelectCmdProc(ClientData clientData)
+{
+ Legend *legendPtr = clientData;
+
+ Tcl_Preserve(legendPtr);
+ legendPtr->flags &= ~SELECT_PENDING;
+ if (legendPtr->selectCmd != NULL) {
+ Tcl_Interp *interp;
+
+ interp = legendPtr->graphPtr->interp;
+ if (Tcl_GlobalEval(interp, legendPtr->selectCmd) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ Tcl_Release(legendPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * EventuallyInvokeSelectCmd --
+ *
+ * Queues a request to execute the -selectcommand code associated with
+ * the widget at the next idle point. Invoked whenever the selection
+ * changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TCL code gets executed for some application-specific task.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+EventuallyInvokeSelectCmd(Legend *legendPtr)
+{
+ if ((legendPtr->flags & SELECT_PENDING) == 0) {
+ legendPtr->flags |= SELECT_PENDING;
+ Tcl_DoWhenIdle(SelectCmdProc, legendPtr);
+ }
+}
+
+static void
+ClearSelection(Legend *legendPtr)
+{
+ Blt_DeleteHashTable(&legendPtr->selectTable);
+ Blt_InitHashTable(&legendPtr->selectTable, BLT_ONE_WORD_KEYS);
+ Blt_Chain_Reset(legendPtr->selected);
+ Blt_Legend_EventuallyRedraw(legendPtr->graphPtr);
+ if (legendPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(legendPtr);
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LostSelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is grabbed away
+ * from a Text widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The existing selection is unhighlighted, and the window is marked as
+ * not containing a selection.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+LostSelectionProc(ClientData clientData)
+{
+ Legend *legendPtr = clientData;
+
+ if (legendPtr->flags & SELECT_EXPORT) {
+ ClearSelection(legendPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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 clientData, XEvent *eventPtr)
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+ if (eventPtr->xfocus.detail == NotifyInferior) {
+ return;
+ }
+ if (eventPtr->type == FocusIn) {
+ legendPtr->flags |= FOCUS;
+ } else {
+ legendPtr->flags &= ~FOCUS;
+ }
+ Tcl_DeleteTimerHandler(legendPtr->timerToken);
+ if ((legendPtr->active) && (legendPtr->flags & FOCUS)) {
+ legendPtr->cursorOn = TRUE;
+ if (legendPtr->offTime != 0) {
+ legendPtr->timerToken = Tcl_CreateTimerHandler(
+ legendPtr->onTime, BlinkCursorProc, graphPtr);
+ }
+ } else {
+ legendPtr->cursorOn = FALSE;
+ legendPtr->timerToken = (Tcl_TimerToken)NULL;
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ } else if (eventPtr->type == DestroyNotify) {
+ Graph *graphPtr = legendPtr->graphPtr;
+
+ if (legendPtr->site == LEGEND_WINDOW) {
+ 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;
+ legendPtr->flags |= HIDE;
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_MoveBindingTable(legendPtr->bindTable, graphPtr->tkwin);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ } else if (eventPtr->type == ConfigureNotify) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+}
+
+static int
+CreateLegendWindow(Tcl_Interp *interp, Legend *legendPtr, const char *pathName)
+{
+ Graph *graphPtr;
+ Tk_Window tkwin;
+
+ graphPtr = legendPtr->graphPtr;
+ tkwin = Tk_CreateWindowFromPath(interp, graphPtr->tkwin, pathName, NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_SetWindowInstanceData(tkwin, legendPtr);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ LegendEventProc, graphPtr);
+ /* Move the legend's binding table to the new window. */
+ Blt_MoveBindingTable(legendPtr->bindTable, tkwin);
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ Tk_DestroyWindow(legendPtr->tkwin);
+ }
+ /* Create a command by the same name as the legend window so that Legend
+ * bindings can use %W interchangably. */
+ legendPtr->cmdToken = Tcl_CreateObjCommand(interp, pathName,
+ Blt_GraphInstCmdProc, graphPtr, NULL);
+ legendPtr->tkwin = tkwin;
+ legendPtr->site = LEGEND_WINDOW;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToPosition --
+ *
+ * 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
+ObjToPosition(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* New legend position string */
+ char *widgRec, /* Widget record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Graph *graphPtr;
+ Legend *legendPtr = (Legend *)widgRec;
+ char c;
+ int length;
+ const char *string;
+
+ graphPtr = legendPtr->graphPtr;
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if (c == '\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->xReq = x;
+ legendPtr->yReq = y;
+ legendPtr->site = LEGEND_XY;
+ } else if (c == '.') {
+ if (CreateLegendWindow(interp, legendPtr, string) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ } else {
+ Tcl_AppendResult(interp, "bad position \"", string, "\": should be \
+\"leftmargin\", \"rightmargin\", \"topmargin\", \"bottommargin\", \
+\"plotarea\", windowName or @x,y", (char *)NULL);
+ return TCL_ERROR;
+ }
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PositionToObj --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+PositionToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Legend *legendPtr = (Legend *)widgRec;
+ Tcl_Obj *objPtr;
+
+ switch (legendPtr->site) {
+ case LEGEND_LEFT:
+ objPtr = Tcl_NewStringObj("leftmargin", -1);
+ break;
+
+ case LEGEND_RIGHT:
+ objPtr = Tcl_NewStringObj("rightmargin", -1);
+ break;
+
+ case LEGEND_TOP:
+ objPtr = Tcl_NewStringObj("topmargin", -1);
+ break;
+
+ case LEGEND_BOTTOM:
+ objPtr = Tcl_NewStringObj("bottommargin", -1);
+ break;
+
+ case LEGEND_PLOT:
+ objPtr = Tcl_NewStringObj("plotarea", -1);
+ break;
+
+ case LEGEND_WINDOW:
+ objPtr = Tcl_NewStringObj(Tk_PathName(legendPtr->tkwin), -1);
+ break;
+
+ case LEGEND_XY:
+ {
+ char string[200];
+
+ sprintf_s(string, 200, "@%d,%d", legendPtr->xReq, legendPtr->yReq);
+ objPtr = Tcl_NewStringObj(string, -1);
+ }
+ default:
+ objPtr = Tcl_NewStringObj("unknown legend position", -1);
+ }
+ return objPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToSelectmode --
+ *
+ * Convert the string reprsenting a select mode, to its numeric form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSelectmode(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
+ char *widgRec,
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ char *string;
+ char c;
+ int *modePtr = (int *)(widgRec + offset);
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ if ((c == 's') && (strcmp(string, "single") == 0)) {
+ *modePtr = SELECT_MODE_SINGLE;
+ } else if ((c == 'm') && (strcmp(string, "multiple") == 0)) {
+ *modePtr = SELECT_MODE_MULTIPLE;
+ } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
+ *modePtr = SELECT_MODE_SINGLE;
+ } else {
+ Tcl_AppendResult(interp, "bad select mode \"", string,
+ "\": should be \"single\" or \"multiple\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectmodeToObj --
+ *
+ * Results:
+ * The string representation of the select mode is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SelectmodeToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec,
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ int mode = *(int *)(widgRec + offset);
+
+ switch (mode) {
+ case SELECT_MODE_SINGLE:
+ return Tcl_NewStringObj("single", -1);
+ case SELECT_MODE_MULTIPLE:
+ return Tcl_NewStringObj("multiple", -1);
+ default:
+ return Tcl_NewStringObj("unknown scroll mode", -1);
+ }
+}
+
+
+static void
+SetLegendOrigin(Legend *legendPtr)
+{
+ Graph *graphPtr;
+ int x, y, w, h;
+
+ graphPtr = legendPtr->graphPtr;
+ x = y = w = h = 0; /* Suppress compiler warning. */
+ switch (legendPtr->site) {
+ case LEGEND_RIGHT:
+ w = graphPtr->rightMargin.width - graphPtr->rightMargin.axesOffset;
+ h = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->right + graphPtr->rightMargin.axesOffset;
+ y = graphPtr->top;
+ break;
+
+ case LEGEND_LEFT:
+ w = graphPtr->leftMargin.width - graphPtr->leftMargin.axesOffset;
+ h = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->inset;
+ y = graphPtr->top;
+ break;
+
+ case LEGEND_TOP:
+ w = graphPtr->right - graphPtr->left;
+ h = graphPtr->topMargin.height - graphPtr->topMargin.axesOffset;
+ if (graphPtr->title != NULL) {
+ h -= graphPtr->titleHeight;
+ }
+ x = graphPtr->left;
+ y = graphPtr->inset;
+ if (graphPtr->title != NULL) {
+ y += graphPtr->titleHeight;
+ }
+ break;
+
+ case LEGEND_BOTTOM:
+ w = graphPtr->right - graphPtr->left;
+ h = graphPtr->bottomMargin.height - graphPtr->bottomMargin.axesOffset;
+ x = graphPtr->left;
+ y = graphPtr->bottom + graphPtr->bottomMargin.axesOffset;
+ break;
+
+ case LEGEND_PLOT:
+ w = graphPtr->right - graphPtr->left;
+ h = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->left;
+ y = graphPtr->top;
+ break;
+
+ case LEGEND_XY:
+ w = legendPtr->width;
+ h = legendPtr->height;
+ x = legendPtr->xReq;
+ y = legendPtr->yReq;
+ 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;
+ }
+
+ switch (legendPtr->anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height) / 2;
+ }
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height);
+ }
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ if (w > legendPtr->width) {
+ x += (w - legendPtr->width) / 2;
+ }
+ break;
+ case TK_ANCHOR_CENTER: /* Center */
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height) / 2;
+ }
+ if (w > legendPtr->width) {
+ x += (w - legendPtr->width) / 2;
+ }
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ if (w > legendPtr->width) {
+ x += (w - legendPtr->width) / 2;
+ }
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height);
+ }
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ if (w > legendPtr->width) {
+ x += w - legendPtr->width;
+ }
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ if (w > legendPtr->width) {
+ x += w - legendPtr->width;
+ }
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height) / 2;
+ }
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ if (w > legendPtr->width) {
+ x += w - legendPtr->width;
+ }
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height);
+ }
+ break;
+ }
+ legendPtr->x = x + legendPtr->padLeft;
+ legendPtr->y = y + legendPtr->padTop;
+}
+
+static int
+EntryIsSelected(Legend *legendPtr, Element *elemPtr)
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&legendPtr->selectTable, (char *)elemPtr);
+ return (hPtr != NULL);
+}
+
+static void
+SelectElement(Legend *legendPtr, Element *elemPtr)
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_CreateHashEntry(&legendPtr->selectTable, (char *)elemPtr,&isNew);
+ if (isNew) {
+ Blt_ChainLink link;
+
+ link = Blt_Chain_Append(legendPtr->selected, elemPtr);
+ Blt_SetHashValue(hPtr, link);
+ }
+}
+
+static void
+DeselectElement(Legend *legendPtr, Element *elemPtr)
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&legendPtr->selectTable, (char *)elemPtr);
+ if (hPtr != NULL) {
+ Blt_ChainLink link;
+
+ link = Blt_GetHashValue(hPtr);
+ Blt_Chain_DeleteLink(legendPtr->selected, link);
+ Blt_DeleteHashEntry(&legendPtr->selectTable, hPtr);
+ }
+}
+
+static void
+SelectEntry(Legend *legendPtr, Element *elemPtr)
+{
+ Blt_HashEntry *hPtr;
+
+ switch (legendPtr->flags & SELECT_MASK) {
+ case SELECT_CLEAR:
+ DeselectElement(legendPtr, elemPtr);
+ break;
+
+ case SELECT_SET:
+ SelectElement(legendPtr, elemPtr);
+ break;
+
+ case SELECT_TOGGLE:
+ hPtr = Blt_FindHashEntry(&legendPtr->selectTable, (char *)elemPtr);
+ if (hPtr != NULL) {
+ DeselectElement(legendPtr, elemPtr);
+ } else {
+ SelectElement(legendPtr, elemPtr);
+ }
+ break;
+ }
+}
+
+#ifdef notdef
+static Element *
+PointerToElement(Legend *legendPtr, int x, int y)
+{
+ Graph *graphPtr = legendPtr->graphPtr;
+ int w, h;
+ int n;
+
+ w = legendPtr->width;
+ h = legendPtr->height;
+
+ x -= legendPtr->x + legendPtr->borderWidth;
+ y -= legendPtr->y + legendPtr->borderWidth;
+ w -= 2 * legendPtr->borderWidth + PADDING(legendPtr->xPad);
+ h -= 2 * legendPtr->borderWidth + PADDING(legendPtr->yPad);
+
+ if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) {
+ return NULL;
+ }
+
+ /* It's in the bounding box, so compute the index. */
+ {
+ int row, column;
+
+ row = y / legendPtr->entryHeight;
+ column = x / legendPtr->entryWidth;
+ n = (column * legendPtr->nRows) + row;
+ }
+ if (n < legendPtr->nEntries) {
+ Blt_ChainLink link;
+ int count;
+
+ count = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if (count > n) {
+ return NULL;
+ } else if (count == n) {
+ return elemPtr;
+ }
+ count++;
+ }
+ }
+ return NULL;
+}
+#endif
+
+/*ARGSUSED*/
+static ClientData
+PickEntryProc(ClientData clientData, int x, int y, ClientData *contextPtr)
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+ int w, h;
+
+ legendPtr = graphPtr->legend;
+ w = legendPtr->width;
+ h = legendPtr->height;
+
+ if (legendPtr->titleHeight > 0) {
+ y -= legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+ x -= legendPtr->x + legendPtr->borderWidth;
+ y -= legendPtr->y + legendPtr->borderWidth;
+ w -= 2 * legendPtr->borderWidth + PADDING(legendPtr->xPad);
+ h -= 2 * legendPtr->borderWidth + PADDING(legendPtr->yPad);
+
+ if ((x >= 0) && (x < w) && (y >= 0) && (y < h)) {
+ int row, column;
+ int n;
+
+ /*
+ * It's in the bounding box, so compute the index.
+ */
+ row = y / legendPtr->entryHeight;
+ column = x / legendPtr->entryWidth;
+ n = (column * legendPtr->nRows) + row;
+ if (n < legendPtr->nEntries) {
+ Blt_ChainLink link;
+ int count;
+
+ /* Legend entries are stored in bottom-to-top. */
+ count = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label != NULL) {
+ if (count == n) {
+ return elemPtr;
+ }
+ count++;
+ }
+ }
+ if (link != NULL) {
+ return Blt_Chain_GetValue(link);
+ }
+ }
+ }
+ 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
+ * entryHeight - height of each entry
+ * entryWidth - width of each entry
+ * height - width of legend (includes borders and padding)
+ * width - height of legend (includes borders and padding)
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_MapLegend(
+ Graph *graphPtr,
+ int plotWidth, /* Maximum width available in window
+ * to draw the legend. Will calculate
+ * # of columns from this. */
+ int plotHeight) /* Maximum height available in window
+ * to draw the legend. Will calculate
+ * # of rows from this. */
+{
+ Legend *legendPtr = graphPtr->legend;
+ Blt_ChainLink link;
+ int nRows, nColumns, nEntries;
+ int lw, lh;
+ int maxWidth, maxHeight;
+ int symbolWidth;
+ Blt_FontMetrics fontMetrics;
+
+ /* Initialize legend values to default (no legend displayed) */
+ legendPtr->entryWidth = legendPtr->entryHeight = 0;
+ legendPtr->nRows = legendPtr->nColumns = 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);
+ }
+ }
+ Blt_Ts_GetExtents(&legendPtr->titleStyle, legendPtr->title,
+ &legendPtr->titleWidth, &legendPtr->titleHeight);
+ /*
+ * Count the number of legend entries and determine the widest and tallest
+ * label. The number of entries would normally be the number of elements,
+ * but elements can have no legend entry (-label "").
+ */
+ nEntries = 0;
+ maxWidth = maxHeight = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ unsigned int w, h;
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue; /* Element has no legend entry. */
+ }
+ Blt_Ts_GetExtents(&legendPtr->style, elemPtr->label, &w, &h);
+ if (maxWidth < w) {
+ maxWidth = w;
+ }
+ if (maxHeight < h) {
+ maxHeight = h;
+ }
+ nEntries++;
+ }
+ if (nEntries == 0) {
+ return; /* No visible legend entries. */
+ }
+
+
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolWidth = 2 * fontMetrics.ascent;
+
+ maxWidth += 2 * legendPtr->entryBW + PADDING(legendPtr->ixPad) +
+ + symbolWidth + 3 * LABEL_PAD;
+
+ maxHeight += 2 * legendPtr->entryBW + PADDING(legendPtr->iyPad);
+
+ maxWidth |= 0x01;
+ maxHeight |= 0x01;
+
+ lw = plotWidth - 2 * legendPtr->borderWidth - PADDING(legendPtr->xPad);
+ lh = plotHeight - 2 * legendPtr->borderWidth - PADDING(legendPtr->yPad);
+
+ /*
+ * The number of rows and columns is computed as one of the following:
+ *
+ * both options set User defined.
+ * -rows Compute columns from rows.
+ * -columns Compute rows from columns.
+ * neither set Compute rows and columns from
+ * size of plot.
+ */
+ if (legendPtr->reqRows > 0) {
+ nRows = MIN(legendPtr->reqRows, nEntries);
+ if (legendPtr->reqColumns > 0) {
+ nColumns = MIN(legendPtr->reqColumns, nEntries);
+ } else {
+ nColumns = ((nEntries - 1) / nRows) + 1; /* Only -rows. */
+ }
+ } else if (legendPtr->reqColumns > 0) { /* Only -columns. */
+ nColumns = MIN(legendPtr->reqColumns, nEntries);
+ nRows = ((nEntries - 1) / nColumns) + 1;
+ } else {
+ /* Compute # of rows and columns from the legend size. */
+ nRows = lh / maxHeight;
+ nColumns = lw / maxWidth;
+ if (nRows < 1) {
+ nRows = nEntries;
+ }
+ if (nColumns < 1) {
+ nColumns = nEntries;
+ }
+ if (nRows > nEntries) {
+ nRows = nEntries;
+ }
+ switch (legendPtr->site) {
+ case LEGEND_TOP:
+ case LEGEND_BOTTOM:
+ nRows = ((nEntries - 1) / nColumns) + 1;
+ break;
+ case LEGEND_LEFT:
+ case LEGEND_RIGHT:
+ default:
+ nColumns = ((nEntries - 1) / nRows) + 1;
+ break;
+ }
+ }
+ if (nColumns < 1) {
+ nColumns = 1;
+ }
+ if (nRows < 1) {
+ nRows = 1;
+ }
+
+ lh = (nRows * maxHeight);
+ if (legendPtr->titleHeight > 0) {
+ lh += legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+ lw = nColumns * maxWidth;
+ if (lw < legendPtr->titleWidth) {
+ lw = legendPtr->titleWidth;
+ }
+ legendPtr->width = lw + 2 * legendPtr->borderWidth +
+ PADDING(legendPtr->xPad);
+ legendPtr->height = lh + 2 * legendPtr->borderWidth +
+ PADDING(legendPtr->yPad);
+ legendPtr->nRows = nRows;
+ legendPtr->nColumns = nColumns;
+ legendPtr->nEntries = nEntries;
+ legendPtr->entryHeight = maxHeight;
+ legendPtr->entryWidth = maxWidth;
+
+ {
+ int row, col, count;
+
+ row = col = count = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ count++;
+ elemPtr->row = row;
+ elemPtr->col = col;
+ row++;
+ if ((count % nRows) == 0) {
+ col++;
+ row = 0;
+ }
+ }
+ }
+ if ((legendPtr->site == LEGEND_WINDOW) &&
+ ((Tk_ReqWidth(legendPtr->tkwin) != legendPtr->width) ||
+ (Tk_ReqHeight(legendPtr->tkwin) != legendPtr->height))) {
+ Tk_GeometryRequest(legendPtr->tkwin,legendPtr->width,legendPtr->height);
+ }
+}
+
+void
+Blt_DrawLegend(Graph *graphPtr, Drawable drawable)
+{
+ Blt_Background bg;
+ Blt_ChainLink link;
+ Blt_FontMetrics fontMetrics;
+ Legend *legendPtr = graphPtr->legend;
+ Pixmap pixmap;
+ Tk_Window tkwin;
+ int count;
+ int symbolSize, xMid, yMid;
+ int x, y, w, h;
+ int xLabel, yStart, xSymbol, ySymbol;
+
+ if ((legendPtr->flags & HIDE) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+
+ SetLegendOrigin(legendPtr);
+ graphPtr = legendPtr->graphPtr;
+ tkwin = legendPtr->tkwin;
+ if (legendPtr->site == LEGEND_WINDOW) {
+ w = Tk_Width(tkwin);
+ h = Tk_Height(tkwin);
+ } else {
+ w = legendPtr->width;
+ h = legendPtr->height;
+ }
+
+ pixmap = Tk_GetPixmap(graphPtr->display, Tk_WindowId(tkwin), w, h,
+ Tk_Depth(tkwin));
+
+ if (legendPtr->normalBg != NULL) {
+ Blt_FillBackgroundRectangle(tkwin, pixmap, legendPtr->normalBg, 0, 0,
+ w, h, 0, TK_RELIEF_FLAT);
+ } else if (legendPtr->site & LEGEND_PLOTAREA_MASK) {
+ /*
+ * 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->cache != None) {
+ XCopyArea(graphPtr->display, graphPtr->cache, pixmap,
+ graphPtr->drawGC, legendPtr->x, legendPtr->y, w, h, 0, 0);
+ } else {
+ Blt_FillBackgroundRectangle(tkwin, pixmap, graphPtr->plotBg, 0, 0,
+ w, h, TK_RELIEF_FLAT, 0);
+ }
+ } else {
+ int xOrigin, yOrigin;
+ /*
+ * The legend is located in one of the margins or the external window.
+ */
+ Blt_GetBackgroundOrigin(graphPtr->normalBg, &xOrigin, &yOrigin);
+ Blt_SetBackgroundOrigin(graphPtr->tkwin, graphPtr->normalBg,
+ xOrigin - legendPtr->x,yOrigin - legendPtr->y);
+ Blt_FillBackgroundRectangle(tkwin, pixmap, graphPtr->normalBg, 0, 0,
+ w, h, 0, TK_RELIEF_FLAT);
+ Blt_SetBackgroundOrigin(tkwin, graphPtr->normalBg, xOrigin, yOrigin);
+ }
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+
+ symbolSize = fontMetrics.ascent;
+ xMid = symbolSize + 1 + legendPtr->entryBW;
+ yMid = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ xLabel = 2 * symbolSize + legendPtr->entryBW +
+ legendPtr->ixPad.side1 + 2 * LABEL_PAD;
+ ySymbol = yMid + legendPtr->iyPad.side1;
+ xSymbol = xMid + LABEL_PAD;
+
+ x = legendPtr->padLeft + legendPtr->borderWidth;
+ y = legendPtr->padTop + legendPtr->borderWidth;
+ Blt_DrawText(tkwin, pixmap, legendPtr->title, &legendPtr->titleStyle, x, y);
+ if (legendPtr->titleHeight > 0) {
+ y += legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+ count = 0;
+ yStart = y;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+ int isSelected;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this entry */
+ }
+ isSelected = EntryIsSelected(legendPtr, elemPtr);
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ int xOrigin, yOrigin;
+
+ Blt_GetBackgroundOrigin(legendPtr->activeBg, &xOrigin, &yOrigin);
+ Blt_SetBackgroundOrigin(tkwin, legendPtr->activeBg,
+ xOrigin - legendPtr->x, yOrigin - legendPtr->y);
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->activeFgColor);
+ Blt_FillBackgroundRectangle(tkwin, pixmap, legendPtr->activeBg,
+ x, y, legendPtr->entryWidth, legendPtr->entryHeight,
+ legendPtr->entryBW, legendPtr->activeRelief);
+ Blt_SetBackgroundOrigin(tkwin, legendPtr->activeBg,
+ xOrigin, yOrigin);
+ } else if (isSelected) {
+ int xOrigin, yOrigin;
+ Blt_Background bg;
+ XColor *fg;
+
+ fg = (legendPtr->flags & FOCUS) ?
+ legendPtr->selInFocusFgColor : legendPtr->selOutFocusFgColor;
+ bg = (legendPtr->flags & FOCUS) ?
+ legendPtr->selInFocusBg : legendPtr->selOutFocusBg;
+ Blt_GetBackgroundOrigin(bg, &xOrigin, &yOrigin);
+ Blt_SetBackgroundOrigin(tkwin, bg, xOrigin - legendPtr->x,
+ yOrigin - legendPtr->y);
+ Blt_Ts_SetForeground(legendPtr->style, fg);
+ Blt_FillBackgroundRectangle(tkwin, pixmap, bg, x, y,
+ legendPtr->entryWidth, legendPtr->entryHeight,
+ legendPtr->selBW, legendPtr->selRelief);
+ Blt_SetBackgroundOrigin(tkwin, bg, xOrigin, yOrigin);
+ } else {
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->fgColor);
+ if (elemPtr->legendRelief != TK_RELIEF_FLAT) {
+ Blt_FillBackgroundRectangle(tkwin, pixmap, graphPtr->normalBg,
+ x, y, legendPtr->entryWidth,
+ legendPtr->entryHeight, legendPtr->entryBW,
+ elemPtr->legendRelief);
+ }
+ }
+ (*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr,
+ x + xSymbol, y + ySymbol, symbolSize);
+ Blt_DrawText(tkwin, pixmap, elemPtr->label, &legendPtr->style,
+ x + xLabel,
+ y + legendPtr->entryBW + legendPtr->iyPad.side1);
+ count++;
+ if (legendPtr->focusPtr == elemPtr) { /* Focus outline */
+ if (isSelected) {
+ XColor *color;
+
+ color = (legendPtr->flags & FOCUS) ?
+ legendPtr->selInFocusFgColor :
+ legendPtr->selOutFocusFgColor;
+ XSetForeground(graphPtr->display, legendPtr->focusGC,
+ color->pixel);
+ }
+ XDrawRectangle(graphPtr->display, pixmap, legendPtr->focusGC,
+ x + 1, y + 1, legendPtr->entryWidth - 3,
+ legendPtr->entryHeight - 3);
+ if (isSelected) {
+ XSetForeground(graphPtr->display, legendPtr->focusGC,
+ legendPtr->focusColor->pixel);
+ }
+ }
+ /* Check when to move to the next column */
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->entryHeight;
+ } else {
+ x += legendPtr->entryWidth;
+ y = yStart;
+ }
+ }
+ /*
+ * Draw the border and/or background of the legend.
+ */
+ bg = legendPtr->normalBg;
+ if (bg == NULL) {
+ bg = graphPtr->normalBg;
+ }
+ /* Disable crosshairs before redisplaying to the screen */
+ if (legendPtr->site & LEGEND_PLOTAREA_MASK) {
+ Blt_DisableCrosshairs(graphPtr);
+ }
+ Blt_DrawBackgroundRectangle(tkwin, pixmap, bg, 0, 0, w, h,
+ legendPtr->borderWidth, legendPtr->relief);
+ XCopyArea(graphPtr->display, pixmap, drawable, graphPtr->drawGC, 0, 0, w, h,
+ legendPtr->x, legendPtr->y);
+ if (legendPtr->site & LEGEND_PLOTAREA_MASK) {
+ Blt_EnableCrosshairs(graphPtr);
+ }
+ Tk_FreePixmap(graphPtr->display, pixmap);
+ graphPtr->flags &= ~DRAW_LEGEND;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_LegendToPostScript --
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_LegendToPostScript(Graph *graphPtr, Blt_Ps ps)
+{
+ Legend *legendPtr = graphPtr->legend;
+ double x, y, yStart;
+ int xLabel, xSymbol, ySymbol;
+ int count;
+ Blt_ChainLink link;
+ int symbolSize, xMid, yMid;
+ int width, height;
+ Blt_FontMetrics fontMetrics;
+
+ if ((legendPtr->flags & HIDE) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+ SetLegendOrigin(legendPtr);
+
+ x = legendPtr->x, y = legendPtr->y;
+ width = legendPtr->width - PADDING(legendPtr->xPad);
+ height = legendPtr->height - PADDING(legendPtr->yPad);
+
+ Blt_Ps_Append(ps, "% Legend\n");
+ graphPtr = legendPtr->graphPtr;
+ if (graphPtr->pageSetup->flags & PS_DECORATIONS) {
+ if (legendPtr->normalBg != NULL) {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(legendPtr->normalBg);
+ Blt_Ps_Fill3DRectangle(ps, border, x, y, width, height,
+ legendPtr->borderWidth, legendPtr->relief);
+ } else {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(graphPtr->normalBg);
+ Blt_Ps_Draw3DRectangle(ps, border, x, y, width, height,
+ legendPtr->borderWidth, legendPtr->relief);
+ }
+ } else {
+ Blt_Ps_SetClearBackground(ps);
+ Blt_Ps_XFillRectangle(ps, x, y, width, height);
+ }
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolSize = fontMetrics.ascent;
+ xMid = symbolSize + 1 + legendPtr->entryBW;
+ yMid = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ xLabel = 2 * symbolSize + legendPtr->entryBW + legendPtr->ixPad.side1 + 5;
+ xSymbol = xMid + legendPtr->ixPad.side1;
+ ySymbol = yMid + legendPtr->iyPad.side1;
+
+ x += legendPtr->borderWidth;
+ y += legendPtr->borderWidth;
+ Blt_Ps_DrawText(ps, legendPtr->title, &legendPtr->titleStyle, x, y);
+ if (legendPtr->titleHeight > 0) {
+ y += legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+
+ count = 0;
+ yStart = y;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this label */
+ }
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(legendPtr->activeBg);
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->activeFgColor);
+ Blt_Ps_Fill3DRectangle(ps, border, x, y, legendPtr->entryWidth,
+ legendPtr->entryHeight, legendPtr->entryBW,
+ legendPtr->activeRelief);
+ } else {
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->fgColor);
+ if (elemPtr->legendRelief != TK_RELIEF_FLAT) {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(graphPtr->normalBg);
+ Blt_Ps_Draw3DRectangle(ps, border, x, y, legendPtr->entryWidth,
+ legendPtr->entryHeight, legendPtr->entryBW,
+ elemPtr->legendRelief);
+ }
+ }
+ (*elemPtr->procsPtr->printSymbolProc) (graphPtr, ps, elemPtr,
+ x + xSymbol, y + ySymbol, symbolSize);
+ Blt_Ps_DrawText(ps, elemPtr->label, &legendPtr->style,
+ x + xLabel, y + legendPtr->entryBW + legendPtr->iyPad.side1);
+ count++;
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->entryHeight;
+ } else {
+ x += legendPtr->entryWidth;
+ y = yStart;
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DisplayLegend --
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DisplayLegend(ClientData clientData)
+{
+ Legend *legendPtr = clientData;
+ Graph *graphPtr;
+
+ legendPtr->flags &= ~REDRAW_PENDING;
+ if (legendPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ graphPtr = legendPtr->graphPtr;
+ if (legendPtr->site == LEGEND_WINDOW) {
+ int w, h;
+
+ w = Tk_Width(legendPtr->tkwin);
+ h = Tk_Height(legendPtr->tkwin);
+ if ((w != legendPtr->width) || (h != legendPtr->height)) {
+ Blt_MapLegend(graphPtr, w, h);
+ }
+ }
+ if (Tk_IsMapped(legendPtr->tkwin)) {
+ Blt_DrawLegend(graphPtr, Tk_WindowId(legendPtr->tkwin));
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ConfigureLegend --
+ *
+ * Routine to configure the legend.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ConfigureLegend(Graph *graphPtr)
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ /* GC for active label. Dashed outline. */
+ gcMask = GCForeground | GCLineStyle;
+ gcValues.foreground = legendPtr->focusColor->pixel;
+ gcValues.line_style = (LineIsDashed(legendPtr->focusDashes))
+ ? LineOnOffDash : LineSolid;
+ newGC = Blt_GetPrivateGC(legendPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(legendPtr->focusDashes)) {
+ legendPtr->focusDashes.offset = 2;
+ Blt_SetDashes(graphPtr->display, newGC, &legendPtr->focusDashes);
+ }
+ if (legendPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, legendPtr->focusGC);
+ }
+ legendPtr->focusGC = newGC;
+
+ /*
+ * 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 (legendPtr->site == LEGEND_WINDOW) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ } else if (Blt_ConfigModified(configSpecs, "-*border*", "-*pad?",
+ "-hide", "-font", "-rows", (char *)NULL)) {
+ graphPtr->flags |= RESET_WORLD;
+ graphPtr->flags |= (REDRAW_WORLD | CACHE_DIRTY);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DestroyLegend --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resources associated with the legend are freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DestroyLegend(Graph *graphPtr)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if (graphPtr->legend == NULL) {
+ return;
+ }
+
+ Blt_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
+ Blt_Ts_FreeStyle(graphPtr->display, &legendPtr->style);
+ Blt_Ts_FreeStyle(graphPtr->display, &legendPtr->titleStyle);
+ Blt_DestroyBindingTable(legendPtr->bindTable);
+
+ if (legendPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, legendPtr->focusGC);
+ }
+ if (legendPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(legendPtr->timerToken);
+ }
+ if (legendPtr->tkwin != NULL) {
+ Tk_DeleteSelHandler(legendPtr->tkwin, XA_PRIMARY, XA_STRING);
+ }
+ if (legendPtr->site == LEGEND_WINDOW) {
+ 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, graphPtr);
+ 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(Graph *graphPtr)
+{
+ Legend *legendPtr;
+
+ legendPtr = Blt_AssertCalloc(1, sizeof(Legend));
+ graphPtr->legend = legendPtr;
+ legendPtr->graphPtr = graphPtr;
+ legendPtr->tkwin = graphPtr->tkwin;
+ legendPtr->xReq = legendPtr->yReq = -SHRT_MAX;
+ legendPtr->relief = TK_RELIEF_SUNKEN;
+ legendPtr->activeRelief = TK_RELIEF_FLAT;
+ legendPtr->entryBW = 2;
+ legendPtr->borderWidth = 2;
+ legendPtr->ixPad.side1 = legendPtr->ixPad.side2 = 1;
+ legendPtr->iyPad.side1 = legendPtr->iyPad.side2 = 1;
+ legendPtr->xPad.side1 = legendPtr->xPad.side2 = 1;
+ legendPtr->yPad.side1 = legendPtr->yPad.side2 = 1;
+ legendPtr->anchor = TK_ANCHOR_N;
+ legendPtr->site = LEGEND_RIGHT;
+ legendPtr->selectMode = SELECT_MODE_MULTIPLE;
+ Blt_Ts_InitStyle(legendPtr->style);
+ Blt_Ts_InitStyle(legendPtr->titleStyle);
+ legendPtr->style.justify = TK_JUSTIFY_LEFT;
+ legendPtr->style.anchor = TK_ANCHOR_NW;
+ legendPtr->titleStyle.justify = TK_JUSTIFY_LEFT;
+ legendPtr->titleStyle.anchor = TK_ANCHOR_NW;
+ legendPtr->bindTable = Blt_CreateBindingTable(graphPtr->interp,
+ graphPtr->tkwin, graphPtr, PickEntryProc, Blt_GraphTags);
+
+ Blt_InitHashTable(&legendPtr->selectTable, BLT_ONE_WORD_KEYS);
+ legendPtr->selected = Blt_Chain_Create();
+ Tk_CreateSelHandler(legendPtr->tkwin, XA_PRIMARY, XA_STRING,
+ SelectionProc, legendPtr, XA_STRING);
+ legendPtr->selRelief = TK_RELIEF_FLAT;
+ legendPtr->selBW = 1;
+ legendPtr->onTime = 600;
+ legendPtr->offTime = 300;
+ if (Blt_ConfigureComponentFromObj(graphPtr->interp, graphPtr->tkwin,
+ "legend", "Legend", configSpecs, 0, (Tcl_Obj **)NULL,
+ (char *)legendPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_ConfigureLegend(graphPtr);
+ return TCL_OK;
+}
+
+static Element *
+GetNextRow(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col;
+ row = focusPtr->row + 1;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetNextColumn(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col + 1;
+ row = focusPtr->row;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr; /* Don't go beyond legend boundaries. */
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetPreviousRow(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col;
+ row = focusPtr->row - 1;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetPreviousColumn(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col - 1;
+ row = focusPtr->row;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetFirstElement(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label != NULL) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetLastElement(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label != NULL) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetElementFromObj --
+ *
+ * Parse an index into an entry and return either its value or an error.
+ *
+ * Results:
+ * A standard TCL result. If all went well, then *indexPtr is filled in
+ * with the character index (into entryPtr) corresponding to string. The
+ * index value is guaranteed to lie between 0 and the number of characters
+ * in the string, inclusive. If an error occurs then an error message is
+ * left in the interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetElementFromObj(Graph *graphPtr, Tcl_Obj *objPtr, Element **elemPtrPtr)
+{
+ Element *elemPtr;
+ Legend *legendPtr;
+ Tcl_Interp *interp;
+ char c;
+ const char *string;
+ int last;
+ int index;
+
+ legendPtr = graphPtr->legend;
+ interp = graphPtr->interp;
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ elemPtr = NULL;
+
+ last = Blt_Chain_GetLength(graphPtr->elements.displayList) - 1;
+ if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+ elemPtr = legendPtr->selAnchorPtr;
+ } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+ elemPtr = (Element *)Blt_GetCurrentItem(legendPtr->bindTable);
+ } else if ((c == 'f') && (strcmp(string, "first") == 0)) {
+ elemPtr = GetFirstElement(graphPtr);
+ } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+ elemPtr = legendPtr->focusPtr;
+ } else if ((c == 'l') && (strcmp(string, "last") == 0)) {
+ elemPtr = GetLastElement(graphPtr);
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ elemPtr = GetLastElement(graphPtr);
+ } else if ((c == 'n') && (strcmp(string, "next.row") == 0)) {
+ elemPtr = GetNextRow(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 'n') && (strcmp(string, "next.column") == 0)) {
+ elemPtr = GetNextColumn(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 'p') && (strcmp(string, "previous.row") == 0)) {
+ elemPtr = GetPreviousRow(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 'p') && (strcmp(string, "previous.column") == 0)) {
+ elemPtr = GetPreviousColumn(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 's') && (strcmp(string, "sel.first") == 0)) {
+ elemPtr = legendPtr->selFirstPtr;
+ } else if ((c == 's') && (strcmp(string, "sel.last") == 0)) {
+ elemPtr = legendPtr->selLastPtr;
+ } else if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(interp, legendPtr->tkwin, string, &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ elemPtr = (Element *)PickEntryProc(graphPtr, x, y, NULL);
+ } else {
+ if (Blt_GetElement(interp, graphPtr, objPtr, &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (elemPtr->link == NULL) {
+ Tcl_AppendResult(interp, "bad legend index \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (elemPtr->label == NULL) {
+ elemPtr = NULL;
+ }
+ }
+ *elemPtrPtr = elemPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectRange --
+ *
+ * Sets the selection flag for a range of nodes. The range is determined
+ * by two pointers which designate the first/last nodes of the range.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SelectRange(Legend *legendPtr, Element *fromPtr, Element *toPtr)
+{
+
+ if (Blt_Chain_IsBefore(fromPtr->link, toPtr->link)) {
+ Blt_ChainLink link;
+
+ for (link = fromPtr->link; link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ SelectEntry(legendPtr, elemPtr);
+ if (link == toPtr->link) {
+ break;
+ }
+ }
+ } else {
+ Blt_ChainLink link;
+
+ for (link = fromPtr->link; link != NULL;
+ link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ SelectEntry(legendPtr, elemPtr);
+ if (link == toPtr->link) {
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+
+#ifdef notdef
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectText --
+ *
+ * Modify the selection by moving its un-anchored end. This could make
+ * the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SelectText(Legend *legendPtr, Element *elemPtr)
+{
+ Element *firstPtr, *lastPtr;
+ Graph *graphPtr = legendPtr->graphPtr;
+
+ /* Grab the selection if we don't own it already. */
+ if ((legendPtr->flags&SELECT_EXPORT) && (legendPtr->selFirstPtr == NULL)) {
+ Tk_OwnSelection(legendPtr->tkwin, XA_PRIMARY, LostSelectionProc,
+ legendPtr);
+ }
+ /* If the anchor hasn't been set, assume the beginning of the legend. */
+ if (legendPtr->selAnchorPtr == NULL) {
+ legendPtr->selAnchorPtr = GetFirstElement(graphPtr);
+ }
+ if (legendPtr->selAnchorPtr != elemPtr) {
+ firstPtr = legendPtr->selAnchorPtr;
+ lastPtr = elemPtr;
+ } else {
+ firstPtr = elemPtr;
+ lastPtr = legendPtr->selAnchorPtr;
+ }
+ if ((legendPtr->selFirstPtr != firstPtr) ||
+ (legendPtr->selLastPtr != lastPtr)) {
+ legendPtr->selFirstPtr = firstPtr;
+ legendPtr->selLastPtr = lastPtr;
+ SelectRange(legendPtr, firstPtr, lastPtr);
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ return TCL_OK;
+}
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ unsigned int active, redraw;
+ const char *string;
+ int i;
+
+ string = Tcl_GetString(objv[2]);
+ active = (string[0] == 'a') ? LABEL_ACTIVE : 0;
+ redraw = FALSE;
+ for (i = 3; i < objc; i++) {
+ Blt_ChainLink link;
+ const char *pattern;
+
+ pattern = Tcl_GetString(objv[i]);
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (Tcl_StringMatch(elemPtr->obj.name, pattern)) {
+ fprintf(stderr, "legend %s(%s) %s is currently %d\n",
+ string, pattern, elemPtr->obj.name,
+ (elemPtr->flags & LABEL_ACTIVE));
+ if (active) {
+ if ((elemPtr->flags & LABEL_ACTIVE) == 0) {
+ elemPtr->flags |= LABEL_ACTIVE;
+ redraw = TRUE;
+ }
+ } else {
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ elemPtr->flags &= ~LABEL_ACTIVE;
+ redraw = TRUE;
+ }
+ }
+ fprintf(stderr, "legend %s(%s) %s is now %d\n",
+ string, pattern, elemPtr->obj.name,
+ (elemPtr->flags & LABEL_ACTIVE));
+ }
+ }
+ }
+ if ((redraw) && ((legendPtr->flags & HIDE) == 0)) {
+ /*
+ * See if how much we need to draw. If the graph is already scheduled
+ * 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 ((legendPtr->site != LEGEND_WINDOW) &&
+ (graphPtr->flags & REDRAW_PENDING)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ graphPtr->flags |= REDRAW_WORLD; /* Redraw entire graph. */
+ } else {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ }
+ {
+ Blt_ChainLink link;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ /* List active elements in stacking order. */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind index sequence command
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.tagTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ const char *tagName;
+ Tcl_Obj *objPtr;
+
+ tagName = Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
+ objPtr = Tcl_NewStringObj(tagName, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindingsFromObj(interp, graphPtr->legend->bindTable,
+ Blt_MakeElementTag(graphPtr, Tcl_GetString(objv[3])), objc - 4,
+ objv + 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(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ return Blt_ConfigureValueFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr->legend, objv[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(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int flags = BLT_CONFIG_OBJV_ONLY;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, (Tcl_Obj *)NULL, flags);
+ } else if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, objv[3], flags);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin, configSpecs,
+ objc - 3, objv + 3, (char *)legendPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_ConfigureLegend(graphPtr);
+ return TCL_OK;
+}
+
+
+/*ARGSUSED*/
+static int
+CurselectionOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (legendPtr->flags & SELECT_SORTED) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(legendPtr->selected); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+ Tcl_Obj *objPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ } else {
+ Blt_ChainLink link;
+
+ /* List of selected entries is in stacking order. */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+
+ if (EntryIsSelected(legendPtr, elemPtr)) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+FocusOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if (objc == 4) {
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((elemPtr != NULL) && (elemPtr != legendPtr->focusPtr)) {
+ /* Changing focus can only affect the visible entries. The entry
+ * layout stays the same. */
+ legendPtr->focusPtr = elemPtr;
+ }
+ Blt_SetFocusItem(legendPtr->bindTable, legendPtr->focusPtr,
+ CID_LEGEND_ENTRY);
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ if (legendPtr->focusPtr != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ legendPtr->focusPtr->obj.name, -1);
+ }
+ 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(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if (((legendPtr->flags & HIDE) == 0) && (legendPtr->nEntries > 0)) {
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (elemPtr != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->obj.name,-1);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IconOp --
+ *
+ * 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.
+ *
+ * .g legend icon elemName image
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IconOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_Picture picture;
+ Element *elemPtr;
+ Legend *legendPtr = graphPtr->legend;
+ Pixmap pixmap;
+ Blt_FontMetrics fontMetrics;
+ Tk_PhotoHandle photo;
+ const char *imageName;
+ int isPicture;
+ int w, h, x, y, s;
+
+ if (GetElementFromObj(graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (elemPtr == NULL) {
+ return TCL_OK; /* Unknown index. */
+ }
+ imageName = Tcl_GetString(objv[4]);
+ photo = Tk_FindPhoto(interp, imageName);
+ if (photo != NULL) {
+ isPicture = FALSE;
+ } else if (Blt_GetPicture(interp, imageName, &picture) == TCL_OK) {
+ isPicture = TRUE;
+ } else {
+ return TCL_ERROR;
+ }
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ s = fontMetrics.ascent;
+ h = s + PADDING(legendPtr->iyPad) + 1;
+ w = s + s + 1 + PADDING(legendPtr->ixPad);
+ x = (w / 2);
+ y = (h / 2);
+
+ pixmap = Tk_GetPixmap(graphPtr->display, Tk_RootWindow(graphPtr->tkwin),
+ w, h, Tk_Depth(graphPtr->tkwin));
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, pixmap, graphPtr->plotBg,
+ 0, 0, w, h, TK_RELIEF_FLAT, 0);
+ (*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr, x, y, s);
+ picture = Blt_DrawableToPicture(graphPtr->tkwin, pixmap, 0, 0, w, h, 1.0);
+ Tk_FreePixmap(graphPtr->display, pixmap);
+ if (picture == NULL) {
+ Tcl_AppendResult(interp, "can't get picture of symbol.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Make the background transparent. */
+ {
+ int y;
+ Blt_Pixel bg;
+ Blt_Pixel *destRowPtr;
+ XColor *colorPtr;
+
+ colorPtr = Blt_BackgroundBorderColor(graphPtr->plotBg);
+ bg.Red = colorPtr->red >> 8;
+ bg.Green = colorPtr->green >> 8;
+ bg.Blue = colorPtr->blue >> 8;
+ bg.Alpha = 0xFF;
+
+ destRowPtr = Blt_PictureBits(picture);
+ for (y = 0; y < h; y++) {
+ Blt_Pixel *dp, *dend;
+
+ for (dp = destRowPtr, dend = dp + w; dp < dend; dp++) {
+ if (dp->u32 == bg.u32) {
+ dp->Alpha = 0x0;
+ }
+ }
+ destRowPtr += Blt_PictureStride(picture);
+ }
+ }
+ Blt_ClassifyPicture(picture);
+ if (isPicture) {
+ Blt_ResetPicture(interp, imageName, picture);
+ } else {
+ Blt_PictureToPhoto(picture, photo);
+ Blt_FreePicture(picture);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionAnchorOp --
+ *
+ * Sets the selection anchor to the element given by a index. The
+ * selection anchor is the end of the selection that is fixed while
+ * dragging out a selection with the mouse. The index "anchor" may be
+ * used to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionAnchorOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[4], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Set both the anchor and the mark. Indicates that a single entry
+ * is selected. */
+ legendPtr->selAnchorPtr = elemPtr;
+ legendPtr->selMarkPtr = NULL;
+ if (elemPtr != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->obj.name, -1);
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionClearallOp
+ *
+ * Clears the entire selection.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionClearallOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ ClearSelection(legendPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionIncludesOp
+ *
+ * Returns 1 if the element indicated by index is currently
+ * selected, 0 if it isn't.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionIncludesOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+ int bool;
+
+ if (GetElementFromObj(graphPtr, objv[4], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bool = EntryIsSelected(legendPtr, elemPtr);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), bool);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionMarkOp --
+ *
+ * Sets the selection mark to the element given by a index. The
+ * selection anchor is the end of the selection that is movable while
+ * dragging out a selection with the mouse. The index "mark" may be used
+ * to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionMarkOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[4], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (legendPtr->selAnchorPtr == NULL) {
+ Tcl_AppendResult(interp, "selection anchor must be set first",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (legendPtr->selMarkPtr != elemPtr) {
+ Blt_ChainLink link, next;
+
+ /* Deselect entry from the list all the way back to the anchor. */
+ for (link = Blt_Chain_LastLink(legendPtr->selected); link != NULL;
+ link = next) {
+ Element *selectPtr;
+
+ next = Blt_Chain_PrevLink(link);
+ selectPtr = Blt_Chain_GetValue(link);
+ if (selectPtr == legendPtr->selAnchorPtr) {
+ break;
+ }
+ DeselectElement(legendPtr, selectPtr);
+ }
+ legendPtr->flags &= ~SELECT_MASK;
+ legendPtr->flags |= SELECT_SET;
+ SelectRange(legendPtr, legendPtr->selAnchorPtr, elemPtr);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->obj.name, -1);
+ legendPtr->selMarkPtr = elemPtr;
+
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ if (legendPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(legendPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionPresentOp
+ *
+ * Returns 1 if there is a selection and 0 if it isn't.
+ *
+ * Results:
+ * A standard TCL result. interp->result will contain a boolean string
+ * indicating if there is a selection.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionPresentOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ int bool;
+
+ bool = (Blt_Chain_GetLength(legendPtr->selected) > 0);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), bool);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionSetOp
+ *
+ * Selects, deselects, or toggles all of the elements in the range
+ * between first and last, inclusive, without affecting the selection
+ * state of elements outside that range.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ * .g legend selection set first last
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionSetOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *firstPtr, *lastPtr;
+ const char *string;
+
+ legendPtr->flags &= ~SELECT_MASK;
+ string = Tcl_GetString(objv[3]);
+ switch (string[0]) {
+ case 's':
+ legendPtr->flags |= SELECT_SET;
+ break;
+ case 'c':
+ legendPtr->flags |= SELECT_CLEAR;
+ break;
+ case 't':
+ legendPtr->flags |= SELECT_TOGGLE;
+ break;
+ }
+ if (GetElementFromObj(graphPtr, objv[4], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((firstPtr->flags & HIDE) && ((legendPtr->flags & SELECT_CLEAR)==0)) {
+ Tcl_AppendResult(interp, "can't select hidden node \"",
+ Tcl_GetString(objv[4]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ lastPtr = firstPtr;
+ if (objc > 5) {
+ if (GetElementFromObj(graphPtr, objv[5], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((lastPtr->flags & HIDE) &&
+ ((legendPtr->flags & SELECT_CLEAR) == 0)) {
+ Tcl_AppendResult(interp, "can't select hidden node \"",
+ Tcl_GetString(objv[5]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (firstPtr == lastPtr) {
+ SelectEntry(legendPtr, firstPtr);
+ } else {
+ SelectRange(legendPtr, firstPtr, lastPtr);
+ }
+ /* Set both the anchor and the mark. Indicates that a single entry is
+ * selected. */
+ if (legendPtr->selAnchorPtr == NULL) {
+ legendPtr->selAnchorPtr = firstPtr;
+ }
+ if (legendPtr->flags & SELECT_EXPORT) {
+ Tk_OwnSelection(legendPtr->tkwin, XA_PRIMARY, LostSelectionProc,
+ legendPtr);
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ if (legendPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(legendPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionOp --
+ *
+ * This procedure handles the individual options for text selections.
+ * The selected text is designated by start and end indices into the text
+ * pool. The selected segment has both a anchored and unanchored ends.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ * .g legend selection anchor
+ *---------------------------------------------------------------------------
+ */
+static Blt_OpSpec selectionOps[] =
+{
+ {"anchor", 1, SelectionAnchorOp, 5, 5, "elem",},
+ {"clear", 5, SelectionSetOp, 5, 6, "firstElem ?lastElem?",},
+ {"clearall", 6, SelectionClearallOp, 4, 4, "",},
+ {"includes", 1, SelectionIncludesOp, 5, 5, "elem",},
+ {"mark", 1, SelectionMarkOp, 5, 5, "elem",},
+ {"present", 1, SelectionPresentOp, 4, 4, "",},
+ {"set", 1, SelectionSetOp, 5, 6, "firstElem ?lastElem?",},
+ {"toggle", 1, SelectionSetOp, 5, 6, "firstElem ?lastElem?",},
+};
+static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
+
+static int
+SelectionOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ GraphLegendProc *proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG3,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_LegendOp --
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Legend is possibly redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static Blt_OpSpec legendOps[] =
+{
+ {"activate", 1, ActivateOp, 3, 0, "?pattern?...",},
+ {"bind", 1, BindOp, 3, 6, "elem sequence command",},
+ {"cget", 2, CgetOp, 4, 4, "option",},
+ {"configure", 2, ConfigureOp, 3, 0, "?option value?...",},
+ {"curselection", 2, CurselectionOp, 3, 3, "",},
+ {"deactivate", 1, ActivateOp, 3, 0, "?pattern?...",},
+ {"focus", 1, FocusOp, 4, 4, "elem",},
+ {"get", 1, GetOp, 4, 4, "elem",},
+ {"icon", 1, IconOp, 5, 5, "elem image",},
+ {"selection", 1, SelectionOp, 3, 0, "args"},
+};
+static int nLegendOps = sizeof(legendOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_LegendOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ GraphLegendProc *proc;
+
+ proc = Blt_GetOpFromObj(interp, nLegendOps, legendOps, BLT_OP_ARG2,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (graphPtr, interp, objc, objv);
+}
+
+int
+Blt_Legend_Site(Graph *graphPtr)
+{
+ return graphPtr->legend->site;
+}
+
+int
+Blt_Legend_Width(Graph *graphPtr)
+{
+ return graphPtr->legend->width;
+}
+
+int
+Blt_Legend_Height(Graph *graphPtr)
+{
+ return graphPtr->legend->height;
+}
+
+int
+Blt_Legend_IsHidden(Graph *graphPtr)
+{
+ return (graphPtr->legend->flags & HIDE);
+}
+
+int
+Blt_Legend_IsRaised(Graph *graphPtr)
+{
+ return (graphPtr->legend->flags & RAISED);
+}
+
+int
+Blt_Legend_X(Graph *graphPtr)
+{
+ return graphPtr->legend->x;
+}
+
+int
+Blt_Legend_Y(Graph *graphPtr)
+{
+ return graphPtr->legend->y;
+}
+
+void
+Blt_Legend_RemoveElement(Graph *graphPtr, Element *elemPtr)
+{
+ Blt_DeleteBindings(graphPtr->legend->bindTable, elemPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is requested by
+ * someone. It returns part or all of the selection in a buffer provided
+ * by the caller.
+ *
+ * Results:
+ * The return value is the number of non-NULL bytes stored at buffer.
+ * Buffer is filled (or partially filled) with a NUL-terminated string
+ * containing part or all of the selection, as given by offset and
+ * maxBytes.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SelectionProc(
+ ClientData clientData, /* Information about the widget. */
+ int offset, /* Offset within selection of first
+ * character to be returned. */
+ char *buffer, /* Location in which to place
+ * selection. */
+ int maxBytes) /* Maximum number of bytes to place at
+ * buffer, not including terminating
+ * NULL character. */
+{
+ Legend *legendPtr = clientData;
+ int nBytes;
+ Tcl_DString dString;
+
+ if ((legendPtr->flags & SELECT_EXPORT) == 0) {
+ return -1;
+ }
+ /* Retrieve the names of the selected entries. */
+ Tcl_DStringInit(&dString);
+ if (legendPtr->flags & SELECT_SORTED) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(legendPtr->selected);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ Tcl_DStringAppend(&dString, elemPtr->obj.name, -1);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ } else {
+ Blt_ChainLink link;
+ Graph *graphPtr;
+
+ graphPtr = legendPtr->graphPtr;
+ /* List of selected entries is in stacking order. */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (EntryIsSelected(legendPtr, elemPtr)) {
+ Tcl_DStringAppend(&dString, elemPtr->obj.name, -1);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ }
+ nBytes = Tcl_DStringLength(&dString) - offset;
+ strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes);
+ Tcl_DStringFree(&dString);
+ buffer[maxBytes] = '\0';
+ return MIN(nBytes, maxBytes);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BlinkCursorProc --
+ *
+ * This procedure is called as a timer handler to blink the insertion
+ * cursor off and on.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The cursor gets turned on or off, redisplay gets invoked, and this
+ * procedure reschedules itself.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+BlinkCursorProc(ClientData clientData)
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (!(legendPtr->flags & FOCUS) || (legendPtr->offTime == 0)) {
+ return;
+ }
+ if (legendPtr->active) {
+ int time;
+
+ legendPtr->cursorOn ^= 1;
+ time = (legendPtr->cursorOn) ? legendPtr->onTime : legendPtr->offTime;
+ legendPtr->timerToken = Tcl_CreateTimerHandler(time, BlinkCursorProc,
+ graphPtr);
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+}