home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / TCL / BLT / BLT1.7L1 / BLT1 / blt-1.7 / src / bltGrLegd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-22  |  28.5 KB  |  927 lines

  1.  
  2. /*
  3.  * bltGrLegd.c --
  4.  *
  5.  *    This module implements legends for the graph widget for
  6.  *    the Tk toolkit.
  7.  *
  8.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  9.  * Permission to use, copy, modify, and distribute this software
  10.  * and its documentation for any purpose and without fee is hereby
  11.  * granted, provided that the above copyright notice appear in all
  12.  * copies and that both that the copyright notice and warranty
  13.  * disclaimer appear in supporting documentation, and that the
  14.  * names of AT&T Bell Laboratories any of their entities not be used
  15.  * in advertising or publicity pertaining to distribution of the
  16.  * software without specific, written prior permission.
  17.  *
  18.  * AT&T disclaims all warranties with regard to this software, including
  19.  * all implied warranties of merchantability and fitness.  In no event
  20.  * shall AT&T be liable for any special, indirect or consequential
  21.  * damages or any damages whatsoever resulting from loss of use, data
  22.  * or profits, whether in an action of contract, negligence or other
  23.  * tortuous action, arising out of or in connection with the use or
  24.  * performance of this software.
  25.  *
  26.  */
  27.  
  28. #include "blt.h"
  29. #include "bltGraph.h"
  30. #include "bltGrElem.h"
  31.  
  32. #define DEF_LEGEND_ACTIVE_BG_COLOR     BISQUE2
  33. #define DEF_LEGEND_ACTIVE_BG_MONO    WHITE
  34. #define DEF_LEGEND_ACTIVE_BORDER_WIDTH  "2"
  35. #define DEF_LEGEND_ACTIVE_FG_COLOR    BLACK
  36. #define DEF_LEGEND_ACTIVE_FG_MONO    BLACK
  37. #define DEF_LEGEND_ACTIVE_RELIEF    "flat"
  38. #define DEF_LEGEND_ANCHOR           "nw"
  39. #define DEF_LEGEND_BG_COLOR           BISQUE1
  40. #define DEF_LEGEND_BG_MONO        WHITE
  41. #define DEF_LEGEND_BORDER_WIDTH     "2"
  42. #define DEF_LEGEND_FG_COLOR        BLACK
  43. #define DEF_LEGEND_FG_MONO        BLACK
  44. #define DEF_LEGEND_FONT            "*-Helvetica-Bold-R-Normal-*-120-*"
  45. #define DEF_LEGEND_IPAD_X        "1"
  46. #define DEF_LEGEND_IPAD_Y        "1"
  47. #define DEF_LEGEND_MAPPED           "1"
  48. #define DEF_LEGEND_PAD_X        "4"
  49. #define DEF_LEGEND_PAD_Y        "0"
  50. #define DEF_LEGEND_POSITION        (char *)NULL
  51. #define DEF_LEGEND_RELIEF        "sunken"
  52.  
  53. extern Tk_CustomOption bltPositionOption;
  54.  
  55. /*
  56.  * -------------------------------------------------------------------
  57.  *
  58.  * Legend --
  59.  *
  60.  *     Contains information specific to how the legend will be
  61.  *    displayed.
  62.  *
  63.  * -------------------------------------------------------------------
  64.  */
  65.  
  66.  
  67. typedef struct {
  68.     int mapped;            /* Requested state of the legend, If non-zero,
  69.                  * legend is displayed */
  70.     unsigned int width, height;    /* Dimensions of the legend */
  71.     XPoint anchorPos;        /* Window coordinates of legend positioning
  72.                  * point. Used in conjunction with the anchor
  73.                  * to determine the location of the legend. If
  74.                  * x or y are DEF_POSITION the legend is set
  75.                  * in the right margin */
  76.     int useDefault;        /* Use the default legend position */
  77.  
  78.     LegendDisplayProc *displayProc;
  79.     LegendPrintProc *printProc;
  80.     LegendDestroyProc *destroyProc;
  81.     LegendGeometryProc *geomProc;
  82.  
  83.     int ipadX, ipadY;        /* # of pixels padding around legend entries */
  84.     int padX, padY;        /* # of pixels padding to exterior of legend */
  85.     unsigned int numLabels;    /* Number of labels (and symbols) to display */
  86.     unsigned int numCols;    /* Number of columns in legend */
  87.     unsigned int numRows;    /* Number of rows in legend */
  88.     unsigned int entryWidth;
  89.     unsigned int entryHeight;
  90.  
  91.     int maxSymSize;        /* Size of largest symbol to be displayed.
  92.                  * Used to calculate size of legend */
  93.     Tk_Anchor anchor;        /* Anchor of legend. Used to interpret the
  94.                  * positioning point of the legend in the
  95.                  * graph*/
  96.     XFontStruct *fontPtr;    /* Font for legend text */
  97.  
  98.     Tk_3DBorder border;        /* 3-D border and background color legend. */
  99.     int borderWidth;        /* Width of legend 3-D border */
  100.     int relief;            /* 3-d effect of border around the legend:
  101.                  * TK_RELIEF_RAISED etc. */
  102.     XColor *normalFg;        /* Foreground color for legend
  103.                  * text. Symbols retain the color
  104.                  * specified for the element*/
  105.     GC normalGC;        /* Normal legend entry graphics context */
  106.  
  107.     Tk_3DBorder activeBorder;    /* Background color for active legend
  108.                  * entries. */
  109.     int activeBW;        /* Width of legend 3-D border */
  110.     int activeRelief;        /* 3-d effect: TK_RELIEF_RAISED etc. */
  111.     XColor *activeFg;        /* Foreground color for active legend
  112.                  * entries. Symbols retain the color
  113.                  * specified for the element*/
  114.     GC activeGC;        /* Active legend entry graphics context */
  115.  
  116. } Legend;
  117.  
  118. static Tk_ConfigSpec configSpecs[] =
  119. {
  120.     {TK_CONFIG_BORDER, "-activebackground", "legendActiveBackground",
  121.     "ActiveBackground", DEF_LEGEND_ACTIVE_BG_COLOR,
  122.     Tk_Offset(Legend, activeBorder), TK_CONFIG_COLOR_ONLY},
  123.     {TK_CONFIG_BORDER, "-activebackground", "legendActiveBackground",
  124.     "ActiveBackground", DEF_LEGEND_ACTIVE_BG_MONO,
  125.     Tk_Offset(Legend, activeBorder), TK_CONFIG_MONO_ONLY},
  126.     {TK_CONFIG_INT, "-activeborderwidth", "legendActiveBorderWidth",
  127.     "BorderWidth", DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, activeBW),
  128.     TK_CONFIG_DONT_SET_DEFAULT},
  129.     {TK_CONFIG_COLOR, "-activeforeground", "legendActiveForeground",
  130.     "ActiveForeground", DEF_LEGEND_ACTIVE_FG_COLOR,
  131.     Tk_Offset(Legend, activeFg), TK_CONFIG_COLOR_ONLY},
  132.     {TK_CONFIG_COLOR, "-activeforeground", "legendActiveForeground",
  133.     "ActiveForeground", DEF_LEGEND_ACTIVE_FG_MONO,
  134.     Tk_Offset(Legend, activeFg), TK_CONFIG_MONO_ONLY},
  135.     {TK_CONFIG_RELIEF, "-activerelief", "legendActiveRelief", "Relief",
  136.     DEF_LEGEND_ACTIVE_RELIEF, Tk_Offset(Legend, activeRelief),
  137.     TK_CONFIG_DONT_SET_DEFAULT},
  138.     {TK_CONFIG_ANCHOR, "-anchor", "legendAnchor", "Anchor",
  139.     DEF_LEGEND_ANCHOR, Tk_Offset(Legend, anchor),
  140.     TK_CONFIG_DONT_SET_DEFAULT},
  141.     {TK_CONFIG_SYNONYM, "-bg", "legendBackground", (char *)NULL,
  142.     (char *)NULL, 0, 0},
  143.     {TK_CONFIG_BORDER, "-background", "legendBackground", "Background",
  144.     DEF_LEGEND_BG_MONO, Tk_Offset(Legend, border),
  145.     TK_CONFIG_MONO_ONLY},
  146.     {TK_CONFIG_BORDER, "-background", "legendBackground", "Background",
  147.     DEF_LEGEND_BG_COLOR, Tk_Offset(Legend, border),
  148.     TK_CONFIG_COLOR_ONLY},
  149.     {TK_CONFIG_INT, "-borderwidth", "legendBorderWidth", "BorderWidth",
  150.     DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, borderWidth),
  151.     TK_CONFIG_DONT_SET_DEFAULT},
  152.     {TK_CONFIG_SYNONYM, "-bd", "legendBorderWidth", (char *)NULL,
  153.     (char *)NULL, 0, 0},
  154.     {TK_CONFIG_FONT, "-font", "legendFont", "Font",
  155.     DEF_LEGEND_FONT, Tk_Offset(Legend, fontPtr), 0},
  156.     {TK_CONFIG_SYNONYM, "-fg", "legendForeground", (char *)NULL, (char *)NULL,
  157.     0, 0},
  158.     {TK_CONFIG_COLOR, "-foreground", "legendForeground", "Foreground",
  159.     DEF_LEGEND_FG_COLOR, Tk_Offset(Legend, normalFg),
  160.     TK_CONFIG_COLOR_ONLY},
  161.     {TK_CONFIG_COLOR, "-foreground", "legendForeground", "Foreground",
  162.     DEF_LEGEND_FG_MONO, Tk_Offset(Legend, normalFg),
  163.     TK_CONFIG_MONO_ONLY},
  164.     {TK_CONFIG_PIXELS, "-ipadx", "legendIPadX", "Pad",
  165.     DEF_LEGEND_IPAD_X, Tk_Offset(Legend, ipadX),
  166.     TK_CONFIG_DONT_SET_DEFAULT},
  167.     {TK_CONFIG_PIXELS, "-ipady", "legendIPadY", "Pad",
  168.     DEF_LEGEND_IPAD_Y, Tk_Offset(Legend, ipadY),
  169.     TK_CONFIG_DONT_SET_DEFAULT},
  170.     {TK_CONFIG_BOOLEAN, "-mapped", "legendMapped", "Mapped",
  171.     DEF_LEGEND_MAPPED, Tk_Offset(Legend, mapped),
  172.     TK_CONFIG_DONT_SET_DEFAULT},
  173.     {TK_CONFIG_PIXELS, "-padx", "legendPadX", "Pad",
  174.     DEF_LEGEND_PAD_X, Tk_Offset(Legend, padX), TK_CONFIG_DONT_SET_DEFAULT},
  175.     {TK_CONFIG_PIXELS, "-pady", "legendPadY", "Pad",
  176.     DEF_LEGEND_PAD_Y, Tk_Offset(Legend, padY), TK_CONFIG_DONT_SET_DEFAULT},
  177.     {TK_CONFIG_CUSTOM, "-position", "legendPosition", "Position",
  178.     DEF_LEGEND_POSITION, Tk_Offset(Legend, anchorPos),
  179.     TK_CONFIG_NULL_OK, &bltPositionOption},
  180.     {TK_CONFIG_RELIEF, "-relief", "legendRelief", "Relief",
  181.     DEF_LEGEND_RELIEF, Tk_Offset(Legend, relief),
  182.     TK_CONFIG_DONT_SET_DEFAULT},
  183.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  184. };
  185.  
  186. extern int Blt_GetPosition _ANSI_ARGS_((Tcl_Interp *interp, char *string,
  187.     XPoint *pointPtr));
  188.  
  189. static XPoint
  190. GetOrigin(graphPtr, legendPtr)
  191.     Graph *graphPtr;
  192.     Legend *legendPtr;
  193. {
  194.     int x, y;
  195.     Tk_Anchor anchor;
  196.     XPoint origin;
  197.  
  198.     x = (graphPtr->width - graphPtr->borderWidth);
  199.     y = (graphPtr->extreme.y - graphPtr->plotBW);
  200.     anchor = TK_ANCHOR_NE;
  201.     if (!legendPtr->useDefault) {
  202.     x = legendPtr->anchorPos.x;
  203.     y = legendPtr->anchorPos.y;
  204.     anchor = legendPtr->anchor;
  205.     }
  206.     origin = Blt_TranslateBoxCoords(x, y, legendPtr->width, legendPtr->height,
  207.     anchor);
  208.     origin.x += legendPtr->padX;
  209.     origin.y += legendPtr->padY;
  210.     return (origin);
  211. }
  212.  
  213.  
  214.  
  215. static int
  216. GetIndex(legendPtr, originPtr, pointPtr)
  217.     Legend *legendPtr;
  218.     XPoint *originPtr, *pointPtr;
  219. {
  220.     int x, y;
  221.     unsigned int width, height;
  222.     unsigned int row, column;
  223.     unsigned int index;
  224.  
  225.     x = originPtr->x + legendPtr->borderWidth;
  226.     y = originPtr->y + legendPtr->borderWidth;
  227.     width = legendPtr->width - 2 * (legendPtr->padX + legendPtr->borderWidth);
  228.     height = legendPtr->height - 2 * (legendPtr->padY + legendPtr->borderWidth);
  229.  
  230.     if ((pointPtr->x < x) || (pointPtr->x > (x + width)) ||
  231.     (pointPtr->y < y) || (pointPtr->y > (y + height))) {
  232.     return -1;
  233.     }
  234.     /* It's in the box. Compute the label index */
  235.  
  236.     row = (pointPtr->y - y) / legendPtr->entryHeight;
  237.     column = (pointPtr->x - x) / legendPtr->entryWidth;
  238.     index = (column * legendPtr->numRows) + row;
  239.     if (index >= legendPtr->numLabels) {
  240.     return -1;
  241.     }
  242.     return index;
  243. }
  244.  
  245.  
  246.  
  247. static Element *
  248. LocateElement(graphPtr, legendPtr, elemId)
  249.     Graph *graphPtr;
  250.     Legend *legendPtr;
  251.     char *elemId;
  252. {
  253.     Blt_ListEntry *entryPtr;
  254.     Element *elemPtr;
  255.  
  256.     elemPtr = NULL;
  257.     if (elemId[0] == '@') {
  258.     XPoint origin, point;
  259.     int count, index;
  260.  
  261.     if (Blt_GetPosition(graphPtr->interp, elemId, &point) != TCL_OK) {
  262.         return NULL;
  263.     }
  264.     origin = GetOrigin(graphPtr, legendPtr);
  265.     index = GetIndex(legendPtr, &origin, &point);
  266.     if (index < 0) {
  267.         return NULL;
  268.     }
  269.     count = 0;
  270.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  271.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  272.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  273.         if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  274.         (elemPtr->label == NULL)) {
  275.         continue;
  276.         }
  277.         if (count == index) {
  278.         break;
  279.         }
  280.         count++;
  281.     }
  282.     }
  283.     return (elemPtr);
  284. }
  285.  
  286. /*
  287.  * -----------------------------------------------------------------
  288.  *
  289.  * ComputeLegendGeometry --
  290.  *
  291.  *     Calculates the dimensions (width and height) needed for
  292.  *    the legend.  Also determines the number of rows and columns
  293.  *    necessary to list all the valid element labels.
  294.  *
  295.  * Results:
  296.  *      None.
  297.  *
  298.  * Side effects:
  299.  *       The following fields of the legend are calculated and set.
  300.  *
  301.  *     numLabels   - number of valid labels of elements in the
  302.  *              display list.
  303.  *     numRows        - number of rows of entries
  304.  *     numCols        - number of columns of entries
  305.  *     entryHeight - height of each entry
  306.  *     entryWidth  - width of each entry
  307.  *     height        - width of legend (includes borders and padding)
  308.  *     width        - height of legend (includes borders and padding)
  309.  *
  310.  * -----------------------------------------------------------------
  311.  */
  312. static void
  313. ComputeLegendGeometry(graphPtr, maxHeight)
  314.     Graph *graphPtr;
  315.     int maxHeight;
  316. {
  317.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  318.     unsigned int width, height, twiceBW;
  319.     unsigned int numLabels, numRows, numCols;
  320.  
  321.     numCols = numRows = numLabels = 0;
  322.     height = width = 0;
  323.     if (legendPtr->mapped) {
  324.     register Blt_ListEntry *entryPtr;
  325.     Element *elemPtr;
  326.     unsigned int textHeight, entryHeight, entryWidth;
  327.     unsigned int w;
  328.  
  329.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  330.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  331.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  332.         if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  333.         (elemPtr->label == NULL)) {
  334.         continue;    /* Ignore elements with incomplete data */
  335.         }
  336.         w = Blt_TextStringWidth(legendPtr->fontPtr, elemPtr->label);
  337.         if (w > width) {
  338.         width = w;
  339.         }
  340.         numLabels++;
  341.     }
  342.     textHeight = TEXTHEIGHT(legendPtr->fontPtr);
  343.     twiceBW = (2 * legendPtr->activeBW);
  344.     entryHeight = textHeight + twiceBW + (2 * legendPtr->ipadY);
  345.     entryWidth = width + textHeight + twiceBW + (3 * legendPtr->ipadX);
  346.     maxHeight -= 2 * (legendPtr->borderWidth + legendPtr->padY);
  347.     numRows = maxHeight / entryHeight;
  348.     if (numRows > 0) {
  349.         numCols = ((numLabels - 1) / numRows) + 1;
  350.         if (numRows > numLabels) {
  351.         numRows = numLabels;
  352.         }
  353.         height = (2 * (legendPtr->borderWidth + legendPtr->padY)) +
  354.         (numRows * entryHeight);
  355.         width = (2 * (legendPtr->borderWidth + legendPtr->padX)) +
  356.         (numCols * entryWidth);
  357.     }
  358.     legendPtr->entryWidth = entryWidth;
  359.     legendPtr->entryHeight = entryHeight;
  360.     legendPtr->numRows = numRows;
  361.     legendPtr->numCols = numCols;
  362.     }
  363.     legendPtr->numLabels = numLabels;
  364.     legendPtr->height = height;
  365.     legendPtr->width = width;
  366. }
  367.  
  368. /*
  369.  * -----------------------------------------------------------------
  370.  *
  371.  * DisplayLegend --
  372.  *
  373.  * -----------------------------------------------------------------
  374.  */
  375. static void
  376. DisplayLegend(graphPtr)
  377.     Graph *graphPtr;
  378. {
  379.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  380.     int x, y, startY;
  381.     register Element *elemPtr;
  382.     unsigned int width, height;
  383.     unsigned int labelX, symbolX, symbolY;
  384.     unsigned int counter;
  385.     Blt_ListEntry *entryPtr;
  386.     XPoint origPos, curPos;
  387.     unsigned int symSize, midPoint;
  388.     TextAttributes textAttr;
  389.     int redraw;
  390.  
  391.     graphPtr->flags &= ~LEGEND_ONLY;
  392.     if ((!legendPtr->mapped) || (legendPtr->numLabels == 0) ||
  393.     (legendPtr->numRows == 0) || (legendPtr->numCols == 0)) {
  394.     return;
  395.     }
  396.     width = legendPtr->width - (2 * legendPtr->padX);
  397.     height = legendPtr->height - (2 * legendPtr->padY);
  398.     symSize = legendPtr->fontPtr->ascent;
  399.     midPoint = (symSize / 2) + 1 + legendPtr->activeBW;
  400.  
  401.     labelX = TEXTHEIGHT(legendPtr->fontPtr) + legendPtr->activeBW +
  402.     (2 * legendPtr->ipadX);
  403.     symbolY = midPoint + legendPtr->ipadY;
  404.     symbolX = midPoint + legendPtr->ipadX;
  405.  
  406.     origPos = GetOrigin(graphPtr, legendPtr);
  407.     x = origPos.x, y = origPos.y, redraw = 0;
  408.  
  409.     if (graphPtr->canvas == None) {
  410.     /*
  411.      * If there's no pixmap already to draw into, then this
  412.      * routine was called from Tk_DoWhenIdle instead of
  413.      * DisplayGraph.  Create a temporary pixmap and reset the x,y
  414.      * coordinates to do a quick redisplay of just the legend.
  415.      */
  416.     graphPtr->canvas = XCreatePixmap(graphPtr->display,
  417.         Tk_WindowId(graphPtr->tkwin), width, height,
  418.         Tk_Depth(graphPtr->tkwin));
  419.     x = y = 0;
  420.     redraw = 1;
  421.     }
  422.     Tk_Fill3DRectangle(graphPtr->display, graphPtr->canvas,
  423.     legendPtr->border, x, y, width, height, legendPtr->borderWidth,
  424.     legendPtr->relief);
  425.  
  426.     x += legendPtr->borderWidth;
  427.     y += legendPtr->borderWidth;
  428.  
  429.     textAttr.fontPtr = legendPtr->fontPtr;
  430.     textAttr.anchor = TK_ANCHOR_W;
  431.     textAttr.theta = 0.0;
  432.     textAttr.bgColorPtr = (XColor *)NULL;
  433.     textAttr.gc = legendPtr->normalGC;
  434.  
  435.     counter = 0;
  436.     startY = y;
  437.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  438.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  439.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  440.  
  441.     /*
  442.      * Draw each symbol and label.  Check that the label isn't NULL and
  443.      * that the element has at least one data point.
  444.      */
  445.  
  446.     if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  447.         (elemPtr->label == NULL)) {
  448.         continue;
  449.     }
  450.     textAttr.gc = legendPtr->normalGC;
  451.  
  452.     if (elemPtr->flags & LABEL_ACTIVE) {
  453.         Tk_Fill3DRectangle(graphPtr->display, graphPtr->canvas,
  454.         legendPtr->activeBorder, x, y, legendPtr->entryWidth,
  455.         legendPtr->entryHeight, legendPtr->activeBW,
  456.         legendPtr->activeRelief);
  457.         textAttr.gc = legendPtr->activeGC;
  458.     }
  459.     curPos.x = x + symbolX;
  460.     curPos.y = y + symbolY;
  461.     (*elemPtr->drawSymbolsProc) (graphPtr, elemPtr, symSize, &curPos, 1,
  462.         ELEM_NORMAL);
  463.     curPos.x = x + labelX;
  464.     Blt_DrawText(graphPtr->display, graphPtr->canvas, elemPtr->label,
  465.         &textAttr, curPos.x, curPos.y);
  466.     counter++;
  467.  
  468.     /* Check when to move to the next column */
  469.     if ((counter % legendPtr->numRows) > 0) {
  470.         y += legendPtr->entryHeight;
  471.     } else {
  472.         x += legendPtr->entryWidth;
  473.         y = startY;
  474.     }
  475.     }
  476.     if (redraw) {
  477.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  478.     XCopyArea(graphPtr->display, graphPtr->canvas,
  479.         Tk_WindowId(graphPtr->tkwin), graphPtr->marginGC, 0, 0,
  480.         width, height, origPos.x, origPos.y);
  481.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  482.     XFreePixmap(graphPtr->display, graphPtr->canvas);
  483.     graphPtr->canvas = None;
  484.     }
  485. }
  486.  
  487. /*
  488.  * -----------------------------------------------------------------
  489.  *
  490.  * PrintLegend --
  491.  *
  492.  * -----------------------------------------------------------------
  493.  */
  494. static void
  495. PrintLegend(graphPtr)
  496.     Graph *graphPtr;
  497. {
  498.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  499.     int x, y, startY;
  500.     register Element *elemPtr;
  501.     unsigned int labelX, symbolX, symbolY;
  502.     unsigned int counter;
  503.     Blt_ListEntry *entryPtr;
  504.     XPoint origPos, curPos;
  505.     Tk_Anchor anchor;        /* Anchor of legend */
  506.     unsigned int symSize, midPoint;
  507.     int width, height;
  508.     TextAttributes textAttr;
  509.  
  510.     if ((!legendPtr->mapped) || (legendPtr->numLabels == 0) ||
  511.     (legendPtr->numRows == 0) || (legendPtr->numCols == 0)) {
  512.     return;
  513.     }
  514.     /*
  515.      * First calculate the upper left coordinate of the legend.
  516.      * Consider the anchor.
  517.      */
  518.  
  519.     x = graphPtr->width - graphPtr->borderWidth;
  520.     y = graphPtr->extreme.y - graphPtr->plotBW;
  521.     anchor = TK_ANCHOR_NE;
  522.  
  523.     if (!legendPtr->useDefault) {
  524.     double scale, coord;
  525.     /*
  526.      * Legend position was given in window coordinates so we have to
  527.      * scale using the current page coordinates.
  528.      */
  529.     scale = (double)graphPtr->width / Tk_Width(graphPtr->tkwin);
  530.     coord = (legendPtr->anchorPos.x * scale);
  531.     x = BLT_RND(coord);
  532.     scale = (double)graphPtr->height / Tk_Height(graphPtr->tkwin);
  533.     coord = (legendPtr->anchorPos.y * scale);
  534.     y = BLT_RND(coord);
  535.     anchor = legendPtr->anchor;
  536.     }
  537.     origPos = Blt_TranslateBoxCoords(x, y, legendPtr->width,
  538.     legendPtr->height, anchor);
  539.  
  540.     x = origPos.x + legendPtr->padX;
  541.     y = origPos.y + legendPtr->padY;
  542.  
  543.     width = legendPtr->width - (2 * legendPtr->padX);
  544.     height = legendPtr->height - (2 * legendPtr->padY);
  545.     Blt_3DRectangleToPostScript(graphPtr, legendPtr->border, x, y, width,
  546.     height, legendPtr->borderWidth, legendPtr->relief);
  547.  
  548.     x += legendPtr->borderWidth;
  549.     y += legendPtr->borderWidth;
  550.  
  551.     symSize = legendPtr->fontPtr->ascent;
  552.     midPoint = (symSize / 2) + 1 + legendPtr->activeBW;
  553.  
  554.     labelX = TEXTHEIGHT(legendPtr->fontPtr) + legendPtr->activeBW +
  555.     (2 * legendPtr->ipadX);
  556.     symbolY = midPoint + legendPtr->ipadY;
  557.     symbolX = midPoint + legendPtr->ipadX;
  558.  
  559.     textAttr.fontPtr = legendPtr->fontPtr;
  560.     textAttr.anchor = TK_ANCHOR_W;
  561.     textAttr.theta = 0.0;
  562.     textAttr.fgColorPtr = graphPtr->marginFg;
  563.     textAttr.bgColorPtr = (XColor *)NULL;
  564.  
  565.     counter = 0;
  566.     startY = y;
  567.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  568.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  569.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  570.  
  571.     /*
  572.      * Print each symbol and label.  Check that the label isn't NULL and
  573.      * that the element has at least one data point.
  574.      */
  575.     if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  576.         (elemPtr->label == NULL)) {
  577.         continue;
  578.     }
  579.     textAttr.fgColorPtr = legendPtr->normalFg;
  580.     if (elemPtr->flags & LABEL_ACTIVE) {
  581.         Blt_3DRectangleToPostScript(graphPtr, legendPtr->activeBorder,
  582.         x, y, legendPtr->entryWidth, legendPtr->entryHeight,
  583.         legendPtr->activeBW, legendPtr->activeRelief);
  584.         textAttr.fgColorPtr = legendPtr->activeFg;
  585.     }
  586.     curPos.x = x + symbolX;
  587.     curPos.y = y + symbolY;
  588.     (*elemPtr->printSymbolsProc) (graphPtr, elemPtr, symSize, &curPos, 1,
  589.         ELEM_NORMAL);
  590.     curPos.x = x + labelX;
  591.     Blt_TextToPostScript(graphPtr, elemPtr->label, &textAttr, curPos.x,
  592.         curPos.y);
  593.     counter++;
  594.     if ((counter % legendPtr->numRows) > 0) {
  595.         y += legendPtr->entryHeight;
  596.     } else {
  597.         x += legendPtr->entryWidth;
  598.         y = startY;
  599.     }
  600.     }
  601. }
  602.  
  603. /*
  604.  *----------------------------------------------------------------------
  605.  *
  606.  * ConfigureLegend --
  607.  *
  608.  *     Routine to configure the legend.
  609.  *
  610.  * Results:
  611.  *    The return value is a standard Tcl result.
  612.  *
  613.  * Side Effects:
  614.  *    Graph will be redrawn to reflect the new legend attributes.
  615.  *
  616.  *----------------------------------------------------------------------
  617.  */
  618. static int
  619. ConfigureLegend(graphPtr, legendPtr, argc, argv, flags)
  620.     Graph *graphPtr;
  621.     Legend *legendPtr;
  622.     int argc;
  623.     char *argv[];
  624.     int flags;
  625. {
  626.     GC newGC;
  627.     XGCValues gcValues;
  628.     unsigned long gcMask;
  629.     int newFlag;
  630.  
  631.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin,
  632.         configSpecs, argc, argv, (char *)legendPtr, flags) != TCL_OK) {
  633.     return TCL_ERROR;
  634.     }
  635.     gcMask = GCForeground | GCFont;
  636.     gcValues.font = legendPtr->fontPtr->fid;
  637.     gcValues.foreground = legendPtr->normalFg->pixel;
  638.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  639.     if (legendPtr->normalGC != NULL) {
  640.     Tk_FreeGC(graphPtr->display, legendPtr->normalGC);
  641.     }
  642.     legendPtr->normalGC = newGC;
  643.  
  644.     gcMask |= GCBackground;
  645.     gcValues.foreground = legendPtr->activeFg->pixel;
  646.     gcValues.background = Tk_3DBorderColor(legendPtr->activeBorder)->pixel;
  647.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  648.     if (legendPtr->activeGC != NULL) {
  649.     Tk_FreeGC(graphPtr->display, legendPtr->activeGC);
  650.     }
  651.     legendPtr->activeGC = newGC;
  652.     newFlag = (legendPtr->anchorPos.x == DEF_POSITION);
  653.  
  654.     /*
  655.      *
  656.      *  Update the layout of the graph (and redraw the elements) if
  657.      *  any of the following legend options which affect the size of
  658.      *    the legend changed.
  659.      *
  660.      *        -activeborderwidth, -borderwidth
  661.      *        -font
  662.      *        -mapped
  663.      *        -ipadx, -ipady, -padx, -pady
  664.      *
  665.      *  If the position of the legend changed to/from the default
  666.      *  position, also indicate that a new layout is needed.
  667.      *
  668.      */
  669.     if ((legendPtr->useDefault != newFlag) || ((legendPtr->useDefault) &&
  670.         (Blt_OptionChanged(configSpecs, "-*borderwidth", "-*pad?",
  671.             "-mapped", "-font", (char *)NULL)))) {
  672.     graphPtr->flags |= (DIRTY | LAYOUT_ALL);
  673.     legendPtr->useDefault = newFlag;
  674.     }
  675.     graphPtr->flags |= REFRESH;
  676.     Blt_RedrawGraph(graphPtr);
  677.     return TCL_OK;
  678. }
  679.  
  680. /*
  681.  *----------------------------------------------------------------------
  682.  *
  683.  * DestroyLegend --
  684.  *
  685.  * Results:
  686.  *    None.
  687.  *
  688.  * Side effects:
  689.  *    Resources associated with the legend are freed.
  690.  *
  691.  *----------------------------------------------------------------------
  692.  */
  693. static void
  694. DestroyLegend(graphPtr)
  695.     Graph *graphPtr;
  696. {
  697.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  698.  
  699.     Tk_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
  700.     if (legendPtr->normalGC != NULL) {
  701.     Tk_FreeGC(graphPtr->display, legendPtr->normalGC);
  702.     }
  703.     if (legendPtr->activeGC != NULL) {
  704.     Tk_FreeGC(graphPtr->display, legendPtr->activeGC);
  705.     }
  706.     free((char *)legendPtr);
  707. }
  708.  
  709. /*
  710.  *----------------------------------------------------------------------
  711.  *
  712.  * Blt_CreateLegend --
  713.  *
  714.  *     Creates and initializes a legend structure with default settings
  715.  *
  716.  * Results:
  717.  *    The return value is a standard Tcl result.
  718.  *
  719.  *----------------------------------------------------------------------
  720.  */
  721. /*ARGSUSED*/
  722. int
  723. Blt_CreateLegend(graphPtr)
  724.     Graph *graphPtr;
  725. {
  726.     Legend *legendPtr;
  727.  
  728.     legendPtr = (Legend *)calloc(1, sizeof(Legend));
  729.     if (legendPtr == NULL) {
  730.     graphPtr->interp->result = "can't allocate legend structure";
  731.     return TCL_ERROR;
  732.     }
  733.     legendPtr->mapped = TRUE;
  734.     legendPtr->anchorPos.x = legendPtr->anchorPos.y = DEF_POSITION;
  735.     legendPtr->useDefault = 1;
  736.     legendPtr->relief = TK_RELIEF_SUNKEN;
  737.     legendPtr->activeRelief = TK_RELIEF_FLAT;
  738.     legendPtr->activeBW = legendPtr->borderWidth = 2;
  739.     legendPtr->ipadX = legendPtr->ipadY = 1;
  740.     legendPtr->padX = 4;
  741.     legendPtr->padY = 0;
  742.     legendPtr->anchor = TK_ANCHOR_NW;
  743.     legendPtr->displayProc = DisplayLegend;
  744.     legendPtr->printProc = PrintLegend;
  745.     legendPtr->destroyProc = DestroyLegend;
  746.     legendPtr->geomProc = ComputeLegendGeometry;
  747.     graphPtr->legendPtr = (GraphLegend *)legendPtr;
  748.     return (ConfigureLegend(graphPtr, legendPtr, 0, (char **)NULL, 0));
  749. }
  750.  
  751. /*
  752.  *----------------------------------------------------------------------
  753.  *
  754.  * GetEntry --
  755.  *
  756.  *     Find the legend entry from the given argument.  The argument
  757.  *    can be either a screen position "@x,y" or the name of an
  758.  *    element.
  759.  *
  760.  *    I don't know how useful it is to test with the name of an
  761.  *    element.
  762.  *
  763.  * Results:
  764.  *    The return value is a standard Tcl result.
  765.  *
  766.  * Side Effects:
  767.  *    Graph will be redrawn to reflect the new legend attributes.
  768.  *
  769.  *----------------------------------------------------------------------
  770.  */
  771. static int
  772. GetEntry(graphPtr, legendPtr, argc, argv)
  773.     Graph *graphPtr;
  774.     Legend *legendPtr;
  775.     int argc;
  776.     char *argv[];
  777. {
  778.     register Element *elemPtr;
  779.  
  780.     if ((!legendPtr->mapped) || (legendPtr->numLabels == 0)) {
  781.     return TCL_OK;
  782.     }
  783.     if (argc != 4) {
  784.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  785.         argv[0], " legend search index\"", NULL);
  786.     return TCL_ERROR;
  787.     }
  788.     elemPtr = LocateElement(graphPtr, legendPtr, argv[3]);
  789.     if (elemPtr != NULL) {
  790.     Tcl_SetResult(graphPtr->interp, elemPtr->id, TCL_STATIC);
  791.     }
  792.     return TCL_OK;
  793. }
  794.  
  795. /*
  796.  *----------------------------------------------------------------------
  797.  *
  798.  * ActivateEntry --
  799.  *
  800.  *     Routine to configure the legend.
  801.  *
  802.  * Results:
  803.  *    The return value is a standard Tcl result.
  804.  *
  805.  * Side Effects:
  806.  *    Graph will be redrawn to reflect the new legend attributes.
  807.  *
  808.  *----------------------------------------------------------------------
  809.  */
  810. static int
  811. ActivateEntry(graphPtr, legendPtr, argc, argv)
  812.     Graph *graphPtr;
  813.     Legend *legendPtr;
  814.     int argc;
  815.     char *argv[];
  816. {
  817.     Element *elemPtr;
  818.     Tcl_HashEntry *entryPtr;
  819.     int active;
  820.     int redrawNeeded;
  821.     register int i;
  822.  
  823.     if (argc < 4) {
  824.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  825.         argv[0], " legend ", argv[2], " name...\"", NULL);
  826.     return TCL_ERROR;
  827.     }
  828.     active = (argv[2][0] == 'a') ? LABEL_ACTIVE : 0;
  829.     redrawNeeded = 0;
  830.     for (i = 3; i < argc; i++) {
  831.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[i]);
  832.     if (entryPtr == NULL) {
  833.         Tcl_AppendResult(graphPtr->interp, "can't find an element \"",
  834.         argv[i], "\"", (char *)NULL);
  835.         return TCL_ERROR;
  836.     }
  837.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  838.     if (active != (elemPtr->flags & LABEL_ACTIVE)) {
  839.         elemPtr->flags ^= LABEL_ACTIVE;
  840.         if (elemPtr->label != NULL) {
  841.         redrawNeeded++;
  842.         }
  843.     }
  844.     }
  845.     if ((redrawNeeded) && (legendPtr->mapped)) {
  846.     /*
  847.      * We need to redraw the legend. If there isn't a redraw
  848.      * already pending for the whole graph, we can redraw just the
  849.      * legend, calling the legend's display routine rather than
  850.      * the graph's.  The window must be mapped though.
  851.      */
  852.     if (graphPtr->flags & REDRAW_PENDING) {
  853.         graphPtr->flags |= REFRESH;
  854.     } else if (!(graphPtr->flags & LEGEND_ONLY)) {
  855.         if ((graphPtr->tkwin != NULL) && (Tk_IsMapped(graphPtr->tkwin))) {
  856.         Tk_DoWhenIdle((Tk_IdleProc *) DisplayLegend,
  857.             (ClientData)graphPtr);
  858.         graphPtr->flags |= LEGEND_ONLY;
  859.         }
  860.     }
  861.     }
  862.     return TCL_OK;
  863. }
  864.  
  865. /*
  866.  *----------------------------------------------------------------------
  867.  *
  868.  * Blt_LegendCmd --
  869.  *
  870.  * Results:
  871.  *    The return value is a standard Tcl result.
  872.  *
  873.  * Side Effects:
  874.  *    Legend is possibly redrawn.
  875.  *
  876.  *----------------------------------------------------------------------
  877.  */
  878. /* ARGSUSED */
  879. int
  880. Blt_LegendCmd(graphPtr, argc, argv)
  881.     Graph *graphPtr;
  882.     int argc;
  883.     char **argv;
  884. {
  885.     char c;
  886.     int length;
  887.     Tcl_Interp *interp = graphPtr->interp;
  888.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  889.  
  890.     /* Initialize the crosshairs on first call */
  891.  
  892.     if (argc < 3) {
  893.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  894.         " legend option ?args?\"", NULL);
  895.     return TCL_ERROR;
  896.     }
  897.     c = argv[2][0];
  898.     length = strlen(argv[2]);
  899.     if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)) {
  900.     int flags = TK_CONFIG_ARGV_ONLY;
  901.  
  902.     if (argc == 3) {
  903.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  904.             configSpecs, (char *)legendPtr, (char *)NULL, flags));
  905.     } else if (argc == 4) {
  906.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  907.             configSpecs, (char *)legendPtr, argv[3], flags));
  908.     }
  909.     if (ConfigureLegend(graphPtr, legendPtr, argc - 3,
  910.         argv + 3, flags) != TCL_OK) {
  911.         return TCL_ERROR;
  912.     }
  913.     } else if ((c == 'g') && (strncmp(argv[2], "get", length) == 0)) {
  914.     return (GetEntry(graphPtr, legendPtr, argc, argv));
  915.     } else if ((c == 'a') && (strncmp(argv[2], "activate", length) == 0)) {
  916.     return (ActivateEntry(graphPtr, legendPtr, argc, argv));
  917.     } else if ((c == 'd') && (strncmp(argv[2], "deactivate", length) == 0)) {
  918.     return (ActivateEntry(graphPtr, legendPtr, argc, argv));
  919.     } else {
  920.     Tcl_AppendResult(interp, "bad legend option \"", argv[2],
  921.         "\": should be activate, configure, deacitvate, or get",
  922.         (char *)NULL);
  923.     return TCL_ERROR;
  924.     }
  925.     return TCL_OK;
  926. }
  927.