summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjoye <joye>2014-02-06 22:26:27 (GMT)
committerjoye <joye>2014-02-06 22:26:27 (GMT)
commit924f85448cfb7379e8b52c8539fda434f526432b (patch)
tree76b9cbf3991621b0850076b096cf621867d3ed5e
parente529c666f58f6a8712138dc221ae2febca8bba7b (diff)
downloadblt-924f85448cfb7379e8b52c8539fda434f526432b.zip
blt-924f85448cfb7379e8b52c8539fda434f526432b.tar.gz
blt-924f85448cfb7379e8b52c8539fda434f526432b.tar.bz2
*** empty log message ***
-rw-r--r--src/bltGraph.C1206
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) {