/* * 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 1993-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 "bltGrBind.h" #include "bltGraph.h" #include "bltGrAxis.h" #include "bltGrElem.h" #include "bltGrElemOp.h" #include "bltGrElemBar.h" #include "bltGrElemLine.h" #include "bltGrLegd.h" using namespace Blt; static int GetIndex(Tcl_Interp* interp, Element* elemPtr, Tcl_Obj *objPtr, int *indexPtr); static Tcl_Obj *DisplayListObj(Graph* graphPtr); int Blt::ElementObjConfigure(Element* elemPtr, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = elemPtr->graphPtr_; Tk_SavedOptions savedOptions; int mask =0; int error; Tcl_Obj* errorResult; for (error=0; error<=1; error++) { if (!error) { if (Tk_SetOptions(interp, (char*)elemPtr->ops(), elemPtr->optionTable(), objc, objv, graphPtr->tkwin_, &savedOptions, &mask) != TCL_OK) continue; } else { errorResult = Tcl_GetObjResult(interp); Tcl_IncrRefCount(errorResult); Tk_RestoreSavedOptions(&savedOptions); } if (elemPtr->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 != 5) { Tcl_WrongNumArgs(interp, 3, objv, "cget option"); return TCL_ERROR; } Element* elemPtr; if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) return TCL_ERROR; Tcl_Obj* objPtr = Tk_GetOptionValue(interp, (char*)elemPtr->ops(), elemPtr->optionTable(), objv[4], 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<4) return TCL_ERROR; Element* elemPtr; if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) return TCL_ERROR; if (objc <= 5) { Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)elemPtr->ops(), elemPtr->optionTable(), (objc == 5) ? objv[4] : NULL, graphPtr->tkwin_); if (objPtr == NULL) return TCL_ERROR; else Tcl_SetObjResult(interp, objPtr); return TCL_OK; } else return ElementObjConfigure(elemPtr, interp, objc-4, objv+4); } static int ActivateOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; // List all the currently active elements if (objc == 3) { Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_HashSearch iter; for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); if (elemPtr->active_) Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(elemPtr->name_, -1)); } Tcl_SetObjResult(interp, listObjPtr); return TCL_OK; } Element* elemPtr; if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) return TCL_ERROR; int* indices = NULL; int nIndices = -1; if (objc > 4) { nIndices = objc - 4; indices = new int[nIndices]; int* activePtr = indices; for (int ii=4; iiactiveIndices_) delete [] elemPtr->activeIndices_; elemPtr->nActiveIndices_ = nIndices; elemPtr->activeIndices_ = indices; elemPtr->active_ = 1; graphPtr->flags |= RESET; graphPtr->eventuallyRedraw(); return TCL_OK; } static int BindOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc == 3) { Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_HashSearch iter; for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&graphPtr->elements_.tagTable, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { char* tagName = (char*)Tcl_GetHashKey(&graphPtr->elements_.tagTable, hPtr); Tcl_ListObjAppendElement(interp, listObjPtr,Tcl_NewStringObj(tagName,-1)); } Tcl_SetObjResult(interp, listObjPtr); return TCL_OK; } return graphPtr->bindTable_->configure(graphPtr->elementTag(Tcl_GetString(objv[3])), objc - 4, objv + 4); } static int ClosestOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc<5) return TCL_ERROR; GraphOptions* gops = (GraphOptions*)graphPtr->ops_; ClosestSearch* searchPtr = &gops->search; if (graphPtr->flags & RESET) graphPtr->resetAxes(); int x; if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { Tcl_AppendResult(interp, ": bad window x-coordinate", NULL); return TCL_ERROR; } int y; if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) { Tcl_AppendResult(interp, ": bad window y-coordinate", NULL); return TCL_ERROR; } searchPtr->x = x; searchPtr->y = y; searchPtr->index = -1; searchPtr->dist = (double)(searchPtr->halo + 1); if (objc>5) { for (int ii=5; iigetElement(objv[ii], &elemPtr) != TCL_OK) return TCL_ERROR; ElementOptions* eops = (ElementOptions*)elemPtr->ops(); if (!eops->hide) elemPtr->closest(); } } else { // Find the closest point from the set of displayed elements, // searching the display list from back to front. That way if // the points from two different elements overlay each other // exactly, the last one picked will be the topmost. for (ChainLink* link = Chain_LastLink(graphPtr->elements_.displayList); link; link = Chain_PrevLink(link)) { Element* elemPtr = (Element*)Chain_GetValue(link); ElementOptions* eops = (ElementOptions*)elemPtr->ops(); if (!eops->hide) elemPtr->closest(); } } if (searchPtr->dist < (double)searchPtr->halo) { Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("name", -1)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(searchPtr->elemPtr->name_, -1)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("index", -1)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(searchPtr->index)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("x", -1)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->point.x)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("y", -1)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->point.y)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("dist", -1)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->dist)); Tcl_SetObjResult(interp, listObjPtr); } return TCL_OK; } static int CreateOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc<4) return TCL_ERROR; if (graphPtr->createElement(objc, objv) != TCL_OK) return TCL_ERROR; Tcl_SetObjResult(interp, objv[3]); graphPtr->flags |= RESET; graphPtr->eventuallyRedraw(); return TCL_OK; } static int DeactivateOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) return TCL_ERROR; if (elemPtr->activeIndices_) { delete [] elemPtr->activeIndices_; elemPtr->activeIndices_ = NULL; } elemPtr->nActiveIndices_ = 0; elemPtr->active_ = 0; } graphPtr->flags |= RESET; graphPtr->eventuallyRedraw(); return TCL_OK; } static int DeleteOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) return TCL_ERROR; graphPtr->legend_->removeElement(elemPtr); delete elemPtr; } graphPtr->flags |= RESET; graphPtr->eventuallyRedraw(); return TCL_OK; } static int ExistsOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc<4) return TCL_ERROR; Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&graphPtr->elements_.table, Tcl_GetString(objv[3])); Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (hPtr != NULL)); return TCL_OK; } static int LowerOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; // Move the links of lowered elements out of the display list into // a temporary list Chain* chain = new Chain(); for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) return TCL_ERROR; graphPtr->elements_.displayList->unlinkLink(elemPtr->link); chain->linkAfter(elemPtr->link, NULL); } // Append the links to end of the display list ChainLink *next; for (ChainLink *link = Chain_FirstLink(chain); link; link = next) { next = Chain_NextLink(link); chain->unlinkLink(link); graphPtr->elements_.displayList->linkAfter(link, NULL); } delete chain; graphPtr->flags |= CACHE; graphPtr->eventuallyRedraw(); Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); return TCL_OK; } static int NamesOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); if (objc == 3) { Tcl_HashSearch iter; for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) { Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); } } else { Tcl_HashSearch iter; for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) { Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); for (int ii=3; iiname_,Tcl_GetString(objv[ii]))) { Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); break; } } } } Tcl_SetObjResult(interp, listObjPtr); return TCL_OK; } static int RaiseOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; Chain* chain = new Chain(); for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) return TCL_ERROR; graphPtr->elements_.displayList->unlinkLink(elemPtr->link); chain->linkAfter(elemPtr->link, NULL); } // Prepend the links to beginning of the display list in reverse order ChainLink *prev; for (ChainLink *link = Chain_LastLink(chain); link; link = prev) { prev = Chain_PrevLink(link); chain->unlinkLink(link); graphPtr->elements_.displayList->linkBefore(link, NULL); } delete chain; graphPtr->flags |= CACHE; graphPtr->eventuallyRedraw(); Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); return TCL_OK; } static int ShowOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc<4) return TCL_ERROR; int elemObjc; Tcl_Obj** elemObjv; if (Tcl_ListObjGetElements(interp, objv[3], &elemObjc, &elemObjv) != TCL_OK) return TCL_ERROR; // Collect the named elements into a list Chain* chain = new Chain(); for (int ii=0; iigetElement(elemObjv[ii], &elemPtr) != TCL_OK) { delete chain; return TCL_ERROR; } chain->append(elemPtr); } // Clear the links from the currently displayed elements for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); link; link = Chain_NextLink(link)) { Element* elemPtr = (Element*)Chain_GetValue(link); elemPtr->link = NULL; } delete graphPtr->elements_.displayList; graphPtr->elements_.displayList = chain; // Set links on all the displayed elements for (ChainLink* link = Chain_FirstLink(chain); link; link = Chain_NextLink(link)) { Element* elemPtr = (Element*)Chain_GetValue(link); elemPtr->link = link; } graphPtr->flags |= RESET; graphPtr->eventuallyRedraw(); Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); return TCL_OK; } static int TypeOp(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { Graph* graphPtr = (Graph*)clientData; if (objc<4) return TCL_ERROR; Element* elemPtr; if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) return TCL_ERROR; Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->typeName(), -1); return TCL_OK; } const Ensemble Blt::elementEnsemble[] = { {"activate", ActivateOp, 0}, {"bind", BindOp, 0}, {"cget", CgetOp, 0}, {"closest", ClosestOp, 0}, {"configure", ConfigureOp, 0}, {"create", CreateOp, 0}, {"deactivate", DeactivateOp, 0}, {"delete", DeleteOp, 0}, {"exists", ExistsOp, 0}, {"lower", LowerOp, 0}, {"names", NamesOp, 0}, {"raise", RaiseOp, 0}, {"show", ShowOp, 0}, {"type", TypeOp, 0}, { 0,0,0 } }; // Support static Tcl_Obj *DisplayListObj(Graph* graphPtr) { Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); link; link = Chain_NextLink(link)) { Element* elemPtr = (Element*)Chain_GetValue(link); Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); Tcl_ListObjAppendElement(graphPtr->interp_, listObjPtr, objPtr); } return listObjPtr; } static int GetIndex(Tcl_Interp* interp, Element* elemPtr, Tcl_Obj *objPtr, int *indexPtr) { ElementOptions* ops = (ElementOptions*)elemPtr->ops(); char *string = Tcl_GetString(objPtr); if ((*string == 'e') && (strcmp("end", string) == 0)) *indexPtr = NUMBEROFPOINTS(ops); else if (Tcl_GetIntFromObj(interp, objPtr, indexPtr) != TCL_OK) return TCL_ERROR; return TCL_OK; }