/* * Smithsonian Astrophysical Observatory, Cambridge, MA, USA * This code has been modified under the terms listed below and is made * available under the same terms. */ /* * Copyright 1991-2004 George A Howlett. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "tkbltGraph.h" #include "tkbltGraphLine.h" #include "tkbltGraphBar.h" #include "tkbltGraphOp.h" #include "tkbltGrAxis.h" #include "tkbltGrAxisOp.h" #include "tkbltGrElem.h" #include "tkbltGrElemOp.h" #include "tkbltGrHairs.h" #include "tkbltGrHairsOp.h" #include "tkbltGrLegd.h" #include "tkbltGrLegdOp.h" #include "tkbltGrMarker.h" #include "tkbltGrMarkerOp.h" #include "tkbltGrPostscript.h" #include "tkbltGrPostscriptOp.h" #include "tkbltGrPen.h" #include "tkbltGrPenOp.h" #include "tkbltGrXAxisOp.h" using namespace Blt; static Tcl_ObjCmdProc BarchartObjCmd; static Tcl_ObjCmdProc GraphObjCmd; static Axis* GetFirstAxis(Chain* chain); int GraphObjConfigure(Graph* graphPtr, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Tk_SavedOptions savedOptions; int mask =0; int error; Tcl_Obj* errorResult; for (error=0; error<=1; error++) { if (!error) { if (Tk_SetOptions(interp, (char*)graphPtr->ops_, graphPtr->optionTable_, objc, objv, graphPtr->tkwin_, &savedOptions, &mask) != TCL_OK) continue; } else { errorResult = Tcl_GetObjResult(interp); Tcl_IncrRefCount(errorResult); Tk_RestoreSavedOptions(&savedOptions); } if (graphPtr->configure() != TCL_OK) return TCL_ERROR; graphPtr->flags |= mask; graphPtr->eventuallyRedraw(); break; } if (!error) { Tk_FreeSavedOptions(&savedOptions); return TCL_OK; } else { Tcl_SetObjResult(interp, errorResult); Tcl_DecrRefCount(errorResult); return TCL_ERROR; } } static int CgetOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "cget option"); return TCL_ERROR; } Tcl_Obj* objPtr = Tk_GetOptionValue(interp, (char*)graphPtr->ops_, graphPtr->optionTable_, objv[2], graphPtr->tkwin_); if (objPtr == NULL) return TCL_ERROR; else Tcl_SetObjResult(interp, objPtr); return TCL_OK; } static int ConfigureOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc <= 3) { Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)graphPtr->ops_, graphPtr->optionTable_, (objc == 3) ? objv[2] : NULL, graphPtr->tkwin_); if (objPtr == NULL) return TCL_ERROR; else Tcl_SetObjResult(interp, objPtr); return TCL_OK; } else return GraphObjConfigure(graphPtr, interp, objc-2, objv+2); } /* *--------------------------------------------------------------------------- * * ExtentsOp -- * * Reports the size of one of several items within the graph. The * following are valid items: * * "bottommargin" Height of the bottom margin * "leftmargin" Width of the left margin * "legend" x y w h of the legend * "plotarea" x y w h of the plotarea * "plotheight" Height of the plot area * "rightmargin" Width of the right margin * "topmargin" Height of the top margin * "plotwidth" Width of the plot area * * Results: * Always returns TCL_OK. * *--------------------------------------------------------------------------- */ static int ExtentsOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; GraphOptions* ops = (GraphOptions*)graphPtr->ops_; int length; const char* string = Tcl_GetStringFromObj(objv[2], &length); char c = string[0]; if ((c == 'p') && (length > 4) && (strncmp("plotheight", string, length) == 0)) { int height = graphPtr->bottom_ - graphPtr->top_ + 1; Tcl_SetIntObj(Tcl_GetObjResult(interp), height); } else if ((c == 'p') && (length > 4) && (strncmp("plotwidth", string, length) == 0)) { int width = graphPtr->right_ - graphPtr->left_ + 1; Tcl_SetIntObj(Tcl_GetObjResult(interp), width); } else if ((c == 'p') && (length > 4) && (strncmp("plotarea", string, length) == 0)) { Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->left_)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->top_)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->right_ - graphPtr->left_+1)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->bottom_ - graphPtr->top_+1)); Tcl_SetObjResult(interp, listObjPtr); } else if ((c == 'l') && (length > 2) && (strncmp("legend", string, length) == 0)) { Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->legend_->x_)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->legend_->y_)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->legend_->width_)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->legend_->height_)); Tcl_SetObjResult(interp, listObjPtr); } else if ((c == 'l') && (length > 2) && (strncmp("leftmargin", string, length) == 0)) { Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->leftMargin.width); } else if ((c == 'r') && (length > 1) && (strncmp("rightmargin", string, length) == 0)) { Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->rightMargin.width); } else if ((c == 't') && (length > 1) && (strncmp("topmargin", string, length) == 0)) { Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->topMargin.height); } else if ((c == 'b') && (length > 1) && (strncmp("bottommargin", string, length) == 0)) { Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->bottomMargin.height); } else { Tcl_AppendResult(interp, "bad extent item \"", objv[2], "\": should be plotheight, plotwidth, leftmargin, rightmargin, \ topmargin, bottommargin, plotarea, or legend", (char*)NULL); return TCL_ERROR; } return TCL_OK; } static int InsideOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "x y"); return TCL_ERROR; } Graph* graphPtr = (Graph*)clientData; int x; if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) return TCL_ERROR; int y; if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) return TCL_ERROR; Region2d exts; graphPtr->extents(&exts); int result = (x<=exts.right && x>=exts.left && y<=exts.bottom && y>=exts.top); Tcl_SetBooleanObj(Tcl_GetObjResult(interp), result); return TCL_OK; } static int InvtransformOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; double x, y; if ((Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK) || (Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK)) return TCL_ERROR; if (graphPtr->flags & RESET) graphPtr->resetAxes(); // Perform the reverse transformation, converting from window coordinates // to graph data coordinates. Note that the point is always mapped to the // bottom and left axes (which may not be what the user wants) Axis* xAxis = GetFirstAxis(graphPtr->axisChain_[0]); Axis* yAxis = GetFirstAxis(graphPtr->axisChain_[1]); Point2d point = graphPtr->invMap2D(x, y, xAxis, yAxis); Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(point.x)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(point.y)); Tcl_SetObjResult(interp, listObjPtr); return TCL_OK; } static int TransformOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; double x, y; if ((Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK) || (Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK)) return TCL_ERROR; if (graphPtr->flags & RESET) graphPtr->resetAxes(); // Perform the transformation from window to graph coordinates. Note that // the points are always mapped onto the bottom and left axes (which may // not be the what the user wants Axis* xAxis = GetFirstAxis(graphPtr->axisChain_[0]); Axis* yAxis = GetFirstAxis(graphPtr->axisChain_[1]); Point2d point = graphPtr->map2D(x, y, xAxis, yAxis); Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj((int)point.x)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj((int)point.y)); Tcl_SetObjResult(interp, listObjPtr); return TCL_OK; } static const Ensemble graphEnsemble[] = { {"axis", 0, Blt::axisEnsemble}, {"bar", 0, Blt::elementEnsemble}, {"cget", CgetOp, 0}, {"configure", ConfigureOp, 0}, {"crosshairs", 0, Blt::crosshairsEnsemble}, {"element", 0, Blt::elementEnsemble}, {"extents", ExtentsOp, 0}, {"inside", InsideOp, 0}, {"invtransform",InvtransformOp, 0}, {"legend", 0, Blt::legendEnsemble}, {"line", 0, Blt::elementEnsemble}, {"marker", 0, Blt::markerEnsemble}, {"pen", 0, Blt::penEnsemble}, {"postscript", 0, Blt::postscriptEnsemble}, {"transform", TransformOp, 0}, {"xaxis", 0, Blt::xaxisEnsemble}, {"yaxis", 0, Blt::xaxisEnsemble}, {"x2axis", 0, Blt::xaxisEnsemble}, {"y2axis", 0, Blt::xaxisEnsemble}, { 0,0,0 } }; // Support static Axis* GetFirstAxis(Chain* chain) { ChainLink* link = Chain_FirstLink(chain); if (!link) return NULL; return (Axis*)Chain_GetValue(link); } // Tk Interface int Blt_GraphCmdInitProc(Tcl_Interp* interp) { Tcl_Namespace* nsPtr = Tcl_FindNamespace(interp, "::blt", NULL, TCL_LEAVE_ERR_MSG); if (nsPtr == NULL) return TCL_ERROR; { const char* cmdPath = "::blt::graph"; Tcl_Command cmdToken = Tcl_FindCommand(interp, cmdPath, NULL, 0); if (cmdToken) return TCL_OK; cmdToken = Tcl_CreateObjCommand(interp, cmdPath, GraphObjCmd, NULL, NULL); if (Tcl_Export(interp, nsPtr, "graph", 0) != TCL_OK) return TCL_ERROR; } { const char* cmdPath = "::blt::barchart"; Tcl_Command cmdToken = Tcl_FindCommand(interp, cmdPath, NULL, 0); if (cmdToken) return TCL_OK; cmdToken = Tcl_CreateObjCommand(interp, cmdPath, BarchartObjCmd, NULL,NULL); if (Tcl_Export(interp, nsPtr, "barchart", 0) != TCL_OK) return TCL_ERROR; } return TCL_OK; } static int GraphObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); return TCL_ERROR; } Graph* graphPtr = new LineGraph(clientData, interp, objc, objv); return graphPtr->valid_ ? TCL_OK : TCL_ERROR; } static int BarchartObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); return TCL_ERROR; } Graph* graphPtr = new BarGraph(clientData, interp, objc, objv); return graphPtr->valid_ ? TCL_OK : TCL_ERROR; } int GraphInstCmdProc(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; Tcl_Preserve(graphPtr); int result = graphPtr->invoke(graphEnsemble, 1, objc, objv); Tcl_Release(graphPtr); return result; } // called by Tcl_DeleteCommand void GraphInstCmdDeleteProc(ClientData clientData) { Graph* graphPtr = (Graph*)clientData; if (!(graphPtr->flags & GRAPH_DELETED)) Tk_DestroyWindow(graphPtr->tkwin_); } void GraphEventProc(ClientData clientData, XEvent* eventPtr) { Graph* graphPtr = (Graph*)clientData; if (eventPtr->type == Expose) { if (eventPtr->xexpose.count == 0) graphPtr->eventuallyRedraw(); } else if (eventPtr->type == FocusIn || eventPtr->type == FocusOut) { if (eventPtr->xfocus.detail != NotifyInferior) { if (eventPtr->type == FocusIn) graphPtr->flags |= FOCUS; else graphPtr->flags &= ~FOCUS; graphPtr->eventuallyRedraw(); } } else if (eventPtr->type == DestroyNotify) { if (!(graphPtr->flags & GRAPH_DELETED)) { graphPtr->flags |= GRAPH_DELETED; Tcl_DeleteCommandFromToken(graphPtr->interp_, graphPtr->cmdToken_); if (graphPtr->flags & REDRAW_PENDING) Tcl_CancelIdleCall(DisplayGraph, graphPtr); Tcl_EventuallyFree(graphPtr, DestroyGraph); } } else if (eventPtr->type == ConfigureNotify) { graphPtr->flags |= RESET; graphPtr->eventuallyRedraw(); } } void DisplayGraph(ClientData clientData) { Graph* graphPtr = (Graph*)clientData; graphPtr->draw(); } // called by Tcl_EventuallyFree and others void DestroyGraph(char* dataPtr) { Graph* graphPtr = (Graph*)dataPtr; delete graphPtr; }