#!/bin/sh # This is `snmp2.24' (part 24 of snmp2). # Do not concatenate these parts, unpack them in order with `/bin/sh'. # File `snmp2/snmptcl/graph.c' is being continued... # touch -am 1231235999 $$.touch >/dev/null 2>&1 if test ! -f 1231235999 && test -f $$.touch; then shar_touch=touch else shar_touch=: echo echo 'WARNING: not restoring timestamps. Consider getting and' echo "installing GNU \`touch', distributed in GNU File Utilities..." echo fi rm -f 1231235999 $$.touch # if test ! -r _sharseq.tmp; then echo 'Please unpack part 1 first!' exit 1 fi shar_sequence=`cat _sharseq.tmp` if test "$shar_sequence" != 24; then echo "Please unpack part $shar_sequence next!" exit 1 fi if test ! -f _sharnew.tmp; then echo 'x - still skipping snmp2/snmptcl/graph.c' else echo 'x - continuing file snmp2/snmptcl/graph.c' sed 's/^X//' << 'SHAR_EOF' >> 'snmp2/snmptcl/graph.c' && X * bounding box (suitable for giving to XCopyArea, etc.) X * X * Results: X * The translated coordinates of the bounding box are returned. X * X * ----------------------------------------------------------------- X */ static XPoint GetBoxCoords(x, y, width, height, anchor) X int x; /* X screen coordinate of anchor */ X int y; /* Y screen coordinate of anchor */ X int width; /* Width of bounding box */ X int height; /* Height of bounding box */ X Tk_Anchor anchor; /* Direction of the anchor */ { X XPoint pt; X X pt.x = x, pt.y = y; X switch (anchor) { X case TK_ANCHOR_NW: /* Upper left corner */ X break; X case TK_ANCHOR_W: /* Left center */ X pt.y -= (height / 2); X break; X case TK_ANCHOR_SW: /* Lower left corner */ X pt.y -= height; X break; X case TK_ANCHOR_N: /* Top center */ X pt.x -= (width / 2); X break; X case TK_ANCHOR_CENTER: /* Centered */ X pt.x -= (width / 2); X pt.y -= (height / 2); X break; X case TK_ANCHOR_S: /* Bottom center */ X pt.x -= (width / 2); X pt.y -= height; X break; X case TK_ANCHOR_NE: /* Upper right corner */ X pt.x -= width; X break; X case TK_ANCHOR_E: /* Right center */ X pt.x -= width; X pt.y -= (height / 2); X break; X case TK_ANCHOR_SE: /* Lower right corner */ X pt.x -= width; X pt.y -= height; X break; X } X return (pt); } X /* X * ----------------------------------------------------------------- X * X * RotateBitmap -- X * X * Create a new bitmap containing the rotated image of the given X * bitmap. The only rotations currently allowed are 90 degree X * rotations. We also need a special GC, so that we do not X * need to rotate more than one plane of the pixmap. X * X * Results: X * Returns a new bitmap containing the rotated image. X * X * ----------------------------------------------------------------- X */ static Pixmap RotateBitmap(dpy, draw, bitmapGC, bitmap, w, h, quadrant) X Display *dpy; /* X display */ X Drawable draw; /* Root window drawable */ X GC bitmapGC; /* GC created from bitmap where fg=1,bg=0 */ X Pixmap bitmap; /* Bitmap to be rotated */ X int w, h; /* Width and height of the bitmap */ X int quadrant; /* Right angle rotation to perform */ { X XImage *src, *dest; X Pixmap newBitmap; X int bmWidth, bmHeight; X register int dx, dy; X register int x, y; X X /* Now create a bitmap and image to contain the rotated text */ X if (quadrant == ROTATE_90 || quadrant == ROTATE_270) { X bmWidth = h, bmHeight = w; X } else { X bmWidth = w, bmHeight = h; X } X newBitmap = XCreatePixmap(dpy, draw, bmWidth, bmHeight, 1); X src = XGetImage(dpy, bitmap, 0, 0, w, h, 1, ZPixmap); X dest = XGetImage(dpy, newBitmap, 0, 0, bmWidth, bmHeight, 1, ZPixmap); X for (x = 0; x < w; x++) { X for (y = 0; y < h; y++) { X switch (quadrant) { X case ROTATE_90: X dx = y, dy = w - x - 1; X break; X case ROTATE_180: X dx = w - x - 1, dy = h - y - 1; X break; X case ROTATE_270: X dx = h - y - 1, dy = x; X break; X default: X case ROTATE_0: X dx = x, dy = y; X break; X } X XPutPixel(dest, dx, dy, XGetPixel(src, x, y)); X } X } X XPutImage(dpy, newBitmap, bitmapGC, dest, 0, 0, 0, 0, bmWidth, bmHeight); X X /* Clean up temporary resources used */ X XDestroyImage(src), XDestroyImage(dest); X return (newBitmap); } X /*----------------------------------------------------------------- X * X-related drawing routines X * -----------------------------------------------------------------*/ X /* X * ----------------------------------------------------------------- X * X * DrawSymbol -- X * X * Draw the symbol centered at the given xy screen coordinate X * based upon the line symbol type. X * X * Results: X * None. X * X * Problems: X * Most notable is the round-off errors generated when X * calculating the centered position of the symbol. X * ----------------------------------------------------------------- X */ static void DrawSymbol(linePtr, x, y) X Line *linePtr; /* Symbol information */ X int x; /* X screen coordinate */ X int y; /* Y screen coordinate */ { X register int dist = linePtr->symbolSize; X Display *dpy; X Drawable draw; X register int radius; X XPoint pts[5]; X X radius = dist / 2; X X /* Get the display and drawable from the graph structure */ X dpy = Tk_Display(linePtr->parent->tkwin); X draw = (Drawable) linePtr->parent->output; X X switch (linePtr->symbol) { X case SOLID_LINESTYLE: X case DOTTED_LINESTYLE: X case DASHED_LINESTYLE: X XDrawLine(dpy, draw, linePtr->gc, x - radius, y, x + radius, y); X break; X case CROSS_SYMBOL: X XDrawLine(dpy, draw, linePtr->gc, x - radius, y - radius, X x + radius, y + radius); X XDrawLine(dpy, draw, linePtr->gc, x - radius, y + radius, X x + radius, y - radius); X break; X case PLUS_SYMBOL: X XDrawLine(dpy, draw, linePtr->gc, x - radius, y, x + radius, y); X XDrawLine(dpy, draw, linePtr->gc, x, y - radius, x, y + radius); X break; X case SQUARE_SYMBOL: X XFillRectangle(dpy, draw, linePtr->gc, x - radius, y - radius, X dist - 1, dist - 1); X break; X case CIRCLE_SYMBOL: X XFillArc(dpy, draw, linePtr->gc, x - radius, y - radius, X dist, dist, 0, 23040); X break; X case DIAMOND_SYMBOL: X pts[4].y = pts[0].y = pts[2].y = y; X pts[1].x = pts[3].x = x; X pts[4].x = pts[0].x = x - radius; X pts[2].x = x + radius; X pts[1].y = y - radius; X pts[3].y = y + radius; X XFillPolygon(dpy, draw, linePtr->gc, pts, 5, Convex, X CoordModeOrigin); X break; X case POINT_SYMBOL: X XDrawPoint(dpy, draw, linePtr->gc, x, y); X break; X } } X /* X * ----------------------------------------------------------------- X * X * DrawRotatedBitmap -- X * X * Draw a bitmap, using the the given screen coordinates X * as an anchor for the text bounding box. In addition, X * consider orthogonal rotations. X * X * Results: X * None. X * X * Side Effects: X * Bitmap is drawn using the given font and GC on the graph X * window at the given coordinates, anchor, and rotation. X * ----------------------------------------------------------------- X */ static void DrawRotatedBitmap(graphPtr, bitmap, gc, w, h, x, y, rotation, anchor) X Graph *graphPtr; /* Widget record */ X Pixmap bitmap; /* Text string to display */ X GC gc; /* Graphic context to use when drawing text */ X int w; /* */ X int h; /* */ X int x; /* Screen x coordinate */ X int y; /* Screen y coordinate */ X int rotation; /* Rotation of text string */ X Tk_Anchor anchor; /* Anchor of the rotated text string */ { X Display *dpy; /* X display structure */ X Drawable draw; /* Window or pixmap to draw into */ X Pixmap newBitmap; /* Temporary pixmap to draw text string into */ X XGCValues gcValues; X GC bitmapGC; /* Temporary GC for bitmaps */ X int bmHeight, bmWidth; /* Bitmap width and height */ X XPoint newPt; /* Translated point based upon the anchor */ X int quadrant; /* Quadrant of rotation */ X X quadrant = Quadrant(rotation); /* Compute quadrant */ X if (quadrant == ROTATE_90 || quadrant == ROTATE_270) { X bmWidth = h, bmHeight = w; X } else { X bmWidth = w, bmHeight = h; X } X draw = (Drawable) graphPtr->output; X dpy = Tk_Display(graphPtr->tkwin); X X if (quadrant == ROTATE_0) { /* No rotation. Handle simple case */ X newPt = GetBoxCoords(x, y, bmWidth, bmHeight, anchor); X XCopyPlane(dpy, bitmap, draw, gc, 0, 0, bmWidth, bmHeight, X newPt.x, newPt.y, 1); X return; X } X /* Create a temporary GC with a foreground pixel value of 0x01 */ X gcValues.foreground = 1, gcValues.background = 0; X bitmapGC = XCreateGC(dpy, bitmap, (GCForeground | GCBackground), X &gcValues); X X newBitmap = RotateBitmap(dpy, draw, bitmapGC, bitmap, w, h, quadrant); X newPt = GetBoxCoords(x, y, bmWidth, bmHeight, anchor); X XCopyPlane(dpy, newBitmap, draw, gc, 0, 0, bmWidth, bmHeight, X newPt.x, newPt.y, 1); X X /* Clean up temporary resources used */ X XFreePixmap(dpy, newBitmap); X XFreeGC(dpy, bitmapGC); } X /* X * ----------------------------------------------------------------- X * X * DrawText -- X * X * Draw a text string, using the the given screen coordinates X * as an anchor for the text bounding box. X * X * Results: X * None. X * X * Side Effects: X * Text string is drawn using the given font and GC on the X * graph window at the given coordinates. X * ----------------------------------------------------------------- X */ static void DrawText(graphPtr, fontPtr, gc, text, x, y, anchor) X Graph *graphPtr; /* Widget to draw into */ X XFontStruct *fontPtr; /* Font to use */ X GC gc; /* Graphic context to use */ X char *text; /* Text string */ X int x; /* x position of text */ X int y; /* y position of text */ X Tk_Anchor anchor; /* Anchor of text string */ { X XPoint newPt; X X newPt = GetTextCoords(fontPtr, text, x, y, anchor, ROTATE_0, NULL); X XDrawString(Tk_Display(graphPtr->tkwin), (Drawable) graphPtr->output, X gc, newPt.x, newPt.y, text, strlen(text)); } X /* X * ----------------------------------------------------------------- X * X * DrawRotatedText -- X * X * Draw a text string, using the the given screen coordinates X * as an anchor for the text bounding box. In addition, X * consider orthogonal rotations. X * X * Results: X * None. X * X * Side Effects: X * Text string is drawn using the given font and GC on the X * graph window at the given coordinates, anchor, and rotation X * ----------------------------------------------------------------- X */ static void DrawRotatedText(graphPtr, fontPtr, gc, text, x, y, rotation, anchor) X Graph *graphPtr; /* Widget record */ X XFontStruct *fontPtr; /* Font to use when calculating text bbox */ X GC gc; /* Graphic context to use when drawing text */ X char *text; /* Text string to display */ X int x; /* Screen x coordinate */ X int y; /* Screen y coordinate */ X int rotation; /* Rotation of text string */ X Tk_Anchor anchor; /* Anchor of the rotated text string */ { X int w, h; /* Width and height of text bounding box */ X Pixmap textBitmap; /* Temporary pixmap to draw text string into */ X Pixmap newBitmap; /* Newly rotated pixmap */ X XGCValues gcValues; X unsigned long valueMask; X GC bitmapGC; X int rotHeight, rotWidth; /* Rotated bitmap width and height */ X XCharStruct bbox; /* Bounding box for text string */ X int dummy; /* not used */ X int numChar; /* Size of text string */ X XPoint newPt; /* Translated point based upon the anchor */ X int quadrant; /* Quadrant of rotation */ X Drawable draw = (Drawable) graphPtr->output; X Display *dpy = Tk_Display(graphPtr->tkwin); X X if (NULLSTR(text)) /* Null string, do nothing */ X return; X numChar = strlen(text); X X quadrant = Quadrant(rotation); /* Compute quadrant */ X if (quadrant == ROTATE_0) { /* No rotation. Handle simple case */ X newPt = GetTextCoords(fontPtr, text, x, y, anchor, ROTATE_0, NULL); X XDrawImageString(dpy, draw, gc, newPt.x, newPt.y, text, numChar); X return; X } X /* Get the width and height of the text string to be created */ X XTextExtents(fontPtr, text, numChar, &dummy, &dummy, &dummy, &bbox); X w = (bbox.rbearing - bbox.lbearing); X h = (bbox.ascent + bbox.descent); X X /* Create temporary bitmap to draw the text string into */ X textBitmap = XCreatePixmap(dpy, draw, w, h, 1); X X /* Create a temporary GC with a foreground pixel value of 0x01 */ X gcValues.font = fontPtr->fid; X gcValues.foreground = gcValues.background = 0; X valueMask = GCFont | GCForeground | GCBackground; X bitmapGC = XCreateGC(dpy, textBitmap, valueMask, &gcValues); X XFillRectangle(dpy, textBitmap, bitmapGC, 0, 0, w, h); X XSetForeground(dpy, bitmapGC, 1); X X /* Draw the text string into the bitmap */ X XDrawString(dpy, textBitmap, bitmapGC, -bbox.lbearing, bbox.ascent, text, X numChar); X /* Now create a rotated bitmap after determining its size */ X if (quadrant == ROTATE_90 || quadrant == ROTATE_270) { X rotWidth = h, rotHeight = w; X } else { X rotWidth = w, rotHeight = h; X } X newBitmap = RotateBitmap(dpy, draw, bitmapGC, textBitmap, w, h, quadrant); X /* Adjust x and y positions for bounding box dimensions */ X newPt = GetBoxCoords(x, y, rotWidth, rotHeight, anchor); X XCopyPlane(dpy, newBitmap, draw, gc, 0, 0, rotWidth, rotHeight, X newPt.x, newPt.y, 1); X X /* Clean up temporary resources used */ X XFreePixmap(dpy, newBitmap); X XFreePixmap(dpy, textBitmap); X XFreeGC(dpy, bitmapGC); } X /* X *-------------------------------------------------------------- X * X * EventuallyRedraw -- X * X * If the window is mapped and no other redraw request has been X * made, tell the Tk dispatcher to call the graph display routine. X * X * Results: X * None. X * X * Side effects: X * If window is eventually redisplayed. X * X *-------------------------------------------------------------- X */ static void EventuallyRedraw(graphPtr) X Graph *graphPtr; /* Graph widget record */ { X if (Tk_IsMapped(graphPtr->tkwin) X && (graphPtr->flags & REDRAW_PENDING) == 0) { X Tk_DoWhenIdle(DisplayGraph, (ClientData) graphPtr); X graphPtr->flags |= REDRAW_PENDING; X } } X /* X *-------------------------------------------------------------- X * X * GraphEventProc -- X * X * This procedure is invoked by the Tk dispatcher for various X * events on graphs. X * X * Results: X * None. X * X * Side effects: X * When the window gets deleted, internal structures get X * cleaned up. When it gets exposed, the graph is eventually X * redisplayed. X * X *-------------------------------------------------------------- X */ static void GraphEventProc(clientData, eventPtr) X ClientData clientData; /* Graph widget record */ X register XEvent *eventPtr; /* Event which triggered call to routine */ { X register Graph *graphPtr = (Graph *) clientData; X X switch (eventPtr->type) { X case Expose: X if (eventPtr->xexpose.count == 0) X EventuallyRedraw(graphPtr); X break; X case DestroyNotify: X Tcl_DeleteCommand(graphPtr->interp, Tk_PathName(graphPtr->tkwin)); X graphPtr->tkwin = NULL; X if (graphPtr->flags & REDRAW_PENDING) X Tk_CancelIdleCall(DisplayGraph, (ClientData) graphPtr); X Tk_EventuallyFree((ClientData) graphPtr, DestroyGraph); X break; X case ConfigureNotify: X graphPtr->flags |= LAYOUT_NEEDED; X EventuallyRedraw(graphPtr); X break; X } } X /* X *---------------------------------------------------------------------- X * X * CreateLine -- X * X * This procedure creates and initializes a new line. X * X * Results: X * The return value is a pointer to a structure describing X * the new line. If an error occurred, then the return X * value is NULL and an error message is left in interp->result. X * X * Side effects: X * Memory is allocated, etc. X * X *---------------------------------------------------------------------- X */ static Line * CreateLine(graphPtr, lineName) X Graph *graphPtr; /* Graph widget record */ X char *lineName; /* Name to associate with new line */ { X Line *newPtr; /* Newly create line */ X Line *linePtr; /* */ X int nameSize; /* Length of name string */ X X /* Reuse existing entries */ X linePtr = (Line *) FindListEntry(&(graphPtr->allLines), lineName); X if (linePtr != NULL) { X linePtr->isVisible = TRUE; X return (linePtr); X } X newPtr = (Line *) calloc(1, sizeof(Line)); X if (newPtr == NULL) X return (NULL); X X newPtr->parent = graphPtr; X nameSize = strlen(lineName) + 1; X newPtr->name = (char *)malloc(nameSize); X strcpy(newPtr->name, lineName); X newPtr->label = (char *)malloc(nameSize); X strcpy(newPtr->label, lineName); X newPtr->isVisible = TRUE; X X /* X * Append the new line to both the line and visible line lists. By X * default, all new lines are visible. X */ X CreateListEntry(&(graphPtr->allLines), newPtr->name, newPtr); X CreateListEntry(&(graphPtr->drawnLines), newPtr->name, newPtr); X return (newPtr); } X /* X *---------------------------------------------------------------------- X * X * DestroyLine -- X * X * This procedure is invoked by Tk_EventuallyFree or Tk_Release X * to clean up the internal structure of a line at a safe time X * (when no-one is using it anymore). X * X * Results: X * None. X * X * Side effects: X * Everything associated with the line is freed up. X * X *---------------------------------------------------------------------- X */ static void DestroyLine(linePtr) X Line *linePtr; { X if (linePtr->label != NULL) X ckfree(linePtr->label); X if (linePtr->name != NULL) X ckfree(linePtr->name); X if (linePtr->stipple != None) X Tk_FreeBitmap(linePtr->stipple); X if (linePtr->fgColor != NULL) X Tk_FreeColor((XColor *) linePtr->fgColor); X if (linePtr->bgColor != NULL) X Tk_FreeColor((XColor *) linePtr->bgColor); X if (linePtr->gc != None) X Tk_FreeGC(linePtr->gc); X if (linePtr->x.valueArr != NULL) X ckfree((char *)linePtr->x.valueArr); X if (linePtr->y.valueArr != NULL) X ckfree((char *)linePtr->y.valueArr); X free((char *)linePtr); } X /* X *---------------------------------------------------------------------- X * X * ConfigureLine -- X * X * This procedure is called to process an argv/argc list, plus X * the Tk option database, in order to configure (or X * reconfigure) one line in a graph. X * X * Results: X * The return value is a standard Tcl result. If TCL_ERROR is X * returned, then interp->result contains an error message. X * X * Side effects: X * Configuration information such as label and accelerator get X * X *---------------------------------------------------------------------- X */ static int ConfigureLine(interp, linePtr, argc, argv, flags) X Tcl_Interp *interp; X register Line *linePtr; X int argc; X char **argv; X int flags; { X GC newGC; X XGCValues gcValues; X unsigned int valueMask; X Graph *graphPtr = linePtr->parent; X Tk_Window tkwin = graphPtr->tkwin; X X if (Tk_ConfigureWidget(interp, tkwin, lineConfigSpecs, argc, argv, X (char *)linePtr, flags) != TCL_OK) X return TCL_ERROR; X X gcValues.foreground = linePtr->fgColor->pixel; X valueMask = GCForeground; X if (graphPtr->type == XYGRAPH_TYPE) { X gcValues.cap_style = CapRound; X gcValues.join_style = JoinRound; X gcValues.line_style = LineSolid; X gcValues.dash_offset = 0; X gcValues.line_width = linePtr->lineWidth; X valueMask |= (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle); X if (linePtr->symbol == DASHED_LINESTYLE) { X gcValues.line_style = LineOnOffDash; X gcValues.dashes = 7; X valueMask |= (GCDashList | GCDashOffset); X } else if (linePtr->symbol == DOTTED_LINESTYLE) { X gcValues.line_style = LineOnOffDash; X gcValues.dashes = 2; X valueMask |= (GCDashList | GCDashOffset); X } X } else { X linePtr->symbol = SQUARE_SYMBOL; /* Use square for bar/pie X * charts */ X if (linePtr->stipple != None) { X gcValues.stipple = linePtr->stipple; X gcValues.fill_style = FillOpaqueStippled; X valueMask |= (GCStipple | GCFillStyle); X } X /* Patterns can have an individual background color */ X gcValues.background = linePtr->bgColor->pixel; X valueMask |= GCBackground; X } X newGC = Tk_GetGC(tkwin, valueMask, &gcValues); X if (linePtr->gc != None) X Tk_FreeGC(linePtr->gc); X linePtr->gc = newGC; X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * DeleteLine -- X * X * This procedure is called to delete the line whose name X * is given. If the line name is not found, processing stops X * and an error message is returned via interp->result. X * X * Results: X * The return value is a standard Tcl result. If TCL_ERROR is X * returned, then interp->result contains an error message. X * X * Side effects: X * Line attributes (GCs, colors, patterns, etc) get destroyed. X * Graph is redrawn if a deleted line was currently visible. X * X *---------------------------------------------------------------------- X */ static int DeleteLine(interp, graphPtr, lineName, redrawFlag) X Tcl_Interp *interp; X Graph *graphPtr; X char *lineName; X int *redrawFlag; { X Line *linePtr; X X *redrawFlag = FALSE; X linePtr = (Line *) FindListEntry(&(graphPtr->allLines), lineName); X if (linePtr == NULL) { X Tcl_AppendResult(interp, "Can't find \"", lineName, "\"", NULL); X return TCL_ERROR; X } X DeleteListEntry(&(graphPtr->allLines), lineName); X if (linePtr->isVisible) { X DeleteListEntry(&(graphPtr->drawnLines), lineName); X *redrawFlag = TRUE; X } X DestroyLine(linePtr); X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * CreateTag -- X * X * This procedure creates and initializes a new tag. X * X * Results: X * The return value is a pointer to a structure describing X * the new tag. If an error occurred, then the return X * value is NULL and an error message is left in interp->result. X * X * Side effects: X * Memory is allocated, etc. X * X *---------------------------------------------------------------------- X */ static Tag * CreateTag(graphPtr, tagName, x, y) X Graph *graphPtr; X char *tagName; X double x, y; { X Tag *newPtr; X Tag *tagPtr; X X /* Reuse existing entries */ X tagPtr = (Tag *) FindListEntry(&(graphPtr->tags), tagName); X if (tagPtr != NULL) { X tagPtr->x = x, tagPtr->y = y; X return (tagPtr); X } X newPtr = (Tag *) calloc(1, sizeof(Tag)); X if (newPtr == NULL) { X Tcl_AppendResult(graphPtr->interp, "Can't allocate new tag", NULL); X Tcl_SetErrorCode(graphPtr->interp, "UNIX", "calloc", X sys_errlist[errno], NULL); X return (NULL); X } X newPtr->name = (char *)malloc(strlen(tagName) + 1); X strcpy(newPtr->name, tagName); X newPtr->x = x, newPtr->y = y; X CreateListEntry(&(graphPtr->tags), newPtr->name, newPtr); X return (newPtr); } X /* X *---------------------------------------------------------------------- X * X * DestroyTag -- X * X * This procedure is invoked by Tk_EventuallyFree or Tk_Release X * to clean up the internal structure of a tag at a safe time X * (when no-one is using it anymore). X * X * Results: X * None. X * X * Side effects: X * Everything associated with the tag is freed up. X * X *---------------------------------------------------------------------- X */ static void DestroyTag(tagPtr) X Tag *tagPtr; { X if (tagPtr->text) /* text */ X ckfree(tagPtr->text); X if (tagPtr->name) /* name */ X ckfree(tagPtr->name); X free((char *)tagPtr); } X /* X *---------------------------------------------------------------------- X * X * ConfigureTag -- X * X * This procedure is called to process an argv/argc list, plus X * the Tk option database, in order to configure (or X * reconfigure) one tag in a graph. X * X * Results: X * The return value is a standard Tcl result. If TCL_ERROR is X * returned, then interp->result contains an error message. X * X * Side effects: X * Configuration information is set. X * X *---------------------------------------------------------------------- X */ static int ConfigureTag(interp, graphPtr, tagPtr, argc, argv, flags) X Tcl_Interp *interp; X Graph *graphPtr; X register Tag *tagPtr; X int argc; X char **argv; X int flags; { X GC newGC; X XGCValues gcValues; X unsigned int valueMask; X X if (Tk_ConfigureWidget(interp, graphPtr->tkwin, tagConfigSpecs, X argc, argv, (char *)tagPtr, flags) != TCL_OK) X return TCL_ERROR; X X gcValues.foreground = tagPtr->fgColor->pixel; X gcValues.background = tagPtr->bgColor->pixel; X gcValues.font = tagPtr->fontPtr->fid; X valueMask = (GCForeground | GCBackground | GCFont); X newGC = Tk_GetGC(graphPtr->tkwin, valueMask, &gcValues); X if (tagPtr->gc != None) X Tk_FreeGC(tagPtr->gc); X tagPtr->gc = newGC; X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * DeleteTag -- X * X * This procedure is called to delete the tag whose name X * is given. If the tag name is not found, processing stops X * and an error message is returned via interp->result. X * X * Results: X * The return value is a standard Tcl result. If TCL_ERROR is X * returned, then interp->result contains an error message. X * X * Side effects: X * Tag attributes (GCs, colors, patterns, etc) get destroyed. X * Graph is redrawn if a deleted tag was currently visible. X * X *---------------------------------------------------------------------- X */ static int DeleteTag(interp, graphPtr, tagName, redrawFlag) X Tcl_Interp *interp; X Graph *graphPtr; X char *tagName; X int *redrawFlag; { X Tag *tagPtr; X X *redrawFlag = FALSE; X tagPtr = (Tag *) FindListEntry(&(graphPtr->tags), tagName); X if (tagPtr == NULL) { X Tcl_AppendResult(interp, "Can't find a tag named \"", tagName, X "\"", NULL); X return TCL_ERROR; X } X *redrawFlag = TRUE; X DeleteListEntry(&(graphPtr->tags), tagName); X DestroyTag(tagPtr); X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * GetLineNames -- X * X * Runs through the given list of line entries and builds a X * Tcl list of line names. This procedure is used in the X * "names" and "show" commands. X * X * Results: X * The return value is a standard Tcl result. X * interp->result contains the list of line names. X * X *---------------------------------------------------------------------- X */ static int GetLineNames(interp, listPtr) X Tcl_Interp *interp; X LinkedList *listPtr; { X register Line *linePtr; X ListEntry *searchId; X X for (linePtr = (Line *) FirstListEntry(listPtr, &searchId); X linePtr != NULL; linePtr = (Line *) NextListEntry(&searchId)) { X Tcl_AppendElement(interp, linePtr->name, FALSE); X } X return TCL_OK; } X /* X * ----------------------------------------------------------------- X * X * GetDataExtents -- X * X * Find the limits of the data, minimum and maximum values for both X * the X and Y axes. Also determine the most points required to X * plot any one line. X * X * ----------------------------------------------------------------- X */ static void GetDataExtents(graphPtr) X Graph *graphPtr; { X register Line *linePtr; X int numValues; X int maxValues; X int firstXmin, firstXmax, firstYmin, firstYmax; X double xmin, xmax, ymin, ymax; X ListEntry *searchId; X X maxValues = -1; X firstXmin = firstXmax = firstYmin = firstYmax = TRUE; X xmin = xmax = ymin = ymax = 0.0; /* Suppress compiler warning */ X X for (linePtr = (Line *) FirstListEntry(&(graphPtr->drawnLines), &searchId); X linePtr != NULL; linePtr = (Line *) NextListEntry(&searchId)) { X if (linePtr->x.numValues > 0) { X if (linePtr->x.minValue != NegativeInfinity) { X if (firstXmin) { X xmin = linePtr->x.minValue; X firstXmin = FALSE; X } else if (xmin > linePtr->x.minValue) { X xmin = linePtr->x.minValue; X } X } X if (linePtr->x.maxValue != PositiveInfinity) { X if (firstXmax) { X xmax = linePtr->x.maxValue; X firstXmax = FALSE; X } else if (xmax < linePtr->x.maxValue) { X xmax = linePtr->x.maxValue; X } X } X } X if (linePtr->y.numValues > 0) { X if (linePtr->y.minValue != NegativeInfinity) { X if (firstYmin) { X ymin = linePtr->y.minValue; X firstYmin = FALSE; X } else if (ymin > linePtr->y.minValue) { X ymin = linePtr->y.minValue; X } X } X if (linePtr->y.maxValue != PositiveInfinity) { X if (firstYmax) { X ymax = linePtr->y.maxValue; X firstYmax = FALSE; X } else if (ymax < linePtr->y.maxValue) { X ymax = linePtr->y.maxValue; X } X } X } X numValues = MIN(linePtr->x.numValues, linePtr->y.numValues); X if (maxValues < numValues) X maxValues = numValues; X } X /* Set artificial limits for non-limits */ X if (firstXmin) X xmin = -1.0; X if (firstXmax) X xmax = 1.0; X if (firstYmin) X ymin = -1.0; X if (firstYmax) X ymax = 1.0; X X /* X * Set both the X and Y axis limits. If the user has requested limits, X * use those; otherwise the limits will be based on the data to be X * plotted. X */ X graphPtr->X.minLimit = ((graphPtr->X.reqMinimum == NegativeInfinity) X ? xmin : graphPtr->X.reqMinimum); X graphPtr->X.maxLimit = ((graphPtr->X.reqMaximum == PositiveInfinity) X ? xmax : graphPtr->X.reqMaximum); X graphPtr->Y.minLimit = ((graphPtr->Y.reqMinimum == NegativeInfinity) X ? ymin : graphPtr->Y.reqMinimum); X graphPtr->Y.maxLimit = ((graphPtr->Y.reqMaximum == PositiveInfinity) X ? ymax : graphPtr->Y.reqMaximum); X graphPtr->maxValues = maxValues; } X /* X * ----------------------------------------------------------------- X * X * CalculateAxisLayout -- X * X * Fill the Axis structure with the necessary information to X * draw the axes. Create a phony range when min equals max. X * X * Autoscale: X * This is the default behavior. X * Find the smallest number of units which contain the range of X * values. The minimum and maximum major tick values will be X * represent the range of values for the axis. This greatest X * number of major ticks possible is 10. X * X * Manual Scaling: X * Make the minimum and maximum data values the represent the X * range of the values for the axis. The minimum and maximum X * major ticks will be inclusive of this range. This provides X * the largest area for plotting and the expected results when X * the axis min and max values have be set by the user (.e.g zooming). X * The maximum number of major ticks is 20. X * X * For log scale, there is always the possibility that the minimum X * and maximum data values are the same magnitude. To represent X * the points properly, at least one full decade should be shown. X * However, if you zoom a log scale plot, the results should be X * predictable. Therefore, in that case, show only minor ticks. X * Lastly, there should be an appropriate way to handle numbers <=0. X * X * maxY X * | units = magnitude (of least significant digit) X * | high = largest unit tick < max axis value X * high _| low = smallest unit tick > min axis value X * | X * | range = high - low X * | # ticks = greatest factor of range/units X * _| X * U | X * n | X * i | X * t _| X * | X * | X * | X * low _| X * | X * |_minX________________maxX__ X * | | | | | X * minY low high X * minY X * X * X * numTicks = Number of ticks X * minValue = Minimum value of axis X * maxValue = Maximum value of axis X * range = Range of values (maxValue - minValue) X * ----------------------------------------------------------------- X */ static void CalculateAxisLayout(axisPtr) X Axis *axisPtr; { X double temp, min, max; X X min = axisPtr->minLimit; X max = axisPtr->maxLimit; X X if (max < min) { X /* Swap min and max */ X temp = max, max = min, min = temp; X } else if (min == max) { X /* Set artificial min and max */ X min = (min * 0.9) - 1.0; X max = (max * 1.1) + 1.0; X } X if (axisPtr->logScale) { X /* X * If the range of values in less than or equal to zero, taking the X * log will fail. Try to do the approximation, by using asinh. X * Caveat Emptor. X */ X double imin, imax; X X if (min <= 0.0) { X imin = floor(log10((min + sqrt(1 + (min * min))) / 2)); X imax = ceil(log10((max + sqrt(1 + (max * max))) / 2)); X } else { X imin = floor(log10(min)), imax = ceil(log10(max)); X } X if (imax == imin) X ++imax; X axisPtr->minLimit = imin; X axisPtr->maxLimit = imax; X axisPtr->major.numSteps = (imax - imin) + 1; X axisPtr->major.stepSize = 1.0; X axisPtr->range = axisPtr->maxLimit - axisPtr->minLimit; X axisPtr->minor.numSteps = 10; X axisPtr->minor.low = axisPtr->major.low = axisPtr->minLimit; X axisPtr->minor.high = axisPtr->major.high = axisPtr->maxLimit; X return; X } else { X double maxTick, minTick; X double units; X double range; X int numTicks; X double newMin, newMax; X X if (axisPtr->major.numSteps <= 0) { X return; X } X range = max - min; X if ((axisPtr->reqStepSize > 0.0) && (axisPtr->reqStepSize < range)) { X units = axisPtr->reqStepSize; X } else { X units = exp10(floor(log10(range))); X } #ifdef notdef X fprintf(stderr, "units=%.15g\n", units); #endif X if (axisPtr->reqMinimum != NegativeInfinity) { X minTick = CEIL(min, units); X newMin = min; X } else { X minTick = FLOOR(min, units); X newMin = minTick; X } X if (axisPtr->reqMaximum != PositiveInfinity) { X maxTick = FLOOR(max, units); X newMax = max; X } else { X maxTick = CEIL(max, units); X newMax = maxTick; X } X /* This happens only when either min and/or max is predefined */ X if (minTick >= maxTick) { X units /= 10.0; X maxTick = FLOOR(max, units); X minTick = CEIL(min, units); X } X range = (newMax - newMin); X numTicks = (int)rint((maxTick - minTick) / units) + 1; #ifdef notdef X fprintf(stderr, "min/max=(%g,%g),lo/hi=(%g,%g),numTicks=%d,\ range=%g,units=%.15g\n", min, max, minTick, maxTick, numTicks, range, units); #endif X axisPtr->minLimit = newMin; X axisPtr->maxLimit = newMax; X X /* Still possible to get -0, add 0.0 to clear sign bit */ X axisPtr->major.low = minTick + 0.0; X axisPtr->major.high = maxTick + 0.0; X X axisPtr->major.stepSize = units; X axisPtr->major.numSteps = numTicks; X axisPtr->range = (newMax - newMin); X if (axisPtr->minor.numSteps > 0) X units /= (double)(axisPtr->minor.numSteps); X axisPtr->minor.stepSize = units; X if (axisPtr->reqMinimum != NegativeInfinity) { X minTick = CEIL(min, units); X } else { X minTick = axisPtr->major.low; X } X if (axisPtr->reqMaximum != PositiveInfinity) { X maxTick = FLOOR(max, units); X } else { X maxTick = axisPtr->major.high; X } X axisPtr->minor.high = maxTick + 0.0; X axisPtr->minor.low = minTick + 0.0; X } } X /* X * ----------------------------------------------------------------- X * X * ConfigureXAxis -- X * X * ----------------------------------------------------------------- X */ static void ConfigureXAxis(graphPtr) X Graph *graphPtr; { X Axis *axisPtr = &(graphPtr->X); X int numLines = graphPtr->drawnLines.numEntries; X X if (graphPtr->type == BARCHART_TYPE) { X axisPtr->minLimit = 0.0; X axisPtr->range = axisPtr->maxLimit = (numLines + 1.0); X if (axisPtr->major.numSteps > 0) X axisPtr->major.numSteps = numLines; X axisPtr->major.low = 1.0; X axisPtr->major.high = (double)numLines; X axisPtr->minor.numSteps = 0; X } else { X CalculateAxisLayout(axisPtr); X } } X /* X * ----------------------------------------------------------------- X * X * ConfigureYAxis -- X * X * ----------------------------------------------------------------- X */ static void ConfigureYAxis(graphPtr) X Graph *graphPtr; { X Axis *axisPtr = &(graphPtr->Y); X X if (graphPtr->type == BARCHART_TYPE) { X /* If no axis limits have been requested, check to make sure X that zero is included in the range */ X if ((axisPtr->reqMinimum == NegativeInfinity) && X (axisPtr->minLimit > 0.0)) X axisPtr->minLimit = 0.0; X if ((axisPtr->reqMaximum == PositiveInfinity) && X (axisPtr->maxLimit < 0.0)) X axisPtr->maxLimit = 0.0; X } X CalculateAxisLayout(axisPtr); } X /* X * ----------------------------------------------------------------- X * X * GetTickWidth -- X * X * ----------------------------------------------------------------- X */ static int GetTickWidth(axisPtr, fontPtr) X Axis *axisPtr; X XFontStruct *fontPtr; { X register int cnt; X register int maxWidth; /* Maximum tick label width found */ X int width; X char tickLabel[80]; X double value; X XCharStruct bbox; X int dummy; X X maxWidth = 0; X value = axisPtr->major.low; X for (cnt = 0; cnt < axisPtr->major.numSteps; cnt++) { X value = ROUND(value, axisPtr->major.stepSize); X FormatLabel(axisPtr->logScale, value, tickLabel); X XTextExtents(fontPtr, tickLabel, strlen(tickLabel), &dummy, &dummy, X &dummy, &bbox); X width = bbox.rbearing + bbox.lbearing; X if (width > maxWidth) X maxWidth = width; X value += axisPtr->major.stepSize; X } X return (maxWidth); } X /* X * ----------------------------------------------------------------- X * X * GetLegendExtents -- X * X * Calculate the width and height needed for the legend X * X * Returns: X * Width of the longest label in pixels. X * X * Side effects: X * The size of each line's symbol is calculated and set. X * ----------------------------------------------------------------- X */ static int GetLegendExtents(graphPtr, width, height) X Graph *graphPtr; X int width; /* Width of window/plot */ X int height; /* Height of window/plot */ { X register Line *linePtr; X register Legend *legendPtr = &(graphPtr->legend); X int w; X int symbolSize; X int maxWidth; X int numEntries; X int maxSymbolSize; X ListEntry *searchId; X int fontHeight = FONTHEIGHT(graphPtr->fontPtr); X X legendPtr->width = legendPtr->height = 0; X legendPtr->maxSymSize = 0; X maxSymbolSize = numEntries = maxWidth = 0; X X /* X * Run through the list of visible lines and determine the number of X * entries in addition to the widest label. X */ X for (linePtr = (Line *) FirstListEntry(&(graphPtr->drawnLines), &searchId); X linePtr != NULL; linePtr = (Line *) NextListEntry(&searchId)) { X if (!NULLSTR(linePtr->label)) { X XCharStruct bbox; X int dummy; X X XTextExtents(graphPtr->fontPtr, linePtr->label, X strlen(linePtr->label), X &dummy, &dummy, &dummy, &bbox); X w = bbox.rbearing + bbox.lbearing; X if (w > maxWidth) X maxWidth = w; X numEntries++; X symbolSize = (int)rint(linePtr->symSizePct * 0.01 * X ((height < width) ? height : width)); X symbolSize |= 0x01; /* Must be an odd number size */ X linePtr->symbolSize = symbolSize; X if (symbolSize > maxSymbolSize) X maxSymbolSize = symbolSize; X } X } X if (!legendPtr->isVisible) { X return (maxWidth); X } X legendPtr->numEntries = numEntries; X legendPtr->width = ((3 * PADX) + (2 * legendPtr->borderWidth) X + maxSymbolSize + maxWidth); X legendPtr->maxSymSize = maxSymbolSize; X legendPtr->height = (2 * (PADY + legendPtr->borderWidth) + X ((fontHeight) * numEntries)); X return (maxWidth); } X /* X * ----------------------------------------------------------------- X * X * DrawTags -- X * X * ----------------------------------------------------------------- X */ static void DrawTags(graphPtr) X Graph *graphPtr; { X ListEntry *searchID; X register Tag *tagPtr; X X for (tagPtr = (Tag *) FirstListEntry(&(graphPtr->tags), &searchID); X tagPtr != NULL; tagPtr = (Tag *) NextListEntry(&searchID)) { X if (!NULLSTR(tagPtr->text) || (tagPtr->bitmap != None)) { X double x, y; X X /* X * If tag is associated with a particular line, see if that line X * is to be plotted. If not, skip the tag. X */ X if (!NULLSTR(tagPtr->lineName) && X (FindListEntry(&(graphPtr->drawnLines), X tagPtr->lineName) == NULL)) X continue; X x = ScaleX(graphPtr, tagPtr->x); X if (x < 0.0 || x > 1.0) X continue; X y = ScaleY(graphPtr, tagPtr->y); X if (y < 0.0 || y > 1.0) X continue; X if (tagPtr->bitmap != None) { X int w, h; X X Tk_SizeOfBitmap(tagPtr->bitmap, &w, &h); X DrawRotatedBitmap(graphPtr, tagPtr->bitmap, tagPtr->gc, w, h, X GX(graphPtr, x), GY(graphPtr, y), X tagPtr->rotation, tagPtr->anchor); X } else { X DrawRotatedText(graphPtr, tagPtr->fontPtr, tagPtr->gc, X tagPtr->text, X GX(graphPtr, x), GY(graphPtr, y), X tagPtr->rotation, tagPtr->anchor); X } X } X } } X /* X * ----------------------------------------------------------------- X * X * DrawLegend -- X * X * ----------------------------------------------------------------- X */ static void DrawLegend(graphPtr) X Graph *graphPtr; { X register int x, y; X register Line *linePtr; X ListEntry *searchId; X Legend *legendPtr; X XPoint newPt; X Tk_Anchor anchor; /* Anchor of legend */ X int fontHeight; X X legendPtr = &(graphPtr->legend); X if (!legendPtr->isVisible || legendPtr->numEntries == 0) X return; X X if (legendPtr->usePosition) { X x = legendPtr->x; X y = legendPtr->y; X if (x < 0) X x += graphPtr->width - legendPtr->width; X if (y < 0) X y += graphPtr->height - legendPtr->height; X anchor = TK_ANCHOR_NW; X } else { X x = graphPtr->width - (legendPtr->borderWidth + 3 * PADX); X y = GY(graphPtr, 0.95); X anchor = TK_ANCHOR_NE; X } X newPt = GetBoxCoords(x, y, legendPtr->width, legendPtr->height, anchor); X if (legendPtr->borderWidth > 0) { X Tk_Fill3DRectangle(Tk_Display(graphPtr->tkwin), X (Drawable) graphPtr->output, X legendPtr->border, newPt.x, newPt.y, X legendPtr->width, legendPtr->height, X legendPtr->borderWidth, legendPtr->relief); X } X fontHeight = FONTHEIGHT(graphPtr->fontPtr); X y = newPt.y + PADY + fontHeight / 2 + legendPtr->borderWidth; X x = newPt.x + PADX + legendPtr->borderWidth; X for (linePtr = (Line *) FirstListEntry(&(graphPtr->drawnLines), &searchId); X linePtr != NULL; linePtr = (Line *) NextListEntry(&searchId)) { X if (!NULLSTR(linePtr->label)) { X DrawSymbol(linePtr, x + legendPtr->maxSymSize / 2, y); X DrawText(graphPtr, graphPtr->fontPtr, graphPtr->gc, X linePtr->label, x + legendPtr->maxSymSize + PADX, y, X TK_ANCHOR_W); X y += fontHeight; X } X } } X static double logTable[]= { X 0.0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1.0 }; X /* X * ----------------------------------------------------------------- X * X * DrawXAxis -- X * X * ----------------------------------------------------------------- X */ static void DrawXAxis(graphPtr) X Graph *graphPtr; { X Axis *axisPtr = &(graphPtr->X); X Line *linePtr; X ListEntry *searchId; X register int i, j; X double x; X int y; /* constant y-coordinate of axis */ X XSegment *segArr; X register int segCnt, need; X char tickLabel[80]; X X /* X * Save all major and minor tick line segment coordinates in an array of X * line segments. Try to draw all ticks in one XDrawSegments call. X */ X need = (axisPtr->major.numSteps + 1 + X ((axisPtr->major.numSteps + 2) * axisPtr->minor.numSteps)); #ifdef NO_ALLOCA X segArr = (XSegment *) ckalloc(need * sizeof(XSegment)); #else X segArr = (XSegment *) alloca(need * sizeof(XSegment)); #endif X segCnt = 0; X X /* Axis line */ X segArr[segCnt++] = Gr_Segment(graphPtr, 0.0, 0.0, 1.0, 0.0); X X if (axisPtr->major.numSteps > 0) { X double subValue; X double value; X X if (!axisPtr->logScale && axisPtr->minor.numSteps > 0) { X subValue = axisPtr->minor.low; X for (j = 1; j < axisPtr->minor.numSteps; j++) { X if (subValue >= axisPtr->major.low) X break; X x = ((subValue - axisPtr->minLimit) / axisPtr->range); X segArr[segCnt++] = Gr_Segment(graphPtr, x, 0.0, x, -MINOR_TICK); X subValue += axisPtr->minor.stepSize; X } X } X if (graphPtr->type == BARCHART_TYPE) { X y = graphPtr->height - (graphPtr->borderWidth + X FONTHEIGHT(graphPtr->fontPtr) + 2 * PADY); X } else { X y = GY(graphPtr, -LABEL_TICK); X } X value = axisPtr->major.low; X linePtr = (Line *) FirstListEntry(&(graphPtr->drawnLines), &searchId); X for (i = 0; i < axisPtr->major.numSteps; i++) { X X /* Clean up round-off error from labels */ X value = ROUND(value, axisPtr->major.stepSize); X X /* Scale the tick value [0..1] */ X x = ((value - axisPtr->minLimit) / axisPtr->range); X X if (graphPtr->type == BARCHART_TYPE) { X if (!NULLSTR(linePtr->label)) { X if (graphPtr->xrotation == ROTATE_270) X DrawRotatedText(graphPtr, graphPtr->fontPtr, graphPtr->gc, X linePtr->label, GX(graphPtr, x), y, X graphPtr->xrotation, TK_ANCHOR_S); X else X DrawRotatedText(graphPtr, graphPtr->fontPtr, graphPtr->gc, X linePtr->label, GX(graphPtr, x), X GY(graphPtr, -LABEL_TICK), X graphPtr->xrotation, TK_ANCHOR_N); X } X linePtr = (Line *) NextListEntry(&searchId); X } else { X /* Draw numeric value string at each major tick */ X FormatLabel(axisPtr->logScale, value, tickLabel); X DrawText(graphPtr, graphPtr->numberFontPtr, X graphPtr->numberGC, tickLabel, GX(graphPtr, x), y, X TK_ANCHOR_N); X } X segArr[segCnt++] = Gr_Segment(graphPtr, x, 0.0, x, -MAJOR_TICK); X if (axisPtr->minor.numSteps > 0 && value < axisPtr->maxLimit) { X if (axisPtr->logScale) { X for (j = 1; j < 9; j++) { X subValue = value + logTable[j]; X x = ((subValue - axisPtr->minLimit) / axisPtr->range); X segArr[segCnt++] = X Gr_Segment(graphPtr, x, 0.0, x, X -MAJOR_TICK * logTable[j]); X } X } else { X subValue = value + axisPtr->minor.stepSize; X for (j = 1; (j < axisPtr->minor.numSteps) && X (subValue <= axisPtr->maxLimit); j++) { X x = ((subValue - axisPtr->minLimit) / axisPtr->range); X segArr[segCnt++] = X Gr_Segment(graphPtr, x, 0.0, x, -MINOR_TICK); X subValue += axisPtr->minor.stepSize; X } X } X } X value += axisPtr->major.stepSize; X } X } X /* Draw the X label */ X if (!NULLSTR(axisPtr->label)) { X y = graphPtr->height - X (graphPtr->borderWidth + PADY + FONTHEIGHT(graphPtr->fontPtr) / 2); X DrawText(graphPtr, graphPtr->fontPtr, graphPtr->gc, axisPtr->label, X GX(graphPtr, 0.5), y, TK_ANCHOR_CENTER); X } X XDrawSegments(Tk_Display(graphPtr->tkwin), (Drawable) graphPtr->output, X graphPtr->numberGC, segArr, segCnt); X if (segCnt > need) X fprintf(stderr, "Number allocated = %d, used = %d\n", need, segCnt); #ifdef NO_ALLOCA X ckfree((char *)segArr); #endif } X /* X * ----------------------------------------------------------------- X * X * DrawYAxis -- X * X * ----------------------------------------------------------------- X */ static void DrawYAxis(graphPtr) X Graph *graphPtr; { X Axis *axisPtr = &(graphPtr->Y); X register int i, j; X register int x; X double y; X XSegment *segArr; X register int segCnt; X int need; X char tickLabel[80]; X X /* X * Try to draw all the ticks and subticks in one XDrawSegments call. Save X * all tick line segment coordinates in an array of line segments. X */ X need = (axisPtr->major.numSteps + 1 + X ((axisPtr->major.numSteps + 2) * axisPtr->minor.numSteps)); #ifdef NO_ALLOCA X segArr = (XSegment *) ckalloc(need * sizeof(XSegment)); #else X segArr = (XSegment *) alloca(need * sizeof(XSegment)); #endif X segCnt = 0; X segArr[segCnt++] = Gr_Segment(graphPtr, 0.0, 0.0, 0.0, 1.0); /* Axis */ X if (!NULLSTR(axisPtr->label)) { X x = PADX + graphPtr->borderWidth + FONTHEIGHT(graphPtr->fontPtr) / 2; X DrawRotatedText(graphPtr, graphPtr->fontPtr, graphPtr->gc, X axisPtr->label, x, GY(graphPtr, 0.5), 90, X TK_ANCHOR_CENTER); X } X if (axisPtr->major.numSteps > 0) { X double subValue; X double value; X X if (!axisPtr->logScale && (axisPtr->minor.numSteps > 0)) { SHAR_EOF : || echo 'restore of snmp2/snmptcl/graph.c failed' fi echo 'End of snmp2 part 24' echo 'File snmp2/snmptcl/graph.c is continued in part 25' echo 25 > _sharseq.tmp exit 0