/* * 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 #include #include #include "tkbltChain.h" #include "tkbltGraph.h" #include "tkbltGrElem.h" #include "tkbltGrElemOption.h" #include "tkbltGrPen.h" #include "tkbltConfig.h" using namespace Blt; #define SETRANGE(l) ((l).range = ((l).max > (l).min) ? ((l).max - (l).min) : DBL_EPSILON) #define SETWEIGHT(l, lo, hi) ((l).min = (lo), (l).max = (hi), SETRANGE(l)) // Defs static int GetPenStyleFromObj(Tcl_Interp* interp, Graph* graphPtr, Tcl_Obj *objPtr, ClassId classId, PenStyle *stylePtr); static int ParseValues(Tcl_Interp* interp, Tcl_Obj *objPtr, int *nValuesPtr, double **arrayPtr); // OptionSpecs static Tk_CustomOptionSetProc ValuesSetProc; static Tk_CustomOptionGetProc ValuesGetProc; static Tk_CustomOptionFreeProc ValuesFreeProc; Tk_ObjCustomOption valuesObjOption = { "values", ValuesSetProc, ValuesGetProc, RestoreProc, ValuesFreeProc, NULL }; static int ValuesSetProc(ClientData clientData, Tcl_Interp* interp, Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, int offset, char* savePtr, int flags) { ElemValues** valuesPtrPtr = (ElemValues**)(widgRec + offset); *(double*)savePtr = *(double*)valuesPtrPtr; ElementOptions* ops = (ElementOptions*)widgRec; Element* elemPtr = ops->elemPtr; if (!valuesPtrPtr) return TCL_OK; Tcl_Obj** objv; int objc; if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) return TCL_ERROR; if (objc == 0) { *valuesPtrPtr = NULL; return TCL_OK; } const char *string = Tcl_GetString(objv[0]); if (objc == 1) { if (Blt_VectorExists2(interp, string)) { ElemValuesVector* valuesPtr = new ElemValuesVector(elemPtr, string); if (valuesPtr->getVector() != TCL_OK) { delete valuesPtr; return TCL_ERROR; } *valuesPtrPtr = valuesPtr; } else return TCL_ERROR; } else { double* values; int nValues; if (ParseValues(interp, *objPtr, &nValues, &values) != TCL_OK) return TCL_ERROR; ElemValuesSource* valuesPtr = new ElemValuesSource(nValues, values); valuesPtr->findRange(); *valuesPtrPtr = valuesPtr; } return TCL_OK; } static Tcl_Obj* ValuesGetProc(ClientData clientData, Tk_Window tkwin, char *widgRec, int offset) { ElemValues* valuesPtr = *(ElemValues**)(widgRec + offset); if (!valuesPtr) return Tcl_NewStringObj("", -1); int cnt = valuesPtr->nValues(); if (!cnt) return Tcl_NewListObj(0, (Tcl_Obj**)NULL); Tcl_Obj** ll = new Tcl_Obj*[cnt]; for (int ii=0; iivalues_[ii]); Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); delete [] ll; return listObjPtr; } static void ValuesFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) { ElemValues* valuesPtr = *(ElemValues**)ptr; delete valuesPtr; } static Tk_CustomOptionSetProc PairsSetProc; static Tk_CustomOptionGetProc PairsGetProc; static Tk_CustomOptionRestoreProc PairsRestoreProc; static Tk_CustomOptionFreeProc PairsFreeProc; Tk_ObjCustomOption pairsObjOption = { "pairs", PairsSetProc, PairsGetProc, PairsRestoreProc, PairsFreeProc, NULL }; static int PairsSetProc(ClientData clientData, Tcl_Interp* interp, Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, int offset, char* savePtr, int flags) { ElemCoords* coordsPtr = (ElemCoords*)(widgRec + offset); *(double*)savePtr = (double)NULL; double* values; int nValues; if (ParseValues(interp, *objPtr, &nValues, &values) != TCL_OK) return TCL_ERROR; if (nValues == 0) return TCL_OK; if (nValues & 1) { Tcl_AppendResult(interp, "odd number of data points", NULL); delete [] values; return TCL_ERROR; } nValues /= 2; delete coordsPtr->x; coordsPtr->x = new ElemValuesSource(nValues); delete coordsPtr->y; coordsPtr->y = new ElemValuesSource(nValues); int ii=0; for (double* p = values; iix->values_[ii] = *p++; coordsPtr->y->values_[ii] = *p++; } delete [] values; coordsPtr->x->findRange(); coordsPtr->y->findRange(); return TCL_OK; }; static Tcl_Obj* PairsGetProc(ClientData clientData, Tk_Window tkwin, char *widgRec, int offset) { ElemCoords* coordsPtr = (ElemCoords*)(widgRec + offset); if (!coordsPtr || !coordsPtr->x || !coordsPtr->y || !coordsPtr->x->nValues() || !coordsPtr->y->nValues()) return Tcl_NewListObj(0, (Tcl_Obj**)NULL); int cnt = MIN(coordsPtr->x->nValues(), coordsPtr->y->nValues()); Tcl_Obj** ll = new Tcl_Obj*[2*cnt]; for (int ii=0, jj=0; iix->values_[ii]); ll[jj++] = Tcl_NewDoubleObj(coordsPtr->y->values_[ii]); } Tcl_Obj* listObjPtr = Tcl_NewListObj(2*cnt, ll); delete [] ll; return listObjPtr; }; static void PairsRestoreProc(ClientData clientData, Tk_Window tkwin, char *ptr, char *savePtr) { // do nothing } static void PairsFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) { // do nothing } int StyleSetProc(ClientData clientData, Tcl_Interp* interp, Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, int offset, char* save, int flags) { Chain* stylePalette = *(Chain**)(widgRec + offset); ElementOptions* ops = (ElementOptions*)(widgRec); Element* elemPtr = ops->elemPtr; size_t size = (size_t)clientData; int objc; Tcl_Obj** objv; if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) return TCL_ERROR; // Reserve the first entry for the "normal" pen. We'll set the style later elemPtr->freeStylePalette(stylePalette); ChainLink* link = Chain_FirstLink(stylePalette); if (!link) { link = new ChainLink(size); stylePalette->linkAfter(link, NULL); } PenStyle* stylePtr = (PenStyle*)Chain_GetValue(link); stylePtr->penPtr = NORMALPEN(ops); for (int ii = 0; iiweight.min = (double)ii; stylePtr->weight.max = (double)ii + 1.0; stylePtr->weight.range = 1.0; if (GetPenStyleFromObj(interp, elemPtr->graphPtr_, objv[ii], elemPtr->classId(), (PenStyle*)stylePtr) != TCL_OK) { elemPtr->freeStylePalette(stylePalette); return TCL_ERROR; } stylePalette->linkAfter(link, NULL); } return TCL_OK; } Tcl_Obj* StyleGetProc(ClientData clientData, Tk_Window tkwin, char *widgRec, int offset) { Chain* stylePalette = *(Chain**)(widgRec + offset); // count how many int cnt =0; for (ChainLink* link = Chain_FirstLink(stylePalette); link; link = Chain_NextLink(link), cnt++) {} if (!cnt) return Tcl_NewListObj(0, (Tcl_Obj**)NULL); Tcl_Obj** ll = new Tcl_Obj*[3*cnt]; int ii=0; for (ChainLink* link = Chain_FirstLink(stylePalette); link; link = Chain_NextLink(link)) { PenStyle *stylePtr = (PenStyle*)Chain_GetValue(link); ll[ii++] = Tcl_NewStringObj(stylePtr->penPtr->name_, -1); ll[ii++] = Tcl_NewDoubleObj(stylePtr->weight.min); ll[ii++] = Tcl_NewDoubleObj(stylePtr->weight.max); } Tcl_Obj *listObjPtr = Tcl_NewListObj(3*cnt,ll); delete [] ll; return listObjPtr; } void StyleRestoreProc(ClientData clientData, Tk_Window tkwin, char *ptr, char *savePtr) { // do nothing } void StyleFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) { // do nothing } // Support static int GetPenStyleFromObj(Tcl_Interp* interp, Graph* graphPtr, Tcl_Obj *objPtr, ClassId classId, PenStyle *stylePtr) { int objc; Tcl_Obj **objv; if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) return TCL_ERROR; if ((objc != 1) && (objc != 3)) { Tcl_AppendResult(interp, "bad style entry \"", Tcl_GetString(objPtr), "\": should be \"penName\" or \"penName min max\"", NULL); return TCL_ERROR; } Pen* penPtr; if (graphPtr->getPen(objv[0], &penPtr) != TCL_OK) return TCL_ERROR; if (objc == 3) { double min, max; if ((Tcl_GetDoubleFromObj(interp, objv[1], &min) != TCL_OK) || (Tcl_GetDoubleFromObj(interp, objv[2], &max) != TCL_OK)) return TCL_ERROR; SETWEIGHT(stylePtr->weight, min, max); } penPtr->refCount_++; stylePtr->penPtr = penPtr; return TCL_OK; } void VectorChangedProc(Tcl_Interp* interp, ClientData clientData, Blt_VectorNotify notify) { ElemValuesVector* valuesPtr = (ElemValuesVector*)clientData; if (!valuesPtr) return; if (notify == BLT_VECTOR_NOTIFY_DESTROY) { valuesPtr->freeSource(); valuesPtr->reset(); } else { Blt_Vector* vector; Blt_GetVectorById(interp, valuesPtr->source_, &vector); if (valuesPtr->fetchValues(vector) != TCL_OK) return; } Element* elemPtr = valuesPtr->elemPtr_; Graph* graphPtr = elemPtr->graphPtr_; graphPtr->flags |= RESET; graphPtr->eventuallyRedraw(); } static int ParseValues(Tcl_Interp* interp, Tcl_Obj *objPtr, int *nValuesPtr, double **arrayPtr) { int objc; Tcl_Obj **objv; if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) return TCL_ERROR; *arrayPtr = NULL; *nValuesPtr = 0; if (objc > 0) { double* array = new double[objc]; if (!array) { Tcl_AppendResult(interp, "can't allocate new vector", NULL); return TCL_ERROR; } int i=0; for (double* p = array; i < objc; i++, p++) { if (Tcl_GetDoubleFromObj(interp, objv[i], p) != TCL_OK) { delete [] array; return TCL_ERROR; } } *arrayPtr = array; *nValuesPtr = objc; } return TCL_OK; }