/* * bltGrHairs.c -- * * This module implements crosshairs for the BLT graph widget. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * Graph widget created by Sani Nassif and George Howlett. */ #include "bltGraph.h" extern Tk_CustomOption bltPointOption; extern Tk_CustomOption bltDistanceOption; extern Tk_CustomOption bltDashesOption; /* * ------------------------------------------------------------------- * * Crosshairs * * Contains the line segments positions and graphics context used * to simulate crosshairs (by XORing) on the graph. * * ------------------------------------------------------------------- */ struct CrosshairsStruct { XPoint hotSpot; /* Hot spot for crosshairs */ int visible; /* Internal state of crosshairs. If non-zero, * crosshairs are displayed. */ int hidden; /* If non-zero, crosshairs are not displayed. * This is not necessarily consistent with the * internal state variable. This is true when * the hot spot is off the graph. */ Blt_Dashes dashes; /* Dashstyle of the crosshairs. This represents * an array of alternatingly drawn pixel * values. If NULL, the hairs are drawn as a * solid line */ int lineWidth; /* Width of the simulated crosshair lines */ XSegment segArr[2]; /* Positions of line segments representing the * simulated crosshairs. */ XColor *colorPtr; /* Foreground color of crosshairs */ GC gc; /* Graphics context for crosshairs. Set to * GXxor to not require redraws of graph */ }; #define DEF_HAIRS_DASHES (char *)NULL #define DEF_HAIRS_FOREGROUND RGB_BLACK #define DEF_HAIRS_FG_MONO RGB_BLACK #define DEF_HAIRS_LINE_WIDTH "0" #define DEF_HAIRS_HIDE "yes" #define DEF_HAIRS_POSITION (char *)NULL static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_COLOR, "-color", "color", "Color", DEF_HAIRS_FOREGROUND, Tk_Offset(Crosshairs, colorPtr), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-color", "color", "Color", DEF_HAIRS_FG_MONO, Tk_Offset(Crosshairs, colorPtr), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes", DEF_HAIRS_DASHES, Tk_Offset(Crosshairs, dashes), TK_CONFIG_NULL_OK, &bltDashesOption}, {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_HAIRS_HIDE, Tk_Offset(Crosshairs, hidden), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "Linewidth", DEF_HAIRS_LINE_WIDTH, Tk_Offset(Crosshairs, lineWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-position", "position", "Position", DEF_HAIRS_POSITION, Tk_Offset(Crosshairs, hotSpot), 0, &bltPointOption}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; /* *---------------------------------------------------------------------- * * TurnOffHairs -- * * XOR's the existing line segments (representing the crosshairs), * thereby erasing them. The internal state of the crosshairs is * tracked. * * Results: * None * * Side Effects: * Crosshairs are erased. * *---------------------------------------------------------------------- */ static void TurnOffHairs(tkwin, chPtr) Tk_Window tkwin; Crosshairs *chPtr; { if (Tk_IsMapped(tkwin) && (chPtr->visible)) { XDrawSegments(Tk_Display(tkwin), Tk_WindowId(tkwin), chPtr->gc, chPtr->segArr, 2); chPtr->visible = FALSE; } } /* *---------------------------------------------------------------------- * * TurnOnHairs -- * * Draws (by XORing) new line segments, creating the effect of * crosshairs. The internal state of the crosshairs is tracked. * * Results: * None * * Side Effects: * Crosshairs are displayed. * *---------------------------------------------------------------------- */ static void TurnOnHairs(graphPtr, chPtr) Graph *graphPtr; Crosshairs *chPtr; { if (Tk_IsMapped(graphPtr->tkwin) && (!chPtr->visible)) { if (!PointInGraph(graphPtr, chPtr->hotSpot.x, chPtr->hotSpot.y)) { return; /* Coordinates are off the graph */ } XDrawSegments(graphPtr->display, Tk_WindowId(graphPtr->tkwin), chPtr->gc, chPtr->segArr, 2); chPtr->visible = TRUE; } } /* *---------------------------------------------------------------------- * * ConfigureCrosshairs -- * * Configures attributes of the crosshairs such as line width, * dashes, and position. The crosshairs are first turned off * before any of the attributes changes. * * Results: * None * * Side Effects: * Crosshair GC is allocated. * *---------------------------------------------------------------------- */ void Blt_ConfigureCrosshairs(graphPtr) Graph *graphPtr; { XGCValues gcValues; unsigned long gcMask; GC newGC; long colorValue; Crosshairs *chPtr = graphPtr->crosshairs; /* * Turn off the crosshairs temporarily. This is in case the new * configuration changes the size, style, or position of the lines. */ TurnOffHairs(graphPtr->tkwin, chPtr); gcValues.function = GXxor; if (graphPtr->plotBg == NULL) { /* The graph's color option may not have been set yet */ colorValue = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin)); } else { colorValue = graphPtr->plotBg->pixel; } gcValues.background = colorValue; gcValues.foreground = (colorValue ^ chPtr->colorPtr->pixel); gcValues.line_width = LineWidth(chPtr->lineWidth); gcMask = (GCForeground | GCBackground | GCFunction | GCLineWidth); if (LineIsDashed(chPtr->dashes)) { gcValues.line_style = LineOnOffDash; gcMask |= GCLineStyle; } newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues); if (LineIsDashed(chPtr->dashes)) { Blt_SetDashes(graphPtr->display, newGC, &(chPtr->dashes)); } if (chPtr->gc != NULL) { Blt_FreePrivateGC(graphPtr->display, chPtr->gc); } chPtr->gc = newGC; /* * Are the new coordinates on the graph? */ chPtr->segArr[0].x2 = chPtr->segArr[0].x1 = chPtr->hotSpot.x; chPtr->segArr[0].y1 = graphPtr->bottom; chPtr->segArr[0].y2 = graphPtr->top; chPtr->segArr[1].y2 = chPtr->segArr[1].y1 = chPtr->hotSpot.y; chPtr->segArr[1].x1 = graphPtr->left; chPtr->segArr[1].x2 = graphPtr->right; if (!chPtr->hidden) { TurnOnHairs(graphPtr, chPtr); } } void Blt_EnableCrosshairs(graphPtr) Graph *graphPtr; { if (!graphPtr->crosshairs->hidden) { TurnOnHairs(graphPtr, graphPtr->crosshairs); } } void Blt_DisableCrosshairs(graphPtr) Graph *graphPtr; { if (!graphPtr->crosshairs->hidden) { TurnOffHairs(graphPtr->tkwin, graphPtr->crosshairs); } } /* *---------------------------------------------------------------------- * * UpdateCrosshairs -- * * Update the length of the hairs (not the hot spot). * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_UpdateCrosshairs(graphPtr) Graph *graphPtr; { Crosshairs *chPtr = graphPtr->crosshairs; chPtr->segArr[0].y1 = graphPtr->bottom; chPtr->segArr[0].y2 = graphPtr->top; chPtr->segArr[1].x1 = graphPtr->left; chPtr->segArr[1].x2 = graphPtr->right; } /* *---------------------------------------------------------------------- * * Blt_DestroyCrosshairs -- * * Results: * None * * Side Effects: * Crosshair GC is allocated. * *---------------------------------------------------------------------- */ void Blt_DestroyCrosshairs(graphPtr) Graph *graphPtr; { Crosshairs *chPtr = graphPtr->crosshairs; Tk_FreeOptions(configSpecs, (char *)chPtr, graphPtr->display, 0); if (chPtr->gc != NULL) { Blt_FreePrivateGC(graphPtr->display, chPtr->gc); } Blt_Free(chPtr); } /* *---------------------------------------------------------------------- * * Blt_CreateCrosshairs -- * * Creates and initializes a new crosshair structure. * * Results: * Returns TCL_ERROR if the crosshair structure can't be created, * otherwise TCL_OK. * * Side Effects: * Crosshair GC is allocated. * *---------------------------------------------------------------------- */ int Blt_CreateCrosshairs(graphPtr) Graph *graphPtr; { Crosshairs *chPtr; chPtr = Blt_Calloc(1, sizeof(Crosshairs)); assert(chPtr); chPtr->hidden = TRUE; chPtr->hotSpot.x = chPtr->hotSpot.y = -1; graphPtr->crosshairs = chPtr; if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin, "crosshairs", "Crosshairs", configSpecs, 0, (char **)NULL, (char *)chPtr, 0) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * CgetOp -- * * Queries configuration attributes of the crosshairs such as * line width, dashes, and position. * * Results: * A standard Tcl result. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static int CgetOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Crosshairs *chPtr = graphPtr->crosshairs; return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs, (char *)chPtr, argv[3], 0); } /* *---------------------------------------------------------------------- * * ConfigureOp -- * * Queries or resets configuration attributes of the crosshairs * such as line width, dashes, and position. * * Results: * A standard Tcl result. * * Side Effects: * Crosshairs are reset. * *---------------------------------------------------------------------- */ static int ConfigureOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Crosshairs *chPtr = graphPtr->crosshairs; if (argc == 3) { return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs, (char *)chPtr, (char *)NULL, 0); } else if (argc == 4) { return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs, (char *)chPtr, argv[3], 0); } if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3, argv + 3, (char *)chPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; } Blt_ConfigureCrosshairs(graphPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * OnOp -- * * Maps the crosshairs. * * Results: * A standard Tcl result. * * Side Effects: * Crosshairs are reset if necessary. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int OnOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Crosshairs *chPtr = graphPtr->crosshairs; if (chPtr->hidden) { TurnOnHairs(graphPtr, chPtr); chPtr->hidden = FALSE; } return TCL_OK; } /* *---------------------------------------------------------------------- * * OffOp -- * * Unmaps the crosshairs. * * Results: * A standard Tcl result. * * Side Effects: * Crosshairs are reset if necessary. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int OffOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Crosshairs *chPtr = graphPtr->crosshairs; if (!chPtr->hidden) { TurnOffHairs(graphPtr->tkwin, chPtr); chPtr->hidden = TRUE; } return TCL_OK; } /* *---------------------------------------------------------------------- * * ToggleOp -- * * Toggles the state of the crosshairs. * * Results: * A standard Tcl result. * * Side Effects: * Crosshairs are reset. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int ToggleOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Crosshairs *chPtr = graphPtr->crosshairs; chPtr->hidden = (chPtr->hidden == 0); if (chPtr->hidden) { TurnOffHairs(graphPtr->tkwin, chPtr); } else { TurnOnHairs(graphPtr, chPtr); } return TCL_OK; } static Blt_OpSpec xhairOps[] = { {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",}, {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?options...?",}, {"off", 2, (Blt_Op)OffOp, 3, 3, "",}, {"on", 2, (Blt_Op)OnOp, 3, 3, "",}, {"toggle", 1, (Blt_Op)ToggleOp, 3, 3, "",}, }; static int nXhairOps = sizeof(xhairOps) / sizeof(Blt_OpSpec); /* *---------------------------------------------------------------------- * * Blt_CrosshairsOp -- * * User routine to configure crosshair simulation. Crosshairs * are simulated by drawing line segments parallel to both axes * using the XOR drawing function. The allows the lines to be * erased (by drawing them again) without redrawing the entire * graph. Care must be taken to erase crosshairs before redrawing * the graph and redraw them after the graph is redraw. * * Results: * The return value is a standard Tcl result. * * Side Effects: * Crosshairs may be drawn in the plotting area. * *---------------------------------------------------------------------- */ int Blt_CrosshairsOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Blt_Op proc; proc = Blt_GetOp(interp, nXhairOps, xhairOps, BLT_OP_ARG2, argc, argv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (graphPtr, interp, argc, argv); }