5 * This module implements a graph widget for the BLT toolkit.
7 * Copyright 1991-1998 Lucent Technologies, Inc.
9 * Permission to use, copy, modify, and distribute this software and
10 * its documentation for any purpose and without fee is hereby
11 * granted, provided that the above copyright notice appear in all
12 * copies and that both that the copyright notice and warranty
13 * disclaimer appear in supporting documentation, and that the names
14 * of Lucent Technologies any of their entities not be used in
15 * advertising or publicity pertaining to distribution of the software
16 * without specific, written prior permission.
18 * Lucent Technologies disclaims all warranties with regard to this
19 * software, including all implied warranties of merchantability and
20 * fitness. In no event shall Lucent Technologies be liable for any
21 * special, indirect or consequential damages or any damages
22 * whatsoever resulting from loss of use, data or profits, whether in
23 * an action of contract, negligence or other tortuous action, arising
24 * out of or in connection with the use or performance of this
27 * The graph widget was created by Sani Nassif and George Howlett.
33 * 2) Update manual pages.
37 * 5) Surface, contour, and flow graphs
39 * 7) Arrows for line markers
45 #include "bltGrElem.h"
46 #include "bltSwitch.h"
47 #include <X11/Xutil.h>
51 Blt_Uid bltBarElementUid;
52 Blt_Uid bltLineElementUid;
53 Blt_Uid bltStripElementUid;
54 Blt_Uid bltContourElementUid;
55 Blt_Uid bltLineMarkerUid;
56 Blt_Uid bltBitmapMarkerUid;
57 Blt_Uid bltImageMarkerUid;
58 Blt_Uid bltTextMarkerUid;
59 Blt_Uid bltPolygonMarkerUid;
60 Blt_Uid bltWindowMarkerUid;
62 extern Tk_CustomOption bltLinePenOption;
63 extern Tk_CustomOption bltBarPenOption;
64 extern Tk_CustomOption bltDistanceOption;
65 extern Tk_CustomOption bltBarModeOption;
66 extern Tk_CustomOption bltPadOption;
67 extern Tk_CustomOption bltTileOption;
68 extern Tk_CustomOption bltShadowOption;
70 #define DEF_GRAPH_ASPECT_RATIO "0.0"
71 #define DEF_GRAPH_BAR_BASELINE "0.0"
72 #define DEF_GRAPH_BAR_MODE "normal"
73 #define DEF_GRAPH_BAR_WIDTH "0.8"
74 #define DEF_GRAPH_BACKGROUND STD_NORMAL_BACKGROUND
75 #define DEF_GRAPH_BG_MONO STD_NORMAL_BG_MONO
76 #define DEF_GRAPH_BORDERWIDTH STD_BORDERWIDTH
77 #define DEF_GRAPH_BUFFER_ELEMENTS "1"
78 #define DEF_GRAPH_BUFFER_GRAPH "1"
79 #define DEF_GRAPH_CURSOR "crosshair"
80 #define DEF_GRAPH_FONT STD_FONT_LARGE
81 #define DEF_GRAPH_HALO "2m"
82 #define DEF_GRAPH_HALO_BAR "0.1i"
83 #define DEF_GRAPH_HEIGHT "4i"
84 #define DEF_GRAPH_HIGHLIGHT_BACKGROUND STD_NORMAL_BACKGROUND
85 #define DEF_GRAPH_HIGHLIGHT_BG_MONO STD_NORMAL_BG_MONO
86 #define DEF_GRAPH_HIGHLIGHT_COLOR RGB_BLACK
87 #define DEF_GRAPH_HIGHLIGHT_WIDTH "0"
88 #define DEF_GRAPH_INVERT_XY "0"
89 #define DEF_GRAPH_JUSTIFY "center"
90 #define DEF_GRAPH_MARGIN "0"
91 #define DEF_GRAPH_MARGIN_VAR (char *)NULL
92 #define DEF_GRAPH_PLOT_BACKGROUND RGB_WHITE
93 #define DEF_GRAPH_PLOT_BG_MONO RGB_WHITE
94 #define DEF_GRAPH_PLOT_BW_COLOR STD_BORDERWIDTH
95 #define DEF_GRAPH_PLOT_BW_MONO "0"
96 #define DEF_GRAPH_PLOT_PADX "8"
97 #define DEF_GRAPH_PLOT_PADY "8"
98 #define DEF_GRAPH_PLOT_RELIEF "sunken"
99 #define DEF_GRAPH_RELIEF "flat"
100 #define DEF_GRAPH_SHADOW_COLOR (char *)NULL
101 #define DEF_GRAPH_SHADOW_MONO (char *)NULL
102 #define DEF_GRAPH_SHOW_VALUES "no"
103 #define DEF_GRAPH_TAKE_FOCUS ""
104 #define DEF_GRAPH_TITLE (char *)NULL
105 #define DEF_GRAPH_TITLE_COLOR STD_NORMAL_FOREGROUND
106 #define DEF_GRAPH_TITLE_MONO STD_NORMAL_FG_MONO
107 #define DEF_GRAPH_WIDTH "5i"
108 #define DEF_GRAPH_DATA (char *)NULL
109 #define DEF_GRAPH_DATA_COMMAND (char *)NULL
111 static Tk_ConfigSpec configSpecs[] =
113 {TK_CONFIG_DOUBLE, "-aspect", "aspect", "Aspect",
114 DEF_GRAPH_ASPECT_RATIO, Tk_Offset(Graph, aspect),
115 TK_CONFIG_DONT_SET_DEFAULT},
116 {TK_CONFIG_BORDER, "-background", "background", "Background",
117 DEF_GRAPH_BACKGROUND, Tk_Offset(Graph, border),
118 TK_CONFIG_COLOR_ONLY},
119 {TK_CONFIG_BORDER, "-background", "background", "Background",
120 DEF_GRAPH_BG_MONO, Tk_Offset(Graph, border),
121 TK_CONFIG_MONO_ONLY},
122 {TK_CONFIG_CUSTOM, "-barmode", "barMode", "BarMode",
123 DEF_GRAPH_BAR_MODE, Tk_Offset(Graph, mode),
124 TK_CONFIG_DONT_SET_DEFAULT, &bltBarModeOption},
125 {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
126 DEF_GRAPH_BAR_WIDTH, Tk_Offset(Graph, barWidth), 0},
127 {TK_CONFIG_DOUBLE, "-baseline", "baseline", "Baseline",
128 DEF_GRAPH_BAR_BASELINE, Tk_Offset(Graph, baseline), 0},
129 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
130 {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
131 {TK_CONFIG_SYNONYM, "-bm", "bottomMargin",
132 (char *)NULL, (char *)NULL, 0, 0},
133 {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
134 DEF_GRAPH_BORDERWIDTH, Tk_Offset(Graph, borderWidth),
135 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
136 {TK_CONFIG_CUSTOM, "-bottommargin", "bottomMargin", "Margin",
137 DEF_GRAPH_MARGIN, Tk_Offset(Graph, bottomMargin.reqSize), 0,
139 {TK_CONFIG_STRING, "-bottomvariable", "bottomVariable", "BottomVariable",
140 DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, bottomMargin.varName),
142 {TK_CONFIG_BOOLEAN, "-bufferelements", "bufferElements", "BufferElements",
143 DEF_GRAPH_BUFFER_ELEMENTS, Tk_Offset(Graph, backingStore),
144 TK_CONFIG_DONT_SET_DEFAULT},
145 {TK_CONFIG_BOOLEAN, "-buffergraph", "bufferGraph", "BufferGraph",
146 DEF_GRAPH_BUFFER_GRAPH, Tk_Offset(Graph, doubleBuffer),
147 TK_CONFIG_DONT_SET_DEFAULT},
148 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
149 DEF_GRAPH_CURSOR, Tk_Offset(Graph, cursor), TK_CONFIG_NULL_OK},
150 {TK_CONFIG_STRING, "-data", "data", "Data",
151 (char *)NULL, Tk_Offset(Graph, data), TK_CONFIG_DONT_SET_DEFAULT},
152 {TK_CONFIG_STRING, "-redrawcmd", "redrawCmd", "RedrawCmd",
153 (char *)NULL, Tk_Offset(Graph, redrawCmd), TK_CONFIG_DONT_SET_DEFAULT},
154 {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
155 {TK_CONFIG_FONT, "-font", "font", "Font",
156 DEF_GRAPH_FONT, Tk_Offset(Graph, titleTextStyle.font), 0},
157 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
158 DEF_GRAPH_TITLE_COLOR, Tk_Offset(Graph, titleTextStyle.color),
159 TK_CONFIG_COLOR_ONLY},
160 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
161 DEF_GRAPH_TITLE_MONO, Tk_Offset(Graph, titleTextStyle.color),
162 TK_CONFIG_MONO_ONLY},
163 {TK_CONFIG_CUSTOM, "-halo", "halo", "Halo",
164 DEF_GRAPH_HALO, Tk_Offset(Graph, halo), 0, &bltDistanceOption},
165 {TK_CONFIG_CUSTOM, "-height", "height", "Height",
166 DEF_GRAPH_HEIGHT, Tk_Offset(Graph, reqHeight), 0, &bltDistanceOption},
167 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
168 "HighlightBackground",
169 DEF_GRAPH_HIGHLIGHT_BACKGROUND, Tk_Offset(Graph, highlightBgColor),
170 TK_CONFIG_COLOR_ONLY},
171 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
172 "HighlightBackground",
173 DEF_GRAPH_HIGHLIGHT_BG_MONO, Tk_Offset(Graph, highlightBgColor),
174 TK_CONFIG_MONO_ONLY},
175 {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
176 DEF_GRAPH_HIGHLIGHT_COLOR, Tk_Offset(Graph, highlightColor), 0},
177 {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
178 "HighlightThickness",
179 DEF_GRAPH_HIGHLIGHT_WIDTH, Tk_Offset(Graph, highlightWidth),
180 TK_CONFIG_DONT_SET_DEFAULT},
181 {TK_CONFIG_BOOLEAN, "-invertxy", "invertXY", "InvertXY",
182 DEF_GRAPH_INVERT_XY, Tk_Offset(Graph, inverted),
183 TK_CONFIG_DONT_SET_DEFAULT},
184 {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
185 DEF_GRAPH_JUSTIFY, Tk_Offset(Graph, titleTextStyle.justify),
186 TK_CONFIG_DONT_SET_DEFAULT},
187 {TK_CONFIG_CUSTOM, "-leftmargin", "leftMargin", "Margin",
188 DEF_GRAPH_MARGIN, Tk_Offset(Graph, leftMargin.reqSize),
189 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
190 {TK_CONFIG_STRING, "-leftvariable", "leftVariable", "LeftVariable",
191 DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, leftMargin.varName),
193 {TK_CONFIG_SYNONYM, "-lm", "leftMargin", (char *)NULL, (char *)NULL, 0, 0},
194 {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
195 DEF_GRAPH_PLOT_BG_MONO, Tk_Offset(Graph, plotBg),
196 TK_CONFIG_MONO_ONLY},
197 {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
198 DEF_GRAPH_PLOT_BACKGROUND, Tk_Offset(Graph, plotBg),
199 TK_CONFIG_COLOR_ONLY},
200 {TK_CONFIG_CUSTOM, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
201 DEF_GRAPH_PLOT_BW_COLOR, Tk_Offset(Graph, plotBorderWidth),
202 TK_CONFIG_COLOR_ONLY, &bltDistanceOption},
203 {TK_CONFIG_CUSTOM, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
204 DEF_GRAPH_PLOT_BW_MONO, Tk_Offset(Graph, plotBorderWidth),
205 TK_CONFIG_MONO_ONLY, &bltDistanceOption},
206 {TK_CONFIG_CUSTOM, "-plotpadx", "plotPadX", "PlotPad",
207 DEF_GRAPH_PLOT_PADX, Tk_Offset(Graph, padX),
208 TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
209 {TK_CONFIG_CUSTOM, "-plotpady", "plotPadY", "PlotPad",
210 DEF_GRAPH_PLOT_PADY, Tk_Offset(Graph, padY),
211 TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
212 {TK_CONFIG_RELIEF, "-plotrelief", "plotRelief", "Relief",
213 DEF_GRAPH_PLOT_RELIEF, Tk_Offset(Graph, plotRelief),
214 TK_CONFIG_DONT_SET_DEFAULT},
215 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
216 DEF_GRAPH_RELIEF, Tk_Offset(Graph, relief), TK_CONFIG_DONT_SET_DEFAULT},
217 {TK_CONFIG_CUSTOM, "-rightmargin", "rightMargin", "Margin",
218 DEF_GRAPH_MARGIN, Tk_Offset(Graph, rightMargin.reqSize),
219 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
220 {TK_CONFIG_STRING, "-rightvariable", "rightVariable", "RightVariable",
221 DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, rightMargin.varName),
223 {TK_CONFIG_SYNONYM, "-rm", "rightMargin", (char *)NULL, (char *)NULL, 0, 0},
224 {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
225 DEF_GRAPH_SHADOW_COLOR, Tk_Offset(Graph, titleTextStyle.shadow),
226 TK_CONFIG_COLOR_ONLY, &bltShadowOption},
227 {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
228 DEF_GRAPH_SHADOW_MONO, Tk_Offset(Graph, titleTextStyle.shadow),
229 TK_CONFIG_MONO_ONLY, &bltShadowOption},
230 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
231 DEF_GRAPH_TITLE_MONO, Tk_Offset(Graph, titleTextStyle.color),
232 TK_CONFIG_MONO_ONLY},
233 {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
234 DEF_GRAPH_TAKE_FOCUS, Tk_Offset(Graph, takeFocus), TK_CONFIG_NULL_OK},
235 {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
236 (char *)NULL, Tk_Offset(Graph, tile),
237 TK_CONFIG_NULL_OK, &bltTileOption},
238 {TK_CONFIG_STRING, "-title", "title", "Title",
239 DEF_GRAPH_TITLE, Tk_Offset(Graph, title), TK_CONFIG_NULL_OK},
240 {TK_CONFIG_SYNONYM, "-tm", "topMargin", (char *)NULL, (char *)NULL, 0, 0},
241 {TK_CONFIG_CUSTOM, "-topmargin", "topMargin", "Margin",
242 DEF_GRAPH_MARGIN, Tk_Offset(Graph, topMargin.reqSize),
243 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
244 {TK_CONFIG_STRING, "-topvariable", "topVariable", "TopVariable",
245 DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, topMargin.varName),
247 {TK_CONFIG_CUSTOM, "-width", "width", "Width",
248 DEF_GRAPH_WIDTH, Tk_Offset(Graph, reqWidth),
249 0, &bltDistanceOption},
250 {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
253 static Blt_SwitchParseProc StringToFormat;
254 static Blt_SwitchCustom formatSwitch =
256 StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
265 enum SnapFormats { FORMAT_PHOTO, FORMAT_EMF, FORMAT_WMF };
267 static Blt_SwitchSpec snapSwitches[] =
269 {BLT_SWITCH_INT_POSITIVE, "-width", Blt_Offset(SnapData, width), 0},
270 {BLT_SWITCH_INT_POSITIVE, "-height", Blt_Offset(SnapData, height), 0},
271 {BLT_SWITCH_CUSTOM, "-format", Blt_Offset(SnapData, format), 0,
273 {BLT_SWITCH_END, NULL, 0, 0}
276 static Tcl_IdleProc DisplayGraph;
277 static Tcl_FreeProc DestroyGraph;
278 static Tk_EventProc GraphEventProc;
279 Tcl_CmdProc Blt_GraphInstCmdProc;
281 static Blt_BindPickProc PickEntry;
282 static Tcl_CmdProc StripchartCmd;
283 static Tcl_CmdProc BarchartCmd;
284 static Tcl_CmdProc GraphCmd;
285 static Tcl_CmdDeleteProc GraphInstCmdDeleteProc;
286 static Blt_TileChangedProc TileChangedProc;
288 static void widgetWorldChanged(ClientData clientData);
290 static Tk_ClassProcs graphClass = {
291 sizeof(Tk_ClassProcs), /* size */
292 widgetWorldChanged, /* worldChangedProc */
297 *--------------------------------------------------------------
299 * Blt_EventuallyRedrawGraph --
301 * Tells the Tk dispatcher to call the graph display routine at
302 * the next idle point. This request is made only if the window
303 * is displayed and no other redraw request is pending.
308 * The window is eventually redisplayed.
310 *--------------------------------------------------------------
313 Blt_EventuallyRedrawGraph(graphPtr)
314 Graph *graphPtr; /* Graph widget record */
316 if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) {
317 Tcl_DoWhenIdle(DisplayGraph, graphPtr);
318 graphPtr->flags |= REDRAW_PENDING;
322 static void widgetWorldChanged(ClientData clientData) {
323 Graph *graphPtr = (Graph*)clientData;
325 graphPtr->flags |= (REDRAW_WORLD | RESET_WORLD | REDRAW_BACKING_STORE | RESET_AXES | MAP_WORLD );
326 Blt_ConfigureAxes(graphPtr);
327 Blt_EventuallyRedrawGraph(graphPtr);
331 *--------------------------------------------------------------
335 * This procedure is invoked by the Tk dispatcher for various
342 * When the window gets deleted, internal structures get
343 * cleaned up. When it gets exposed, the graph is eventually
346 *--------------------------------------------------------------
349 GraphEventProc(clientData, eventPtr)
350 ClientData clientData; /* Graph widget record */
351 register XEvent *eventPtr; /* Event which triggered call to routine */
353 Graph *graphPtr = (Graph *)clientData;
355 if (eventPtr->type == Expose) {
356 if (eventPtr->xexpose.count == 0) {
357 graphPtr->flags |= REDRAW_WORLD;
358 Blt_EventuallyRedrawGraph(graphPtr);
360 } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
361 if (eventPtr->xfocus.detail != NotifyInferior) {
362 if (eventPtr->type == FocusIn) {
363 graphPtr->flags |= GRAPH_FOCUS;
365 graphPtr->flags &= ~GRAPH_FOCUS;
367 graphPtr->flags |= REDRAW_WORLD;
368 Blt_EventuallyRedrawGraph(graphPtr);
370 } else if (eventPtr->type == DestroyNotify) {
371 if (graphPtr->tkwin != NULL) {
372 Blt_DeleteAxisLabelsGC(graphPtr->tkwin);
373 Blt_DeleteWindowInstanceData(graphPtr->tkwin);
374 graphPtr->tkwin = NULL;
375 Tcl_DeleteCommandFromToken(graphPtr->interp, graphPtr->cmdToken);
377 if (graphPtr->flags & REDRAW_PENDING) {
378 Tcl_CancelIdleCall(DisplayGraph, graphPtr);
380 Tcl_EventuallyFree(graphPtr, DestroyGraph);
381 } else if (eventPtr->type == ConfigureNotify) {
382 graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
383 Blt_EventuallyRedrawGraph(graphPtr);
388 *----------------------------------------------------------------------
390 * GraphInstCmdDeleteProc --
392 * This procedure is invoked when a widget command is deleted. If
393 * the widget isn't already in the process of being destroyed,
394 * this command destroys it.
400 * The widget is destroyed.
402 *---------------------------------------------------------------------- */
404 GraphInstCmdDeleteProc(clientData)
405 ClientData clientData; /* Pointer to widget record. */
407 Graph *graphPtr = (Graph *)clientData;
409 if (graphPtr->tkwin != NULL) { /* NULL indicates window has
410 * already been destroyed. */
413 tkwin = graphPtr->tkwin;
414 graphPtr->tkwin = NULL;
415 #ifdef ITCL_NAMESPACES
416 Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
417 #endif /* ITCL_NAMESPACES */
418 Blt_DeleteWindowInstanceData(tkwin);
419 Tk_DestroyWindow(tkwin);
424 *----------------------------------------------------------------------
428 * Rebuilds the designated GC with the new tile pixmap.
433 *----------------------------------------------------------------------
437 TileChangedProc(clientData, tile)
438 ClientData clientData;
439 Blt_Tile tile; /* Not used. */
441 Graph *graphPtr = (Graph *)clientData;
443 if (graphPtr->tkwin != NULL) {
444 graphPtr->flags |= REDRAW_WORLD;
445 Blt_EventuallyRedrawGraph(graphPtr);
450 *--------------------------------------------------------------
452 * AdjustAxisPointers --
454 * Sets the axis pointers according to whether the axis is
455 * inverted on not. The axis sites are also reset.
460 *--------------------------------------------------------------
463 AdjustAxisPointers(graphPtr)
464 Graph *graphPtr; /* Graph widget record */
466 if (graphPtr->inverted) {
467 graphPtr->leftMargin.axes = graphPtr->axisChain[0];
468 graphPtr->bottomMargin.axes = graphPtr->axisChain[1];
469 graphPtr->rightMargin.axes = graphPtr->axisChain[2];
470 graphPtr->topMargin.axes = graphPtr->axisChain[3];
472 graphPtr->leftMargin.axes = graphPtr->axisChain[1];
473 graphPtr->bottomMargin.axes = graphPtr->axisChain[0];
474 graphPtr->rightMargin.axes = graphPtr->axisChain[3];
475 graphPtr->topMargin.axes = graphPtr->axisChain[2];
483 Blt_InitHashTable(&(graphPtr->penTable), BLT_STRING_KEYS);
484 if (Blt_CreatePen(graphPtr, "activeLine", bltLineElementUid, 0,
485 (char **)NULL) == NULL) {
488 if (Blt_CreatePen(graphPtr, "activeBar", bltBarElementUid, 0,
489 (char **)NULL) == NULL) {
496 *----------------------------------------------------------------------
500 * Sets the binding tags for a graph object. This routine is
501 * called by Tk when an event occurs in the graph. It fills
502 * an array of pointers with bind tag addresses.
504 * The object addresses are strings hashed in one of two tag
505 * tables: one for elements and the another for markers. Note
506 * that there's only one binding table for elements and markers.
507 * [We don't want to trigger both a marker and element bind
508 * command for the same event.] But we don't want a marker and
509 * element with the same tag name to activate the others
510 * bindings. A tag "all" for markers should mean all markers, not
511 * all markers and elements. As a result, element and marker
512 * tags are stored in separate hash tables, which means we can't
513 * generate the same tag address for both an elements and marker,
514 * even if they have the same name.
520 * This information will be used by the binding code in bltUtil.c
521 * to determine what graph objects match the current event. The
522 * tags are placed in tagArr and *nTagsPtr is set with the
523 * number of tags found.
525 *----------------------------------------------------------------------
529 Blt_GraphTags(table, object, context, list)
532 ClientData context; /* Not used. */
536 MakeTagProc *tagProc;
539 graphPtr = (Graph *)Blt_GetBindingData(table);
541 * Trick: Markers, elements, and axes have the same first few
542 * fields in their structures, such as "type", "name", or
543 * "tags". This is so we can look at graph objects
544 * interchangably. It doesn't matter what we cast the
547 elemPtr = (Element *)object;
549 if ((elemPtr->classUid == bltLineElementUid) ||
550 (elemPtr->classUid == bltStripElementUid) ||
551 (elemPtr->classUid == bltBarElementUid)) {
552 tagProc = Blt_MakeElementTag;
553 } else if ((elemPtr->classUid == bltXAxisUid) ||
554 (elemPtr->classUid == bltYAxisUid)) {
555 tagProc = Blt_MakeAxisTag;
557 tagProc = Blt_MakeMarkerTag;
560 * Always add the name of the object to the tag array.
562 Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->name), 0);
563 Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->classUid), 0);
564 if (elemPtr->tags != NULL) {
567 for (p = elemPtr->tags; *p != NULL; p++) {
568 Blt_ListAppend(list, (*tagProc) (graphPtr, *p), 0);
574 * Find the closest point from the set of displayed elements,
575 * searching the display list from back to front. That way, if
576 * the points from two different elements overlay each other exactly,
577 * the one that's on top (visible) is picked.
581 PickEntry(clientData, x, y, contextPtr)
582 ClientData clientData;
584 ClientData *contextPtr; /* Not used. */
586 Graph *graphPtr = (Graph *)clientData;
587 Blt_ChainLink *linkPtr;
592 if (graphPtr->flags & MAP_ALL) {
593 return NULL; /* Can't pick anything until the next
596 Blt_GraphExtents(graphPtr, &exts);
598 if ((x > exts.right) || (x < exts.left) || (y > exts.bottom) ||
601 * Sample coordinate is in one of the graph margins. Can only
604 return (int *)Blt_NearestAxis(graphPtr, x, y);
608 * From top-to-bottom check:
609 * 1. markers drawn on top (-under false).
610 * 2. elements using its display list back to front.
611 * 3. markers drawn under element (-under true).
613 markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, FALSE);
614 if (markerPtr != NULL) {
615 return (int *)markerPtr; /* Found a marker (-under false). */
618 ClosestSearch search;
620 search.along = SEARCH_BOTH;
621 search.halo = graphPtr->halo + 1;
625 search.dist = (double)(search.halo + 1);
626 search.mode = SEARCH_AUTO;
628 for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
629 linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
630 elemPtr = (Element *)Blt_ChainGetValue(linkPtr);
631 if ((elemPtr->flags & MAP_ITEM) ||
632 (Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
633 (Blt_VectorNotifyPending(elemPtr->y.clientId))) {
636 if ((!elemPtr->hidden) && (elemPtr->state == STATE_NORMAL)) {
637 (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
640 if (search.dist <= (double)search.halo) {
641 return (int *)search.elemPtr; /* Found an element within the
642 * minimum halo distance. */
645 markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, TRUE);
646 if (markerPtr != NULL) {
647 return (int *)markerPtr; /* Found a marker (-under true) */
649 return NULL; /* Nothing found. */
653 *----------------------------------------------------------------------
657 * Allocates resources for the graph.
663 * Configuration information, such as text string, colors, font,
664 * etc. get set for graphPtr; old resources get freed, if there
665 * were any. The graph is redisplayed.
667 *----------------------------------------------------------------------
670 ConfigureGraph(graphPtr)
671 Graph *graphPtr; /* Graph widget record */
676 unsigned long gcMask;
677 Tcl_Interp *interp = graphPtr->interp;
679 /* Don't allow negative bar widths. Reset to an arbitrary value (0.1) */
680 if (graphPtr->barWidth <= 0.0) {
681 graphPtr->barWidth = 0.1;
683 graphPtr->inset = graphPtr->borderWidth + graphPtr->highlightWidth + 1;
684 if ((graphPtr->reqHeight != Tk_ReqHeight(graphPtr->tkwin)) ||
685 (graphPtr->reqWidth != Tk_ReqWidth(graphPtr->tkwin))) {
686 Tk_GeometryRequest(graphPtr->tkwin, graphPtr->reqWidth,
687 graphPtr->reqHeight);
689 Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
690 colorPtr = Tk_3DBorderColor(graphPtr->border);
692 if (graphPtr->title != NULL) {
695 Blt_GetTextExtents(&graphPtr->titleTextStyle, graphPtr->title, &w, &h);
696 graphPtr->titleTextStyle.height = h + 10;
698 graphPtr->titleTextStyle.width = graphPtr->titleTextStyle.height = 0;
702 * Create GCs for interior and exterior regions, and a background
703 * GC for clearing the margins with XFillRectangle
708 gcValues.foreground = graphPtr->titleTextStyle.color->pixel;
709 gcValues.background = colorPtr->pixel;
710 gcMask = (GCForeground | GCBackground);
711 newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
712 if (graphPtr->drawGC != NULL) {
713 Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
715 graphPtr->drawGC = newGC;
717 /* Plot fill GC (Background = Foreground) */
719 gcValues.foreground = graphPtr->plotBg->pixel;
720 newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
721 if (graphPtr->plotFillGC != NULL) {
722 Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
724 graphPtr->plotFillGC = newGC;
726 /* Margin fill GC (Background = Foreground) */
728 gcValues.foreground = colorPtr->pixel;
729 gcValues.background = graphPtr->titleTextStyle.color->pixel;
730 newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
731 if (graphPtr->fillGC != NULL) {
732 Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
734 graphPtr->fillGC = newGC;
735 if (graphPtr->tile != NULL) {
736 Blt_SetTileChangedProc(graphPtr->tile, TileChangedProc, graphPtr);
739 Blt_ResetTextStyle(graphPtr->tkwin, &graphPtr->titleTextStyle);
741 if (Blt_ConfigModified(configSpecs, interp, "-invertxy", (char *)NULL)) {
744 * If the -inverted option changed, we need to readjust the pointers
745 * to the axes and recompute the their scales.
748 AdjustAxisPointers(graphPtr);
749 graphPtr->flags |= RESET_AXES;
751 if ((!graphPtr->backingStore) && (graphPtr->backPixmap != None)) {
754 * Free the pixmap if we're not buffering the display of elements
758 Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
759 graphPtr->backPixmap = None;
762 * Reconfigure the crosshairs, just in case the background color of
763 * the plotarea has been changed.
765 Blt_ConfigureCrosshairs(graphPtr);
768 * Update the layout of the graph (and redraw the elements) if
769 * any of the following graph options which affect the size of
770 * the plotting area has changed.
773 * -borderwidth, -plotborderwidth
777 * -bottommargin, -leftmargin, -rightmargin, -topmargin,
778 * -barmode, -barwidth
780 if (Blt_ConfigModified(configSpecs, interp, "-invertxy", "-title", "-font",
781 "-*margin", "-*width", "-height", "-barmode", "-*pad*", "-aspect",
783 graphPtr->flags |= RESET_WORLD;
785 if (Blt_ConfigModified(configSpecs, interp, "-plotbackground", (char *)NULL)) {
786 graphPtr->flags |= REDRAW_BACKING_STORE;
788 graphPtr->flags |= REDRAW_WORLD;
789 Blt_EventuallyRedrawGraph(graphPtr);
793 *----------------------------------------------------------------------
797 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
798 * to clean up the internal structure of a graph at a safe time
799 * (when no-one is using it anymore).
805 * Everything associated with the widget is freed up.
807 *----------------------------------------------------------------------
810 DestroyGraph(dataPtr)
813 Graph *graphPtr = (Graph *)dataPtr;
815 Tk_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display, 0);
817 * Destroy the individual components of the graph: elements, markers,
818 * X and Y axes, legend, display lists etc.
820 Blt_DestroyMarkers(graphPtr);
821 Blt_DestroyElements(graphPtr);
823 Blt_DestroyAxes(graphPtr);
824 Blt_DestroyPens(graphPtr);
826 if (graphPtr->legend != NULL) {
827 Blt_DestroyLegend(graphPtr);
829 if (graphPtr->postscript != NULL) {
830 Blt_DestroyPostScript(graphPtr);
832 if (graphPtr->crosshairs != NULL) {
833 Blt_DestroyCrosshairs(graphPtr);
835 if (graphPtr->gridPtr != NULL) {
836 Blt_DestroyGrid(graphPtr);
838 if (graphPtr->bindTable != NULL) {
839 Blt_DestroyBindingTable(graphPtr->bindTable);
842 /* Release allocated X resources and memory. */
843 if (graphPtr->drawGC != NULL) {
844 Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
846 if (graphPtr->fillGC != NULL) {
847 Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
849 if (graphPtr->plotFillGC != NULL) {
850 Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
852 Blt_FreeTextStyle(graphPtr->display, &graphPtr->titleTextStyle);
853 if (graphPtr->backPixmap != None) {
854 Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
856 if (graphPtr->freqArr != NULL) {
857 Blt_Free(graphPtr->freqArr);
859 if (graphPtr->nStacks > 0) {
860 Blt_DeleteHashTable(&graphPtr->freqTable);
862 if (graphPtr->tile != NULL) {
863 Blt_FreeTile(graphPtr->tile);
869 *----------------------------------------------------------------------
873 * This procedure creates and initializes a new widget.
876 * The return value is a pointer to a structure describing
877 * the new widget. If an error occurred, then the return
878 * value is NULL and an error message is left in interp->result.
881 * Memory is allocated, a Tk_Window is created, etc.
883 *----------------------------------------------------------------------
887 CreateGraph(interp, argc, argv, classUid)
896 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
901 graphPtr = Blt_Calloc(1, sizeof(Graph));
904 /* Initialize the graph data structure. */
906 graphPtr->tkwin = tkwin;
907 graphPtr->display = Tk_Display(tkwin);
908 graphPtr->interp = interp;
909 graphPtr->classUid = classUid;
910 graphPtr->backingStore = TRUE;
911 graphPtr->doubleBuffer = TRUE;
912 graphPtr->highlightWidth = 0;
913 graphPtr->plotRelief = TK_RELIEF_SUNKEN;
914 graphPtr->relief = TK_RELIEF_FLAT;
915 graphPtr->flags = (RESET_WORLD);
916 graphPtr->nextMarkerId = 1;
917 graphPtr->padLeft = graphPtr->padRight = 8;
918 graphPtr->padTop = graphPtr->padBottom = 8;
919 graphPtr->bottomMargin.site = MARGIN_BOTTOM;
920 graphPtr->leftMargin.site = MARGIN_LEFT;
921 graphPtr->topMargin.site = MARGIN_TOP;
922 graphPtr->rightMargin.site = MARGIN_RIGHT;
923 Blt_InitTextStyle(&graphPtr->titleTextStyle);
925 Blt_InitHashTable(&graphPtr->axes.table, BLT_STRING_KEYS);
926 Blt_InitHashTable(&graphPtr->axes.tagTable, BLT_STRING_KEYS);
927 Blt_InitHashTable(&graphPtr->elements.table, BLT_STRING_KEYS);
928 Blt_InitHashTable(&graphPtr->elements.tagTable, BLT_STRING_KEYS);
929 Blt_InitHashTable(&graphPtr->markers.table, BLT_STRING_KEYS);
930 Blt_InitHashTable(&graphPtr->markers.tagTable, BLT_STRING_KEYS);
931 graphPtr->elements.displayList = Blt_ChainCreate();
932 graphPtr->markers.displayList = Blt_ChainCreate();
933 graphPtr->axes.displayList = Blt_ChainCreate();
935 if (classUid == bltLineElementUid) {
936 Tk_SetClass(tkwin, "Graph");
937 } else if (classUid == bltBarElementUid) {
938 Tk_SetClass(tkwin, "Barchart");
939 } else if (classUid == bltStripElementUid) {
940 Tk_SetClass(tkwin, "Stripchart");
942 Blt_SetWindowInstanceData(tkwin, graphPtr);
944 if (InitPens(graphPtr) != TCL_OK) {
947 if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc - 2, argv + 2,
948 (char *)graphPtr, 0) != TCL_OK) {
951 if (Blt_DefaultAxes(graphPtr) != TCL_OK) {
954 AdjustAxisPointers(graphPtr);
956 if (Blt_CreatePostScript(graphPtr) != TCL_OK) {
959 if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
962 if (Blt_CreateLegend(graphPtr) != TCL_OK) {
965 if (Blt_CreateGrid(graphPtr) != TCL_OK) {
968 Tk_CreateEventHandler(graphPtr->tkwin,
969 ExposureMask | StructureNotifyMask | FocusChangeMask, GraphEventProc,
972 graphPtr->cmdToken = Tcl_CreateCommand(interp, argv[1],
973 Blt_GraphInstCmdProc, graphPtr, GraphInstCmdDeleteProc);
974 #ifdef ITCL_NAMESPACES
975 Itk_SetWidgetCommand(graphPtr->tkwin, graphPtr->cmdToken);
977 ConfigureGraph(graphPtr);
978 graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr,
979 PickEntry, Blt_GraphTags);
980 Tk_SetClassProcs(tkwin, &graphClass, (ClientData)graphPtr);
985 DestroyGraph((DestroyData)graphPtr);
989 /* Widget sub-commands */
993 XAxisOp(graphPtr, interp, argc, argv)
995 Tcl_Interp *interp; /* Not used. */
1001 margin = (graphPtr->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM;
1002 return Blt_AxisOp(graphPtr, margin, argc, argv);
1007 X2AxisOp(graphPtr, interp, argc, argv)
1009 Tcl_Interp *interp; /* Not used. */
1015 margin = (graphPtr->inverted) ? MARGIN_RIGHT : MARGIN_TOP;
1016 return Blt_AxisOp(graphPtr, margin, argc, argv);
1021 YAxisOp(graphPtr, interp, argc, argv)
1023 Tcl_Interp *interp; /* Not used. */
1029 margin = (graphPtr->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT;
1030 return Blt_AxisOp(graphPtr, margin, argc, argv);
1035 Y2AxisOp(graphPtr, interp, argc, argv)
1037 Tcl_Interp *interp; /* Not used. */
1043 margin = (graphPtr->inverted) ? MARGIN_TOP : MARGIN_RIGHT;
1044 return Blt_AxisOp(graphPtr, margin, argc, argv);
1049 BarOp(graphPtr, interp, argc, argv)
1051 Tcl_Interp *interp; /* Not used. */
1055 return Blt_ElementOp(graphPtr, interp, argc, argv, bltBarElementUid);
1060 LineOp(graphPtr, interp, argc, argv)
1062 Tcl_Interp *interp; /* Not used. */
1066 return Blt_ElementOp(graphPtr, interp, argc, argv, bltLineElementUid);
1071 ElementOp(graphPtr, interp, argc, argv)
1073 Tcl_Interp *interp; /* Not used. */
1077 return Blt_ElementOp(graphPtr, interp, argc, argv, graphPtr->classUid);
1081 ConfigureOp(graphPtr, interp, argc, argv)
1089 flags = TK_CONFIG_ARGV_ONLY;
1091 return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1092 (char *)graphPtr, (char *)NULL, flags);
1093 } else if (argc == 3) {
1094 return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1095 (char *)graphPtr, argv[2], flags);
1097 if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 2,
1098 argv + 2, (char *)graphPtr, flags) != TCL_OK) {
1101 ConfigureGraph(graphPtr);
1108 CgetOp(graphPtr, interp, argc, argv)
1111 int argc; /* Not used. */
1114 return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
1115 (char *)graphPtr, argv[2], 0);
1119 *--------------------------------------------------------------
1123 * Reports the size of one of several items within the graph.
1124 * The following are valid items:
1126 * "bottommargin" Height of the bottom margin
1127 * "leftmargin" Width of the left margin
1128 * "legend" x y w h of the legend
1129 * "plotarea" x y w h of the plotarea
1130 * "plotheight" Height of the plot area
1131 * "rightmargin" Width of the right margin
1132 * "topmargin" Height of the top margin
1133 * "plotwidth" Width of the plot area
1136 * Always returns TCL_OK.
1138 *--------------------------------------------------------------
1142 ExtentsOp(graphPtr, interp, argc, argv)
1145 int argc; /* Not used. */
1149 unsigned int length;
1153 length = strlen(argv[2]);
1154 if ((c == 'p') && (length > 4) &&
1155 (strncmp("plotheight", argv[2], length) == 0)) {
1156 Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottom - graphPtr->top + 1),
1158 } else if ((c == 'p') && (length > 4) &&
1159 (strncmp("plotwidth", argv[2], length) == 0)) {
1160 Tcl_SetResult(interp, Blt_Itoa(graphPtr->right - graphPtr->left + 1),
1162 } else if ((c == 'p') && (length > 4) &&
1163 (strncmp("plotarea", argv[2], length) == 0)) {
1164 sprintf(string, "%d %d %d %d", graphPtr->left, graphPtr->top,
1165 graphPtr->right - graphPtr->left + 1,
1166 graphPtr->bottom - graphPtr->top + 1);
1167 Tcl_SetResult(interp, string, TCL_VOLATILE);
1168 } else if ((c == 'l') && (length > 2) &&
1169 (strncmp("legend", argv[2], length) == 0)) {
1170 sprintf(string, "%d %d %d %d", Blt_LegendX(graphPtr->legend),
1171 Blt_LegendY(graphPtr->legend),
1172 Blt_LegendWidth(graphPtr->legend),
1173 Blt_LegendHeight(graphPtr->legend));
1174 Tcl_SetResult(interp, string, TCL_VOLATILE);
1175 } else if ((c == 'l') && (length > 2) &&
1176 (strncmp("leftmargin", argv[2], length) == 0)) {
1177 Tcl_SetResult(interp, Blt_Itoa(graphPtr->leftMargin.width),
1179 } else if ((c == 'r') && (length > 1) &&
1180 (strncmp("rightmargin", argv[2], length) == 0)) {
1181 Tcl_SetResult(interp, Blt_Itoa(graphPtr->rightMargin.width),
1183 } else if ((c == 't') && (length > 1) &&
1184 (strncmp("topmargin", argv[2], length) == 0)) {
1185 Tcl_SetResult(interp, Blt_Itoa(graphPtr->topMargin.height), TCL_VOLATILE);
1186 } else if ((c == 'b') && (length > 1) &&
1187 (strncmp("bottommargin", argv[2], length) == 0)) {
1188 Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottomMargin.height),
1191 Tcl_AppendResult(interp, "bad extent item \"", argv[2],
1192 "\": should be plotheight, plotwidth, leftmargin, rightmargin, \
1193 topmargin, bottommargin, plotarea, or legend", (char *)NULL);
1200 *--------------------------------------------------------------
1204 * Returns true of false whether the given point is inside
1205 * the plotting area (defined by left,bottom right, top).
1208 * Always returns TCL_OK. interp->result will contain
1209 * the boolean string representation.
1211 *--------------------------------------------------------------
1215 InsideOp(graphPtr, interp, argc, argv)
1218 int argc; /* Not used. */
1225 if (Tk_GetPixels(interp, graphPtr->tkwin, argv[2], &x) != TCL_OK) {
1228 if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &y) != TCL_OK) {
1231 Blt_GraphExtents(graphPtr, &exts);
1232 result = PointInRegion(&exts, x, y);
1233 Blt_SetBooleanResult(interp, result);
1238 * -------------------------------------------------------------------------
1242 * This procedure returns a list of the graph coordinate
1243 * values corresponding with the given window X and Y
1244 * coordinate positions.
1247 * Returns a standard Tcl result. If an error occurred while
1248 * parsing the window positions, TCL_ERROR is returned, and
1249 * interp->result will contain the error message. Otherwise
1250 * interp->result will contain a Tcl list of the x and y
1253 * ------------------------------------------------------------------------
1257 InvtransformOp(graphPtr, interp, argc, argv)
1258 Graph *graphPtr; /* Graph widget record */
1260 int argc; /* Not used. */
1267 if (Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK ||
1268 Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK) {
1271 if (graphPtr->flags & RESET_AXES) {
1272 Blt_ResetAxes(graphPtr);
1274 /* Perform the reverse transformation, converting from window
1275 * coordinates to graph data coordinates. Note that the point is
1276 * always mapped to the bottom and left axes (which may not be
1277 * what the user wants). */
1279 /* Pick the first pair of axes */
1280 axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
1281 axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
1282 point = Blt_InvMap2D(graphPtr, x, y, &axes);
1284 Tcl_AppendElement(interp, Blt_Dtoa(interp, point.x));
1285 Tcl_AppendElement(interp, Blt_Dtoa(interp, point.y));
1290 * --------------------------------------------------------------------------
1294 * This procedure returns a list of the window coordinates
1295 * corresponding with the given graph x and y coordinates.
1298 * Returns a standard Tcl result. interp->result contains
1299 * the list of the graph coordinates. If an error occurred
1300 * while parsing the window positions, TCL_ERROR is returned,
1301 * then interp->result will contain an error message.
1303 * -------------------------------------------------------------------------
1307 TransformOp(graphPtr, interp, argc, argv)
1308 Graph *graphPtr; /* Graph widget record */
1310 int argc; /* Not used. */
1317 if ((Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK) ||
1318 (Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK)) {
1321 if (graphPtr->flags & RESET_AXES) {
1322 Blt_ResetAxes(graphPtr);
1325 * Perform the transformation from window to graph coordinates.
1326 * Note that the points are always mapped onto the bottom and left
1327 * axes (which may not be the what the user wants).
1329 axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
1330 axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
1332 point = Blt_Map2D(graphPtr, x, y, &axes);
1333 Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.x)));
1334 Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.y)));
1341 * --------------------------------------------------------------------------
1345 * Prints the equivalent of a screen snapshot of the graph
1346 * to the designated printer.
1349 * Returns a standard Tcl result. If an error occurred
1350 * TCL_ERROR is returned and interp->result will contain an
1353 * -------------------------------------------------------------------------
1357 Print1Op(graphPtr, interp, argc, argv)
1358 Graph *graphPtr; /* Graph widget record */
1360 int argc; /* Not used. */
1363 int noBackingStore = 0;
1373 double pageWidth, pageHeight;
1375 double scale, sx, sy;
1378 graphPtr->width = Tk_Width(graphPtr->tkwin);
1379 graphPtr->height = Tk_Height(graphPtr->tkwin);
1380 if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
1381 graphPtr->width = graphPtr->reqWidth;
1383 if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
1384 graphPtr->height = graphPtr->reqHeight;
1387 result = Blt_PrintDialog(interp, &drawable);
1388 if (result == TCL_ERROR) {
1391 if (result == TCL_RETURN) {
1395 if (Blt_GetOpenPrinter(interp, argv[2], &drawable) != TCL_OK) {
1400 * This is a taken from Blt_SnapPhoto. The difference is that
1401 * here we're using the DIBSection directly, without converting
1402 * the section into a ColorImage.
1404 ZeroMemory(&info, sizeof(info));
1405 info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1406 info.bmiHeader.biWidth = graphPtr->width;
1407 info.bmiHeader.biHeight = graphPtr->height;
1408 info.bmiHeader.biPlanes = 1;
1409 info.bmiHeader.biBitCount = 32;
1410 info.bmiHeader.biCompression = BI_RGB;
1411 hDC = TkWinGetDrawableDC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
1413 hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
1414 TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hDC, &state);
1417 * Create our own drawable by hand using the DIB we just created.
1418 * We'll then draw into it using the standard drawing functions.
1420 bd.type = TWD_BITMAP;
1421 bd.handle = hBitmap;
1422 bd.colormap = DefaultColormap(graphPtr->display,
1423 DefaultScreen(graphPtr->display));
1424 bd.depth = Tk_Depth(graphPtr->tkwin);
1426 graphPtr->flags |= RESET_WORLD;
1427 Blt_DrawGraph(graphPtr, (Drawable)&bd, noBackingStore);
1430 * Now that the DIB contains the image of the graph, get the the
1431 * data bits and write them to the printer device, stretching the
1432 * image to the fit the printer's resolution.
1435 if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
1436 Tcl_AppendResult(interp, "can't get object: ", Blt_LastError(),
1440 hDC = ((TkWinDC *) drawable)->hdc;
1441 /* Get the resolution of the printer device. */
1442 sx = (double)GetDeviceCaps(hDC, HORZRES) / (double)graphPtr->width;
1443 sy = (double)GetDeviceCaps(hDC, VERTRES) / (double)graphPtr->height;
1444 scale = MIN(sx, sy);
1445 pageWidth = scale * graphPtr->width;
1446 pageHeight = scale * graphPtr->height;
1448 ZeroMemory(&di, sizeof(di));
1449 di.cbSize = sizeof(di);
1450 di.lpszDocName = "Graph Contents";
1451 jobId = StartDoc(hDC, &di);
1453 Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(),
1457 if (StartPage(hDC) <= 0) {
1458 Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(),
1462 StretchDIBits(hDC, 0, 0, ROUND(pageWidth), ROUND(pageHeight), 0, 0,
1463 graphPtr->width, graphPtr->height, ds.dsBm.bmBits,
1464 (LPBITMAPINFO)&ds.dsBmih, DIB_RGB_COLORS, SRCCOPY);
1469 DeleteBitmap(hBitmap);
1474 * --------------------------------------------------------------------------
1478 * Prints directly to the designated printer device.
1481 * Returns a standard Tcl result. If an error occurred,
1482 * TCL_ERROR is returned and interp->result will contain an
1485 * -------------------------------------------------------------------------
1489 Print2Op(graphPtr, interp, argc, argv)
1490 Graph *graphPtr; /* Graph widget record */
1492 int argc; /* Not used. */
1496 int noBackingStore = 0;
1499 graphPtr->width = Tk_Width(graphPtr->tkwin);
1500 graphPtr->height = Tk_Height(graphPtr->tkwin);
1501 if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
1502 graphPtr->width = graphPtr->reqWidth;
1504 if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
1505 graphPtr->height = graphPtr->reqHeight;
1508 result = Blt_PrintDialog(interp, &drawable);
1509 if (result == TCL_ERROR) {
1512 if (result == TCL_RETURN) {
1516 result = Blt_GetOpenPrinter(interp, argv[2], &drawable);
1518 if (result == TCL_OK) {
1521 double xRatio, yRatio;
1523 double vportWidth, vportHeight;
1525 drawPtr = (TkWinDC *) drawable;
1527 Blt_GetPrinterScale(hDC, &xRatio, &yRatio);
1528 oldMode = SetMapMode(hDC, MM_ISOTROPIC);
1530 Tcl_AppendResult(interp, "can't set mode for printer DC: ",
1531 Blt_LastError(), (char *)NULL);
1534 vportWidth = graphPtr->width * xRatio;
1535 vportHeight = graphPtr->height * yRatio;
1536 SetViewportExtEx(hDC, ROUND(vportWidth), ROUND(vportHeight), NULL);
1537 SetWindowExtEx(hDC, graphPtr->width, graphPtr->height, NULL);
1539 Blt_StartPrintJob(interp, drawable);
1540 graphPtr->flags |= RESET_WORLD;
1541 Blt_DrawGraph(graphPtr, drawable, noBackingStore);
1542 Blt_EndPrintJob(interp, drawable);
1547 #endif /* NO_PRINTER */
1550 *----------------------------------------------------------------------
1554 * Convert a string represent a node number into its integer
1558 * The return value is a standard Tcl result.
1560 *----------------------------------------------------------------------
1564 StringToFormat(clientData, interp, switchName, string, record, offset)
1565 ClientData clientData; /* Contains a pointer to the tabset containing
1567 Tcl_Interp *interp; /* Interpreter to send results back to */
1568 char *switchName; /* Not used. */
1569 char *string; /* String representation */
1570 char *record; /* Structure record */
1571 int offset; /* Offset to field in structure */
1573 int *formatPtr = (int *)(record + offset);
1577 if ((c == 'p') && (strcmp(string, "photo") == 0)) {
1578 *formatPtr = FORMAT_PHOTO;
1580 } else if ((c == 'e') && (strcmp(string, "emf") == 0)) {
1581 *formatPtr = FORMAT_EMF;
1582 } else if ((c == 'w') && (strcmp(string, "wmf") == 0)) {
1583 *formatPtr = FORMAT_WMF;
1587 Tcl_AppendResult(interp, "bad format \"", string,
1588 "\": should be photo, emf, or wmf.", (char *)NULL);
1590 Tcl_AppendResult(interp, "bad format \"", string,
1591 "\": should be photo.", (char *)NULL);
1599 static int InitMetaFileHeader(
1601 int width, int height,
1607 #define MM_INCH 25.4
1610 mfhPtr->key = 0x9ac6cdd7L;
1612 mfhPtr->inch = 1440;
1614 screen = Tk_Screen(tkwin);
1615 dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
1616 dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
1618 mfhPtr->bbox.Left = mfhPtr->bbox.Top = 0;
1619 mfhPtr->bbox.Bottom = (SHORT)((width * 1440)/ dpiX);
1620 mfhPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
1621 mfhPtr->reserved = 0;
1623 for (p = (unsigned int *)mfhPtr;
1624 p < (unsigned int *)&(mfhPtr->checksum); p++) {
1627 mfhPtr->checksum = sum;
1643 DWORD count, nBytes;
1648 fileName, /* File path */
1649 GENERIC_WRITE, /* Access mode */
1650 0, /* No sharing. */
1651 NULL, /* Security attributes */
1652 CREATE_ALWAYS, /* Overwrite any existing file */
1653 FILE_ATTRIBUTE_NORMAL,
1654 NULL); /* No template file */
1655 if (hFile == INVALID_HANDLE_VALUE) {
1656 Tcl_AppendResult(interp, "can't create metafile \"", fileName,
1657 "\":", Blt_LastError(), (char *)NULL);
1660 if ((!WriteFile(hFile, (LPVOID)mfhPtr, sizeof(APMHEADER), &count,
1661 NULL)) || (count != sizeof(APMHEADER))) {
1662 Tcl_AppendResult(interp, "can't create metafile header to \"",
1663 fileName, "\":", Blt_LastError(), (char *)NULL);
1666 nBytes = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hDC);
1667 hMem = GlobalAlloc(GHND, nBytes);
1669 Tcl_AppendResult(interp, "can't create allocate global memory:",
1670 Blt_LastError(), (char *)NULL);
1673 buffer = (LPVOID)GlobalLock(hMem);
1674 if (!GetWinMetaFileBits(hMetaFile, nBytes, buffer, MM_ANISOTROPIC, hDC)) {
1675 Tcl_AppendResult(interp, "can't get metafile bits:",
1676 Blt_LastError(), (char *)NULL);
1679 if ((!WriteFile(hFile, buffer, nBytes, &count, NULL)) ||
1680 (count != nBytes)) {
1681 Tcl_AppendResult(interp, "can't write metafile bits:",
1682 Blt_LastError(), (char *)NULL);
1697 * --------------------------------------------------------------------------
1701 * Snaps a picture of the graph and stores it in the specified image
1704 * Returns a standard Tcl result. interp->result contains
1705 * the list of the graph coordinates. If an error occurred
1706 * while parsing the window positions, TCL_ERROR is returned,
1707 * then interp->result will contain an error message.
1709 * -------------------------------------------------------------------------
1713 SnapOp(graphPtr, interp, argc, argv)
1714 Graph *graphPtr; /* Graph widget record */
1716 int argc; /* Not used. */
1721 int noBackingStore = 0;
1725 /* .g snap ?switches? name */
1726 data.height = Tk_Height(graphPtr->tkwin);
1727 data.width = Tk_Width(graphPtr->tkwin);
1728 data.format = FORMAT_PHOTO;
1729 /* Process switches */
1730 i = Blt_ProcessSwitches(interp, snapSwitches, argc - 2, argv + 2,
1731 (char *)&data, BLT_SWITCH_OBJV_PARTIAL);
1737 Tcl_AppendResult(interp, "missing name argument: should be \"",
1738 argv[0], "snap ?switches? name\"", (char *)NULL);
1741 data.name = argv[i];
1742 if (data.width < 2) {
1745 if (data.height < 2) {
1748 /* Always re-compute the layout of the graph before snapping the photo. */
1749 graphPtr->width = data.width;
1750 graphPtr->height = data.height;
1751 Blt_LayoutGraph(graphPtr);
1753 drawable = Tk_WindowId(graphPtr->tkwin);
1754 if (data.format == FORMAT_PHOTO) {
1755 drawable = Tk_GetPixmap(graphPtr->display, drawable, graphPtr->width,
1756 graphPtr->height, Tk_Depth(graphPtr->tkwin));
1758 assert(drawable != None);
1760 graphPtr->flags |= RESET_WORLD;
1761 Blt_DrawGraph(graphPtr, drawable, noBackingStore);
1762 result = Blt_SnapPhoto(interp, graphPtr->tkwin, drawable, 0, 0,
1763 data.width, data.height, data.width, data.height, data.name, 1.0);
1764 Tk_FreePixmap(graphPtr->display, drawable);
1766 } else if ((data.format == FORMAT_WMF) || (data.format == FORMAT_EMF)) {
1770 HENHMETAFILE hMetaFile;
1771 Tcl_DString dString;
1774 hRefDC = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
1776 Tcl_DStringInit(&dString);
1777 Tcl_DStringAppend(&dString, "BLT Graph ", -1);
1778 Tcl_DStringAppend(&dString, BLT_VERSION, -1);
1779 Tcl_DStringAppend(&dString, "\0", -1);
1780 Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
1781 Tcl_DStringAppend(&dString, "\0", -1);
1782 title = Tcl_DStringValue(&dString);
1783 hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, title);
1784 Tcl_DStringFree(&dString);
1787 Tcl_AppendResult(interp, "can't create metafile: ",
1788 Blt_LastError(), (char *)NULL);
1792 drawableDC.hdc = hDC;
1793 drawableDC.type = TWD_WINDC;
1795 Blt_LayoutGraph(graphPtr);
1796 graphPtr->flags |= RESET_WORLD;
1797 Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
1799 hMetaFile = CloseEnhMetaFile(hDC);
1800 if (strcmp(data.name, "CLIPBOARD") == 0) {
1803 hWnd = Tk_GetHWND(drawable);
1804 OpenClipboard(hWnd);
1806 SetClipboardData(CF_ENHMETAFILE, hMetaFile);
1811 if (data.format == FORMAT_WMF) {
1814 assert(sizeof(mfh) == 22);
1815 InitMetaFileHeader(graphPtr->tkwin, data.width, data.height,
1817 result = CreateAPMetaFile(interp, hMetaFile, hRefDC, &mfh,
1820 HENHMETAFILE hMetaFile2;
1822 hMetaFile2 = CopyEnhMetaFile(hMetaFile, data.name);
1823 if (hMetaFile2 != NULL) {
1825 DeleteEnhMetaFile(hMetaFile2);
1828 DeleteEnhMetaFile(hMetaFile);
1830 TkWinReleaseDrawableDC(drawable, hRefDC, &state);
1833 Tcl_AppendResult(interp, "bad snapshot format", (char *)NULL);
1836 graphPtr->flags = MAP_WORLD;
1837 Blt_EventuallyRedrawGraph(graphPtr);
1842 * --------------------------------------------------------------------------
1846 * This procedure is invoked to process the Tcl command that
1847 * corresponds to a widget managed by this module. See the user
1848 * documentation for details on what it does.
1851 * A standard Tcl result.
1854 * See the user documentation.
1856 * --------------------------------------------------------------------------
1858 static Blt_OpSpec graphOps[] =
1860 {"axis", 1, (Blt_Op)Blt_VirtualAxisOp, 2, 0, "oper ?args?",},
1861 {"bar", 2, (Blt_Op)BarOp, 2, 0, "oper ?args?",},
1862 {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
1863 {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
1864 {"crosshairs", 2, (Blt_Op)Blt_CrosshairsOp, 2, 0, "oper ?args?",},
1865 {"element", 2, (Blt_Op)ElementOp, 2, 0, "oper ?args?",},
1866 {"extents", 2, (Blt_Op)ExtentsOp, 3, 3, "item",},
1867 {"grid", 1, (Blt_Op)Blt_GridOp, 2, 0, "oper ?args?",},
1868 {"inside", 3, (Blt_Op)InsideOp, 4, 4, "winX winY",},
1869 {"invtransform", 3, (Blt_Op)InvtransformOp, 4, 4, "winX winY",},
1870 {"legend", 2, (Blt_Op)Blt_LegendOp, 2, 0, "oper ?args?",},
1871 {"line", 2, (Blt_Op)LineOp, 2, 0, "oper ?args?",},
1872 {"marker", 2, (Blt_Op)Blt_MarkerOp, 2, 0, "oper ?args?",},
1873 {"pen", 2, (Blt_Op)Blt_PenOp, 2, 0, "oper ?args?",},
1874 {"postscript", 2, (Blt_Op)Blt_PostScriptOp, 2, 0, "oper ?args?",},
1876 {"print1", 2, (Blt_Op)Print1Op, 2, 3, "?printerName?",},
1877 {"print2", 2, (Blt_Op)Print2Op, 2, 3, "?printerName?",},
1878 #endif /*NO_PRINTER*/
1879 {"snap", 1, (Blt_Op)SnapOp, 3, 0, "?switches? name",},
1880 {"transform", 1, (Blt_Op)TransformOp, 4, 4, "x y",},
1881 {"x2axis", 2, (Blt_Op)X2AxisOp, 2, 0, "oper ?args?",},
1882 {"xaxis", 2, (Blt_Op)XAxisOp, 2, 0, "oper ?args?",},
1883 {"y2axis", 2, (Blt_Op)Y2AxisOp, 2, 0, "oper ?args?",},
1884 {"yaxis", 2, (Blt_Op)YAxisOp, 2, 0, "oper ?args?",},
1886 static int nGraphOps = sizeof(graphOps) / sizeof(Blt_OpSpec);
1889 Blt_GraphInstCmdProc(clientData, interp, argc, argv)
1890 ClientData clientData;
1897 Graph *graphPtr = (Graph *)clientData;
1899 proc = Blt_GetOp(interp, nGraphOps, graphOps, BLT_OP_ARG1, argc, argv, 0);
1903 Tcl_Preserve(graphPtr);
1904 result = (*proc) (graphPtr, interp, argc, argv);
1905 Tcl_Release(graphPtr);
1910 * --------------------------------------------------------------------------
1914 * Creates a new window and Tcl command representing an
1915 * instance of a graph widget.
1918 * A standard Tcl result.
1921 * See the user documentation.
1923 * --------------------------------------------------------------------------
1926 NewGraph(interp, argc, argv, classUid)
1935 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1936 " pathName ?option value?...\"", (char *)NULL);
1939 graphPtr = CreateGraph(interp, argc, argv, classUid);
1940 if (graphPtr == NULL) {
1943 Tcl_SetResult(interp, Tk_PathName(graphPtr->tkwin), TCL_VOLATILE);
1948 * --------------------------------------------------------------------------
1952 * Creates a new window and Tcl command representing an
1953 * instance of a graph widget.
1956 * A standard Tcl result.
1959 * See the user documentation.
1961 * --------------------------------------------------------------------------
1965 GraphCmd(clientData, interp, argc, argv)
1966 ClientData clientData; /* Not used. */
1971 return NewGraph(interp, argc, argv, bltLineElementUid);
1975 *--------------------------------------------------------------
1979 * Creates a new window and Tcl command representing an
1980 * instance of a barchart widget.
1983 * A standard Tcl result.
1986 * See the user documentation.
1988 *--------------------------------------------------------------
1992 BarchartCmd(clientData, interp, argc, argv)
1993 ClientData clientData; /* Not used. */
1998 return NewGraph(interp, argc, argv, bltBarElementUid);
2002 *--------------------------------------------------------------
2006 * Creates a new window and Tcl command representing an
2007 * instance of a barchart widget.
2010 * A standard Tcl result.
2013 * See the user documentation.
2015 *--------------------------------------------------------------
2019 StripchartCmd(clientData, interp, argc, argv)
2020 ClientData clientData; /* Not used. */
2025 return NewGraph(interp, argc, argv, bltStripElementUid);
2029 * -----------------------------------------------------------------------
2033 * Draws the exterior region of the graph (axes, ticks, titles, etc)
2034 * onto a pixmap. The interior region is defined by the given
2035 * rectangle structure.
2037 * ---------------------------------
2041 * ---------------------------------
2052 * ---------------------------------
2056 * ---------------------------------
2063 * titles (X and Y axis, graph)
2069 * Exterior of graph is displayed in its window.
2071 * -----------------------------------------------------------------------
2074 DrawMargins(graphPtr, drawable)
2076 Drawable drawable; /* Pixmap or window to draw into */
2078 XRectangle rects[4];
2080 * Draw the four outer rectangles which encompass the plotting
2081 * surface. This clears the surrounding area and clips the plot.
2083 rects[0].x = rects[0].y = rects[3].x = rects[1].x = 0;
2084 rects[0].width = rects[3].width = (short int)graphPtr->width;
2085 rects[0].height = (short int)graphPtr->top;
2086 rects[3].y = graphPtr->bottom + 1;
2087 rects[3].height = graphPtr->height - graphPtr->bottom;
2088 rects[2].y = rects[1].y = graphPtr->top;
2089 rects[1].width = graphPtr->left;
2090 rects[2].height = rects[1].height = graphPtr->bottom - graphPtr->top + 1;
2091 rects[2].x = graphPtr->right + 1;
2092 rects[2].width = graphPtr->width - graphPtr->right;
2094 if (Blt_HasTile(graphPtr->tile)) {
2095 Blt_SetTileOrigin(graphPtr->tkwin, graphPtr->tile, 0, 0);
2096 Blt_TileRectangles(graphPtr->tkwin, drawable, graphPtr->tile, rects, 4);
2098 XFillRectangles(graphPtr->display, drawable, graphPtr->fillGC, rects,
2102 /* Draw 3D border around the plotting area */
2104 if (graphPtr->plotBorderWidth > 0) {
2105 int x, y, width, height;
2107 x = graphPtr->left - graphPtr->plotBorderWidth;
2108 y = graphPtr->top - graphPtr->plotBorderWidth;
2109 width = (graphPtr->right - graphPtr->left + 1) +
2110 (2 * graphPtr->plotBorderWidth);
2111 height = (graphPtr->bottom - graphPtr->top + 1) +
2112 (2 * graphPtr->plotBorderWidth);
2113 Blt_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border, x, y,
2114 width, height, graphPtr->plotBorderWidth, graphPtr->plotRelief);
2116 if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
2117 /* Legend is drawn on one of the graph margins */
2118 Blt_DrawLegend(graphPtr->legend, drawable);
2120 if (graphPtr->title != NULL) {
2121 Blt_DrawText(graphPtr->tkwin, drawable, graphPtr->title,
2122 &graphPtr->titleTextStyle, graphPtr->titleX, graphPtr->titleY);
2124 Blt_DrawAxes(graphPtr, drawable);
2129 *----------------------------------------------------------------------
2133 * Draws the contents of the plotting area. This consists of
2134 * the elements, markers (draw under elements), axis limits,
2135 * grid lines, and possibly the legend. Typically, the output
2136 * will be cached into a backing store pixmap, so that redraws
2137 * can occur quickly.
2142 *----------------------------------------------------------------------
2145 DrawPlotRegion(graphPtr, drawable)
2147 Drawable drawable; /* Pixmap or window to draw into */
2149 /* Clear the background of the plotting area. */
2150 XFillRectangle(graphPtr->display, drawable, graphPtr->plotFillGC,
2151 graphPtr->left, graphPtr->top, graphPtr->right - graphPtr->left + 1,
2152 graphPtr->bottom - graphPtr->top + 1);
2154 /* Draw the elements, markers, legend, and axis limits. */
2156 if (!graphPtr->gridPtr->hidden) {
2157 Blt_DrawGrid(graphPtr, drawable);
2159 Blt_DrawMarkers(graphPtr, drawable, MARKER_UNDER);
2160 if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
2161 (!Blt_LegendIsRaised(graphPtr->legend))) {
2162 Blt_DrawLegend(graphPtr->legend, drawable);
2164 Blt_DrawAxisLimits(graphPtr, drawable);
2165 Blt_DrawElements(graphPtr, drawable);
2169 Blt_LayoutGraph(graphPtr)
2172 if (graphPtr->flags & RESET_AXES) {
2173 Blt_ResetAxes(graphPtr);
2175 if (graphPtr->flags & LAYOUT_NEEDED) {
2176 Blt_LayoutMargins(graphPtr);
2177 graphPtr->flags &= ~LAYOUT_NEEDED;
2179 /* Compute coordinate transformations for graph components */
2180 if ((graphPtr->vRange > 1) && (graphPtr->hRange > 1)) {
2181 if (graphPtr->flags & MAP_WORLD) {
2182 Blt_MapAxes(graphPtr);
2184 Blt_MapElements(graphPtr);
2185 Blt_MapMarkers(graphPtr);
2186 Blt_MapGrid(graphPtr);
2187 graphPtr->flags &= ~(MAP_ALL);
2192 Blt_DrawGraph(graphPtr, drawable, backingStore)
2194 Drawable drawable; /* Pixmap or window to draw into */
2195 int backingStore; /* If non-zero, use backing store for
2200 * Create another pixmap to save elements if one doesn't
2201 * already exist or the size of the window has changed.
2203 if ((graphPtr->backPixmap == None) ||
2204 (graphPtr->backWidth != graphPtr->width) ||
2205 (graphPtr->backHeight != graphPtr->height)) {
2207 if (graphPtr->backPixmap != None) {
2208 Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
2210 graphPtr->backPixmap = Tk_GetPixmap(graphPtr->display,
2211 Tk_WindowId(graphPtr->tkwin), graphPtr->width,
2212 graphPtr->height, Tk_Depth(graphPtr->tkwin));
2213 graphPtr->backWidth = graphPtr->width;
2214 graphPtr->backHeight = graphPtr->height;
2215 graphPtr->flags |= REDRAW_BACKING_STORE;
2217 if (graphPtr->flags & REDRAW_BACKING_STORE) {
2219 /* The backing store is new or out-of-date. */
2221 DrawPlotRegion(graphPtr, graphPtr->backPixmap);
2222 graphPtr->flags &= ~REDRAW_BACKING_STORE;
2225 /* Copy the pixmap to the one used for drawing the entire graph. */
2227 XCopyArea(graphPtr->display, graphPtr->backPixmap, drawable,
2228 graphPtr->drawGC, graphPtr->left, graphPtr->top,
2229 (graphPtr->right - graphPtr->left + 1),
2230 (graphPtr->bottom - graphPtr->top + 1),
2231 graphPtr->left, graphPtr->top);
2233 DrawPlotRegion(graphPtr, drawable);
2236 /* Draw markers above elements */
2237 Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE);
2238 Blt_DrawActiveElements(graphPtr, drawable);
2240 if (graphPtr->flags & DRAW_MARGINS) {
2241 DrawMargins(graphPtr, drawable);
2243 if (graphPtr->gridPtr->hidden == 0 && graphPtr->gridPtr->raised) {
2244 Blt_DrawGrid(graphPtr, drawable);
2246 if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
2247 (Blt_LegendIsRaised(graphPtr->legend))) {
2248 Blt_DrawLegend(graphPtr->legend, drawable);
2250 /* Draw 3D border just inside of the focus highlight ring. */
2251 if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) {
2252 Blt_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border,
2253 graphPtr->highlightWidth, graphPtr->highlightWidth,
2254 graphPtr->width - 2 * graphPtr->highlightWidth,
2255 graphPtr->height - 2 * graphPtr->highlightWidth,
2256 graphPtr->borderWidth, graphPtr->relief);
2258 /* Draw focus highlight ring. */
2259 if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & GRAPH_FOCUS)) {
2262 gc = Tk_GCForColor(graphPtr->highlightColor, drawable);
2263 Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth,
2269 UpdateMarginTraces(graphPtr)
2275 char *oldVal, *newVal;
2277 for (i = 0; i < 4; i++) {
2278 marginPtr = graphPtr->margins + i;
2279 if (marginPtr->varName != NULL) { /* Trigger variable traces */
2280 if ((marginPtr->site == MARGIN_LEFT) ||
2281 (marginPtr->site == MARGIN_RIGHT)) {
2282 size = marginPtr->width;
2284 size = marginPtr->height;
2286 newVal = Blt_Itoa(size);
2287 oldVal = Tcl_GetVar(graphPtr->interp, marginPtr->varName,
2289 if (oldVal && !strcmp(oldVal,newVal)) continue;
2290 Tcl_SetVar(graphPtr->interp, marginPtr->varName, newVal,
2297 *----------------------------------------------------------------------
2301 * This procedure is invoked to display a graph widget.
2307 * Commands are output to X to display the graph in its
2310 *----------------------------------------------------------------------
2313 DisplayGraph(clientData)
2314 ClientData clientData;
2316 Graph *graphPtr = (Graph *)clientData;
2319 graphPtr->flags &= ~REDRAW_PENDING;
2320 if (graphPtr->tkwin == NULL) {
2321 return; /* Window destroyed (should not get here) */
2324 fprintf(stderr, "Calling DisplayGraph(%s)\n", Tk_PathName(graphPtr->tkwin));
2326 if (Blt_GraphUpdateNeeded(graphPtr)) {
2328 * One of the elements of the graph has a vector notification
2329 * pending. This means that the vector will eventually notify
2330 * the graph that its data has changed. Since the graph uses
2331 * the actual vector (not a copy) we need to keep in-sync.
2332 * Therefore don't draw right now but wait until we've been
2333 * notified before redrawing.
2339 Process the 'redrawcmd' callback as a REDRAW_BACKING_STORE
2340 event had been triggered.
2342 This callback can be used to generate a stream of postcript
2345 if ((graphPtr->flags & REDRAW_BACKING_STORE)
2346 && !(graphPtr->flags & EXEC_REDRAWCMD)
2347 && (graphPtr->redrawCmd != NULL) ) {
2348 Tcl_Interp *interp = graphPtr->interp;
2349 Tk_Window tkwin = graphPtr->tkwin;
2351 graphPtr->flags |= EXEC_REDRAWCMD;
2352 if (Tcl_VarEval(interp, graphPtr->redrawCmd, " ",
2353 Tk_PathName(tkwin), (char *)NULL) != TCL_OK) {
2354 Tcl_BackgroundError(interp);
2355 return; /* Error in after data changed proc */
2360 graphPtr->width = Tk_Width(graphPtr->tkwin);
2361 graphPtr->height = Tk_Height(graphPtr->tkwin);
2362 Blt_LayoutGraph(graphPtr);
2363 Blt_UpdateCrosshairs(graphPtr);
2364 if (!Tk_IsMapped(graphPtr->tkwin)) {
2365 /* The graph's window isn't displayed, so don't bother
2366 * drawing anything. By getting this far, we've at least
2367 * computed the coordinates of the graph's new layout. */
2371 /* Disable crosshairs before redisplaying to the screen */
2372 Blt_DisableCrosshairs(graphPtr);
2374 * Create a pixmap the size of the window for double buffering.
2376 if (graphPtr->doubleBuffer) {
2377 drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
2378 graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
2380 drawable = Tk_WindowId(graphPtr->tkwin);
2383 assert(drawable != None);
2385 Blt_DrawGraph(graphPtr, drawable,
2386 graphPtr->backingStore && graphPtr->doubleBuffer);
2387 if (graphPtr->flags & DRAW_MARGINS) {
2388 XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
2389 graphPtr->drawGC, 0, 0, graphPtr->width, graphPtr->height, 0, 0);
2391 XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
2392 graphPtr->drawGC, graphPtr->left, graphPtr->top,
2393 (graphPtr->right - graphPtr->left + 1),
2394 (graphPtr->bottom - graphPtr->top + 1),
2395 graphPtr->left, graphPtr->top);
2397 if (graphPtr->doubleBuffer) {
2398 Tk_FreePixmap(graphPtr->display, drawable);
2400 graphPtr->flags &= ~EXEC_REDRAWCMD;
2401 Blt_EnableCrosshairs(graphPtr);
2402 graphPtr->flags &= ~RESET_WORLD;
2403 UpdateMarginTraces(graphPtr);
2408 Blt_GraphInit(interp)
2411 static Blt_CmdSpec cmdSpecs[] =
2413 {"graph", GraphCmd,},
2414 {"barchart", BarchartCmd,},
2415 {"stripchart", StripchartCmd,},
2417 bltBarElementUid = (Blt_Uid)Tk_GetUid("BarElement");
2418 bltLineElementUid = (Blt_Uid)Tk_GetUid("LineElement");
2419 bltStripElementUid = (Blt_Uid)Tk_GetUid("StripElement");
2420 bltContourElementUid = (Blt_Uid)Tk_GetUid("ContourElement");
2422 bltLineMarkerUid = (Blt_Uid)Tk_GetUid("LineMarker");
2423 bltBitmapMarkerUid = (Blt_Uid)Tk_GetUid("BitmapMarker");
2424 bltImageMarkerUid = (Blt_Uid)Tk_GetUid("ImageMarker");
2425 bltTextMarkerUid = (Blt_Uid)Tk_GetUid("TextMarker");
2426 bltPolygonMarkerUid = (Blt_Uid)Tk_GetUid("PolygonMarker");
2427 bltWindowMarkerUid = (Blt_Uid)Tk_GetUid("WindowMarker");
2429 bltXAxisUid = (Blt_Uid)Tk_GetUid("X");
2430 bltYAxisUid = (Blt_Uid)Tk_GetUid("Y");
2432 return Blt_InitCmds(interp, "blt", cmdSpecs, 3);
2436 Blt_GetGraphFromWindowData(tkwin)
2441 while (tkwin != NULL) {
2442 graphPtr = (Graph *)Blt_GetWindowInstanceData(tkwin);
2443 if (graphPtr != NULL) {
2446 tkwin = Tk_Parent(tkwin);
2452 Blt_GraphType(graphPtr)
2455 if (graphPtr->classUid == bltLineElementUid) {
2457 } else if (graphPtr->classUid == bltBarElementUid) {
2459 } else if (graphPtr->classUid == bltStripElementUid) {