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

  1. /*
  2.  * bltGraph.c --
  3.  *
  4.  *    This module implements a graph widget for the
  5.  *    Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortuous action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  * Graph widget created by Sani Nassif and George Howlett.
  26.  */
  27.  
  28. /*
  29.  * To do:
  30.  *
  31.  * 1) Fix log manual scale for log axes.
  32.  *
  33.  * 2) Update manual pages.
  34.  *
  35.  * 3) Update comments.
  36.  *
  37.  * 5) Contour and flow graphs
  38.  *
  39.  * 6) Account for roundoff error when calculating bar widths
  40.  *
  41.  * 7) Arrows for line tags
  42.  *
  43.  * 9) Make sure reasonable defaults show for configuration items
  44.  *
  45.  * 10) Rubberbox for zooming???
  46.  */
  47.  
  48. #include "blt.h"
  49. #include "bltGraph.h"
  50. #include "bltGrTag.h"
  51. #include "bltGrElem.h"
  52. #include <X11/Xutil.h>
  53. #include <X11/Xatom.h>
  54.  
  55. #ifndef GRAPH_VERSION
  56. #define GRAPH_VERSION "4.0"
  57. #endif
  58.  
  59. static char *classNames[] =
  60. {
  61.     "Blt_graph", "Blt_barchart"
  62. };
  63.  
  64. static unsigned int configFlags[] =
  65. {
  66.     XYGRAPH_MASK, BARCHART_MASK
  67. };
  68.  
  69. #define DEF_GRAPH_BAR_WIDTH    "0.95"
  70. #define DEF_GRAPH_BG_COLOR    BISQUE1
  71. #define DEF_GRAPH_BG_MONO    WHITE
  72. #define DEF_GRAPH_BORDER_WIDTH    "0"
  73. #define DEF_GRAPH_BORDER_WIDTH  "0"
  74. #define DEF_GRAPH_BUFFERED    "1"
  75. #define DEF_GRAPH_CURSOR      "crosshair"
  76. #define DEF_GRAPH_FG_COLOR    BLACK
  77. #define DEF_GRAPH_FG_MONO    BLACK
  78. #define DEF_GRAPH_FONT        "*-Helvetica-Bold-R-Normal-*-120-*"
  79. #define DEF_GRAPH_HALO        "0.5i"
  80. #define DEF_GRAPH_HALO_BAR    "0.1i"
  81. #define DEF_GRAPH_HEIGHT    "400"
  82. #define DEF_GRAPH_INVERT_XY    "0"
  83. #define DEF_GRAPH_MARGIN    "0"
  84. #define DEF_GRAPH_PLOT_BG_COLOR    ANTIQUEWHITE1
  85. #define DEF_GRAPH_PLOT_BG_MONO    WHITE
  86. #define DEF_GRAPH_PLOT_BW_COLOR "2"
  87. #define DEF_GRAPH_PLOT_BW_MONO  "0"
  88. #define DEF_GRAPH_PLOT_RELIEF    "sunken"
  89. #define DEF_GRAPH_RELIEF    "flat"
  90. #define DEF_GRAPH_TITLE        (char *)NULL
  91. #define DEF_GRAPH_WIDTH        "400"
  92.  
  93. static Tk_ConfigSpec configSpecs[] =
  94. {
  95.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  96.     DEF_GRAPH_BG_COLOR, Tk_Offset(Graph, border),
  97.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  98.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  99.     DEF_GRAPH_BG_MONO, Tk_Offset(Graph, border),
  100.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  101.     {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
  102.     DEF_GRAPH_BAR_WIDTH, Tk_Offset(Graph, barWidth), BARCHART_MASK},
  103.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,
  104.     ALL_MASK},
  105.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0,
  106.     ALL_MASK},
  107.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  108.     DEF_GRAPH_BORDER_WIDTH, Tk_Offset(Graph, borderWidth),
  109.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  110.     {TK_CONFIG_PIXELS, "-bottommargin", "bottomMargin", "Margin",
  111.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, bottomMargin), ALL_MASK},
  112.     {TK_CONFIG_BOOLEAN, "-bufferelements", "bufferElements", "BufferElements",
  113.     DEF_GRAPH_BUFFERED, Tk_Offset(Graph, buffered),
  114.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  115.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  116.     DEF_GRAPH_CURSOR, Tk_Offset(Graph, cursor),
  117.     ALL_MASK | TK_CONFIG_NULL_OK},
  118.     {TK_CONFIG_FONT, "-font", "font", "Font",
  119.     DEF_GRAPH_FONT, Tk_Offset(Graph, fontPtr), ALL_MASK},
  120.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0,
  121.     ALL_MASK},
  122.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  123.     DEF_GRAPH_FG_COLOR, Tk_Offset(Graph, marginFg),
  124.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  125.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  126.     DEF_GRAPH_FG_MONO, Tk_Offset(Graph, marginFg),
  127.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  128.     {TK_CONFIG_PIXELS, "-halo", "halo", "Halo",
  129.     DEF_GRAPH_HALO_BAR, Tk_Offset(Graph, halo), BARCHART_MASK},
  130.     {TK_CONFIG_PIXELS, "-halo", "halo", "Halo",
  131.     DEF_GRAPH_HALO, Tk_Offset(Graph, halo), XYGRAPH_MASK},
  132.     {TK_CONFIG_PIXELS, "-height", "height", "Height",
  133.     DEF_GRAPH_HEIGHT, Tk_Offset(Graph, reqHeight),
  134.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  135.     {TK_CONFIG_BOOLEAN, "-invertxy", "invertXY", "InvertXY",
  136.     DEF_GRAPH_INVERT_XY, Tk_Offset(Graph, inverted),
  137.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  138.     {TK_CONFIG_PIXELS, "-leftmargin", "leftMargin", "Margin",
  139.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, leftMargin),
  140.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  141.     {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
  142.     DEF_GRAPH_PLOT_BG_MONO, Tk_Offset(Graph, plotBg),
  143.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  144.     {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
  145.     DEF_GRAPH_PLOT_BG_COLOR, Tk_Offset(Graph, plotBg),
  146.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  147.     {TK_CONFIG_INT, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
  148.     DEF_GRAPH_PLOT_BW_COLOR, Tk_Offset(Graph, plotBW),
  149.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  150.     {TK_CONFIG_INT, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
  151.     DEF_GRAPH_PLOT_BW_MONO, Tk_Offset(Graph, plotBW),
  152.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  153.     {TK_CONFIG_RELIEF, "-plotrelief", "plotRelief", "Relief",
  154.     DEF_GRAPH_PLOT_RELIEF, Tk_Offset(Graph, plotRelief),
  155.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  156.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  157.     DEF_GRAPH_RELIEF, Tk_Offset(Graph, relief),
  158.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  159.     {TK_CONFIG_PIXELS, "-rightmargin", "rightMargin", "Margin",
  160.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, rightMargin),
  161.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  162.     {TK_CONFIG_STRING, "-title", "title", "Title",
  163.     DEF_GRAPH_TITLE, Tk_Offset(Graph, title),
  164.     ALL_MASK | TK_CONFIG_NULL_OK},
  165.     {TK_CONFIG_PIXELS, "-topmargin", "topMargin", "Margin",
  166.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, topMargin),
  167.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  168.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  169.     DEF_GRAPH_WIDTH, Tk_Offset(Graph, reqWidth),
  170.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  171.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  172. };
  173.  
  174. double Blt_negInfinity, Blt_posInfinity;
  175.  
  176. extern int Blt_CreateAxis _ANSI_ARGS_((Graph *graphPtr, AxisType type, int));
  177. extern int Blt_AxisCmd _ANSI_ARGS_((Graph *graphPtr, GraphAxis *axisPtr,
  178.     int argc, char **argv, int flags));
  179. extern void Blt_ComputeAxes _ANSI_ARGS_((Graph *graphPtr));
  180. extern void Blt_UpdateAxisBackgrounds _ANSI_ARGS_((Graph *graphPtr,
  181.     XColor *bgColorPtr));
  182. extern int Blt_ComputeLayout _ANSI_ARGS_((Graph *graphPtr));
  183. extern int Blt_LegendCmd _ANSI_ARGS_((Graph *graphPtr, int argc, char **argv));
  184. extern int Blt_CreateLegend _ANSI_ARGS_((Graph *graphPtr));
  185. extern int Blt_CreatePostScript _ANSI_ARGS_((Graph *graphPtr));
  186. extern int Blt_CreateCrosshairs _ANSI_ARGS_((Graph *graphPtr));
  187. extern int Blt_CrosshairsCmd _ANSI_ARGS_((Graph *graphPtr, int argc,
  188.     char **argv));
  189. static void DisplayGraph _ANSI_ARGS_((ClientData clientData));
  190. static void DestroyGraph _ANSI_ARGS_((ClientData clientData));
  191. static int GraphWidgetCmd _ANSI_ARGS_((ClientData clientData,
  192.     Tcl_Interp *interp, int argc, char **argv));
  193. extern int Blt_TagCmd _ANSI_ARGS_((Graph *graphPtr, int argc, char **argv));
  194. extern int Blt_ElementCmd _ANSI_ARGS_((Graph *graphPtr, int argc,
  195.     char **argv));
  196.  
  197. /*
  198.  *--------------------------------------------------------------
  199.  *
  200.  * Blt_RedrawGraph --
  201.  *
  202.  *    Tell the Tk dispatcher to call the graph display routine at
  203.  *    the next idle point.  This request is made only if the window
  204.  *    is mapped and no other redraw request is pending.
  205.  *
  206.  * Results:
  207.  *    None.
  208.  *
  209.  * Side effects:
  210.  *    The window is eventually redisplayed.
  211.  *
  212.  *--------------------------------------------------------------
  213.  */
  214. void
  215. Blt_RedrawGraph(graphPtr)
  216.     Graph *graphPtr;        /* Graph widget record */
  217. {
  218.     if (Tk_IsMapped(graphPtr->tkwin) && !(graphPtr->flags & REDRAW_PENDING)) {
  219.     Tk_DoWhenIdle(DisplayGraph, (ClientData)graphPtr);
  220.     graphPtr->flags |= REDRAW_PENDING;
  221.     }
  222. }
  223.  
  224. /*
  225.  *--------------------------------------------------------------
  226.  *
  227.  * GraphEventProc --
  228.  *
  229.  *    This procedure is invoked by the Tk dispatcher for various
  230.  *    events on graphs.
  231.  *
  232.  * Results:
  233.  *    None.
  234.  *
  235.  * Side effects:
  236.  *    When the window gets deleted, internal structures get cleaned
  237.  *    up.  When it gets exposed, the graph is eventually redisplayed.
  238.  *
  239.  *--------------------------------------------------------------
  240.  */
  241. static void
  242. GraphEventProc(clientData, eventPtr)
  243.     ClientData clientData;    /* Graph widget record */
  244.     register XEvent *eventPtr;    /* Event which triggered call to routine */
  245. {
  246.     register Graph *graphPtr = (Graph *)clientData;
  247.  
  248.     if (eventPtr->type == Expose) {
  249.     if (eventPtr->xexpose.count == 0) {
  250.         graphPtr->flags |= REFRESH;
  251.         Blt_RedrawGraph(graphPtr);
  252.     }
  253.     } else if (eventPtr->type == DestroyNotify) {
  254.     Tcl_DeleteCommand(graphPtr->interp, Tk_PathName(graphPtr->tkwin));
  255.     graphPtr->tkwin = NULL;
  256.     if (graphPtr->flags & REDRAW_PENDING) {
  257.         Tk_CancelIdleCall(DisplayGraph, (ClientData)graphPtr);
  258.     }
  259.     Tk_EventuallyFree((ClientData)graphPtr, DestroyGraph);
  260.     } else if (eventPtr->type == ConfigureNotify) {
  261.     graphPtr->flags |= (LAYOUT_ALL | REFRESH);
  262.     Blt_RedrawGraph(graphPtr);
  263.     }
  264. }
  265.  
  266. /*
  267.  *--------------------------------------------------------------
  268.  *
  269.  * GraphCoords --
  270.  *
  271.  *    This procedure returns a list of the graph coordinate
  272.  *    values corresponding with the given window X and Y
  273.  *    coordinate positions.
  274.  *
  275.  * Results:
  276.  *    Returns a standard Tcl result.  The interp->result field is
  277.  *    a Tcl list of the corresponding graph X and Y coordinates.
  278.  *    If an error occurred while parsing the window positions,
  279.  *    TCL_ERROR is returned, and interp->result will contain
  280.  *    the error message.
  281.  *
  282.  *--------------------------------------------------------------
  283.  */
  284. static int
  285. GraphCoords(graphPtr, argc, argv)
  286.     Graph *graphPtr;        /* Graph widget record */
  287.     int argc;
  288.     char **argv;
  289. {
  290.     int winX, winY;        /* Integer window coordinates representation */
  291.     char string[TCL_DOUBLE_SPACE + 1];
  292.     double x, y;
  293.  
  294.     if (argc != 4) {
  295.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  296.         argv[0], " invtransform winX winY\"", NULL);
  297.     return TCL_ERROR;
  298.     }
  299.     if (Tcl_GetInt(graphPtr->interp, argv[2], &winX) != TCL_OK ||
  300.     Tcl_GetInt(graphPtr->interp, argv[3], &winY) != TCL_OK) {
  301.     return TCL_ERROR;
  302.     }
  303.     /*
  304.      * Perform the reverse transformation from window coordinates to
  305.      * data coordinates
  306.      */
  307.     x = Blt_InvTransform(graphPtr->axisArr[X1_AXIS], winX);
  308.     y = Blt_InvTransform(graphPtr->axisArr[Y1_AXIS], winY);
  309.  
  310.     if (graphPtr->inverted) {
  311.     Tcl_PrintDouble(graphPtr->interp, y, string);
  312.     Tcl_AppendElement(graphPtr->interp, string);
  313.     Tcl_PrintDouble(graphPtr->interp, x, string);
  314.     Tcl_AppendElement(graphPtr->interp, string);
  315.     } else {
  316.     Tcl_PrintDouble(graphPtr->interp, x, string);
  317.     Tcl_AppendElement(graphPtr->interp, string);
  318.     Tcl_PrintDouble(graphPtr->interp, y, string);
  319.     Tcl_AppendElement(graphPtr->interp, string);
  320.     }
  321.     return TCL_OK;
  322. }
  323.  
  324. /*
  325.  *--------------------------------------------------------------
  326.  *
  327.  * WindowCoords --
  328.  *
  329.  *    This procedure returns a list of the window coordinates
  330.  *    corresponding with the given graph x and y coordinates.
  331.  *
  332.  * Results:
  333.  *    Returns a standard Tcl result.  interp->result contains
  334.  *    the list of the graph coordinates. If an error occurred
  335.  *    while parsing the window positions, TCL_ERROR is returned,
  336.  *    then interp->result will contain an error message.
  337.  *
  338.  *--------------------------------------------------------------
  339.  */
  340. static int
  341. WindowCoords(graphPtr, argc, argv)
  342.     Graph *graphPtr;        /* Graph widget record */
  343.     int argc;
  344.     char **argv;
  345. {
  346.     double x, y;
  347.     char string[TCL_DOUBLE_SPACE + 1];
  348.  
  349.     if (argc != 4) {
  350.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  351.         argv[0], " transform x y\"", NULL);
  352.     return TCL_ERROR;
  353.     }
  354.     if (Tcl_ExprDouble(graphPtr->interp, argv[2], &x) != TCL_OK ||
  355.     Tcl_ExprDouble(graphPtr->interp, argv[3], &y) != TCL_OK) {
  356.     return TCL_ERROR;
  357.     }
  358.     /* Perform the transformation from window coordinates to data */
  359.     if (graphPtr->inverted) {
  360.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[Y1_AXIS], y));
  361.     Tcl_AppendElement(graphPtr->interp, string);
  362.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[X1_AXIS], x));
  363.     Tcl_AppendElement(graphPtr->interp, string);
  364.     } else {
  365.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[X1_AXIS], x));
  366.     Tcl_AppendElement(graphPtr->interp, string);
  367.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[Y1_AXIS], y));
  368.     Tcl_AppendElement(graphPtr->interp, string);
  369.     }
  370.     return TCL_OK;
  371. }
  372.  
  373. /*
  374.  *--------------------------------------------------------------
  375.  *
  376.  * AdjustAxisPointers --
  377.  *
  378.  *    Sets the axis pointers according to whether the axis is
  379.  *    inverted on not.  The axis locations are also reset.
  380.  *
  381.  * Results:
  382.  *    None.
  383.  *
  384.  *--------------------------------------------------------------
  385.  */
  386. static void
  387. AdjustAxisPointers(graphPtr)
  388.     Graph *graphPtr;        /* Graph widget record */
  389. {
  390.     if (graphPtr->inverted) {
  391.     graphPtr->bottomAxis = graphPtr->axisArr[Y1_AXIS];
  392.     graphPtr->leftAxis = graphPtr->axisArr[X1_AXIS];
  393.     graphPtr->topAxis = graphPtr->axisArr[Y2_AXIS];
  394.     graphPtr->rightAxis = graphPtr->axisArr[X2_AXIS];
  395.     } else {
  396.     graphPtr->bottomAxis = graphPtr->axisArr[X1_AXIS];
  397.     graphPtr->leftAxis = graphPtr->axisArr[Y1_AXIS];
  398.     graphPtr->topAxis = graphPtr->axisArr[X2_AXIS];
  399.     graphPtr->rightAxis = graphPtr->axisArr[Y2_AXIS];
  400.     }
  401.     graphPtr->bottomAxis->location = BOTTOM_AXIS;
  402.     graphPtr->leftAxis->location = LEFT_AXIS;
  403.     graphPtr->topAxis->location = TOP_AXIS;
  404.     graphPtr->rightAxis->location = RIGHT_AXIS;
  405. }
  406.  
  407. /*
  408.  *----------------------------------------------------------------------
  409.  *
  410.  * DestroyGraph --
  411.  *
  412.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  413.  *    to clean up the internal structure of a graph at a safe time
  414.  *    (when no-one is using it anymore).
  415.  *
  416.  * Results:
  417.  *    None.
  418.  *
  419.  * Side effects:
  420.  *    Everything associated with the widget is freed up.
  421.  *
  422.  *----------------------------------------------------------------------
  423.  */
  424. static void
  425. DestroyGraph(clientData)
  426.     ClientData clientData;
  427. {
  428.     register Graph *graphPtr = (Graph *)clientData;
  429.     Tcl_HashEntry *entryPtr;
  430.     Tcl_HashSearch cursor;
  431.     Element *elemPtr;
  432.     Tag *tagPtr;
  433.     register int i;
  434.     GraphAxis *axisPtr;
  435.  
  436.     /*
  437.      * Destroy the individual components of the graph: elements, tags,
  438.      * X and Y axes, legend, display lists etc.
  439.      */
  440.     for (entryPtr = Tcl_FirstHashEntry(&(graphPtr->elemTable), &cursor);
  441.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  442.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  443.     (*elemPtr->destroyProc) (graphPtr, elemPtr);
  444.     }
  445.     Tcl_DeleteHashTable(&(graphPtr->elemTable));
  446.     Blt_ClearList(&(graphPtr->elemList));
  447.     for (entryPtr = Tcl_FirstHashEntry(&(graphPtr->tagTable), &cursor);
  448.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  449.     tagPtr = (Tag *)Tcl_GetHashValue(entryPtr);
  450.     (*tagPtr->destroyProc) (graphPtr, tagPtr);
  451.     }
  452.     Tcl_DeleteHashTable(&(graphPtr->tagTable));
  453.     Blt_ClearList(&(graphPtr->tagList));
  454.  
  455.     for (i = 0; i < 4; i++) {
  456.     axisPtr = graphPtr->axisArr[i];
  457.     (*axisPtr->destroyProc) (graphPtr, axisPtr);
  458.     }
  459.     (*graphPtr->legendPtr->destroyProc) (graphPtr);
  460.     (*graphPtr->postscript->destroyProc) (graphPtr);
  461.     (*graphPtr->crosshairs->destroyProc) (graphPtr);
  462.  
  463.     /* Release allocated X resources and memory. */
  464.     if (graphPtr->marginGC != NULL) {
  465.     Tk_FreeGC(graphPtr->display, graphPtr->marginGC);
  466.     }
  467.     if (graphPtr->marginFillGC != NULL) {
  468.     Tk_FreeGC(graphPtr->display, graphPtr->marginFillGC);
  469.     }
  470.     if (graphPtr->elemMap != None) {
  471.     XFreePixmap(graphPtr->display, graphPtr->elemMap);
  472.     }
  473.     Tk_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display,
  474.     configFlags[graphPtr->type]);
  475.     free((char *)graphPtr);
  476. }
  477.  
  478. /*
  479.  *----------------------------------------------------------------------
  480.  *
  481.  * CreateGraph --
  482.  *
  483.  *    This procedure creates and initializes a new widget.
  484.  *
  485.  * Results:
  486.  *    The return value is a pointer to a structure describing
  487.  *    the new widget.  If an error occurred, then the return
  488.  *    value is NULL and an error message is left in interp->result.
  489.  *
  490.  * Side effects:
  491.  *    Memory is allocated, a Tk_Window is created, etc.
  492.  *
  493.  *----------------------------------------------------------------------
  494.  */
  495.  
  496. static Graph *
  497. CreateGraph(interp, parent, pathName, type)
  498.     Tcl_Interp *interp;
  499.     Tk_Window parent;
  500.     char *pathName;
  501.     GraphClassType type;
  502. {
  503.     Graph *graphPtr;
  504.     Tk_Window tkwin;
  505.     unsigned int flags;
  506.     register int i;
  507.  
  508.     tkwin = Tk_CreateWindowFromPath(interp, parent, pathName, (char *)NULL);
  509.     if (tkwin == (Tk_Window)NULL) {
  510.     return (Graph *) NULL;
  511.     }
  512.     graphPtr = (Graph *)calloc(1, sizeof(Graph));
  513.     if (graphPtr == (Graph *)NULL) {
  514.     interp->result = "can't allocate graph structure";
  515.     return (Graph *) NULL;
  516.     }
  517.     Tk_SetClass(tkwin, classNames[type]);
  518.  
  519.     /* Initialize the data structure for the graph. */
  520.  
  521.     graphPtr->tkwin = tkwin;
  522.     graphPtr->pathName = Tk_PathName(tkwin);
  523.     graphPtr->display = Tk_Display(tkwin);
  524.     graphPtr->interp = interp;
  525.     graphPtr->type = type;
  526.     graphPtr->width = graphPtr->height = 400;
  527.     graphPtr->reqWidth = graphPtr->reqHeight = 400;
  528.     graphPtr->buffered = 1;
  529.     graphPtr->plotRelief = TK_RELIEF_SUNKEN;
  530.     graphPtr->relief = TK_RELIEF_FLAT;
  531.     graphPtr->flags |= (DIRTY | LAYOUT_ALL);
  532.  
  533.     Tcl_InitHashTable(&(graphPtr->elemTable), TCL_STRING_KEYS);
  534.     Blt_InitLinkedList(&(graphPtr->elemList), TCL_STRING_KEYS);
  535.     Tcl_InitHashTable(&(graphPtr->tagTable), TCL_STRING_KEYS);
  536.     Blt_InitLinkedList(&(graphPtr->tagList), TCL_STRING_KEYS);
  537.  
  538.     graphPtr->nextTagId = 1;
  539.     /*
  540.      * Axes and tags (text, bitmap) need private GCs, so create the
  541.      * window now.
  542.      */
  543.     Tk_MakeWindowExist(tkwin);
  544.  
  545.     flags = configFlags[type];
  546.     for (i = 0; i < 4; i++) {
  547.     if (Blt_CreateAxis(graphPtr, (AxisType)i, flags) != TCL_OK) {
  548.         goto error;
  549.     }
  550.     }
  551.     AdjustAxisPointers(graphPtr);
  552.  
  553.     if (Blt_CreatePostScript(graphPtr) != TCL_OK) {
  554.     goto error;
  555.     }
  556.     if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
  557.     goto error;
  558.     }
  559.     if (Blt_CreateLegend(graphPtr) != TCL_OK) {
  560.     goto error;
  561.     }
  562.     /* Compute the initial axis ticks and labels */
  563.  
  564.     Blt_ComputeAxes(graphPtr);
  565.     Tk_CreateEventHandler(graphPtr->tkwin, ExposureMask | StructureNotifyMask,
  566.     GraphEventProc, (ClientData)graphPtr);
  567.     Tcl_CreateCommand(interp, Tk_PathName(graphPtr->tkwin), GraphWidgetCmd,
  568.     (ClientData)graphPtr, (Tcl_CmdDeleteProc *)NULL);
  569.     return (graphPtr);
  570.  
  571.   error:
  572.     if (tkwin != (Tk_Window)NULL) {
  573.     Tk_DestroyWindow(tkwin);
  574.     }
  575.     return (Graph *) NULL;
  576. }
  577.  
  578. /*
  579.  *----------------------------------------------------------------------
  580.  *
  581.  * ConfigureGraph --
  582.  *
  583.  *    This procedure is called to process an argv/argc list, plus
  584.  *    the Tk option database, in order to configure (or
  585.  *    reconfigure) a graph widget.
  586.  *
  587.  * Results:
  588.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  589.  *    returned, then interp->result contains an error message.
  590.  *
  591.  * Side effects:
  592.  *    Configuration information, such as text string, colors, font,
  593.  *    etc. get set for graphPtr;  old resources get freed, if there
  594.  *    were any.  The graph is redisplayed.
  595.  *
  596.  *----------------------------------------------------------------------
  597.  */
  598. static int
  599. ConfigureGraph(graphPtr, argc, argv, flags)
  600.     register Graph *graphPtr;    /* Graph widget record */
  601.     int argc;            /* Number of configuration arguments */
  602.     char **argv;        /* Configuration arguments */
  603.     unsigned int flags;        /* Configuration flags */
  604. {
  605.     XColor *colorPtr;
  606.     GC newGC;
  607.     XGCValues gcValues;
  608.     unsigned long gcMask;
  609.  
  610.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  611.         argc, argv, (char *)graphPtr, flags) != TCL_OK) {
  612.     return TCL_ERROR;
  613.     }
  614.     if ((graphPtr->reqWidth < 1) || (graphPtr->reqHeight < 1)) {
  615.     Tcl_AppendResult(graphPtr->interp,
  616.         "impossible width/height specifications for \"",
  617.         Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
  618.     return TCL_ERROR;
  619.     }
  620.     Tk_GeometryRequest(graphPtr->tkwin, graphPtr->reqWidth,
  621.     graphPtr->reqHeight);
  622.     Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
  623.     Tk_SetBackgroundFromBorder(graphPtr->tkwin, graphPtr->border);
  624.  
  625.     colorPtr = Tk_3DBorderColor(graphPtr->border);
  626.  
  627.     /* Update background color for axis text GCs */
  628.     Blt_UpdateAxisBackgrounds(graphPtr, colorPtr);
  629.  
  630.     /*
  631.      * Create GCs for interior and exterior regions, and a background
  632.      * GC for clearing the margins with XFillRectangle
  633.      */
  634.     gcValues.foreground = graphPtr->marginFg->pixel;
  635.     gcValues.background = colorPtr->pixel;
  636.     gcValues.font = graphPtr->fontPtr->fid;
  637.     gcMask = (GCForeground | GCBackground | GCFont);
  638.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  639.     if (graphPtr->marginGC != NULL) {
  640.     Tk_FreeGC(graphPtr->display, graphPtr->marginGC);
  641.     }
  642.     graphPtr->marginGC = newGC;
  643.     gcValues.foreground = colorPtr->pixel;
  644.     gcValues.background = graphPtr->marginFg->pixel;
  645.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  646.     if (graphPtr->marginFillGC != NULL) {
  647.     Tk_FreeGC(graphPtr->display, graphPtr->marginFillGC);
  648.     }
  649.     graphPtr->marginFillGC = newGC;
  650.     gcValues.foreground = graphPtr->plotBg->pixel;
  651.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  652.     if (graphPtr->plotFillGC != NULL) {
  653.     Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
  654.     }
  655.     graphPtr->plotFillGC = newGC;
  656.     /*
  657.      * If the -inverted option changed, we need to readjust the pointers
  658.      * to the axes and recompute the their scales.
  659.      */
  660.     if (Blt_OptionChanged(configSpecs, "-invertxy", (char *)NULL)) {
  661.     AdjustAxisPointers(graphPtr);
  662.     Blt_ComputeAxes(graphPtr);
  663.     }
  664.     /*
  665.      * Free the pixmap if we're not buffering the display of elements
  666.      * anymore.
  667.      */
  668.     if ((!graphPtr->buffered) && (graphPtr->elemMap != None)) {
  669.     XFreePixmap(graphPtr->display, graphPtr->elemMap);
  670.     graphPtr->elemMap = None;
  671.     }
  672.     /*
  673.      * Reconfigure the crosshairs in case the plotting area's
  674.      * background color changed
  675.      */
  676.     (*graphPtr->crosshairs->configProc) (graphPtr);
  677.  
  678.     /*
  679.      *  Update the layout of the graph (and redraw the elements) if
  680.      *  any of the following graph options which affect the size of
  681.      *    the plotting area has changed.
  682.      *
  683.      *      -borderwidth, -plotborderwidth
  684.      *        -font, -title
  685.      *        -width, -height
  686.      *        -invertxy
  687.      *        -bottommargin, -leftmargin, -rightmargin, -topmargin
  688.      *
  689.      */
  690.     if (Blt_OptionChanged(configSpecs, "-invertxy", "-title", "-font",
  691.         "-*margin", "-*width", "-height", (char *)NULL)) {
  692.     graphPtr->flags |= (DIRTY | LAYOUT_ALL);
  693.     }
  694.     graphPtr->flags |= REFRESH;
  695.     Blt_RedrawGraph(graphPtr);
  696.     return TCL_OK;
  697. }
  698.  
  699. /*
  700.  *--------------------------------------------------------------
  701.  *
  702.  * GraphCmd --
  703.  *
  704.  *    Creates a new window and Tcl command representing an
  705.  *    instance of a graph widget.
  706.  *
  707.  * Results:
  708.  *    A standard Tcl result.
  709.  *
  710.  * Side effects:
  711.  *    See the user documentation.
  712.  *
  713.  *--------------------------------------------------------------
  714.  */
  715. static int
  716. GraphCmd(clientData, interp, argc, argv)
  717.     ClientData clientData;
  718.     Tcl_Interp *interp;
  719.     int argc;
  720.     char **argv;
  721. {
  722.     Tk_Window tkwin = (Tk_Window)clientData;
  723.     register Graph *graphPtr;
  724.     GraphClassType type;
  725.     unsigned int flags;
  726.  
  727.     if (argc < 2) {
  728.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  729.         " pathName ?options?\"", (char *)NULL);
  730.     return TCL_ERROR;
  731.     }
  732.     if (strcmp(argv[0], "blt_graph") == 0) {
  733.     type = GRAPH;
  734.     } else if (strcmp(argv[0], "blt_barchart") == 0) {
  735.     type = BARCHART;
  736.     } else if (strcmp(argv[0], "blt_contour") == 0) {
  737.     type = CONTOUR;
  738.     } else {
  739.     Tcl_AppendResult(interp, "unknown graph-creation command \"", argv[0],
  740.         "\"", (char *)NULL);
  741.     return TCL_ERROR;
  742.     }
  743.     Blt_negInfinity = (double)MAX_NEG_VAL;
  744.     Blt_posInfinity = (double)MAX_POS_VAL;
  745.     graphPtr = CreateGraph(interp, tkwin, argv[1], type);
  746.     if (graphPtr == NULL) {
  747.     return TCL_ERROR;
  748.     }
  749.     flags = configFlags[type];
  750.     if (ConfigureGraph(graphPtr, argc - 2, argv + 2, flags) != TCL_OK) {
  751.     Tk_DestroyWindow(graphPtr->tkwin);
  752.     return TCL_ERROR;
  753.     }
  754.     interp->result = graphPtr->pathName;
  755.     return TCL_OK;
  756. }
  757.  
  758. /*
  759.  *--------------------------------------------------------------
  760.  *
  761.  * GraphWidgetCmd --
  762.  *
  763.  *    This procedure is invoked to process the Tcl command
  764.  *    that corresponds to a widget managed by this module.
  765.  *    See the user documentation for details on what it does.
  766.  *
  767.  * Results:
  768.  *    A standard Tcl result.
  769.  *
  770.  * Side effects:
  771.  *    See the user documentation.
  772.  *
  773.  *--------------------------------------------------------------
  774.  */
  775. static int
  776. GraphWidgetCmd(clientData, interp, argc, argv)
  777.     ClientData clientData;
  778.     Tcl_Interp *interp;
  779.     int argc;
  780.     char **argv;
  781. {
  782.     register Graph *graphPtr = (Graph *)clientData;
  783.     int result = TCL_ERROR;
  784.     Tk_Window tkwin = graphPtr->tkwin;
  785.     char c;
  786.     unsigned int length;
  787.     unsigned int flags;
  788.     GraphClassType type;
  789.  
  790.     if (argc < 2) {
  791.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  792.         " option ?arg arg ...?\"", (char *)NULL);
  793.     return TCL_ERROR;
  794.     }
  795.     Tk_Preserve((ClientData)graphPtr);
  796.  
  797.     c = argv[1][0];
  798.     length = strlen(argv[1]);
  799.     type = graphPtr->type;
  800.     flags = (configFlags[type] | TK_CONFIG_ARGV_ONLY);
  801.  
  802.     if ((c == 'c') && (length > 2) &&
  803.     (strncmp(argv[1], "configure", length) == 0)) {
  804.     if (argc == 2)
  805.         result = Tk_ConfigureInfo(interp, tkwin, configSpecs,
  806.         (char *)graphPtr, (char *)NULL, flags);
  807.     else if (argc == 3)
  808.         result = Tk_ConfigureInfo(interp, tkwin, configSpecs,
  809.         (char *)graphPtr, argv[2], flags);
  810.     else {
  811.         result = ConfigureGraph(graphPtr, argc - 2, argv + 2, flags);
  812.     }
  813.     } else if ((c == 'c') && (length > 1) &&
  814.     (strncmp(argv[1], "crosshairs", length) == 0)) {
  815.     result = Blt_CrosshairsCmd(graphPtr, argc, argv);
  816.     } else if ((c == 'e') && (strncmp(argv[1], "element", length) == 0)) {
  817.     result = Blt_ElementCmd(graphPtr, argc, argv);
  818.     } else if ((c == 't') && (strncmp(argv[1], "tag", length) == 0)) {
  819.     result = Blt_TagCmd(graphPtr, argc, argv);
  820.     } else if ((c == 'x') && (length > 1) &&
  821.     (strncmp(argv[1], "xaxis", length) == 0)) {
  822.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[X1_AXIS], argc, argv,
  823.         flags);
  824.     } else if ((c == 'x') && (length > 1) &&
  825.     (strncmp(argv[1], "x2axis", length) == 0)) {
  826.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[X2_AXIS], argc, argv,
  827.         flags);
  828.     } else if ((c == 'y') && (length > 1) &&
  829.     (strncmp(argv[1], "yaxis", length) == 0)) {
  830.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[Y1_AXIS], argc, argv,
  831.         flags);
  832.     } else if ((c == 'y') && (length > 1) &&
  833.     (strncmp(argv[1], "y2axis", length) == 0)) {
  834.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[Y2_AXIS], argc, argv,
  835.         flags);
  836.     } else if ((c == 'l') && (length > 1) &&
  837.     (strncmp(argv[1], "legend", length) == 0)) {
  838.     result = Blt_LegendCmd(graphPtr, argc, argv);
  839.     } else if ((c == 'l') && (length > 1) &&
  840.     (strncmp(argv[1], "locate", length) == 0)) {
  841.     /* Obsolete syntax "locate" */
  842.     result = GraphCoords(graphPtr, argc, argv);
  843.     } else if ((c == 'i') && (length > 1) &&
  844.     (strncmp(argv[1], "invtransform", length) == 0)) {
  845.     result = GraphCoords(graphPtr, argc, argv);
  846.     } else if ((c == 't') && (length > 1) &&
  847.     (strncmp(argv[1], "transform", length) == 0)) {
  848.     result = WindowCoords(graphPtr, argc, argv);
  849.     } else if ((c == 'p') && (strncmp(argv[1], "postscript", length) == 0)) {
  850.     result = (*graphPtr->postscript->printProc) (graphPtr, argc, argv);
  851.     } else if ((c == 'p') && (length > 1) &&
  852.     (strncmp(argv[1], "psconfigure", length) == 0)) {
  853.     result = (*graphPtr->postscript->configProc) (graphPtr, argc, argv);
  854.     } else {
  855.     Tcl_AppendResult(interp, "bad option \"", argv[1], "\": should be\
  856.  configure, crosshairs, element, invtransform, legend, postscript,\
  857.  psconfigure, tag, transform, xaxis, yaxis, or y2axis", (char *)NULL);
  858.     }
  859.     Tk_Release((ClientData)graphPtr);
  860.     return result;
  861. }
  862.  
  863. /*
  864.  * -----------------------------------------------------------------
  865.  *
  866.  * Blt_LayoutGraph --
  867.  *
  868.  *    Based upon the computes sizing of the plotting area, calls
  869.  *    routines to calculate the positions for the graph components.
  870.  *    This routine is called when the layout changes.  It is called
  871.  *    from DisplayGraph and PrintPostScript.
  872.  *
  873.  * Results:
  874.  *    None
  875.  *
  876.  * Side Effects:
  877.  *    Old storage is freed and new memory is allocated for holding
  878.  *    component positions.  These positions will eventually affect
  879.  *    where the components are drawn in the graph window.
  880.  *
  881.  * -----------------------------------------------------------------
  882.  */
  883. void
  884. Blt_LayoutGraph(graphPtr)
  885.     Graph *graphPtr;
  886. {
  887.     Blt_ListEntry *entryPtr;
  888.     Element *elemPtr;
  889.     Tag *tagPtr;
  890.  
  891.     /* Layout axes */
  892.     if (graphPtr->flags & LAYOUT_ALL) {
  893.     GraphAxis *axisPtr;
  894.     register int i;
  895.  
  896.     for (i = 0; i < 4; i++) {
  897.         axisPtr = graphPtr->axisArr[i];
  898.         (*axisPtr->layoutProc) (graphPtr, axisPtr);
  899.     }
  900.     }
  901.     /* Layout elements */
  902.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  903.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  904.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  905.     if ((graphPtr->flags & LAYOUT_DIRTY) ||
  906.         (elemPtr->flags & LAYOUT_NEEDED)) {
  907.         (*elemPtr->layoutProc) (graphPtr, elemPtr);
  908.         elemPtr->flags &= ~LAYOUT_NEEDED;
  909.     }
  910.     }
  911.  
  912.     /* Layout tags */
  913.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->tagList));
  914.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  915.     tagPtr = (Tag *)Blt_GetListValue(entryPtr);
  916.     if ((graphPtr->flags & LAYOUT_DIRTY) ||
  917.         (tagPtr->flags & LAYOUT_NEEDED)) {
  918.         (*tagPtr->layoutProc) (graphPtr, tagPtr);
  919.         tagPtr->flags &= ~LAYOUT_NEEDED;
  920.     }
  921.     }
  922.     graphPtr->flags &= ~LAYOUT_ALL;
  923. }
  924.  
  925. /*
  926.  * -----------------------------------------------------------------
  927.  *
  928.  * DisplayElements --
  929.  *
  930.  *    Calls the individual element drawing routines for each
  931.  *    element.
  932.  *
  933.  * Results:
  934.  *    None
  935.  *
  936.  * Side Effects:
  937.  *    Elements are drawn into the drawable (pixmap) which will
  938.  *    eventually be displayed in the graph window.
  939.  *
  940.  * -----------------------------------------------------------------
  941.  */
  942. static void
  943. DisplayElements(graphPtr)
  944.     Graph *graphPtr;
  945. {
  946.     Blt_ListEntry *entryPtr;
  947.     register Element *elemPtr;
  948.  
  949.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  950.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  951.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  952.     (*elemPtr->displayProc) (graphPtr, elemPtr, ELEM_NORMAL);
  953.     }
  954. }
  955.  
  956. /*
  957.  * -----------------------------------------------------------------
  958.  *
  959.  * DisplayActiveElements --
  960.  *
  961.  *    Calls the individual element drawing routines to display
  962.  *    the active colors for each element.
  963.  *
  964.  * Results:
  965.  *    None
  966.  *
  967.  * Side Effects:
  968.  *    Elements are drawn into the drawable (pixmap) which will
  969.  *    eventually be displayed in the graph window.
  970.  *
  971.  * -----------------------------------------------------------------
  972.  */
  973. static void
  974. DisplayActiveElements(graphPtr)
  975.     Graph *graphPtr;
  976. {
  977.     Blt_ListEntry *entryPtr;
  978.     register Element *elemPtr;
  979.  
  980.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  981.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  982.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  983.     if (elemPtr->flags & ACTIVE) {
  984.         (*elemPtr->displayProc) (graphPtr, elemPtr, ELEM_ACTIVE);
  985.     }
  986.     }
  987. }
  988.  
  989. /*
  990.  * -----------------------------------------------------------------
  991.  *
  992.  * DisplayTags --
  993.  *
  994.  *    Calls the individual tag drawing routines for each tag.
  995.  *    A tag will not be drawn if there is an element associated
  996.  *    with the tag (elemId is non-NULL) and that element is
  997.  *    not in the element display list.
  998.  *
  999.  * Results:
  1000.  *    None
  1001.  *
  1002.  * Side Effects:
  1003.  *    Tags are drawn into the drawable (pixmap) which will eventually
  1004.  *    be displayed in the graph window.
  1005.  *
  1006.  * -----------------------------------------------------------------
  1007.  */
  1008. static void
  1009. DisplayTags(graphPtr)
  1010.     Graph *graphPtr;
  1011. {
  1012.     Blt_ListEntry *listPtr;
  1013.     Tag *tagPtr;
  1014.     int mapped;
  1015.  
  1016.     for (listPtr = Blt_FirstListEntry(&(graphPtr->tagList)); listPtr != NULL;
  1017.     listPtr = Blt_NextListEntry(listPtr)) {
  1018.     tagPtr = (Tag *)Blt_GetListValue(listPtr);
  1019.     mapped = 1;
  1020.  
  1021.     if (tagPtr->elemId != NULL) {
  1022.         Tcl_HashEntry *entryPtr;
  1023.         Element *elemPtr;
  1024.  
  1025.         entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable),
  1026.         (char *)tagPtr->elemId);
  1027.         if (entryPtr == NULL) {
  1028.         continue;
  1029.         }
  1030.         elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  1031.         mapped = elemPtr->mapped;
  1032.     }
  1033.     if (mapped) {
  1034.         (*tagPtr->displayProc) (graphPtr, tagPtr);
  1035.     }
  1036.     }
  1037. }
  1038.  
  1039. /*
  1040.  * -----------------------------------------------------------------
  1041.  *
  1042.  * DisplayExterior --
  1043.  *
  1044.  *     Draws the exterior region of the graph (axes, ticks, titles, etc)
  1045.  *    onto a pixmap. The interior region is defined by the given
  1046.  *    rectangle structure.
  1047.  *
  1048.  *        X coordinate axis
  1049.  *        Y coordinate axis
  1050.  *        legend
  1051.  *        interior border
  1052.  *        exterior border
  1053.  *        titles (X and Y axis, graph)
  1054.  *
  1055.  * Returns:
  1056.  *    None.
  1057.  *
  1058.  * Side Effects:
  1059.  *    Exterior of graph is displayed in its window.
  1060.  *
  1061.  * -----------------------------------------------------------------
  1062.  */
  1063. static void
  1064. DisplayExterior(graphPtr, rectPtr)
  1065.     Graph *graphPtr;
  1066.     XRectangle *rectPtr;    /* Interior plotting region */
  1067. {
  1068.     int x, y;
  1069.     XRectangle rectArr[4];
  1070.     GraphAxis *axisPtr;
  1071.     TextAttributes textAttr;
  1072.     register int i;
  1073.  
  1074.     /*
  1075.      * Draw the four outer rectangles which encompass the plotting
  1076.      * surface. This clears the surrounding area and clips the plot.
  1077.      */
  1078.  
  1079.     rectArr[0].x = rectArr[0].y = rectArr[3].x = rectArr[1].x = 0;
  1080.     rectArr[0].width = rectArr[3].width = graphPtr->width;
  1081.     rectArr[0].height = rectPtr->y;
  1082.     rectArr[3].y = rectPtr->y + rectPtr->height;
  1083.     rectArr[3].height = graphPtr->height - rectArr[3].y;
  1084.     rectArr[2].y = rectArr[1].y = rectPtr->y;
  1085.     rectArr[1].width = rectPtr->x;
  1086.     rectArr[2].height = rectArr[1].height = rectPtr->height;
  1087.     rectArr[2].x = rectPtr->x + rectPtr->width;
  1088.     rectArr[2].width = graphPtr->width - rectArr[2].x;
  1089.     XFillRectangles(graphPtr->display, graphPtr->canvas,
  1090.     graphPtr->marginFillGC, rectArr, 4);
  1091.  
  1092.     /* Interior 3D border */
  1093.  
  1094.     if ((graphPtr->plotRelief != TK_RELIEF_FLAT) &&
  1095.     (graphPtr->plotBW > 0)) {
  1096.     Tk_Draw3DRectangle(graphPtr->display, graphPtr->canvas,
  1097.         graphPtr->border, rectPtr->x, rectPtr->y, rectPtr->width,
  1098.         rectPtr->height, graphPtr->plotBW, graphPtr->plotRelief);
  1099.     }
  1100.     /* Legend */
  1101.     if (graphPtr->legendPtr->useDefault) {
  1102.     (*graphPtr->legendPtr->displayProc) (graphPtr);
  1103.     }
  1104.     /* Initialize text attributes for graph and axis titles */
  1105.  
  1106.     textAttr.theta = 0.0;
  1107.     textAttr.anchor = TK_ANCHOR_CENTER;
  1108.     textAttr.gc = graphPtr->marginGC;
  1109.     textAttr.bgColorPtr = Tk_3DBorderColor(graphPtr->border);
  1110.     textAttr.fontPtr = graphPtr->fontPtr;
  1111.  
  1112.     if (graphPtr->title != NULL) {
  1113.     x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1114.     y = graphPtr->borderWidth + TEXTHEIGHT(graphPtr->fontPtr);
  1115.     Blt_DrawText(graphPtr->display, graphPtr->canvas, graphPtr->title,
  1116.         &textAttr, x, y);
  1117.     }
  1118.     /* Draw axes */
  1119.     for (i = 0; i < 4; i++) {
  1120.     axisPtr = graphPtr->axisArr[i];
  1121.     if (axisPtr->mapped) {
  1122.         (*axisPtr->displayProc) (graphPtr, axisPtr, &textAttr);
  1123.     }
  1124.     }
  1125.     /* Exterior 3D border */
  1126.     if ((graphPtr->relief != TK_RELIEF_FLAT) &&
  1127.     (graphPtr->borderWidth > 0)) {
  1128.     Tk_Draw3DRectangle(graphPtr->display, graphPtr->canvas,
  1129.         graphPtr->border, 0, 0, graphPtr->width, graphPtr->height,
  1130.         graphPtr->borderWidth, graphPtr->relief);
  1131.     }
  1132. }
  1133.  
  1134. /*
  1135.  *----------------------------------------------------------------------
  1136.  *
  1137.  * DisplayGraph --
  1138.  *
  1139.  *    This procedure is invoked to display a graph widget.
  1140.  *
  1141.  * Results:
  1142.  *    None.
  1143.  *
  1144.  * Side effects:
  1145.  *    Commands are output to X to display the graph in its
  1146.  *    current mode.
  1147.  *
  1148.  *----------------------------------------------------------------------
  1149.  */
  1150. static void
  1151. DisplayGraph(clientData)
  1152.     ClientData clientData;
  1153. {
  1154.     register Graph *graphPtr = (Graph *)clientData;
  1155.     XRectangle plotArea;
  1156.     int twiceBW;
  1157.     GraphLegend *legendPtr;
  1158.  
  1159.     /*
  1160.      * If the window has been deleted or is no longer mapped, do
  1161.      * nothing
  1162.      */
  1163.     graphPtr->flags &= ~REDRAW_PENDING;
  1164.     if ((graphPtr->tkwin == NULL) || (!Tk_IsMapped(graphPtr->tkwin))) {
  1165.     return;
  1166.     }
  1167. #ifdef notdef
  1168.     fprintf(stderr, "Calling DisplayGraph\n");
  1169. #endif
  1170.  
  1171.     graphPtr->width = Tk_Width(graphPtr->tkwin);
  1172.     graphPtr->height = Tk_Height(graphPtr->tkwin);
  1173.  
  1174.     if (graphPtr->flags & LAYOUT_NEEDED) {
  1175.     if (graphPtr->flags & LAYOUT_DIRTY) {
  1176.         if (Blt_ComputeLayout(graphPtr) != TCL_OK) {
  1177.         return;        /* Not enough room to plot graph */
  1178.         }
  1179.     }
  1180.     Blt_LayoutGraph(graphPtr);
  1181.     }
  1182.     /*
  1183.      * Determine the plotting surface region of the graph window
  1184.      */
  1185.     twiceBW = (graphPtr->plotBW * 2);
  1186.     plotArea.x = graphPtr->origin.x - graphPtr->plotBW;
  1187.     plotArea.y = graphPtr->extreme.y - graphPtr->plotBW;
  1188.     plotArea.width = (graphPtr->extreme.x - graphPtr->origin.x) + twiceBW;
  1189.     plotArea.height = (graphPtr->origin.y - graphPtr->extreme.y) + twiceBW;
  1190.  
  1191.     /*
  1192.      * Create a pixmap (for double buffering) the size of the window
  1193.      */
  1194.     graphPtr->canvas = XCreatePixmap(graphPtr->display,
  1195.     Tk_WindowId(graphPtr->tkwin), graphPtr->width, graphPtr->height,
  1196.     Tk_Depth(graphPtr->tkwin));
  1197.  
  1198.     if (graphPtr->buffered) {
  1199.     if ((graphPtr->elemWidth != graphPtr->width) ||
  1200.         (graphPtr->elemHeight != graphPtr->height) ||
  1201.         (graphPtr->elemMap == None)) {
  1202.         /*
  1203.          * Create a pixmap to save elements if one doesn't already
  1204.          * exist or the size of the window has changed.
  1205.          */
  1206.         graphPtr->elemWidth = graphPtr->width;
  1207.         graphPtr->elemHeight = graphPtr->height;
  1208.         if (graphPtr->elemMap != None) {
  1209.         XFreePixmap(graphPtr->display, graphPtr->elemMap);
  1210.         }
  1211.         graphPtr->elemMap = XCreatePixmap(graphPtr->display,
  1212.         Tk_WindowId(graphPtr->tkwin), graphPtr->elemWidth,
  1213.         graphPtr->elemHeight, Tk_Depth(graphPtr->tkwin));
  1214.         graphPtr->flags |= DIRTY;
  1215.     }
  1216.     if (graphPtr->flags & DIRTY) {
  1217.         Pixmap save;
  1218.         /*
  1219.          * Clear the background and draw the elements into the pixmap.
  1220.          */
  1221.         XFillRectangles(graphPtr->display, graphPtr->elemMap,
  1222.         graphPtr->plotFillGC, &plotArea, 1);
  1223.         save = graphPtr->canvas;
  1224.         graphPtr->canvas = graphPtr->elemMap;
  1225.         DisplayElements(graphPtr);
  1226.         graphPtr->canvas = save;
  1227.         graphPtr->flags &= ~DIRTY;
  1228.     }
  1229.     XCopyArea(graphPtr->display, graphPtr->elemMap, graphPtr->canvas,
  1230.         graphPtr->marginGC, plotArea.x, plotArea.y,
  1231.         (unsigned int)plotArea.width, (unsigned int)plotArea.height,
  1232.         plotArea.x, plotArea.y);
  1233.     } else {
  1234.     XFillRectangles(graphPtr->display, graphPtr->canvas,
  1235.         graphPtr->plotFillGC, &plotArea, 1);
  1236.     DisplayElements(graphPtr);
  1237.     }
  1238.     DisplayTags(graphPtr);
  1239.  
  1240.     legendPtr = graphPtr->legendPtr;
  1241.     if (!legendPtr->useDefault) {
  1242.     (*legendPtr->displayProc) (graphPtr);
  1243.     }
  1244.     DisplayActiveElements(graphPtr);
  1245.  
  1246.     /* Disable crosshairs and update lengths before drawing to the display */
  1247.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  1248.     (*graphPtr->crosshairs->updateProc) (graphPtr);
  1249.  
  1250.     if (graphPtr->flags & REFRESH) {
  1251.     DisplayExterior(graphPtr, &plotArea);
  1252.     XCopyArea(graphPtr->display, graphPtr->canvas,
  1253.         Tk_WindowId(graphPtr->tkwin), graphPtr->marginGC, 0, 0,
  1254.         graphPtr->width, graphPtr->height, 0, 0);
  1255.     } else {
  1256.     /* Draw just the interior plotting region */
  1257.  
  1258.     plotArea.x += graphPtr->plotBW;
  1259.     plotArea.y += graphPtr->plotBW;
  1260.     plotArea.width -= (graphPtr->plotBW * 2);
  1261.     plotArea.height -= (graphPtr->plotBW * 2);
  1262.     XCopyArea(graphPtr->display, graphPtr->canvas,
  1263.         Tk_WindowId(graphPtr->tkwin), graphPtr->marginGC, plotArea.x,
  1264.         plotArea.y, (unsigned int)plotArea.width,
  1265.         (unsigned int)plotArea.height, plotArea.x, plotArea.y);
  1266.     }
  1267.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  1268.  
  1269.     XFreePixmap(graphPtr->display, graphPtr->canvas);
  1270.     graphPtr->canvas = None;
  1271.     graphPtr->flags &= ~REFRESH;
  1272. }
  1273.  
  1274. int
  1275. Blt_GraphInit(interp)
  1276.     Tcl_Interp *interp;
  1277. {
  1278.     Tk_Window tkwin;
  1279.  
  1280.     if (Blt_FindCmd(interp, "blt_graph", (ClientData *)NULL) == TCL_OK) {
  1281.     Tcl_AppendResult(interp, "\"blt_graph\" command already exists",
  1282.         (char *)NULL);
  1283.     return TCL_ERROR;
  1284.     }
  1285.     tkwin = Tk_MainWindow(interp);
  1286.     if (tkwin == NULL) {
  1287.     Tcl_AppendResult(interp, "\"blt_graph\" requires Tk", (char *)NULL);
  1288.     return TCL_ERROR;
  1289.     }
  1290.     Tcl_SetVar2(interp, "blt_versions", "blt_graph", GRAPH_VERSION,
  1291.     TCL_GLOBAL_ONLY);
  1292.     Tcl_CreateCommand(interp, "blt_graph", GraphCmd, (ClientData)tkwin,
  1293.     (Tcl_CmdDeleteProc *)NULL);
  1294.     return TCL_OK;
  1295. }
  1296.  
  1297. int
  1298. Blt_BarchartInit(interp)
  1299.     Tcl_Interp *interp;
  1300. {
  1301.     Tk_Window tkwin;
  1302.  
  1303.     if (Blt_FindCmd(interp, "blt_barchart", (ClientData *)NULL) == TCL_OK) {
  1304.     Tcl_AppendResult(interp, "\"blt_barchart\" command already exists",
  1305.         (char *)NULL);
  1306.     return TCL_ERROR;
  1307.     }
  1308.     tkwin = Tk_MainWindow(interp);
  1309.     if (tkwin == NULL) {
  1310.     Tcl_AppendResult(interp, "\"blt_barchart\" requires Tk",
  1311.         (char *)NULL);
  1312.     return TCL_ERROR;
  1313.     }
  1314.     Tcl_SetVar2(interp, "blt_versions", "blt_barchart", GRAPH_VERSION,
  1315.     TCL_GLOBAL_ONLY);
  1316.     Tcl_CreateCommand(interp, "blt_barchart", GraphCmd, (ClientData)tkwin,
  1317.     (Tcl_CmdDeleteProc *)NULL);
  1318.     return TCL_OK;
  1319. }
  1320.