diff options
author | joye <joye> | 2014-02-06 22:26:27 (GMT) |
---|---|---|
committer | joye <joye> | 2014-02-06 22:26:27 (GMT) |
commit | 924f85448cfb7379e8b52c8539fda434f526432b (patch) | |
tree | 76b9cbf3991621b0850076b096cf621867d3ed5e | |
parent | e529c666f58f6a8712138dc221ae2febca8bba7b (diff) | |
download | blt-924f85448cfb7379e8b52c8539fda434f526432b.zip blt-924f85448cfb7379e8b52c8539fda434f526432b.tar.gz blt-924f85448cfb7379e8b52c8539fda434f526432b.tar.bz2 |
*** empty log message ***
-rw-r--r-- | src/bltGraph.C | 1206 |
1 files changed, 586 insertions, 620 deletions
diff --git a/src/bltGraph.C b/src/bltGraph.C index a5bc671..ff55fa9 100644 --- a/src/bltGraph.C +++ b/src/bltGraph.C @@ -43,14 +43,6 @@ #include "bltGraph.h" #include "bltOp.h" #include "bltGrElem.h" -#include "bltSwitch.h" - -typedef int (GraphCmdProc)(Graph* graphPtr, Tcl_Interp* interp, int objc, - Tcl_Obj* const objv[]); - -#define PointInRegion(e,x,y) \ - (((x) <= (e)->right) && ((x) >= (e)->left) && \ - ((y) <= (e)->bottom) && ((y) >= (e)->top)) /* * Objects in the graph have their own class names. These class names are @@ -63,6 +55,7 @@ typedef int (GraphCmdProc)(Graph* graphPtr, Tcl_Interp* interp, int objc, * an object is initially configured. The class name of the temporary window * will be from the list below. */ + static const char* objectClassNames[] = { "unknown", "XAxis", @@ -79,10 +72,6 @@ static const char* objectClassNames[] = { "WindowMarker", }; -extern Blt_CustomOption bltLinePenOption; -extern Blt_CustomOption bltBarPenOption; -extern Blt_CustomOption bltBarModeOption; - #define DEF_GRAPH_ASPECT_RATIO "0.0" #define DEF_GRAPH_BAR_BASELINE "0.0" #define DEF_GRAPH_BAR_MODE "normal" @@ -118,8 +107,6 @@ extern Blt_CustomOption bltBarModeOption; #define DEF_GRAPH_TITLE_COLOR STD_NORMAL_FOREGROUND #define DEF_GRAPH_WIDTH "5i" -// BarMode - static char* barmodeObjOption[] = {"normal", "stacked", "aligned", "overlap"}; static Tk_OptionSpec optionSpecs[] = { @@ -292,284 +279,272 @@ static Tcl_CmdDeleteProc GraphInstCmdDeleteProc; static Blt_BindPickProc PickEntry; -/* - *--------------------------------------------------------------------------- - * - * Blt_UpdateGraph -- - * - * Tells the Tk dispatcher to call the graph display routine at the next - * idle point. This request is made only if the window is displayed and - * no other redraw request is pending. - * - *--------------------------------------------------------------------------- - */ -void Blt_UpdateGraph(ClientData clientData) -{ - Graph* graphPtr = clientData; +static int NewGraph(ClientData clientData, Tcl_Interp*interp, + int objc, Tcl_Obj* const objv[], ClassId classId); +static void DeleteGraph(ClientData clientData); +static Graph* CreateGraph(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[], ClassId classId); +static int GraphObjConfigure(Tcl_Interp* interp, Graph* graphPtr, + int objc, Tcl_Obj* const objv[]); +static void AdjustAxisPointers(Graph* graphPtr); +static void ConfigureGraph(Graph* graphPtr); +static void DrawPlot(Graph* graphPtr, Drawable drawable); +static void UpdateMarginTraces(Graph* graphPtr); - graphPtr->flags |= REDRAW_WORLD; - if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) { - Tcl_DoWhenIdle(DisplayGraph, graphPtr); - graphPtr->flags |= REDRAW_PENDING; - } -} +// Graph Widget -/* - *--------------------------------------------------------------------------- - * - * Blt_EventuallyRedrawGraph -- - * - * Tells the Tk dispatcher to call the graph display routine at the next - * idle point. This request is made only if the window is displayed and - * no other redraw request is pending. - * - *--------------------------------------------------------------------------- - */ -void Blt_EventuallyRedrawGraph(Graph* graphPtr) +int Blt_GraphCmdInitProc(Tcl_Interp* interp) { - if ((graphPtr->tkwin != NULL) && - Tk_IsMapped(graphPtr->tkwin) && - !(graphPtr->flags & REDRAW_PENDING)) { - Tcl_DoWhenIdle(DisplayGraph, graphPtr); - graphPtr->flags |= REDRAW_PENDING; - } + static Blt_InitCmdSpec graphSpec = + {"graph", GraphObjCmd, GraphObjDelete, NULL}; + static Blt_InitCmdSpec barchartSpec = + {"barchart", BarchartObjCmd, BarchartObjDelete, NULL}; + + if (Blt_InitCmd(interp, "::blt", &graphSpec) != TCL_OK) + return TCL_ERROR; + if (Blt_InitCmd(interp, "::blt", &barchartSpec) != TCL_OK) + return TCL_ERROR; + + return TCL_OK; } -const char* Blt_GraphClassName(ClassId classId) +static int GraphObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) { - if ((classId >= CID_NONE) && (classId <= CID_MARKER_WINDOW)) { - return objectClassNames[classId]; - } - return NULL; + return NewGraph(clientData, interp, objc, objv, CID_ELEM_LINE); } -void Blt_GraphSetObjectClass(GraphObj* graphObjPtr, ClassId classId) +static int BarchartObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) { - graphObjPtr->classId = classId; - graphObjPtr->className = Blt_GraphClassName(classId); + return NewGraph(clientData, interp, objc, objv, CID_ELEM_BAR); } -static void GraphEventProc(ClientData clientData, XEvent* eventPtr) +static int NewGraph(ClientData clientData, Tcl_Interp*interp, + int objc, Tcl_Obj* const objv[], ClassId classId) { - Graph* graphPtr = clientData; - - if (eventPtr->type == Expose) { - if (eventPtr->xexpose.count == 0) { - graphPtr->flags |= REDRAW_WORLD; - Blt_EventuallyRedrawGraph(graphPtr); - } - } 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->flags |= REDRAW_WORLD; - Blt_EventuallyRedrawGraph(graphPtr); - } - } else if (eventPtr->type == DestroyNotify) { - if (graphPtr->tkwin != NULL) { - Tk_FreeConfigOptions((char*)graphPtr, graphPtr->optionTable, - graphPtr->tkwin); - graphPtr->tkwin = NULL; - 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 |= (MAP_WORLD | REDRAW_WORLD); - Blt_EventuallyRedrawGraph(graphPtr); + if (objc < 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " pathName ?option value?...\"", + (char*)NULL); + return TCL_ERROR; } -} -static void GraphInstCmdDeleteProc(ClientData clientData) -{ - Graph* graphPtr = clientData; + if (!CreateGraph(clientData, interp, objc, objv, classId)) + return TCL_ERROR; - // NULL indicates window has already been destroyed. - if (graphPtr->tkwin != NULL) { - Tk_Window tkwin = graphPtr->tkwin; - graphPtr->tkwin = NULL; - Tk_DestroyWindow(tkwin); - } + return TCL_OK; } -static void AdjustAxisPointers(Graph* graphPtr) +static Graph* CreateGraph(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[], ClassId classId) { - if (graphPtr->inverted) { - graphPtr->leftMargin.axes = graphPtr->axisChain[0]; - graphPtr->bottomMargin.axes = graphPtr->axisChain[1]; - graphPtr->rightMargin.axes = graphPtr->axisChain[2]; - graphPtr->topMargin.axes = graphPtr->axisChain[3]; - } else { - graphPtr->leftMargin.axes = graphPtr->axisChain[1]; - graphPtr->bottomMargin.axes = graphPtr->axisChain[0]; - graphPtr->rightMargin.axes = graphPtr->axisChain[3]; - graphPtr->topMargin.axes = graphPtr->axisChain[2]; + Tk_OptionTable optionTable = (Tk_OptionTable)clientData; + if (!optionTable) { + optionTable = Tk_CreateOptionTable(interp, optionSpecs); + char* name = Tcl_GetString(objv[0]); + Tcl_CmdInfo info; + Tcl_GetCommandInfo(interp, name, &info); + info.objClientData = (ClientData)optionTable; + Tcl_SetCommandInfo(interp, name, &info); } -} -static int InitPens(Graph* graphPtr) -{ - Tcl_InitHashTable(&graphPtr->penTable, TCL_STRING_KEYS); - if (Blt_CreatePen(graphPtr, "activeLine", CID_ELEM_LINE, 0, NULL) == NULL) { - return TCL_ERROR; - } - if (Blt_CreatePen(graphPtr, "activeBar", CID_ELEM_BAR, 0, NULL) == NULL) { - return TCL_ERROR; - } + Tk_Window tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), + Tcl_GetString(objv[1]), + (char*)NULL); + if (tkwin == NULL) + return NULL; - return TCL_OK; -} + Graph* graphPtr = calloc(1, sizeof(Graph)); -/* - *--------------------------------------------------------------------------- - * - * Blt_GraphTags -- - * - * Sets the binding tags for a graph obj. This routine is called by Tk - * when an event occurs in the graph. It fills an array of pointers with - * bind tag addresses. - * - * The object addresses are strings hashed in one of two tag tables: one - * for elements and the another for markers. Note that there's only one - * binding table for elements and markers. [We don't want to trigger - * both a marker and element bind command for the same event.] But we - * don't want a marker and element with the same tag name to activate the - * others bindings. A tag "all" for markers should mean all markers, not - * all markers and elements. As a result, element and marker tags are - * stored in separate hash tables, which means we can't generate the same - * tag address for both an elements and marker, even if they have the - * same name. - * - *--------------------------------------------------------------------------- - */ + /* Initialize the graph data structure. */ -void Blt_GraphTags(Blt_BindTable table, ClientData object, ClientData context, - Blt_List list) -{ - Graph* graphPtr = (Graph*)Blt_GetBindingData(table); + graphPtr->interp = interp; + graphPtr->tkwin = tkwin; + graphPtr->display = Tk_Display(tkwin); + graphPtr->optionTable = optionTable; + graphPtr->classId = classId; + graphPtr->backingStore = TRUE; + graphPtr->doubleBuffer = TRUE; + graphPtr->borderWidth = 2; + graphPtr->plotBW = 1; + graphPtr->highlightWidth = 2; + graphPtr->plotRelief = TK_RELIEF_SOLID; + graphPtr->relief = TK_RELIEF_FLAT; + graphPtr->flags = MAP_WORLD | REDRAW_WORLD; + graphPtr->nextMarkerId = 1; + graphPtr->bottomMargin.site = MARGIN_BOTTOM; + graphPtr->leftMargin.site = MARGIN_LEFT; + graphPtr->topMargin.site = MARGIN_TOP; + graphPtr->rightMargin.site = MARGIN_RIGHT; - /* - * All graph objects (markers, elements, axes, etc) have the same starting - * fields in their structures, such as "classId", "name", "className", and - * "tags". - */ - GraphObj* graphObjPtr = (GraphObj*)object; + Blt_Ts_InitStyle(graphPtr->titleTextStyle); + Blt_Ts_SetAnchor(graphPtr->titleTextStyle, TK_ANCHOR_N); - MakeTagProc* tagProc; - switch (graphObjPtr->classId) { - case CID_ELEM_BAR: - case CID_ELEM_LINE: - tagProc = Blt_MakeElementTag; - break; - case CID_AXIS_X: - case CID_AXIS_Y: - tagProc = Blt_MakeAxisTag; - break; - case CID_MARKER_BITMAP: - case CID_MARKER_IMAGE: - case CID_MARKER_LINE: - case CID_MARKER_POLYGON: - case CID_MARKER_TEXT: - case CID_MARKER_WINDOW: - tagProc = Blt_MakeMarkerTag; + Tcl_InitHashTable(&graphPtr->axes.table, TCL_STRING_KEYS); + Tcl_InitHashTable(&graphPtr->axes.tagTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&graphPtr->elements.table, TCL_STRING_KEYS); + Tcl_InitHashTable(&graphPtr->elements.tagTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&graphPtr->markers.table, TCL_STRING_KEYS); + Tcl_InitHashTable(&graphPtr->markers.tagTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&graphPtr->dataTables, TCL_STRING_KEYS); + graphPtr->elements.displayList = Blt_Chain_Create(); + graphPtr->markers.displayList = Blt_Chain_Create(); + graphPtr->axes.displayList = Blt_Chain_Create(); + + if (Blt_CreatePageSetup(graphPtr) != TCL_OK) + goto error; + if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) + goto error; + if (Blt_CreateLegend(graphPtr) != TCL_OK) + goto error; + + Tcl_InitHashTable(&graphPtr->penTable, TCL_STRING_KEYS); + if (Blt_CreatePen(graphPtr, "activeLine", CID_ELEM_LINE, 0, NULL) == NULL) + goto error; + if (Blt_CreatePen(graphPtr, "activeBar", CID_ELEM_BAR, 0, NULL) == NULL) + goto error; + + switch (classId) { + case CID_ELEM_LINE: + Tk_SetClass(tkwin, "Graph"); break; - case CID_NONE: - Blt_Panic("unknown object type"); - tagProc = NULL; + case CID_ELEM_BAR: + Tk_SetClass(tkwin, "Barchart"); break; default: - Blt_Panic("bogus object type"); - tagProc = NULL; + Tk_SetClass(tkwin, "???"); break; } - assert(graphObjPtr->name != NULL); - /* Always add the name of the object to the tag array. */ - Blt_List_Append(list, (*tagProc)(graphPtr, graphObjPtr->name), 0); - Blt_List_Append(list, (*tagProc)(graphPtr, graphObjPtr->className), 0); - if (graphObjPtr->tags != NULL) { - const char **p; + ((TkWindow*)tkwin)->instanceData = graphPtr; - for (p = graphObjPtr->tags; *p != NULL; p++) { - Blt_List_Append(list, (*tagProc) (graphPtr, *p), 0); - } - } + if (Tk_InitOptions(interp, (char*)graphPtr, optionTable, tkwin) != TCL_OK) + goto error; + if (GraphObjConfigure(interp, graphPtr, objc-2, objv+2) != TCL_OK) + goto error; + + if (Blt_DefaultAxes(graphPtr) != TCL_OK) + goto error; + + AdjustAxisPointers(graphPtr); + + if (Blt_ConfigurePageSetup(graphPtr) != TCL_OK) + goto error; + if (Blt_ConfigureObjCrosshairs(graphPtr) != TCL_OK) + goto error; + Blt_ConfigureLegend(graphPtr); + + Tk_CreateEventHandler(graphPtr->tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask, + GraphEventProc, (ClientData)graphPtr); + + graphPtr->cmdToken = Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), + Blt_GraphInstCmdProc, + (ClientData)graphPtr, + GraphInstCmdDeleteProc); + + graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr, + PickEntry, Blt_GraphTags); + + Tcl_SetObjResult(interp, objv[1]); + return graphPtr; + + error: + DestroyGraph((char*)graphPtr); + return NULL; } -/* - * 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 one that's on - * top (visible) is picked. - */ +static int ConfigureOp(Graph* graphPtr, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) +{ + if (objc <= 3) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)graphPtr, + graphPtr->optionTable, + (objc == 3) ? objv[2] : NULL, + graphPtr->tkwin); + if (!objPtr) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + } else { + if (GraphObjConfigure(interp, graphPtr, objc-2, objv+2) != TCL_OK) + return TCL_ERROR; + } + return TCL_OK; +} -static ClientData PickEntry(ClientData clientData, int x, int y, - ClientData* contextPtr) +static int CgetOp(Graph* graphPtr, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) { - Graph* graphPtr = clientData; + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, (char*)graphPtr, + graphPtr->optionTable, + (objc == 3) ? objv[2] : NULL, + graphPtr->tkwin); + if (!objPtr) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; +} - if (graphPtr->flags & MAP_ALL) { - return NULL; /* Don't pick anything until the next - * redraw occurs. */ - } - Region2d exts; - Blt_GraphExtents(graphPtr, &exts); +static Blt_OpSpec graphOps[]; +static int nGraphOps; +typedef int (GraphCmdProc)(Graph* graphPtr, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]); - if ((x >= exts.right) || (x < exts.left) || - (y >= exts.bottom) || (y < exts.top)) { - /* - * Sample coordinate is in one of the graph margins. Can only pick an - * axis. - */ - return Blt_NearestAxis(graphPtr, x, y); - } - /* - * From top-to-bottom check: - * 1. markers drawn on top (-under false). - * 2. elements using its display list back to front. - * 3. markers drawn under element (-under true). - */ - Marker* markerPtr = Blt_NearestMarker(graphPtr, x, y, FALSE); - if (markerPtr != NULL) { - return markerPtr; /* Found a marker (-under false). */ +int Blt_GraphInstCmdProc(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) +{ + Graph* graphPtr = clientData; + GraphCmdProc* proc = Blt_GetOpFromObj(interp, nGraphOps, graphOps, + BLT_OP_ARG1, objc, objv, 0); + if (proc == NULL) { + return TCL_ERROR; } + Tcl_Preserve(graphPtr); + int result = (*proc) (graphPtr, interp, objc, objv); + Tcl_Release(graphPtr); + return result; +} - ClosestSearch search; +static int GraphObjConfigure(Tcl_Interp* interp, Graph* graphPtr, + int objc, Tcl_Obj* const objv[]) +{ + Tk_SavedOptions savedOptions; + int mask =0; + int error; + Tcl_Obj* errorResult; - search.along = SEARCH_BOTH; - search.halo = graphPtr->halo; - search.index = -1; - search.x = x; - search.y = y; - search.dist = (double)(search.halo + 1); - search.mode = SEARCH_AUTO; - - Blt_ChainLink link; - Element* elemPtr; - for (link = Blt_Chain_LastLink(graphPtr->elements.displayList); - link != NULL; link = Blt_Chain_PrevLink(link)) { - elemPtr = Blt_Chain_GetValue(link); - if (elemPtr->flags & (HIDE|MAP_ITEM)) { - continue; + for (error=0; error<=1; error++) { + if (!error) { + if (Tk_SetOptions(interp, (char*)graphPtr, graphPtr->optionTable, + objc, objv, graphPtr->tkwin, &savedOptions, &mask) + != TCL_OK) + continue; } - if (elemPtr->state == BLT_STATE_NORMAL) { - (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search); + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); } - } - if (search.dist <= (double)search.halo) { - return search.elemPtr;// Found an element within the minimum halo distance. + + graphPtr->flags |= mask; + ConfigureGraph(graphPtr); + Blt_EventuallyRedrawGraph(graphPtr); + + break; } - markerPtr = Blt_NearestMarker(graphPtr, x, y, TRUE); - if (markerPtr != NULL) { - return markerPtr; /* Found a marker (-under true) */ + if (!error) { + Tk_FreeSavedOptions(&savedOptions); + return TCL_OK; + } + else { + Tcl_SetObjResult(interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; } - return NULL; /* Nothing found. */ } static void ConfigureGraph(Graph* graphPtr) @@ -633,209 +608,204 @@ static void ConfigureGraph(Graph* graphPtr) // Blt_ConfigureCrosshairs(graphPtr); } -/* - *--------------------------------------------------------------------------- - * - * DestroyGraph -- - * - * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to - * clean up the internal structure of a graph at a safe time (when no-one - * is using it anymore). - * - *--------------------------------------------------------------------------- - */ -static void DestroyGraph(char* dataPtr) +static void DisplayGraph(ClientData clientData) { - Graph* graphPtr = (Graph*)dataPtr; + Graph* graphPtr = clientData; + Pixmap drawable; + Tk_Window tkwin; + int site; - /* - * Destroy the individual components of the graph: elements, markers, - * axes, legend, display lists etc. Be careful to remove them in - * order. For example, axes are used by elements and markers, so they have - * to be removed after the markers and elements. Same it true with the - * legend and pens (they use elements), so can't be removed until the - * elements are destroyed. - */ - Blt_DestroyMarkers(graphPtr); - Blt_DestroyElements(graphPtr); - Blt_DestroyLegend(graphPtr); - Blt_DestroyAxes(graphPtr); - Blt_DestroyPens(graphPtr); - Blt_DestroyCrosshairs(graphPtr); - Blt_DestroyPageSetup(graphPtr); - Blt_DestroyBarSets(graphPtr); - if (graphPtr->bindTable != NULL) { - Blt_DestroyBindingTable(graphPtr->bindTable); + graphPtr->flags &= ~REDRAW_PENDING; + if (graphPtr->tkwin == NULL) { + return; /* Window has been destroyed (we + * should not get here) */ } - - /* Release allocated X resources and memory. */ - if (graphPtr->drawGC != NULL) { - Tk_FreeGC(graphPtr->display, graphPtr->drawGC); + tkwin = graphPtr->tkwin; + if ((Tk_Width(tkwin) <= 1) || (Tk_Height(tkwin) <= 1)) { + /* Don't bother computing the layout until the size of the window is + * something reasonable. */ + return; } - Blt_Ts_FreeStyle(graphPtr->display, &graphPtr->titleTextStyle); - if (graphPtr->cache != None) { - Tk_FreePixmap(graphPtr->display, graphPtr->cache); + graphPtr->width = Tk_Width(tkwin); + graphPtr->height = Tk_Height(tkwin); + Blt_MapGraph(graphPtr); + if (!Tk_IsMapped(tkwin)) { + /* The graph's window isn't displayed, so don't bother drawing + * anything. By getting this far, we've at least computed the + * coordinates of the graph's new layout. */ + return; } - free(graphPtr); -} + /* Create a pixmap the size of the window for double buffering. */ + if (graphPtr->doubleBuffer) { + drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(tkwin), + graphPtr->width, graphPtr->height, Tk_Depth(tkwin)); + } else { + drawable = Tk_WindowId(tkwin); + } + if (graphPtr->backingStore) { + if ((graphPtr->cache == None) || + (graphPtr->cacheWidth != graphPtr->width) || + (graphPtr->cacheHeight != graphPtr->height)) { + if (graphPtr->cache != None) { + Tk_FreePixmap(graphPtr->display, graphPtr->cache); + } -static int GraphObjConfigure(Tcl_Interp* interp, Graph* graphPtr, - 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, graphPtr->optionTable, - objc, objv, graphPtr->tkwin, &savedOptions, &mask) - != TCL_OK) - continue; + graphPtr->cache = Tk_GetPixmap(graphPtr->display, + Tk_WindowId(tkwin), + graphPtr->width, graphPtr->height, + Tk_Depth(tkwin)); + graphPtr->cacheWidth = graphPtr->width; + graphPtr->cacheHeight = graphPtr->height; + graphPtr->flags |= CACHE_DIRTY; } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); + } + if (graphPtr->backingStore) { + if (graphPtr->flags & CACHE_DIRTY) { + /* The backing store is new or out-of-date. */ + DrawPlot(graphPtr, graphPtr->cache); + graphPtr->flags &= ~CACHE_DIRTY; } - - graphPtr->flags |= mask; - ConfigureGraph(graphPtr); - Blt_EventuallyRedrawGraph(graphPtr); - - // All ok - break; + /* Copy the pixmap to the one used for drawing the entire graph. */ + XCopyArea(graphPtr->display, graphPtr->cache, drawable, + graphPtr->drawGC, 0, 0, Tk_Width(graphPtr->tkwin), + Tk_Height(graphPtr->tkwin), 0, 0); + } else { + DrawPlot(graphPtr, drawable); } - - if (!error) { - Tk_FreeSavedOptions(&savedOptions); - return TCL_OK; + /* Draw markers above elements */ + Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE); + Blt_DrawActiveElements(graphPtr, drawable); + /* Don't draw legend in the plot area. */ + site = Blt_Legend_Site(graphPtr); + if ((site & LEGEND_PLOTAREA_MASK) && (Blt_Legend_IsRaised(graphPtr))) { + Blt_DrawLegend(graphPtr, drawable); } - else { - Tcl_SetObjResult(interp, errorResult); - Tcl_DecrRefCount(errorResult); - return TCL_ERROR; + if (site == LEGEND_WINDOW) { + Blt_Legend_EventuallyRedraw(graphPtr); } -} - -static Graph* CreateGraph(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[], ClassId classId) -{ - Tk_OptionTable optionTable = (Tk_OptionTable)clientData; - if (!optionTable) { - optionTable = Tk_CreateOptionTable(interp, optionSpecs); - char* name = Tcl_GetString(objv[0]); - Tcl_CmdInfo info; - Tcl_GetCommandInfo(interp, name, &info); - info.objClientData = (ClientData)optionTable; - Tcl_SetCommandInfo(interp, name, &info); + /* Draw 3D border just inside of the focus highlight ring. */ + if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) { + Blt_DrawBackgroundRectangle(graphPtr->tkwin, drawable, + graphPtr->normalBg, graphPtr->highlightWidth, + graphPtr->highlightWidth, + graphPtr->width - 2*graphPtr->highlightWidth, + graphPtr->height - 2*graphPtr->highlightWidth, + graphPtr->borderWidth, graphPtr->relief); } + /* Draw focus highlight ring. */ + if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & FOCUS)) { + GC gc; - Tk_Window tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), - Tcl_GetString(objv[1]), - (char*)NULL); - if (tkwin == NULL) - return NULL; - - Graph* graphPtr = calloc(1, sizeof(Graph)); - - /* Initialize the graph data structure. */ - - graphPtr->interp = interp; - graphPtr->tkwin = tkwin; - graphPtr->display = Tk_Display(tkwin); - graphPtr->optionTable = optionTable; - graphPtr->classId = classId; - graphPtr->backingStore = TRUE; - graphPtr->doubleBuffer = TRUE; - graphPtr->borderWidth = 2; - graphPtr->plotBW = 1; - graphPtr->highlightWidth = 2; - graphPtr->plotRelief = TK_RELIEF_SOLID; - graphPtr->relief = TK_RELIEF_FLAT; - graphPtr->flags = MAP_WORLD | REDRAW_WORLD; - graphPtr->nextMarkerId = 1; - graphPtr->bottomMargin.site = MARGIN_BOTTOM; - graphPtr->leftMargin.site = MARGIN_LEFT; - graphPtr->topMargin.site = MARGIN_TOP; - graphPtr->rightMargin.site = MARGIN_RIGHT; - - Blt_Ts_InitStyle(graphPtr->titleTextStyle); - Blt_Ts_SetAnchor(graphPtr->titleTextStyle, TK_ANCHOR_N); + gc = Tk_GCForColor(graphPtr->highlightColor, drawable); + Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth, + drawable); + } + /* Disable crosshairs before redisplaying to the screen */ + Blt_DisableCrosshairs(graphPtr); + XCopyArea(graphPtr->display, drawable, Tk_WindowId(tkwin), + graphPtr->drawGC, 0, 0, graphPtr->width, graphPtr->height, 0, 0); + Blt_EnableCrosshairs(graphPtr); + if (graphPtr->doubleBuffer) { + Tk_FreePixmap(graphPtr->display, drawable); + } - Tcl_InitHashTable(&graphPtr->axes.table, TCL_STRING_KEYS); - Tcl_InitHashTable(&graphPtr->axes.tagTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&graphPtr->elements.table, TCL_STRING_KEYS); - Tcl_InitHashTable(&graphPtr->elements.tagTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&graphPtr->markers.table, TCL_STRING_KEYS); - Tcl_InitHashTable(&graphPtr->markers.tagTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&graphPtr->dataTables, TCL_STRING_KEYS); - graphPtr->elements.displayList = Blt_Chain_Create(); - graphPtr->markers.displayList = Blt_Chain_Create(); - graphPtr->axes.displayList = Blt_Chain_Create(); + graphPtr->flags &= ~MAP_WORLD; + graphPtr->flags &= ~REDRAW_WORLD; + UpdateMarginTraces(graphPtr); +} - if (Blt_CreatePageSetup(graphPtr) != TCL_OK) - goto error; - if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) - goto error; - if (Blt_CreateLegend(graphPtr) != TCL_OK) - goto error; +static void GraphEventProc(ClientData clientData, XEvent* eventPtr) +{ + Graph* graphPtr = clientData; - switch (classId) { - case CID_ELEM_LINE: - Tk_SetClass(tkwin, "Graph"); - break; - case CID_ELEM_BAR: - Tk_SetClass(tkwin, "Barchart"); - break; - default: - Tk_SetClass(tkwin, "???"); - break; + if (eventPtr->type == Expose) { + if (eventPtr->xexpose.count == 0) { + graphPtr->flags |= REDRAW_WORLD; + Blt_EventuallyRedrawGraph(graphPtr); + } + } 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->flags |= REDRAW_WORLD; + Blt_EventuallyRedrawGraph(graphPtr); + } + } else if (eventPtr->type == DestroyNotify) { + if (graphPtr->tkwin != NULL) { + Tk_FreeConfigOptions((char*)graphPtr, graphPtr->optionTable, + graphPtr->tkwin); + graphPtr->tkwin = NULL; + 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 |= (MAP_WORLD | REDRAW_WORLD); + Blt_EventuallyRedrawGraph(graphPtr); } +} - ((TkWindow*)tkwin)->instanceData = graphPtr; - - if (InitPens(graphPtr) != TCL_OK) - goto error; +static void GraphInstCmdDeleteProc(ClientData clientData) +{ + Graph* graphPtr = clientData; - if (Tk_InitOptions(interp, (char*)graphPtr, optionTable, tkwin) != TCL_OK) - goto error; - if (GraphObjConfigure(interp, graphPtr, objc-2, objv+2) != TCL_OK) - goto error; + // NULL indicates window has already been destroyed. + if (graphPtr->tkwin != NULL) { + Tk_Window tkwin = graphPtr->tkwin; + graphPtr->tkwin = NULL; + Tk_DestroyWindow(tkwin); + } +} - if (Blt_DefaultAxes(graphPtr) != TCL_OK) - goto error; +static void DestroyGraph(char* dataPtr) +{ + Graph* graphPtr = (Graph*)dataPtr; - AdjustAxisPointers(graphPtr); + Blt_DestroyMarkers(graphPtr); + Blt_DestroyElements(graphPtr); + Blt_DestroyLegend(graphPtr); + Blt_DestroyAxes(graphPtr); + Blt_DestroyPens(graphPtr); + Blt_DestroyCrosshairs(graphPtr); + Blt_DestroyPageSetup(graphPtr); + Blt_DestroyBarSets(graphPtr); + if (graphPtr->bindTable != NULL) + Blt_DestroyBindingTable(graphPtr->bindTable); - if (Blt_ConfigurePageSetup(graphPtr) != TCL_OK) - goto error; - if (Blt_ConfigureObjCrosshairs(graphPtr) != TCL_OK) - goto error; - Blt_ConfigureLegend(graphPtr); + if (graphPtr->drawGC != NULL) + Tk_FreeGC(graphPtr->display, graphPtr->drawGC); - Tk_CreateEventHandler(graphPtr->tkwin, - ExposureMask|StructureNotifyMask|FocusChangeMask, - GraphEventProc, (ClientData)graphPtr); + Blt_Ts_FreeStyle(graphPtr->display, &graphPtr->titleTextStyle); + if (graphPtr->cache != None) + Tk_FreePixmap(graphPtr->display, graphPtr->cache); - graphPtr->cmdToken = Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), - Blt_GraphInstCmdProc, - (ClientData)graphPtr, - GraphInstCmdDeleteProc); + free(graphPtr); +} - graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr, - PickEntry, Blt_GraphTags); +static void GraphObjDelete(ClientData clientData) +{ + DeleteGraph(clientData); +} - Tcl_SetObjResult(interp, objv[1]); - return graphPtr; +static void BarchartObjDelete(ClientData clientData) +{ + DeleteGraph(clientData); +} - error: - DestroyGraph((char*)graphPtr); - return NULL; +static void DeleteGraph(ClientData clientData) +{ + Tk_OptionTable optionTable = (Tk_OptionTable)clientData; + if (clientData) + Tk_DeleteOptionTable(optionTable); } -/* Widget sub-commands */ +// Widget commands static int XAxisOp(Graph* graphPtr, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) @@ -883,39 +853,6 @@ static int ElementOp(Graph* graphPtr, Tcl_Interp* interp, int objc, return Blt_ElementOp(graphPtr, interp, objc, objv, graphPtr->classId); } -static int ConfigureOp(Graph* graphPtr, Tcl_Interp* interp, int objc, - Tcl_Obj* const objv[]) -{ - if (objc <= 3) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)graphPtr, - graphPtr->optionTable, - (objc == 3) ? objv[2] : NULL, - graphPtr->tkwin); - if (!objPtr) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - } else { - if (GraphObjConfigure(interp, graphPtr, objc-2, objv+2) != TCL_OK) - return TCL_ERROR; - } - return TCL_OK; -} - -static int CgetOp(Graph* graphPtr, Tcl_Interp* interp, int objc, - Tcl_Obj* const objv[]) -{ - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, (char*)graphPtr, - graphPtr->optionTable, - (objc == 3) ? objv[2] : NULL, - graphPtr->tkwin); - if (!objPtr) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; -} - /* *--------------------------------------------------------------------------- * @@ -967,9 +904,9 @@ static int ExtentsOp(Graph* graphPtr, Tcl_Interp* interp, int objc, Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->top)); Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewIntObj(graphPtr->right - graphPtr->left + 1)); + Tcl_NewIntObj(graphPtr->right - graphPtr->left+1)); Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewIntObj(graphPtr->bottom - graphPtr->top + 1)); + Tcl_NewIntObj(graphPtr->bottom - graphPtr->top+1)); Tcl_SetObjResult(interp, listObjPtr); } else if ((c == 'l') && (length > 2) && (strncmp("legend", string, length) == 0)) { @@ -1006,6 +943,10 @@ topmargin, bottommargin, plotarea, or legend", (char*)NULL); return TCL_OK; } +#define PointInRegion(e,x,y) \ + (((x) <= (e)->right) && ((x) >= (e)->left) && \ + ((y) <= (e)->bottom) && ((y) >= (e)->top)) + static int InsideOp(Graph* graphPtr, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { @@ -1107,64 +1048,203 @@ static Blt_OpSpec graphOps[] = }; static int nGraphOps = sizeof(graphOps) / sizeof(Blt_OpSpec); -int Blt_GraphInstCmdProc(ClientData clientData, Tcl_Interp* interp, int objc, - Tcl_Obj* const objv[]) +// Support + +void Blt_UpdateGraph(ClientData clientData) { Graph* graphPtr = clientData; - GraphCmdProc* proc = Blt_GetOpFromObj(interp, nGraphOps, graphOps, - BLT_OP_ARG1, objc, objv, 0); - if (proc == NULL) { - return TCL_ERROR; + + graphPtr->flags |= REDRAW_WORLD; + if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) { + Tcl_DoWhenIdle(DisplayGraph, graphPtr); + graphPtr->flags |= REDRAW_PENDING; } - Tcl_Preserve(graphPtr); - int result = (*proc) (graphPtr, interp, objc, objv); - Tcl_Release(graphPtr); - return result; } -static int NewGraph(ClientData clientData, Tcl_Interp*interp, - int objc, Tcl_Obj* const objv[], ClassId classId) +void Blt_EventuallyRedrawGraph(Graph* graphPtr) { - if (objc < 2) { - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tcl_GetString(objv[0]), " pathName ?option value?...\"", - (char*)NULL); - return TCL_ERROR; + if ((graphPtr->tkwin != NULL) && + Tk_IsMapped(graphPtr->tkwin) && + !(graphPtr->flags & REDRAW_PENDING)) { + Tcl_DoWhenIdle(DisplayGraph, graphPtr); + graphPtr->flags |= REDRAW_PENDING; } - - if (!CreateGraph(clientData, interp, objc, objv, classId)) - return TCL_ERROR; - - return TCL_OK; } -static void DeleteGraph(ClientData clientData) +const char* Blt_GraphClassName(ClassId classId) { - Tk_OptionTable optionTable = (Tk_OptionTable)clientData; - if (clientData) - Tk_DeleteOptionTable(optionTable); + if ((classId >= CID_NONE) && (classId <= CID_MARKER_WINDOW)) { + return objectClassNames[classId]; + } + return NULL; } -static int GraphObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, - Tcl_Obj* const objv[]) +void Blt_GraphSetObjectClass(GraphObj* graphObjPtr, ClassId classId) { - return NewGraph(clientData, interp, objc, objv, CID_ELEM_LINE); + graphObjPtr->classId = classId; + graphObjPtr->className = Blt_GraphClassName(classId); } -static void GraphObjDelete(ClientData clientData) +static void AdjustAxisPointers(Graph* graphPtr) { - DeleteGraph(clientData); + if (graphPtr->inverted) { + graphPtr->leftMargin.axes = graphPtr->axisChain[0]; + graphPtr->bottomMargin.axes = graphPtr->axisChain[1]; + graphPtr->rightMargin.axes = graphPtr->axisChain[2]; + graphPtr->topMargin.axes = graphPtr->axisChain[3]; + } else { + graphPtr->leftMargin.axes = graphPtr->axisChain[1]; + graphPtr->bottomMargin.axes = graphPtr->axisChain[0]; + graphPtr->rightMargin.axes = graphPtr->axisChain[3]; + graphPtr->topMargin.axes = graphPtr->axisChain[2]; + } } -static int BarchartObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, - Tcl_Obj* const objv[]) +/* + *--------------------------------------------------------------------------- + * + * Blt_GraphTags -- + * + * Sets the binding tags for a graph obj. This routine is called by Tk + * when an event occurs in the graph. It fills an array of pointers with + * bind tag addresses. + * + * The object addresses are strings hashed in one of two tag tables: one + * for elements and the another for markers. Note that there's only one + * binding table for elements and markers. [We don't want to trigger + * both a marker and element bind command for the same event.] But we + * don't want a marker and element with the same tag name to activate the + * others bindings. A tag "all" for markers should mean all markers, not + * all markers and elements. As a result, element and marker tags are + * stored in separate hash tables, which means we can't generate the same + * tag address for both an elements and marker, even if they have the + * same name. + * + *--------------------------------------------------------------------------- + */ + +void Blt_GraphTags(Blt_BindTable table, ClientData object, ClientData context, + Blt_List list) { - return NewGraph(clientData, interp, objc, objv, CID_ELEM_BAR); + Graph* graphPtr = (Graph*)Blt_GetBindingData(table); + + /* + * All graph objects (markers, elements, axes, etc) have the same starting + * fields in their structures, such as "classId", "name", "className", and + * "tags". + */ + GraphObj* graphObjPtr = (GraphObj*)object; + + MakeTagProc* tagProc; + switch (graphObjPtr->classId) { + case CID_ELEM_BAR: + case CID_ELEM_LINE: + tagProc = Blt_MakeElementTag; + break; + case CID_AXIS_X: + case CID_AXIS_Y: + tagProc = Blt_MakeAxisTag; + break; + case CID_MARKER_BITMAP: + case CID_MARKER_IMAGE: + case CID_MARKER_LINE: + case CID_MARKER_POLYGON: + case CID_MARKER_TEXT: + case CID_MARKER_WINDOW: + tagProc = Blt_MakeMarkerTag; + break; + case CID_NONE: + Blt_Panic("unknown object type"); + tagProc = NULL; + break; + default: + Blt_Panic("bogus object type"); + tagProc = NULL; + break; + } + assert(graphObjPtr->name != NULL); + + /* Always add the name of the object to the tag array. */ + Blt_List_Append(list, (*tagProc)(graphPtr, graphObjPtr->name), 0); + Blt_List_Append(list, (*tagProc)(graphPtr, graphObjPtr->className), 0); + if (graphObjPtr->tags != NULL) { + const char **p; + + for (p = graphObjPtr->tags; *p != NULL; p++) { + Blt_List_Append(list, (*tagProc) (graphPtr, *p), 0); + } + } } -static void BarchartObjDelete(ClientData clientData) +/* + * 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 one that's on + * top (visible) is picked. + */ + +static ClientData PickEntry(ClientData clientData, int x, int y, + ClientData* contextPtr) { - DeleteGraph(clientData); + Graph* graphPtr = clientData; + + if (graphPtr->flags & MAP_ALL) { + return NULL; /* Don't pick anything until the next + * redraw occurs. */ + } + Region2d exts; + Blt_GraphExtents(graphPtr, &exts); + + if ((x >= exts.right) || (x < exts.left) || + (y >= exts.bottom) || (y < exts.top)) { + /* + * Sample coordinate is in one of the graph margins. Can only pick an + * axis. + */ + return Blt_NearestAxis(graphPtr, x, y); + } + /* + * From top-to-bottom check: + * 1. markers drawn on top (-under false). + * 2. elements using its display list back to front. + * 3. markers drawn under element (-under true). + */ + Marker* markerPtr = Blt_NearestMarker(graphPtr, x, y, FALSE); + if (markerPtr != NULL) { + return markerPtr; /* Found a marker (-under false). */ + } + + ClosestSearch search; + + search.along = SEARCH_BOTH; + search.halo = graphPtr->halo; + search.index = -1; + search.x = x; + search.y = y; + search.dist = (double)(search.halo + 1); + search.mode = SEARCH_AUTO; + + Blt_ChainLink link; + Element* elemPtr; + for (link = Blt_Chain_LastLink(graphPtr->elements.displayList); + link != NULL; link = Blt_Chain_PrevLink(link)) { + elemPtr = Blt_Chain_GetValue(link); + if (elemPtr->flags & (HIDE|MAP_ITEM)) { + continue; + } + if (elemPtr->state == BLT_STATE_NORMAL) { + (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search); + } + } + if (search.dist <= (double)search.halo) { + return search.elemPtr;// Found an element within the minimum halo distance. + } + + markerPtr = Blt_NearestMarker(graphPtr, x, y, TRUE); + if (markerPtr != NULL) { + return markerPtr; /* Found a marker (-under true) */ + } + return NULL; /* Nothing found. */ } /* @@ -1232,16 +1312,20 @@ static void DrawMargins(Graph* graphPtr, Drawable drawable) rects[2].width = graphPtr->width - graphPtr->right; Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg, - rects[0].x, rects[0].y, rects[0].width, rects[0].height, + rects[0].x, rects[0].y, + rects[0].width, rects[0].height, 0, TK_RELIEF_FLAT); Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg, - rects[1].x, rects[1].y, rects[1].width, rects[1].height, + rects[1].x, rects[1].y, + rects[1].width, rects[1].height, 0, TK_RELIEF_FLAT); Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg, - rects[2].x, rects[2].y, rects[2].width, rects[2].height, + rects[2].x, rects[2].y, + rects[2].width, rects[2].height, 0, TK_RELIEF_FLAT); Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg, - rects[3].x, rects[3].y, rects[3].width, rects[3].height, + rects[3].x, rects[3].y, + rects[3].width, rects[3].height, 0, TK_RELIEF_FLAT); /* Draw 3D border around the plotting area */ @@ -1254,7 +1338,8 @@ static void DrawMargins(Graph* graphPtr, Drawable drawable) w = (graphPtr->right - graphPtr->left) + (2*graphPtr->plotBW); h = (graphPtr->bottom - graphPtr->top) + (2*graphPtr->plotBW); Blt_DrawBackgroundRectangle(graphPtr->tkwin, drawable, - graphPtr->normalBg, x, y, w, h, graphPtr->plotBW, + graphPtr->normalBg, x, y, w, h, + graphPtr->plotBW, graphPtr->plotRelief); } int site = Blt_Legend_Site(graphPtr); @@ -1280,8 +1365,10 @@ static void DrawPlot(Graph* graphPtr, Drawable drawable) Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->plotBg, graphPtr->left - graphPtr->plotBW, graphPtr->top - graphPtr->plotBW, - graphPtr->right - graphPtr->left + 1 + 2 * graphPtr->plotBW, - graphPtr->bottom - graphPtr->top + 1 + 2 * graphPtr->plotBW, + graphPtr->right - + graphPtr->left + 1 + 2 * graphPtr->plotBW, + graphPtr->bottom - + graphPtr->top + 1 + 2 * graphPtr->plotBW, graphPtr->plotBW, graphPtr->plotRelief); /* Draw the elements, markers, legend, and axis limits. */ @@ -1337,8 +1424,8 @@ void Blt_DrawGraph(Graph* graphPtr, Drawable drawable) Blt_DrawBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg, graphPtr->highlightWidth, graphPtr->highlightWidth, - graphPtr->width - 2 * graphPtr->highlightWidth, - graphPtr->height - 2 * graphPtr->highlightWidth, + graphPtr->width - 2*graphPtr->highlightWidth, + graphPtr->height - 2*graphPtr->highlightWidth, graphPtr->borderWidth, graphPtr->relief); } /* Draw focus highlight ring. */ @@ -1373,127 +1460,6 @@ static void UpdateMarginTraces(Graph* graphPtr) } } -static void DisplayGraph(ClientData clientData) -{ - Graph* graphPtr = clientData; - Pixmap drawable; - Tk_Window tkwin; - int site; - - graphPtr->flags &= ~REDRAW_PENDING; - if (graphPtr->tkwin == NULL) { - return; /* Window has been destroyed (we - * should not get here) */ - } - tkwin = graphPtr->tkwin; - if ((Tk_Width(tkwin) <= 1) || (Tk_Height(tkwin) <= 1)) { - /* Don't bother computing the layout until the size of the window is - * something reasonable. */ - return; - } - graphPtr->width = Tk_Width(tkwin); - graphPtr->height = Tk_Height(tkwin); - Blt_MapGraph(graphPtr); - if (!Tk_IsMapped(tkwin)) { - /* The graph's window isn't displayed, so don't bother drawing - * anything. By getting this far, we've at least computed the - * coordinates of the graph's new layout. */ - return; - } - /* Create a pixmap the size of the window for double buffering. */ - if (graphPtr->doubleBuffer) { - drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(tkwin), - graphPtr->width, graphPtr->height, Tk_Depth(tkwin)); - } else { - drawable = Tk_WindowId(tkwin); - } - if (graphPtr->backingStore) { - if ((graphPtr->cache == None) || - (graphPtr->cacheWidth != graphPtr->width) || - (graphPtr->cacheHeight != graphPtr->height)) { - if (graphPtr->cache != None) { - Tk_FreePixmap(graphPtr->display, graphPtr->cache); - } - - - graphPtr->cache = Tk_GetPixmap(graphPtr->display, - Tk_WindowId(tkwin), graphPtr->width, graphPtr->height, - Tk_Depth(tkwin)); - graphPtr->cacheWidth = graphPtr->width; - graphPtr->cacheHeight = graphPtr->height; - graphPtr->flags |= CACHE_DIRTY; - } - } - if (graphPtr->backingStore) { - if (graphPtr->flags & CACHE_DIRTY) { - /* The backing store is new or out-of-date. */ - DrawPlot(graphPtr, graphPtr->cache); - graphPtr->flags &= ~CACHE_DIRTY; - } - /* Copy the pixmap to the one used for drawing the entire graph. */ - XCopyArea(graphPtr->display, graphPtr->cache, drawable, - graphPtr->drawGC, 0, 0, Tk_Width(graphPtr->tkwin), - Tk_Height(graphPtr->tkwin), 0, 0); - } else { - DrawPlot(graphPtr, drawable); - } - /* Draw markers above elements */ - Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE); - Blt_DrawActiveElements(graphPtr, drawable); - /* Don't draw legend in the plot area. */ - site = Blt_Legend_Site(graphPtr); - if ((site & LEGEND_PLOTAREA_MASK) && (Blt_Legend_IsRaised(graphPtr))) { - Blt_DrawLegend(graphPtr, drawable); - } - if (site == LEGEND_WINDOW) { - Blt_Legend_EventuallyRedraw(graphPtr); - } - /* Draw 3D border just inside of the focus highlight ring. */ - if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) { - Blt_DrawBackgroundRectangle(graphPtr->tkwin, drawable, - graphPtr->normalBg, graphPtr->highlightWidth, - graphPtr->highlightWidth, - graphPtr->width - 2 * graphPtr->highlightWidth, - graphPtr->height - 2 * graphPtr->highlightWidth, - graphPtr->borderWidth, graphPtr->relief); - } - /* Draw focus highlight ring. */ - if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & FOCUS)) { - GC gc; - - gc = Tk_GCForColor(graphPtr->highlightColor, drawable); - Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth, - drawable); - } - /* Disable crosshairs before redisplaying to the screen */ - Blt_DisableCrosshairs(graphPtr); - XCopyArea(graphPtr->display, drawable, Tk_WindowId(tkwin), - graphPtr->drawGC, 0, 0, graphPtr->width, graphPtr->height, 0, 0); - Blt_EnableCrosshairs(graphPtr); - if (graphPtr->doubleBuffer) { - Tk_FreePixmap(graphPtr->display, drawable); - } - - graphPtr->flags &= ~MAP_WORLD; - graphPtr->flags &= ~REDRAW_WORLD; - UpdateMarginTraces(graphPtr); -} - -int Blt_GraphCmdInitProc(Tcl_Interp* interp) -{ - static Blt_InitCmdSpec graphSpec = - {"graph", GraphObjCmd, GraphObjDelete, NULL}; - static Blt_InitCmdSpec barchartSpec = - {"barchart", BarchartObjCmd, BarchartObjDelete, NULL}; - - if (Blt_InitCmd(interp, "::blt", &graphSpec) != TCL_OK) - return TCL_ERROR; - if (Blt_InitCmd(interp, "::blt", &barchartSpec) != TCL_OK) - return TCL_ERROR; - - return TCL_OK; -} - Graph* Blt_GetGraphFromWindowData(Tk_Window tkwin) { while (tkwin) { |