#!/bin/sh # This is `snmp2.23' (part 23 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" != 23; 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 int flags; /* Flags; see below for definitions. */ X GC gc; /* Private graphic context */ X ClientData output; /* PS file, pixmap, or window to draw into */ X int psWidth; /* PS plot width */ X int psHeight; /* PS plot height */ X int width; /* Graph window width */ X int height; /* Graph window height */ X int type; /* Type of graph: XYGRAPH_TYPE, BARCHART_TYPE, X * or PIECHART_TYPE. */ X int xrotation; /* Rotation of X axis tick label for barcharts */ X int maxValues; /* Size of largest vector of data */ X int showRetrace; /* Draw all line segments, even when the next X * x-coordinate value is less than the previous */ X int doubleBuffered; /* If non-zero, use an off-screen pixmap for X * drawing operations */ X char *title; /* Graph title */ X int leftMargin; /* # pixels padding for left margin */ X int rightMargin; /* # pixels padding for right margin */ X int topMargin; /* # pixels padding for top margin */ X int bottomMargin; /* # pixels padding for bottom margin */ X int axisThickness; /* Axis line thickness */ X double barWidthPct; /* Width of bar as a percentage of the unit X * distance */ X XFontStruct *fontPtr; /* Information about the text font */ X XFontStruct *numberFontPtr; /* Number font. */ X XColor *numberFg; /* Number color */ X GC numberGC; /* Graphic context for numbers/axis */ X XColor *fgColor; /* Text color */ X Cursor cursor; /* Graph X11 Cursor */ X Axis X, Y; /* X and Y Axis information */ X Legend legend; /* Legend information */ X LinkedList allLines; /* Table of lines */ X LinkedList drawnLines; /* Table of visible lines */ X LinkedList tags; /* Table of tags */ } Graph; X /* Flag bits for graphs: X * X * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has X * already been queued to redraw this window. X * X * LAYOUT_NEEDED: Non-zero means that a graph configuration X * has changed (line, tag, axis, legend, etc) X * and the layout of the graph (position of the X * graph in the window) needs to be recalculated. X */ #define REDRAW_PENDING 1 #define LAYOUT_NEEDED 2 X #define DEF_BAR_WIDTH_PCT "80.0" #define DEF_GRAPH_BG_COLOR WHITE #define DEF_GRAPH_BG_MONO WHITE #define DEF_GRAPH_BUFFERING "true" #define DEF_GRAPH_CURSOR "crosshair" #define DEF_GRAPH_FG_COLOR BLACK #define DEF_GRAPH_FG_MONO BLACK #define DEF_GRAPH_FONT "*-Helvetica-Bold-R-Normal-*-120-*" #define DEF_GRAPH_GEOMETRY "400x400" #define DEF_GRAPH_RELIEF "flat" #define DEF_GRAPH_TITLE "Graph Title" #define DEF_LEGEND_BG_COLOR WHITE #define DEF_LEGEND_BG_MONO WHITE #define DEF_LEGEND_BORDER_WIDTH "2" #define DEF_LEGEND_POSITION "" #define DEF_LEGEND_RELIEF "raised" #define DEF_LEGEND_SHOW "true" #define DEF_NUMBER_FG_COLOR BLACK #define DEF_NUMBER_FG_MONO BLACK #define DEF_NUMBER_FONT "*-Courier-Bold-R-Normal-*-100-*" #define DEF_NUM_MINOR_TICKS "5" #define DEF_X_AXIS_LABEL "X" #define DEF_X_AXIS_ROTATION "90" #define DEF_X_AXIS_STEP "0.0" #define DEF_X_AXIS_TICKS "true" #define DEF_Y_AXIS_LABEL "Y" #define DEF_Y_AXIS_STEP "0.0" #define DEF_Y_AXIS_TICKS "true" X static Tk_ConfigSpec configSpecs[]= { X {TK_CONFIG_BORDER, "-background", "background", "Background", X DEF_GRAPH_BG_COLOR, Tk_Offset(Graph, border), X TK_CONFIG_COLOR_ONLY | ALL_MASK}, X {TK_CONFIG_BORDER, "-background", "background", "Background", X DEF_GRAPH_BG_MONO, Tk_Offset(Graph, border), X TK_CONFIG_MONO_ONLY | ALL_MASK}, X {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", X (char *)NULL, Tk_Offset(Graph, borderWidth), ALL_MASK}, X {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, X ALL_MASK}, X {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, X ALL_MASK}, X {TK_CONFIG_DOUBLE, "-barwidthpct", "barWidthPct", "BarWidthPct", X DEF_BAR_WIDTH_PCT, Tk_Offset(Graph, barWidthPct), BARCHART_MASK}, X {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", X DEF_GRAPH_CURSOR, Tk_Offset(Graph, cursor), X ALL_MASK | TK_CONFIG_NULL_OK}, X {TK_CONFIG_BOOLEAN, "-doublebuffered", "doubleBuffered", (char *)NULL, X DEF_GRAPH_BUFFERING, Tk_Offset(Graph, doubleBuffered), ALL_MASK}, X {TK_CONFIG_SYNONYM, "-dbl", "doubleBuffered", (char *)NULL, X (char *)NULL, 0, ALL_MASK}, X {TK_CONFIG_FONT, "-font", "font", "Font", X DEF_GRAPH_FONT, Tk_Offset(Graph, fontPtr), ALL_MASK}, X {TK_CONFIG_COLOR, "-numbercolor", "numberColor", "Foreground", X DEF_NUMBER_FG_COLOR, Tk_Offset(Graph, numberFg), X TK_CONFIG_COLOR_ONLY | ALL_MASK}, X {TK_CONFIG_COLOR, "-numbercolor", "numberColor", "Foreground", X DEF_NUMBER_FG_MONO, Tk_Offset(Graph, numberFg), X TK_CONFIG_MONO_ONLY | ALL_MASK}, X {TK_CONFIG_FONT, "-numberfont", "numberFont", "Font", X DEF_NUMBER_FONT, Tk_Offset(Graph, numberFontPtr), ALL_MASK}, X {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, X ALL_MASK}, X {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", X DEF_GRAPH_FG_COLOR, Tk_Offset(Graph, fgColor), X TK_CONFIG_COLOR_ONLY | ALL_MASK}, X {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", X DEF_GRAPH_FG_MONO, Tk_Offset(Graph, fgColor), X TK_CONFIG_MONO_ONLY | ALL_MASK}, X {TK_CONFIG_STRING, "-geometry", "geometry", "Geometry", X DEF_GRAPH_GEOMETRY, Tk_Offset(Graph, geometry), ALL_MASK}, X {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", X DEF_GRAPH_RELIEF, Tk_Offset(Graph, relief), ALL_MASK}, X {TK_CONFIG_STRING, "-title", "title", "Title", X DEF_GRAPH_TITLE, Tk_Offset(Graph, title), ALL_MASK}, X {TK_CONFIG_STRING, "-xlabel", "xLabel", "Label", X DEF_X_AXIS_LABEL, Tk_Offset(Graph, X.label), ALL_MASK}, X {TK_CONFIG_STRING, "-ylabel", "yLabel", "Label", X DEF_Y_AXIS_LABEL, Tk_Offset(Graph, Y.label), ALL_MASK}, X {TK_CONFIG_CUSTOM, "-legendposition", "legendPosition", "LegendPosition", X DEF_LEGEND_POSITION, Tk_Offset(Graph, legend), X TK_CONFIG_NULL_OK | ALL_MASK, &LegendOption}, X {TK_CONFIG_SYNONYM, "-lpos", "legendPosition", "LegendPosition", X (char *)NULL, 0, ALL_MASK}, X {TK_CONFIG_BOOLEAN, "-showlegend", "showLegend", "ShowLegend", X DEF_LEGEND_SHOW, Tk_Offset(Graph, legend.isVisible), ALL_MASK}, X {TK_CONFIG_INT, "-legendborderwidth", "legendBorderWidth", "BorderWidth", X DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Graph, legend.borderWidth), ALL_MASK}, X {TK_CONFIG_SYNONYM, "-lbd", "legendBorderWidth", (char *)NULL, X (char *)NULL, 0, ALL_MASK}, X {TK_CONFIG_RELIEF, "-legendrelief", "legendRelief", "Relief", X DEF_LEGEND_RELIEF, Tk_Offset(Graph, legend.relief), ALL_MASK}, X {TK_CONFIG_SYNONYM, "-lrelief", "legendRelief", (char *)NULL, X (char *)NULL, 0, ALL_MASK}, X {TK_CONFIG_BORDER, "-legendbackground", "legendBackground", "Background", X DEF_LEGEND_BG_MONO, Tk_Offset(Graph, legend.border), X TK_CONFIG_MONO_ONLY | ALL_MASK}, X {TK_CONFIG_BORDER, "-legendbackground", "legendBackground", "Background", X DEF_LEGEND_BG_COLOR, Tk_Offset(Graph, legend.border), X TK_CONFIG_COLOR_ONLY | ALL_MASK}, X {TK_CONFIG_SYNONYM, "-lbg", "legendBackground", (char *)NULL, X (char *)NULL, 0, ALL_MASK}, X {TK_CONFIG_CUSTOM, "-xmin", "xMin", "Minimum", X (char *)NULL, Tk_Offset(Graph, X.reqMinimum), X XYGRAPH_MASK | TK_CONFIG_NULL_OK, &MinLimitOption}, X {TK_CONFIG_CUSTOM, "-xmax", "xMax", "Maximum", X (char *)NULL, Tk_Offset(Graph, X.reqMaximum), X XYGRAPH_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption}, X {TK_CONFIG_CUSTOM, "-ymin", "yMin", "Minimum", X (char *)NULL, Tk_Offset(Graph, Y.reqMinimum), X ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption}, X {TK_CONFIG_CUSTOM, "-ymax", "yMax", "Maximum", X (char *)NULL, Tk_Offset(Graph, Y.reqMaximum), X ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption}, X {TK_CONFIG_BOOLEAN, "-xlogscale", "xLogScale", "LogScale", X (char *)NULL, Tk_Offset(Graph, X.logScale), XYGRAPH_MASK}, X {TK_CONFIG_BOOLEAN, "-ylogscale", "yLogScale", "LogScale", X (char *)NULL, Tk_Offset(Graph, Y.logScale), ALL_MASK}, X {TK_CONFIG_PIXELS, "-axisthickness", "axisThickness", "Thickness", X (char *)NULL, Tk_Offset(Graph, axisThickness), ALL_MASK}, X {TK_CONFIG_DOUBLE, "-xstepsize", "xStepSize", "StepSize", X DEF_X_AXIS_STEP, Tk_Offset(Graph, X.reqStepSize), XYGRAPH_MASK}, X {TK_CONFIG_DOUBLE, "-ystepsize", "yStepSize", "StepSize", X DEF_Y_AXIS_STEP, Tk_Offset(Graph, Y.reqStepSize), ALL_MASK}, X {TK_CONFIG_INT, "-xrotation", "xRotation", "Rotation", X DEF_X_AXIS_ROTATION, Tk_Offset(Graph, xrotation), BARCHART_MASK}, X {TK_CONFIG_INT, "-xsubticks", "xSubTicks", "SubTicks", X DEF_NUM_MINOR_TICKS, Tk_Offset(Graph, X.minor.numSteps), XYGRAPH_MASK}, X {TK_CONFIG_INT, "-ysubticks", "ySubticks", "SubTicks", X DEF_NUM_MINOR_TICKS, Tk_Offset(Graph, Y.minor.numSteps), ALL_MASK}, X {TK_CONFIG_BOOLEAN, "-xticks", "xTicks", "Ticks", X DEF_X_AXIS_TICKS, Tk_Offset(Graph, X.major.numSteps), ALL_MASK}, X {TK_CONFIG_BOOLEAN, "-yticks", "yTicks", "Ticks", X DEF_Y_AXIS_TICKS, Tk_Offset(Graph, Y.major.numSteps), ALL_MASK}, X {TK_CONFIG_BOOLEAN, "-retrace", "retrace", "Retrace", X (char *)NULL, Tk_Offset(Graph, showRetrace), XYGRAPH_MASK}, X {TK_CONFIG_PIXELS, "-bottommargin", "bottomMargin", "BottomMargin", X (char *)NULL, Tk_Offset(Graph, bottomMargin), ALL_MASK}, X {TK_CONFIG_PIXELS, "-leftmargin", "leftMargin", "LeftMargin", X (char *)NULL, Tk_Offset(Graph, leftMargin), ALL_MASK}, X {TK_CONFIG_PIXELS, "-rightmargin", "rightMargin", "RightMargin", X (char *)NULL, Tk_Offset(Graph, rightMargin), ALL_MASK}, X {TK_CONFIG_PIXELS, "-topmargin", "topMargin", "TopMargin", X (char *)NULL, Tk_Offset(Graph, topMargin), ALL_MASK}, X {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; X static void DisplayGraph _ANSI_ARGS_((ClientData clientData)); static void DestroyGraph _ANSI_ARGS_((ClientData clientData)); static int GraphWidgetCmd _ANSI_ARGS_((ClientData clientData, X Tcl_Interp * interp, int argc, char **argv)); extern void TkBindError _ANSI_ARGS_((Tcl_Interp * interp)); X extern char *sys_errlist[]; X /* Graph to screen coordinate transformations */ #define GX(g,x) ((int)rint((x)*(g)->X.scale)+(g)->X.offset) #define GY(g,y) ((g)->Y.offset-(int)rint((y)*(g)->Y.scale)) X INLINE static void FormatLabel(logScale, value, label) /* Convert a value to a label */ X int logScale; X double value; X char *label; { X if (logScale) X sprintf(label, "1E%d", (int)rint(value)); X else X sprintf(label, "%.10g", value); } X INLINE static XPoint Gr_Point(graphPtr, x, y) X Graph *graphPtr; X double x, y; { X XPoint point; X X point.x = GX(graphPtr, x), point.y = GY(graphPtr, y); X return (point); } X INLINE static XSegment Gr_Segment(graphPtr, x1, y1, x2, y2) X Graph *graphPtr; X double x1, y1, x2, y2; { X XSegment seg; X X seg.x1 = GX(graphPtr, x1), seg.y1 = GY(graphPtr, y1); X seg.x2 = GX(graphPtr, x2), seg.y2 = GY(graphPtr, y2); X return (seg); } X INLINE static double ScaleX(graphPtr, x) X register Graph *graphPtr; X register double x; { X if (x == PositiveInfinity) X return (1.0); X else if (x == NegativeInfinity) X return (0.0); X if (graphPtr->X.logScale) { X if (x > 0.0) X x = log10(x); X else X return (-1.0); /* out of range */ X } X return ((x - graphPtr->X.minLimit) / graphPtr->X.range); } X INLINE static double ScaleY(graphPtr, y) X register Graph *graphPtr; X register double y; { X if (y == PositiveInfinity) X return (1.0); X else if (y == NegativeInfinity) X return (0.0); X if (graphPtr->Y.logScale) { X if (y > 0.0) X y = log10(y); X else X return (-1.0); /* out of range */ X } X return ((y - graphPtr->Y.minLimit) / graphPtr->Y.range); } X INLINE static double UnscaleX(graphPtr, x) X register Graph *graphPtr; X register double x; { X x = (x * graphPtr->X.range) + graphPtr->X.minLimit; X return ((graphPtr->X.logScale) ? exp10(x) : x); } X INLINE static double UnscaleY(graphPtr, y) X register Graph *graphPtr; X register double y; { X y = (y * graphPtr->Y.range) + graphPtr->Y.minLimit; X return ((graphPtr->Y.logScale) ? exp10(y) : y); } X /* ---------------------------------------------------------------------- X * Generic list management routines. X * ---------------------------------------------------------------------- */ X /* X *---------------------------------------------------------------------- X * X * CreateListEntry -- X * X * Appends a pointer on the end of a generic list, using the character X * string given as its identifier. The is no attempt to maintain X * consistency at this level. For example, more than one object may X * use the same key. X * X * Results: X * The return value is a standard Tcl result. X * X * Side Effects: X * The key is not copied, only the pointer is kept. It is assumed X * this key will remain static. X * X *---------------------------------------------------------------------- X */ static int CreateListEntry(listPtr, key, clientData) X LinkedList *listPtr; /* The list to append the object */ X char *key; /* Unique key to reference object */ X ClientData clientData; /* Pointer to the object */ { X register ListEntry *newPtr; X X newPtr = (ListEntry *) malloc(sizeof(ListEntry)); X if (newPtr == NULL) X return TCL_ERROR; X newPtr->clientData = clientData; X newPtr->keyPtr = key; X newPtr->prevPtr = newPtr->nextPtr = NULL; X X if (listPtr->headPtr == NULL) X listPtr->tailPtr = listPtr->headPtr = newPtr; X else { X listPtr->tailPtr->nextPtr = newPtr; X newPtr->prevPtr = listPtr->tailPtr; X listPtr->tailPtr = newPtr; X } X listPtr->numEntries++; X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * DeleteListEntry -- X * X * Remove the first entry (contains pointers to object key and data), X * using the given name as the key. Only the pointers to the object X * and key are deleted. X * X * Results: X * The return value is a standard Tcl result. If no object matching X * the key given is found, then TCL_ERROR is returned. X * X *---------------------------------------------------------------------- X */ static int DeleteListEntry(listPtr, name) X LinkedList *listPtr; /* List to delete entry from */ X char *name; /* Key to use in search */ { X register ListEntry *entryPtr; X char c = *name; X X for (entryPtr = listPtr->headPtr; entryPtr != NULL; X entryPtr = entryPtr->nextPtr) { X if ((c == *entryPtr->keyPtr) && strcmp(name, entryPtr->keyPtr) == 0) X break; X } X if (entryPtr == NULL) { /* Item is not in list */ X fprintf(stderr, "ListDelete: no such key `%s'\n", name); X return TCL_ERROR; X } X if (listPtr->headPtr == listPtr->tailPtr) { X listPtr->tailPtr = listPtr->headPtr = NULL; X } else if (entryPtr == listPtr->tailPtr) { X listPtr->tailPtr = entryPtr->prevPtr; X entryPtr->prevPtr->nextPtr = NULL; X } else if (entryPtr == listPtr->headPtr) { X listPtr->headPtr = entryPtr->nextPtr; X entryPtr->nextPtr->prevPtr = NULL; X } else { X entryPtr->prevPtr->nextPtr = entryPtr->nextPtr; X entryPtr->nextPtr->prevPtr = entryPtr->prevPtr; X } X listPtr->numEntries--; X free(entryPtr); X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * ClearList -- X * X * Removes all the entries from a list, removing pointers to the X * objects and keys (not the objects or keys themselves). X * The entry counter is reset to zero. X * X * Results: X * None. X * X *---------------------------------------------------------------------- X */ static void ClearList(listPtr) X LinkedList *listPtr; /* List to clear */ { X register ListEntry *oldPtr; X register ListEntry *entryPtr = listPtr->headPtr; X X while (entryPtr != NULL) { X oldPtr = entryPtr; X entryPtr = entryPtr->nextPtr; X ckfree((char *)oldPtr); X } X listPtr->numEntries = 0; } X /* X *---------------------------------------------------------------------- X * X * FindListEntry -- X * X * Find the first entry (containing pointers to object key and data), X * using the given name as the key. X * X * Results: X * Returns the pointer to the object. If no object matching X * the key given is found, then NULL is returned. X * X *---------------------------------------------------------------------- X */ static ClientData FindListEntry(listPtr, name) X LinkedList *listPtr; /* List to search */ X char *name; /* Key to match */ { X register ListEntry *entryPtr; X char c = *name; X X for (entryPtr = listPtr->headPtr; X entryPtr != NULL; entryPtr = entryPtr->nextPtr) { X if ((c == *entryPtr->keyPtr) && strcmp(name, entryPtr->keyPtr) == 0) X return (entryPtr->clientData); X } X return (NULL); } X /* X *---------------------------------------------------------------------- X * X * FirstListEntry -- X * X * Find the first entry in the list and return the pointer to X * the data object. In addition, update the given search pointer. X * X * Results: X * A pointer to the first object in the list is returned. If the X * list is empty, NULL is returned. The search pointer (used in X * subsequent searches) is set to the appropriate value. X * X *---------------------------------------------------------------------- X */ static ClientData FirstListEntry(listPtr, entryPtr) X LinkedList *listPtr; /* The list we are searching */ X ListEntry **entryPtr; /* Search pointer to set */ { X if (listPtr == NULL) X return (NULL); X *entryPtr = listPtr->headPtr; X if (listPtr->headPtr == NULL) X return (NULL); X return (listPtr->headPtr->clientData); } X /* X *---------------------------------------------------------------------- X * X * NextListEntry -- X * X * Find the next entry in the list using the given search pointer X * as the current location and return the pointer to X * its data object. In addition, update the given search pointer. X * X * Results: X * A pointer to the next object in the list is returned. If the X * list is at end, NULL is returned. The search pointer (used in X * subsequent searches) is set to the appropriate value. X * X *---------------------------------------------------------------------- X */ static ClientData NextListEntry(entryPtr) X ListEntry **entryPtr; /* Search pointer indicates current position */ { X if (entryPtr == NULL || *entryPtr == NULL) X return (NULL); X *entryPtr = (*entryPtr)->nextPtr; X if (*entryPtr == NULL) X return (NULL); X return ((*entryPtr)->clientData); } X /* ---------------------------------------------------------------------- X * Custom Option Procs X * ---------------------------------------------------------------------- X */ X /* X *---------------------------------------------------------------------- X * X * LimitParseProc -- X * X * Convert the string representation of an axis limit into its X * numeric form. X * X * Results: X * The return value is a standard Tcl result. The symbol type is X * written into the widget record. X * X *---------------------------------------------------------------------- X */ static int LimitParseProc(clientData, interp, tkwin, value, widgRec, offset) X ClientData clientData; /* Default value for limit */ X Tcl_Interp *interp; /* Interpreter to send results back to */ X Tk_Window tkwin; /* not used */ X char *value; /* */ X char *widgRec; /* */ X int offset; /* */ { X double *limitPtr = (double *)(widgRec + offset); X X if (NULLSTR(value)) { X double absLimit = *(double *)clientData; X X *limitPtr = absLimit; X } else { X double newLimit; X X if (Tcl_ExprDouble(interp, value, &newLimit) != TCL_OK) X return TCL_ERROR; X *limitPtr = newLimit; X } X return TCL_OK; } X X /* X *---------------------------------------------------------------------- X * X * LimitParseProc -- X * X * Convert the floating point axis limit into a string. X * X * Results: X * The string representation fo the limit is returned. X * X *---------------------------------------------------------------------- X */ static char * LimitPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) X ClientData clientData; /* Either -Inf or Inf */ X Tk_Window tkwin; /* not used */ X char *widgRec; /* */ X int offset; X Tcl_FreeProc **freeProcPtr; { X double limit = *(double *)(widgRec + offset); X double absLimit = *(double *)clientData; X char *result; X X result = ""; X if (limit != absLimit) { X char buf[80]; X X sprintf(buf, "%g", limit); X result = (char *)malloc(strlen(buf) + 1); X if (result == NULL) X return ""; X *freeProcPtr = TCL_DYNAMIC; X strcpy(result, buf); X } X return result; } X static struct SymbolInfo { X char *name; X int value; X } symInfo[]= { X X "solid", SOLID_LINESTYLE, X "dashed", DASHED_LINESTYLE, X "dotted", DOTTED_LINESTYLE, X "point", POINT_SYMBOL, X "square", SQUARE_SYMBOL, X "circle", CIRCLE_SYMBOL, X "diamond", DIAMOND_SYMBOL, X "plus", PLUS_SYMBOL, X "cross", CROSS_SYMBOL }; static int numSymbols = sizeof(symInfo) / sizeof(struct SymbolInfo); X /* X *---------------------------------------------------------------------- X * X * SymbolParseProc -- X * X * Convert the string representation of a line style or symbol name X * into its numeric form. X * X * Results: X * The return value is a standard Tcl result. The symbol type is X * written into the widget record. X * X *---------------------------------------------------------------------- X */ static int SymbolParseProc(clientData, interp, tkwin, value, widgRec, offset) X ClientData clientData; /* not used */ X Tcl_Interp *interp; /* Interpreter to send results back to */ X Tk_Window tkwin; /* not used */ X char *value; /* String representing symbol type */ X char *widgRec; /* Line information record */ X int offset; /* Offset of symbol type field in record */ { X register char c; X int *symbolPtr = (int *)(widgRec + offset); X register int i; X X c = *value; X for (i = 0; i < numSymbols; i++) { X if (c == *(symInfo[i].name) && strcmp(value, symInfo[i].name) == 0) { X *symbolPtr = symInfo[i].value; X return TCL_OK; X } X } X Tcl_AppendResult(interp, "Bad symbol name \"", value, "\"", NULL); X return TCL_ERROR; } X /* X *---------------------------------------------------------------------- X * X * NameOfSymbol -- X * X * Convert the symbol value into a string. X * X * Results: X * The string representing the symbol type or line style is returned. X * X *---------------------------------------------------------------------- X */ static char * NameOfSymbol(symbol) X int symbol; { X register int i; X X for (i = 0; i < numSymbols; i++) { X if (symbol == symInfo[i].value) X return (symInfo[i].name); X } X return NULL; } X /* X *---------------------------------------------------------------------- X * X * SymbolParseProc -- X * X * Convert the symbol value into a string. X * X * Results: X * The string representing the symbol type or line style is returned. X * X *---------------------------------------------------------------------- X */ static char * SymbolPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) X ClientData clientData; /* not used */ X Tk_Window tkwin; /* not used */ X char *widgRec; /* Line information record */ X int offset; /* Offset of symbol type field in record */ X Tcl_FreeProc **freeProcPtr; /* not used */ { X int symbol = *(int *)(widgRec + offset); X X return (NameOfSymbol(symbol)); } X X /* X *---------------------------------------------------------------------- X * X * GetExprValue -- X * X * Convert the expression string into a double precision value. X * The only reason we use this routine instead of Tcl_ExprDouble X * is to handle *elastic* bounds. That is, convert the strings X * "-Inf", "Inf" into NegativeInfinity, PositiveInfinity respectively. X * Under Sun's libc.a this is handled automatically. X * X * Results: X * The return value is a standard Tcl result. The value of the X * expression is passed back via valuePtr. X * X *---------------------------------------------------------------------- X */ static int GetExprValue(interp, expr, valuePtr) X Tcl_Interp *interp; /* Interpreter to send results back to */ X char *expr; /* Numeric expression string to parse */ X double *valuePtr; /* Real-valued result of expression */ { X if (Tcl_ExprDouble(interp, expr, valuePtr) != TCL_OK) { X if (strcmp(expr, "-Inf") == 0) { X *valuePtr = NegativeInfinity; /* Elastic lower bound */ X } else if (strcmp(expr, "Inf") == 0) { X *valuePtr = PositiveInfinity; /* Elastic upper bound */ X } else { X Tcl_AppendResult(interp, "Bad expression \"", expr, "\"", NULL); X return TCL_ERROR; X } X } X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * GetExprString -- X * X * Convert the expression double precision value into a string. X * The only reason we use this routine instead of sprintf X * is to handle *elastic* bounds. That is, convert the strings X * NegativeInfinity, PositiveInfinity into "-Inf", "Inf" respectively. X * X * Results: X * The return value is a standard Tcl result. The string of the X * expression is passed back via string. X * X *---------------------------------------------------------------------- X */ static int GetExprString(value, string) X double value; /* Expression value */ X char *string; /* String representation of value */ { X if (value == PositiveInfinity) X strcpy (string, "Inf"); X else if (value == NegativeInfinity) X strcpy (string, "-Inf"); X else X sprintf (string, "%g", value); X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * VectorParseProc -- X * X * Given a Tcl list of numeric expression representing the line X * values, convert into an array of double precision values. In X * addition, the minimum and maximum values are saved. Since X * elastic values are allow (values which translate to the X * min/max of the graph), we must try to get the non-elastic X * (not +-Infinity) minimum and maximum. X * X * Results: X * The return value is a standard Tcl result. The vector is passed X * back via the vecPtr. X * X *---------------------------------------------------------------------- X */ static int VectorParseProc(clientData, interp, tkwin, value, widgRec, offset) X ClientData clientData; /* not used */ X Tcl_Interp *interp; /* Interpreter to send results back to */ X Tk_Window tkwin; /* not used */ X char *value; /* Tcl list of expressions */ X char *widgRec; /* Line information record */ X int offset; /* Offset of vector in Line structure */ { X register int i; X register double max, min; X register Vector *vecPtr = (Vector *) (widgRec + offset); X register double *valuePtr; X int numExpr; X char **exprArr = NULL; X double *valueArr = NULL; X int firstMin, firstMax; X X /* Split the list of expressions and check the values */ X if (Tcl_SplitList(interp, value, &numExpr, &exprArr) != TCL_OK) { X return TCL_ERROR; X } X if (numExpr < 1) { X interp->result = "Empty list of numeric expressions"; X goto error; X } else if (numExpr >= 65535) { X interp->result = "Vector is too large"; /* XDrawLines limit */ X goto error; X } X /* Allocate an array of doubles to hold the values */ X valueArr = (double *)ckalloc(numExpr * sizeof(double)); X X if (valueArr == NULL) { X Tcl_AppendResult(interp, "Can't allocate data vector", (char *)NULL); X Tcl_SetErrorCode(interp, "UNIX", "malloc", sys_errlist[errno], NULL); X goto error; X } X /* X * Parse list of numeric expressions and and evaluate into an array X * real-valued numbers and Track the minimum and maximum values for the X * line which are not elastic (i.e. +/- Infinity) X */ X firstMin = firstMax = TRUE; X max = min = 0.0; X X for (valuePtr = valueArr, i = 0; i < numExpr; i++, valuePtr++) { X if (GetExprValue(interp, exprArr[i], valuePtr) != TCL_OK) X goto error; X if (*valuePtr != NegativeInfinity && *valuePtr != PositiveInfinity) { X if (firstMin) { X min = *valuePtr; X firstMin = FALSE; X } else if (*valuePtr < min) { X min = *valuePtr; X } X if (firstMax) { X max = *valuePtr; X firstMax = FALSE; X } else if (*valuePtr > max) { X max = *valuePtr; X } X } X } X interp->result = ""; X ckfree((char *)exprArr); X vecPtr->minValue = min, vecPtr->maxValue = max; X if (vecPtr->valueArr != NULL) X free((char *)vecPtr->valueArr); X vecPtr->valueArr = valueArr; X vecPtr->numValues = numExpr; X return TCL_OK; X X error: X /* Clean up, release allocated storage */ X if (exprArr) X ckfree((char *)exprArr); X if (valueArr) X ckfree((char *)valueArr); X return TCL_ERROR; } X /* X *---------------------------------------------------------------------- X * X * VectorPrintProc -- X * X * Convert the vector of double precision values into a Tcl list. X * X * Results: X * The string representation of the vector is returned. X * X *---------------------------------------------------------------------- X */ static char * VectorPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) X ClientData clientData; /* not used */ X Tk_Window tkwin; /* not used */ X char *widgRec; /* Line information record */ X int offset; /* Offset of vector in Line structure */ X Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { X register Vector *vecPtr = (Vector *) (widgRec + offset); X register int i; X register char *elemPtr; X char **stringArr; X char *result; X char buf[80]; X X result = ""; X if (vecPtr->numValues == 0) X return result; #ifdef NO_ALLOCA X stringArr = (char **)malloc(vecPtr->numValues * sizeof(char *)); #else X stringArr = (char **)alloca(vecPtr->numValues * sizeof(char *)); #endif /* NO_ALLOCA */ X if (stringArr == NULL) X return NULL; X for (i = 0; i < vecPtr->numValues; i++) { X GetExprString (vecPtr->valueArr[i], buf); #ifdef NO_ALLOCA X elemPtr = (char *)malloc(strlen(buf) + 1); #else X elemPtr = (char *)alloca(strlen(buf) + 1); #endif /* NO_ALLOCA */ X stringArr[i] = elemPtr; X if (elemPtr == NULL) X goto error; X strcpy(elemPtr, buf); X } X result = Tcl_Merge(vecPtr->numValues, stringArr); X *freeProcPtr = TCL_DYNAMIC; X X error: #ifdef NO_ALLOCA X for (i = 0; i < vecPtr->numValues; i++) { X if (stringArr[i] == NULL) X break; X free(stringArr[i]); X } X free((char *)stringArr); #endif /* NO_ALLOCA */ X return (result); } X /* X *---------------------------------------------------------------------- X * X * TwinVectorParseProc -- X * X * This procedure is like VectorParseProc except that it X * interprets the list of numeric expressions as X Y coordinate X * pairs. The minimum and maximum for both the X and Y vectors are X * determined. X * X * Results: X * The return value is a standard Tcl result. The vectors are passed X * back via the widget record (linePtr). X * X *---------------------------------------------------------------------- X */ static int TwinVectorParseProc(clientData, interp, tkwin, value, widgRec, offset) X ClientData clientData; /* not used */ X Tcl_Interp *interp; /* Interpreter to send results back to */ X Tk_Window tkwin; /* not used */ X char *value; /* Tcl list of expressions */ X char *widgRec; /* Line information record */ X int offset; /* not used */ { X register int i; X register double *valuePtr; X Line *linePtr = (Line *) widgRec; X int numExpr; X char **exprArr = NULL; X double *xValueArr = NULL, *yValueArr = NULL; X double min, max; X int firstMin, firstMax; X int arraySize; X X /* Split the list of numbers and check the values */ X if (Tcl_SplitList(interp, value, &numExpr, &exprArr) != TCL_OK) { X return TCL_ERROR; X } X if (numExpr < 1) { X interp->result = "Empty list of numeric expressions"; X goto error; X } else if (numExpr >= 131070) { X interp->result = "Vector is too large"; /* XDrawLines limit */ X goto error; X } else if (numExpr & 1) { X interp->result = "Odd number of values in -xydata option"; X goto error; X } X arraySize = numExpr / 2; X /* Allocate an array of doubles to hold the values */ X xValueArr = (double *)ckalloc(arraySize * sizeof(double)); X X if (xValueArr == NULL) { X Tcl_AppendResult(interp, "Can't allocate X vector for ", X linePtr->name, (char *)NULL); X Tcl_SetErrorCode(interp, "UNIX", "malloc", sys_errlist[errno], NULL); X goto error; X } X /* X * Parse list of numeric expressions and and evaluate into an array X * real-valued numbers X */ X firstMin = firstMax = TRUE; X min = max = 0.0; X for (valuePtr = xValueArr, i = 0; i < numExpr; i += 2, valuePtr++) { X if (GetExprValue(interp, exprArr[i], valuePtr) != TCL_OK) X goto error; X if (*valuePtr != NegativeInfinity && *valuePtr != PositiveInfinity) { X if (firstMin) { X min = *valuePtr; X firstMin = FALSE; X } else if (*valuePtr < min) { X min = *valuePtr; X } X if (firstMax) { X max = *valuePtr; X firstMax = FALSE; X } else if (*valuePtr > max) { X max = *valuePtr; X } X } X } X linePtr->x.minValue = min; X linePtr->x.maxValue = max; X yValueArr = (double *)ckalloc(arraySize * sizeof(double)); X X if (yValueArr == NULL) { X Tcl_AppendResult(interp, "Can't allocate Y vector for ", X linePtr->name, (char *)NULL); X Tcl_SetErrorCode(interp, "UNIX", "malloc", sys_errlist[errno], NULL); X goto error; X } X /* Now parse and evaluate the Y values */ X firstMin = firstMax = TRUE; X min = max = 0.0; X for (valuePtr = yValueArr, i = 1; i < numExpr; i += 2, valuePtr++) { X if (GetExprValue(interp, exprArr[i], valuePtr) != TCL_OK) X goto error; X if (*valuePtr != NegativeInfinity && *valuePtr != PositiveInfinity) { X if (firstMin) { X min = *valuePtr; X firstMin = FALSE; X } else if (*valuePtr < min) { X min = *valuePtr; X } X if (firstMax) { X max = *valuePtr; X firstMax = FALSE; X } else if (*valuePtr > max) { X max = *valuePtr; X } X } X } X linePtr->y.minValue = min; X linePtr->y.maxValue = max; X X /* Save arrays and limits */ X linePtr->x.numValues = linePtr->y.numValues = arraySize; X if (linePtr->x.valueArr != NULL) X free((char *)linePtr->x.valueArr); X linePtr->x.valueArr = xValueArr; X if (linePtr->y.valueArr != NULL) X free((char *)linePtr->y.valueArr); X linePtr->y.valueArr = yValueArr; X X interp->result = ""; X ckfree((char *)exprArr); X return TCL_OK; X error: X /* Clean up, release allocated storage */ X if (exprArr) X ckfree((char *)exprArr); X if (xValueArr) X ckfree((char *)xValueArr); X if (yValueArr) X ckfree((char *)yValueArr); X return TCL_ERROR; } X /* X *---------------------------------------------------------------------- X * X * TwinVectorPrintProc -- X * X * Given a Tcl list of numeric expression representing the line X * values, convert into an array of double precision values, saving X * the high and low values found. X * X * Results: X * The return value is a standard Tcl result. The vector is passed X * back via the vecPtr. X * X *---------------------------------------------------------------------- X */ static char * TwinVectorPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) X ClientData clientData; /* not used */ X Tk_Window tkwin; /* not used */ X char *widgRec; /* Line information record */ X int offset; /* not used */ X Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { X register Line *linePtr = (Line *) widgRec; X register int i, cnt; X char *elemPtr; X char **stringArr; X int arraySize; X char *result; X char buf[80]; X X result = ""; X if (linePtr->x.numValues == 0 || linePtr->y.numValues == 0) X return result; X arraySize = linePtr->x.numValues + linePtr->y.numValues; #ifdef NO_ALLOCA X stringArr = (char **)malloc(arraySize * sizeof(char *)); #else X stringArr = (char **)alloca(arraySize * sizeof(char *)); #endif /* NO_ALLOCA */ X X if (stringArr == NULL) X return NULL; X for (cnt = i = 0; i < linePtr->x.numValues; i++) { X GetExprString (linePtr->x.valueArr[i], buf); #ifdef NO_ALLOCA X elemPtr = (char *)malloc(strlen(buf) + 1); #else X elemPtr = (char *)alloca(strlen(buf) + 1); #endif /* NO_ALLOCA */ X stringArr[cnt++] = elemPtr; X if (elemPtr == NULL) X goto error; X strcpy(elemPtr, buf); X GetExprString (linePtr->y.valueArr[i], buf); #ifdef NO_ALLOCA X elemPtr = (char *)malloc(strlen(buf) + 1); #else X elemPtr = (char *)alloca(strlen(buf) + 1); #endif /* NO_ALLOCA */ X stringArr[cnt++] = elemPtr; X if (elemPtr == NULL) X goto error; X strcpy(elemPtr, buf); X } X result = Tcl_Merge(arraySize, stringArr); X *freeProcPtr = TCL_DYNAMIC; X X error: #ifdef NO_ALLOCA X for (i = 0; i < arraySize; i++) { X if (stringArr[i] == NULL) X break; X free(stringArr[i]); X } X free((char *)stringArr); #endif /* NO_ALLOCA */ X return (result); } X /* X *---------------------------------------------------------------------- X * X * LegendParseProc -- X * X * Convert the string representation of a legend XY position into X * screen coordinates. The form of the string must be "@x,y" or X * none. X * X * Results: X * The return value is a standard Tcl result. The symbol type is X * written into the widget record. X * X * Side Effects: X * If no legend position is given, the right margin of the graph X * will be automatically increased to hold the legend. X * X *---------------------------------------------------------------------- X */ static int LegendParseProc(clientData, interp, tkwin, value, widgRec, offset) X ClientData clientData; /* not used */ X Tcl_Interp *interp; /* Interpreter to send results back to */ X Tk_Window tkwin; /* not used */ X char *value; /* New legend position string */ X char *widgRec; /* Graph widget record */ X int offset; /* Offset of legend in widget record */ { X Legend *legendPtr = (Legend *) (widgRec + offset); X int x, y; X X if (NULLSTR(value)) { X legendPtr->usePosition = FALSE; X return TCL_OK; X } X if (*value != '@' || sscanf(value + 1, "%d,%d", &x, &y) != 2) { X Tcl_AppendResult(interp, "Bad position: should be \"@x,y\"", NULL); X return TCL_ERROR; X } X legendPtr->usePosition = TRUE; X legendPtr->x = x; X legendPtr->y = y; X return TCL_OK; } X /* X *---------------------------------------------------------------------- X * X * LegendPrintProc -- X * X * Convert the legend XY position into a string. X * X * Results: X * The string representing the legend position is returned. X * X *---------------------------------------------------------------------- X */ static char * LegendPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) X ClientData clientData; /* not used */ X Tk_Window tkwin; /* not used */ X char *widgRec; /* Graph widget record */ X int offset; /* Offset of legend in widget record */ X Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { X char *result; X Legend *legendPtr = (Legend *) (widgRec + offset); X X result = ""; X if (legendPtr->usePosition) { X char buf[80]; X X sprintf(buf, "@%d,%d", legendPtr->x, legendPtr->y); X result = (char *)malloc(strlen(buf) + 1); X if (result == NULL) X return (""); X strcpy(result, buf); X *freeProcPtr = TCL_DYNAMIC; X } X return (result); } X /* X *---------------------------------------------------------------------- X * X * ExprParseProc -- X * X * Convert the string representation of a floating point expression X * into its double precision value. The only difference here is X * that "Inf" and "-Inf" are translated into HUGE_VAL and -HUGE_VAL X * respectively. X * X * Results: X * The return value is a standard Tcl result. The value is X * written into the widget record. X * X *---------------------------------------------------------------------- X */ static int ExprParseProc(clientData, interp, tkwin, value, widgRec, offset) X ClientData clientData; /* not used */ X Tcl_Interp *interp; /* Interpreter to send results back to */ X Tk_Window tkwin; /* not used */ X char *value; /* Floating point expression */ X char *widgRec; /* Widget record */ X int offset; /* Offset of double in widget record */ { X double *resultPtr = (double *) (widgRec + offset); X X return (GetExprValue (interp, value, resultPtr)); } X /* X *---------------------------------------------------------------------- X * X * ExprPrintProc -- X * X * Convert the legend XY position into a string. X * X * Results: X * The string representing the legend position is returned. X * X *---------------------------------------------------------------------- X */ static char * ExprPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) X ClientData clientData; /* not used */ X Tk_Window tkwin; /* not used */ X char *widgRec; /* Graph widget record */ X int offset; /* Offset of legend in widget record */ X Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { X char *result; X char buf [80]; X double value = *(double *)(widgRec + offset); X X GetExprString (value, buf); X result = (char *)malloc(strlen(buf) + 1); X if (result == NULL) X return (""); X strcpy(result, buf); X *freeProcPtr = TCL_DYNAMIC; X return (result); } X /* X * ----------------------------------------------------------------- X * General X * ----------------------------------------------------------------- X */ X INLINE static int Quadrant(rotation) /* Compute quadrant */ X int rotation; { X rotation %= 360; X if (rotation < 0) X rotation += 360; X return (rotation / 90); } X /* X * ----------------------------------------------------------------- X * X * GetTextCoords -- X * X * Translate the coordinates of a given text string based upon X * the anchor specified. The anchor indicates where the given X * xy position are in relation to the text bounding box. X * X * nw --- n --- ne X * | | X * w center e X * | | X * sw --- s --- se X * X * The coordinates returned are translated to the baseline X * origin of the text bounding box (suitable for giving to X * XDrawString, XDrawText, etc). X * X * Results: X * The translated text coordinates are returned. X * X * ----------------------------------------------------------------- X */ static XPoint GetTextCoords(fontPtr, text, x, y, anchor, quadrant) X XFontStruct *fontPtr; /* Font information */ X char *text; /* Text string */ X int x; /* X position of anchor */ X int y; /* Y position of anchor */ X Tk_Anchor anchor; /* Direction of the anchor */ X int quadrant; /* Quadrant of rotation */ { X register int width, height; X XPoint pt; X XCharStruct bbox; /* Bounding box for text string */ X int dummy; /* not used */ X X /* Get the width and height of the text string to be created */ X XTextExtents(fontPtr, text, strlen(text), &dummy, &dummy, &dummy, &bbox); X width = (bbox.rbearing - bbox.lbearing); X height = (bbox.ascent + bbox.descent); X X pt.x = x, pt.y = y; X switch (quadrant) { X case ROTATE_0: X switch (anchor) { X case TK_ANCHOR_NW: X pt.y += bbox.ascent; X break; X case TK_ANCHOR_W: X pt.y += (bbox.ascent - bbox.descent) / 2; X break; X case TK_ANCHOR_SW: X pt.y -= bbox.descent; X break; X case TK_ANCHOR_NE: X pt.x -= width; X pt.y += bbox.ascent; X break; X case TK_ANCHOR_E: X pt.x -= width; X pt.y += (bbox.ascent - bbox.descent) / 2; X break; X case TK_ANCHOR_SE: X pt.x -= width; X pt.y -= bbox.descent; X break; X case TK_ANCHOR_N: X pt.y += bbox.ascent; X pt.x -= width / 2; X break; X case TK_ANCHOR_S: X pt.y -= bbox.descent; X pt.x -= width / 2; X break; X case TK_ANCHOR_CENTER: X pt.x -= width / 2; X pt.y += (bbox.ascent - bbox.descent) / 2; X break; X } X break; X case ROTATE_90: X switch (anchor) { X case TK_ANCHOR_NW: X pt.x += bbox.ascent; X pt.y += width; X break; X case TK_ANCHOR_W: X pt.x += bbox.ascent; X pt.y += width / 2; X break; X case TK_ANCHOR_SW: X pt.x += bbox.ascent; X break; X case TK_ANCHOR_N: X pt.x += (bbox.ascent - bbox.descent) / 2; X pt.y += width; X break; X case TK_ANCHOR_CENTER: X pt.x += (bbox.ascent - bbox.descent) / 2; X pt.y += width / 2; X break; X case TK_ANCHOR_S: X pt.x += (bbox.ascent - bbox.descent) / 2; X break; X case TK_ANCHOR_NE: X pt.x -= bbox.descent; X pt.y += width; X break; X case TK_ANCHOR_E: X pt.x -= bbox.descent; X pt.y += width / 2; X break; X case TK_ANCHOR_SE: X pt.x -= bbox.descent; X break; X } X break; X case ROTATE_180: X switch (anchor) { X case TK_ANCHOR_NW: X pt.x += width; X pt.y += bbox.descent; X break; X case TK_ANCHOR_W: X pt.x += width; X pt.y += -(bbox.ascent - bbox.descent) / 2; X break; X case TK_ANCHOR_SW: X pt.x += width; X pt.y += bbox.ascent; X break; X case TK_ANCHOR_N: X pt.x += width / 2; X pt.y += bbox.descent; X break; X case TK_ANCHOR_CENTER: X pt.x += width / 2; X pt.y -= (bbox.ascent - bbox.descent) / 2; X break; X case TK_ANCHOR_S: X pt.x += width / 2; X pt.y += bbox.ascent; X break; X case TK_ANCHOR_NE: X pt.y += bbox.descent; X break; X case TK_ANCHOR_E: X pt.y -= (bbox.ascent - bbox.descent) / 2; X break; X case TK_ANCHOR_SE: X pt.y += bbox.ascent; X break; X } X break; X case ROTATE_270: X switch (anchor) { X case TK_ANCHOR_NW: X pt.x += bbox.descent; X break; X case TK_ANCHOR_W: X pt.x += bbox.descent; X pt.y -= width / 2; X break; X case TK_ANCHOR_SW: X pt.x += bbox.descent; X pt.y -= width; X break; X case TK_ANCHOR_N: X pt.x -= (bbox.ascent - bbox.descent) / 2; X break; X case TK_ANCHOR_CENTER: X pt.x -= (bbox.ascent - bbox.descent) / 2; X pt.y -= width / 2; X break; X case TK_ANCHOR_S: X pt.x -= (bbox.ascent - bbox.descent) / 2; X pt.y -= width; X break; X case TK_ANCHOR_NE: X pt.x -= bbox.ascent; X break; X case TK_ANCHOR_E: X pt.x -= bbox.ascent; X pt.y -= width / 2; X break; X case TK_ANCHOR_SE: X pt.x -= bbox.ascent; X pt.y -= width; X break; X } X break; X } X return (pt); } X /* X * ----------------------------------------------------------------- X * X * GetBoxCoords -- X * X * Translate the coordinates of a given bounding box based X * upon the anchor specified. The anchor indicates where X * the given xy position is in relation to the bounding box. X * X * nw --- n --- ne X * | | X * w center e X * | | X * sw --- s --- se X * X * The coordinates returned are translated to the origin of the SHAR_EOF : || echo 'restore of snmp2/snmptcl/graph.c failed' fi echo 'End of snmp2 part 23' echo 'File snmp2/snmptcl/graph.c is continued in part 24' echo 24 > _sharseq.tmp exit 0