1 
2 /*
3  * bltGraph.c --
4  *
5  *	This module implements a graph widget for the BLT toolkit.
6  *
7  * Copyright 1991-1998 Lucent Technologies, Inc.
8  *
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.
17  *
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
25  * software.
26  *
27  *	The graph widget was created by Sani Nassif and George Howlett.
28  */
29 
30 /*
31  * To do:
32  *
33  * 2) Update manual pages.
34  *
35  * 3) Update comments.
36  *
37  * 5) Surface, contour, and flow graphs
38  *
39  * 7) Arrows for line markers
40  *
41  */
42 
43 #include "bltGraph.h"
44 #include "bltBind.h"
45 #include "bltGrElem.h"
46 #include "bltSwitch.h"
47 #include <X11/Xutil.h>
48 
49 Blt_Uid bltXAxisUid;
50 Blt_Uid bltYAxisUid;
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;
61 
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;
69 
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
110 
111 static Tk_ConfigSpec configSpecs[] =
112 {
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,
138 	&bltDistanceOption},
139     {TK_CONFIG_STRING, "-bottomvariable", "bottomVariable", "BottomVariable",
140 	DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, bottomMargin.varName),
141 	TK_CONFIG_NULL_OK},
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),
192 	TK_CONFIG_NULL_OK},
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),
222 	TK_CONFIG_NULL_OK},
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),
246 	TK_CONFIG_NULL_OK},
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}
251 };
252 
253 static Blt_SwitchParseProc StringToFormat;
254 static Blt_SwitchCustom formatSwitch =
255 {
256     StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
257 };
258 
259 typedef struct {
260     char *name;
261     int width, height;
262     int format;
263 } SnapData;
264 
265 enum SnapFormats { FORMAT_PHOTO, FORMAT_EMF, FORMAT_WMF };
266 
267 static Blt_SwitchSpec snapSwitches[] =
268 {
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,
272 	&formatSwitch},
273     {BLT_SWITCH_END, NULL, 0, 0}
274 };
275 
276 static Tcl_IdleProc DisplayGraph;
277 static Tcl_FreeProc DestroyGraph;
278 static Tk_EventProc GraphEventProc;
279 Tcl_CmdProc Blt_GraphInstCmdProc;
280 
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;
287 
288 static void widgetWorldChanged(ClientData clientData);
289 
290 static Tk_ClassProcs graphClass = {
291     sizeof(Tk_ClassProcs),	/* size */
292     widgetWorldChanged,		/* worldChangedProc */
293 };
294 
295 
296 /*
297  *--------------------------------------------------------------
298  *
299  * Blt_EventuallyRedrawGraph --
300  *
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.
304  *
305  * Results: None.
306  *
307  * Side effects:
308  *	The window is eventually redisplayed.
309  *
310  *--------------------------------------------------------------
311  */
312 void
Blt_EventuallyRedrawGraph(graphPtr)313 Blt_EventuallyRedrawGraph(graphPtr)
314     Graph *graphPtr;		/* Graph widget record */
315 {
316     if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) {
317 	Tcl_DoWhenIdle(DisplayGraph, graphPtr);
318 	graphPtr->flags |= REDRAW_PENDING;
319     }
320 }
321 
widgetWorldChanged(ClientData clientData)322 static void widgetWorldChanged(ClientData clientData) {
323     Graph *graphPtr = (Graph*)clientData;
324 
325     graphPtr->flags |= (REDRAW_WORLD | RESET_WORLD | REDRAW_BACKING_STORE | RESET_AXES | MAP_WORLD );
326     Blt_ConfigureAxes(graphPtr);
327     Blt_EventuallyRedrawGraph(graphPtr);
328 }
329 
330 /*
331  *--------------------------------------------------------------
332  *
333  * GraphEventProc --
334  *
335  *	This procedure is invoked by the Tk dispatcher for various
336  *	events on graphs.
337  *
338  * Results:
339  *	None.
340  *
341  * Side effects:
342  *	When the window gets deleted, internal structures get
343  *	cleaned up.  When it gets exposed, the graph is eventually
344  *	redisplayed.
345  *
346  *--------------------------------------------------------------
347  */
348 static void
GraphEventProc(clientData,eventPtr)349 GraphEventProc(clientData, eventPtr)
350     ClientData clientData;	/* Graph widget record */
351     register XEvent *eventPtr;	/* Event which triggered call to routine */
352 {
353     Graph *graphPtr = (Graph *)clientData;
354 
355     if (eventPtr->type == Expose) {
356 	if (eventPtr->xexpose.count == 0) {
357 	    graphPtr->flags |= REDRAW_WORLD;
358 	    Blt_EventuallyRedrawGraph(graphPtr);
359 	}
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;
364 	    } else {
365 		graphPtr->flags &= ~GRAPH_FOCUS;
366 	    }
367 	    graphPtr->flags |= REDRAW_WORLD;
368 	    Blt_EventuallyRedrawGraph(graphPtr);
369 	}
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);
376 	}
377 	if (graphPtr->flags & REDRAW_PENDING) {
378 	    Tcl_CancelIdleCall(DisplayGraph, graphPtr);
379 	}
380 	Tcl_EventuallyFree(graphPtr, DestroyGraph);
381     } else if (eventPtr->type == ConfigureNotify) {
382 	graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
383 	Blt_EventuallyRedrawGraph(graphPtr);
384     }
385 }
386 
387 /*
388  *----------------------------------------------------------------------
389  *
390  * GraphInstCmdDeleteProc --
391  *
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.
395  *
396  * Results:
397  *	None.
398  *
399  * Side effects:
400  *	The widget is destroyed.
401  *
402  *---------------------------------------------------------------------- */
403 static void
GraphInstCmdDeleteProc(clientData)404 GraphInstCmdDeleteProc(clientData)
405     ClientData clientData;	/* Pointer to widget record. */
406 {
407     Graph *graphPtr = (Graph *)clientData;
408 
409     if (graphPtr->tkwin != NULL) {	/* NULL indicates window has
410 					 * already been destroyed. */
411 	Tk_Window tkwin;
412 
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);
420     }
421 }
422 
423 /*
424  *----------------------------------------------------------------------
425  *
426  * TileChangedProc
427  *
428  *	Rebuilds the designated GC with the new tile pixmap.
429  *
430  * Results:
431  *	None.
432  *
433  *----------------------------------------------------------------------
434  */
435 /*ARGSUSED*/
436 static void
TileChangedProc(clientData,tile)437 TileChangedProc(clientData, tile)
438     ClientData clientData;
439     Blt_Tile tile;		/* Not used. */
440 {
441     Graph *graphPtr = (Graph *)clientData;
442 
443     if (graphPtr->tkwin != NULL) {
444 	graphPtr->flags |= REDRAW_WORLD;
445 	Blt_EventuallyRedrawGraph(graphPtr);
446     }
447 }
448 
449 /*
450  *--------------------------------------------------------------
451  *
452  * AdjustAxisPointers --
453  *
454  *	Sets the axis pointers according to whether the axis is
455  *	inverted on not.  The axis sites are also reset.
456  *
457  * Results:
458  *	None.
459  *
460  *--------------------------------------------------------------
461  */
462 static void
AdjustAxisPointers(graphPtr)463 AdjustAxisPointers(graphPtr)
464     Graph *graphPtr;		/* Graph widget record */
465 {
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];
471     } else {
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];
476     }
477 }
478 
479 static int
InitPens(graphPtr)480 InitPens(graphPtr)
481     Graph *graphPtr;
482 {
483     Blt_InitHashTable(&(graphPtr->penTable), BLT_STRING_KEYS);
484     if (Blt_CreatePen(graphPtr, "activeLine", bltLineElementUid, 0,
485 	      (char **)NULL) == NULL) {
486   	return TCL_ERROR;
487     }
488     if (Blt_CreatePen(graphPtr, "activeBar", bltBarElementUid, 0,
489 	      (char **)NULL) == NULL) {
490   	return TCL_ERROR;
491     }
492     return TCL_OK;
493 }
494 
495 /*
496  *----------------------------------------------------------------------
497  *
498  * Blt_GraphTags --
499  *
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.
503  *
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.
515  *
516  * Results:
517  *	None.
518  *
519  * Side effects:
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.
524  *
525  *----------------------------------------------------------------------
526  */
527 /*ARGSUSED*/
528 void
Blt_GraphTags(table,object,context,list)529 Blt_GraphTags(table, object, context, list)
530     Blt_BindTable table;
531     ClientData object;
532     ClientData context;		/* Not used. */
533     Blt_List list;
534 {
535     Element *elemPtr;
536     MakeTagProc *tagProc;
537     Graph *graphPtr;
538 
539     graphPtr = (Graph *)Blt_GetBindingData(table);
540     /*
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
545      *		object to.
546      */
547     elemPtr = (Element *)object;
548 
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;
556     } else {
557 	tagProc = Blt_MakeMarkerTag;
558     }
559     /*
560      * Always add the name of the object to the tag array.
561      */
562     Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->name), 0);
563     Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->classUid), 0);
564     if (elemPtr->tags != NULL) {
565 	register char **p;
566 
567 	for (p = elemPtr->tags; *p != NULL; p++) {
568 	    Blt_ListAppend(list, (*tagProc) (graphPtr, *p), 0);
569 	}
570     }
571 }
572 
573 /*
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.
578  */
579 /*ARGSUSED*/
580 static ClientData
PickEntry(clientData,x,y,contextPtr)581 PickEntry(clientData, x, y, contextPtr)
582     ClientData clientData;
583     int x, y;
584     ClientData *contextPtr;	/* Not used. */
585 {
586     Graph *graphPtr = (Graph *)clientData;
587     Blt_ChainLink *linkPtr;
588     Element *elemPtr;
589     Marker *markerPtr;
590     Extents2D exts;
591 
592     if (graphPtr->flags & MAP_ALL) {
593 	return NULL;		/* Can't pick anything until the next
594 				 * redraw occurs. */
595     }
596     Blt_GraphExtents(graphPtr, &exts);
597 
598     if ((x > exts.right) || (x < exts.left) || (y > exts.bottom) ||
599 	(y < exts.top)) {
600 	/*
601 	 * Sample coordinate is in one of the graph margins.  Can only
602 	 * pick an axis.
603 	 */
604 	return (int *)Blt_NearestAxis(graphPtr, x, y);
605     }
606 
607     /*
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).
612      */
613     markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, FALSE);
614     if (markerPtr != NULL) {
615 	return (int *)markerPtr;	/* Found a marker (-under false). */
616     }
617     {
618 	ClosestSearch search;
619 
620 	search.along = SEARCH_BOTH;
621 	search.halo = graphPtr->halo + 1;
622 	search.index = -1;
623 	search.x = x;
624 	search.y = y;
625 	search.dist = (double)(search.halo + 1);
626 	search.mode = SEARCH_AUTO;
627 
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))) {
634 		continue;
635 	    }
636 	    if ((!elemPtr->hidden) && (elemPtr->state == STATE_NORMAL)) {
637 		(*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
638 	    }
639 	}
640 	if (search.dist <= (double)search.halo) {
641 	    return (int *)search.elemPtr;	/* Found an element within the
642 					 * minimum halo distance. */
643 	}
644     }
645     markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, TRUE);
646     if (markerPtr != NULL) {
647 	return (int *)markerPtr;	/* Found a marker (-under true) */
648     }
649     return NULL;		/* Nothing found. */
650 }
651 
652 /*
653  *----------------------------------------------------------------------
654  *
655  * ConfigureGraph --
656  *
657  *	Allocates resources for the graph.
658  *
659  * Results:
660  *	None.
661  *
662  * Side effects:
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.
666  *
667  *----------------------------------------------------------------------
668  */
669 static void
ConfigureGraph(graphPtr)670 ConfigureGraph(graphPtr)
671     Graph *graphPtr;		/* Graph widget record */
672 {
673     XColor *colorPtr;
674     GC newGC;
675     XGCValues gcValues;
676     unsigned long gcMask;
677     Tcl_Interp *interp = graphPtr->interp;
678 
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;
682     }
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);
688     }
689     Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
690     colorPtr = Tk_3DBorderColor(graphPtr->border);
691 
692     if (graphPtr->title != NULL) {
693 	int w, h;
694 
695 	Blt_GetTextExtents(&graphPtr->titleTextStyle, graphPtr->title, &w, &h);
696 	graphPtr->titleTextStyle.height = h + 10;
697     } else {
698 	graphPtr->titleTextStyle.width = graphPtr->titleTextStyle.height = 0;
699     }
700 
701     /*
702      * Create GCs for interior and exterior regions, and a background
703      * GC for clearing the margins with XFillRectangle
704      */
705 
706     /* Margin GC */
707 
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);
714     }
715     graphPtr->drawGC = newGC;
716 
717     /* Plot fill GC (Background = Foreground) */
718 
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);
723     }
724     graphPtr->plotFillGC = newGC;
725 
726     /* Margin fill GC (Background = Foreground) */
727 
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);
733     }
734     graphPtr->fillGC = newGC;
735     if (graphPtr->tile != NULL) {
736 	Blt_SetTileChangedProc(graphPtr->tile, TileChangedProc, graphPtr);
737     }
738 
739     Blt_ResetTextStyle(graphPtr->tkwin, &graphPtr->titleTextStyle);
740 
741     if (Blt_ConfigModified(configSpecs, interp, "-invertxy", (char *)NULL)) {
742 
743 	/*
744 	 * If the -inverted option changed, we need to readjust the pointers
745 	 * to the axes and recompute the their scales.
746 	 */
747 
748 	AdjustAxisPointers(graphPtr);
749 	graphPtr->flags |= RESET_AXES;
750     }
751     if ((!graphPtr->backingStore) && (graphPtr->backPixmap != None)) {
752 
753 	/*
754 	 * Free the pixmap if we're not buffering the display of elements
755 	 * anymore.
756 	 */
757 
758 	Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
759 	graphPtr->backPixmap = None;
760     }
761     /*
762      * Reconfigure the crosshairs, just in case the background color of
763      * the plotarea has been changed.
764      */
765     Blt_ConfigureCrosshairs(graphPtr);
766 
767     /*
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.
771      *
772      *	    -aspect
773      *      -borderwidth, -plotborderwidth
774      *	    -font, -title
775      *	    -width, -height
776      *	    -invertxy
777      *	    -bottommargin, -leftmargin, -rightmargin, -topmargin,
778      *	    -barmode, -barwidth
779      */
780     if (Blt_ConfigModified(configSpecs, interp, "-invertxy", "-title", "-font",
781 	    "-*margin", "-*width", "-height", "-barmode", "-*pad*", "-aspect",
782 	    (char *)NULL)) {
783 	graphPtr->flags |= RESET_WORLD;
784     }
785     if (Blt_ConfigModified(configSpecs, interp, "-plotbackground", (char *)NULL)) {
786 	graphPtr->flags |= REDRAW_BACKING_STORE;
787     }
788     graphPtr->flags |= REDRAW_WORLD;
789     Blt_EventuallyRedrawGraph(graphPtr);
790 }
791 
792 /*
793  *----------------------------------------------------------------------
794  *
795  * DestroyGraph --
796  *
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).
800  *
801  * Results:
802  *	None.
803  *
804  * Side effects:
805  *	Everything associated with the widget is freed up.
806  *
807  *----------------------------------------------------------------------
808  */
809 static void
DestroyGraph(dataPtr)810 DestroyGraph(dataPtr)
811     DestroyData dataPtr;
812 {
813     Graph *graphPtr = (Graph *)dataPtr;
814 
815     Tk_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display, 0);
816     /*
817      * Destroy the individual components of the graph: elements, markers,
818      * X and Y axes, legend, display lists etc.
819      */
820     Blt_DestroyMarkers(graphPtr);
821     Blt_DestroyElements(graphPtr);
822 
823     Blt_DestroyAxes(graphPtr);
824     Blt_DestroyPens(graphPtr);
825 
826     if (graphPtr->legend != NULL) {
827 	Blt_DestroyLegend(graphPtr);
828     }
829     if (graphPtr->postscript != NULL) {
830 	Blt_DestroyPostScript(graphPtr);
831     }
832     if (graphPtr->crosshairs != NULL) {
833 	Blt_DestroyCrosshairs(graphPtr);
834     }
835     if (graphPtr->gridPtr != NULL) {
836 	Blt_DestroyGrid(graphPtr);
837     }
838     if (graphPtr->bindTable != NULL) {
839 	Blt_DestroyBindingTable(graphPtr->bindTable);
840     }
841 
842     /* Release allocated X resources and memory. */
843     if (graphPtr->drawGC != NULL) {
844 	Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
845     }
846     if (graphPtr->fillGC != NULL) {
847 	Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
848     }
849     if (graphPtr->plotFillGC != NULL) {
850 	Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
851     }
852     Blt_FreeTextStyle(graphPtr->display, &graphPtr->titleTextStyle);
853     if (graphPtr->backPixmap != None) {
854 	Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
855     }
856     if (graphPtr->freqArr != NULL) {
857 	Blt_Free(graphPtr->freqArr);
858     }
859     if (graphPtr->nStacks > 0) {
860 	Blt_DeleteHashTable(&graphPtr->freqTable);
861     }
862     if (graphPtr->tile != NULL) {
863 	Blt_FreeTile(graphPtr->tile);
864     }
865     Blt_Free(graphPtr);
866 }
867 
868 /*
869  *----------------------------------------------------------------------
870  *
871  * CreateGraph --
872  *
873  *	This procedure creates and initializes a new widget.
874  *
875  * Results:
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.
879  *
880  * Side effects:
881  *	Memory is allocated, a Tk_Window is created, etc.
882  *
883  *----------------------------------------------------------------------
884  */
885 
886 static Graph *
CreateGraph(interp,argc,argv,classUid)887 CreateGraph(interp, argc, argv, classUid)
888     Tcl_Interp *interp;
889     int argc;
890     CONST char **argv;
891     Blt_Uid classUid;
892 {
893     Graph *graphPtr;
894     Tk_Window tkwin;
895 
896     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
897 	    (char *)NULL);
898     if (tkwin == NULL) {
899 	return NULL;
900     }
901     graphPtr = Blt_Calloc(1, sizeof(Graph));
902     assert(graphPtr);
903 
904     /* Initialize the graph data structure. */
905 
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);
924 
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();
934 
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");
941     }
942     Blt_SetWindowInstanceData(tkwin, graphPtr);
943 
944     if (InitPens(graphPtr) != TCL_OK) {
945 	goto error;
946     }
947     if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc - 2, argv + 2,
948 	    (char *)graphPtr, 0) != TCL_OK) {
949 	goto error;
950     }
951     if (Blt_DefaultAxes(graphPtr) != TCL_OK) {
952 	goto error;
953     }
954     AdjustAxisPointers(graphPtr);
955 
956     if (Blt_CreatePostScript(graphPtr) != TCL_OK) {
957 	goto error;
958     }
959     if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
960 	goto error;
961     }
962     if (Blt_CreateLegend(graphPtr) != TCL_OK) {
963 	goto error;
964     }
965     if (Blt_CreateGrid(graphPtr) != TCL_OK) {
966 	goto error;
967     }
968     Tk_CreateEventHandler(graphPtr->tkwin,
969 	ExposureMask | StructureNotifyMask | FocusChangeMask, GraphEventProc,
970 	graphPtr);
971 
972     graphPtr->cmdToken = Tcl_CreateCommand(interp, argv[1],
973 	Blt_GraphInstCmdProc, graphPtr, GraphInstCmdDeleteProc);
974 #ifdef ITCL_NAMESPACES
975     Itk_SetWidgetCommand(graphPtr->tkwin, graphPtr->cmdToken);
976 #endif
977     ConfigureGraph(graphPtr);
978     graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr,
979 	PickEntry, Blt_GraphTags);
980     Tk_SetClassProcs(tkwin, &graphClass, (ClientData)graphPtr);
981 
982     return graphPtr;
983 
984  error:
985     DestroyGraph((DestroyData)graphPtr);
986     return NULL;
987 }
988 
989 /* Widget sub-commands */
990 
991 /*ARGSUSED*/
992 static int
XAxisOp(graphPtr,interp,argc,argv)993 XAxisOp(graphPtr, interp, argc, argv)
994     Graph *graphPtr;
995     Tcl_Interp *interp;		/* Not used. */
996     int argc;
997     char **argv;
998 {
999     int margin;
1000 
1001     margin = (graphPtr->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM;
1002     return Blt_AxisOp(graphPtr, margin, argc, argv);
1003 }
1004 
1005 /*ARGSUSED*/
1006 static int
X2AxisOp(graphPtr,interp,argc,argv)1007 X2AxisOp(graphPtr, interp, argc, argv)
1008     Graph *graphPtr;
1009     Tcl_Interp *interp;		/* Not used. */
1010     int argc;
1011     char **argv;
1012 {
1013     int margin;
1014 
1015     margin = (graphPtr->inverted) ? MARGIN_RIGHT : MARGIN_TOP;
1016     return Blt_AxisOp(graphPtr, margin, argc, argv);
1017 }
1018 
1019 /*ARGSUSED*/
1020 static int
YAxisOp(graphPtr,interp,argc,argv)1021 YAxisOp(graphPtr, interp, argc, argv)
1022     Graph *graphPtr;
1023     Tcl_Interp *interp;		/* Not used. */
1024     int argc;
1025     char **argv;
1026 {
1027     int margin;
1028 
1029     margin = (graphPtr->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT;
1030     return Blt_AxisOp(graphPtr, margin, argc, argv);
1031 }
1032 
1033 /*ARGSUSED*/
1034 static int
Y2AxisOp(graphPtr,interp,argc,argv)1035 Y2AxisOp(graphPtr, interp, argc, argv)
1036     Graph *graphPtr;
1037     Tcl_Interp *interp;		/* Not used. */
1038     int argc;
1039     char **argv;
1040 {
1041     int margin;
1042 
1043     margin = (graphPtr->inverted) ? MARGIN_TOP : MARGIN_RIGHT;
1044     return Blt_AxisOp(graphPtr, margin, argc, argv);
1045 }
1046 
1047 /*ARGSUSED*/
1048 static int
BarOp(graphPtr,interp,argc,argv)1049 BarOp(graphPtr, interp, argc, argv)
1050     Graph *graphPtr;
1051     Tcl_Interp *interp;		/* Not used. */
1052     int argc;
1053     char **argv;
1054 {
1055     return Blt_ElementOp(graphPtr, interp, argc, argv, bltBarElementUid);
1056 }
1057 
1058 /*ARGSUSED*/
1059 static int
LineOp(graphPtr,interp,argc,argv)1060 LineOp(graphPtr, interp, argc, argv)
1061     Graph *graphPtr;
1062     Tcl_Interp *interp;		/* Not used. */
1063     int argc;
1064     char **argv;
1065 {
1066     return Blt_ElementOp(graphPtr, interp, argc, argv, bltLineElementUid);
1067 }
1068 
1069 /*ARGSUSED*/
1070 static int
ElementOp(graphPtr,interp,argc,argv)1071 ElementOp(graphPtr, interp, argc, argv)
1072     Graph *graphPtr;
1073     Tcl_Interp *interp;		/* Not used. */
1074     int argc;
1075     char **argv;
1076 {
1077     return Blt_ElementOp(graphPtr, interp, argc, argv, graphPtr->classUid);
1078 }
1079 
1080 static int
ConfigureOp(graphPtr,interp,argc,argv)1081 ConfigureOp(graphPtr, interp, argc, argv)
1082     Graph *graphPtr;
1083     Tcl_Interp *interp;
1084     int argc;
1085     CONST char **argv;
1086 {
1087     int flags;
1088 
1089     flags = TK_CONFIG_ARGV_ONLY;
1090     if (argc == 2) {
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);
1096     } else {
1097 	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 2,
1098 		argv + 2, (char *)graphPtr, flags) != TCL_OK) {
1099 	    return TCL_ERROR;
1100 	}
1101 	ConfigureGraph(graphPtr);
1102 	return TCL_OK;
1103     }
1104 }
1105 
1106 /* ARGSUSED*/
1107 static int
CgetOp(graphPtr,interp,argc,argv)1108 CgetOp(graphPtr, interp, argc, argv)
1109     Graph *graphPtr;
1110     Tcl_Interp *interp;
1111     int argc;			/* Not used. */
1112     char **argv;
1113 {
1114     return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
1115 	(char *)graphPtr, argv[2], 0);
1116 }
1117 
1118 /*
1119  *--------------------------------------------------------------
1120  *
1121  * ExtentsOp --
1122  *
1123  *	Reports the size of one of several items within the graph.
1124  *	The following are valid items:
1125  *
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
1134  *
1135  * Results:
1136  *	Always returns TCL_OK.
1137  *
1138  *--------------------------------------------------------------
1139  */
1140 /* ARGSUSED*/
1141 static int
ExtentsOp(graphPtr,interp,argc,argv)1142 ExtentsOp(graphPtr, interp, argc, argv)
1143     Graph *graphPtr;
1144     Tcl_Interp *interp;
1145     int argc;			/* Not used. */
1146     char **argv;
1147 {
1148     char c;
1149     unsigned int length;
1150     char string[200];
1151 
1152     c = argv[2][0];
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),
1157 	    TCL_VOLATILE);
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),
1161 	    TCL_VOLATILE);
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),
1178 		      TCL_VOLATILE);
1179     } else if ((c == 'r') && (length > 1) &&
1180 	(strncmp("rightmargin", argv[2], length) == 0)) {
1181 	Tcl_SetResult(interp, Blt_Itoa(graphPtr->rightMargin.width),
1182 		      TCL_VOLATILE);
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),
1189 		      TCL_VOLATILE);
1190     } else {
1191 	Tcl_AppendResult(interp, "bad extent item \"", argv[2],
1192 	    "\": should be plotheight, plotwidth, leftmargin, rightmargin, \
1193 topmargin, bottommargin, plotarea, or legend", (char *)NULL);
1194 	return TCL_ERROR;
1195     }
1196     return TCL_OK;
1197 }
1198 
1199 /*
1200  *--------------------------------------------------------------
1201  *
1202  * InsideOp --
1203  *
1204  *	Returns true of false whether the given point is inside
1205  *	the plotting area (defined by left,bottom right, top).
1206  *
1207  * Results:
1208  *	Always returns TCL_OK.  interp->result will contain
1209  *	the boolean string representation.
1210  *
1211  *--------------------------------------------------------------
1212  */
1213 /* ARGSUSED*/
1214 static int
InsideOp(graphPtr,interp,argc,argv)1215 InsideOp(graphPtr, interp, argc, argv)
1216     Graph *graphPtr;
1217     Tcl_Interp *interp;
1218     int argc;			/* Not used. */
1219     char **argv;
1220 {
1221     int x, y;
1222     Extents2D exts;
1223     int result;
1224 
1225     if (Tk_GetPixels(interp, graphPtr->tkwin, argv[2], &x) != TCL_OK) {
1226 	return TCL_ERROR;
1227     }
1228     if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &y) != TCL_OK) {
1229 	return TCL_ERROR;
1230     }
1231     Blt_GraphExtents(graphPtr, &exts);
1232     result = PointInRegion(&exts, x, y);
1233     Blt_SetBooleanResult(interp, result);
1234     return TCL_OK;
1235 }
1236 
1237 /*
1238  * -------------------------------------------------------------------------
1239  *
1240  * InvtransformOp --
1241  *
1242  *	This procedure returns a list of the graph coordinate
1243  *	values corresponding with the given window X and Y
1244  *	coordinate positions.
1245  *
1246  * Results:
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
1251  *	coordinates.
1252  *
1253  * ------------------------------------------------------------------------
1254  */
1255 /*ARGSUSED*/
1256 static int
InvtransformOp(graphPtr,interp,argc,argv)1257 InvtransformOp(graphPtr, interp, argc, argv)
1258     Graph *graphPtr;		/* Graph widget record */
1259     Tcl_Interp *interp;
1260     int argc;			/* Not used. */
1261     char **argv;
1262 {
1263     double x, y;
1264     Point2D point;
1265     Axis2D axes;
1266 
1267     if (Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK ||
1268 	Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK) {
1269 	return TCL_ERROR;
1270     }
1271     if (graphPtr->flags & RESET_AXES) {
1272 	Blt_ResetAxes(graphPtr);
1273     }
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).  */
1278 
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);
1283 
1284     Tcl_AppendElement(interp, Blt_Dtoa(interp, point.x));
1285     Tcl_AppendElement(interp, Blt_Dtoa(interp, point.y));
1286     return TCL_OK;
1287 }
1288 
1289 /*
1290  * --------------------------------------------------------------------------
1291  *
1292  * TransformOp --
1293  *
1294  *	This procedure returns a list of the window coordinates
1295  *	corresponding with the given graph x and y coordinates.
1296  *
1297  * Results:
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.
1302  *
1303  * -------------------------------------------------------------------------
1304  */
1305 /*ARGSUSED*/
1306 static int
TransformOp(graphPtr,interp,argc,argv)1307 TransformOp(graphPtr, interp, argc, argv)
1308     Graph *graphPtr;		/* Graph widget record */
1309     Tcl_Interp *interp;
1310     int argc;			/* Not used. */
1311     char **argv;
1312 {
1313     double x, y;
1314     Point2D point;
1315     Axis2D axes;
1316 
1317     if ((Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK) ||
1318 	(Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK)) {
1319 	return TCL_ERROR;
1320     }
1321     if (graphPtr->flags & RESET_AXES) {
1322 	Blt_ResetAxes(graphPtr);
1323     }
1324     /*
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).
1328      */
1329     axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
1330     axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
1331 
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)));
1335     return TCL_OK;
1336 }
1337 
1338 #ifndef NO_PRINTER
1339 
1340 /*
1341  * --------------------------------------------------------------------------
1342  *
1343  * Print1Op --
1344  *
1345  *	Prints the equivalent of a screen snapshot of the graph
1346  *	to the designated printer.
1347  *
1348  * Results:
1349  *	Returns a standard Tcl result.  If an error occurred
1350  *	TCL_ERROR is returned and interp->result will contain an
1351  *	error message.
1352  *
1353  * -------------------------------------------------------------------------
1354  */
1355 /*ARGSUSED*/
1356 static int
Print1Op(graphPtr,interp,argc,argv)1357 Print1Op(graphPtr, interp, argc, argv)
1358     Graph *graphPtr;		/* Graph widget record */
1359     Tcl_Interp *interp;
1360     int argc;			/* Not used. */
1361     char **argv;
1362 {
1363     int noBackingStore = 0;
1364     BITMAPINFO info;
1365     void *data;
1366     TkWinDCState state;
1367     TkWinBitmap bd;
1368     DIBSECTION ds;
1369     Drawable drawable;
1370     HBITMAP hBitmap;
1371     HDC hDC;
1372     DOCINFO di;
1373     double pageWidth, pageHeight;
1374     int result;
1375     double scale, sx, sy;
1376     int jobId;
1377 
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;
1382     }
1383     if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
1384 	graphPtr->height = graphPtr->reqHeight;
1385     }
1386     if (argc == 2) {
1387 	result = Blt_PrintDialog(interp, &drawable);
1388 	if (result == TCL_ERROR) {
1389 	    return TCL_ERROR;
1390 	}
1391 	if (result == TCL_RETURN) {
1392 	    return TCL_OK;
1393 	}
1394     } else {
1395 	if (Blt_GetOpenPrinter(interp, argv[2], &drawable) != TCL_OK) {
1396 	    return TCL_ERROR;
1397 	}
1398     }
1399     /*
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.
1403      */
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),
1412 		&state);
1413     hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
1414     TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hDC, &state);
1415 
1416     /*
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.
1419      */
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);
1425 
1426     graphPtr->flags |= RESET_WORLD;
1427     Blt_DrawGraph(graphPtr, (Drawable)&bd, noBackingStore);
1428 
1429     /*
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.
1433      */
1434     result = TCL_ERROR;
1435     if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
1436 	Tcl_AppendResult(interp, "can't get object: ", Blt_LastError(),
1437 	    (char *)NULL);
1438 	goto done;
1439     }
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;
1447 
1448     ZeroMemory(&di, sizeof(di));
1449     di.cbSize = sizeof(di);
1450     di.lpszDocName = "Graph Contents";
1451     jobId = StartDoc(hDC, &di);
1452     if (jobId <= 0) {
1453 	Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(),
1454 	    (char *)NULL);
1455 	goto done;
1456     }
1457     if (StartPage(hDC) <= 0) {
1458 	Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(),
1459 	    (char *)NULL);
1460 	goto done;
1461     }
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);
1465     EndPage(hDC);
1466     EndDoc(hDC);
1467     result = TCL_OK;
1468   done:
1469     DeleteBitmap(hBitmap);
1470     return result;
1471 }
1472 
1473 /*
1474  * --------------------------------------------------------------------------
1475  *
1476  * Print2Op --
1477  *
1478  *	Prints directly to the designated printer device.
1479  *
1480  * Results:
1481  *	Returns a standard Tcl result.  If an error occurred,
1482  *	TCL_ERROR is returned and interp->result will contain an
1483  *	error message.
1484  *
1485  * -------------------------------------------------------------------------
1486  */
1487 /*ARGSUSED*/
1488 static int
Print2Op(graphPtr,interp,argc,argv)1489 Print2Op(graphPtr, interp, argc, argv)
1490     Graph *graphPtr;		/* Graph widget record */
1491     Tcl_Interp *interp;
1492     int argc;			/* Not used. */
1493     char **argv;
1494 {
1495     Drawable drawable;
1496     int noBackingStore = 0;
1497     int result;
1498 
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;
1503     }
1504     if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
1505 	graphPtr->height = graphPtr->reqHeight;
1506     }
1507     if (argc == 2) {
1508 	result = Blt_PrintDialog(interp, &drawable);
1509 	if (result == TCL_ERROR) {
1510 	    return TCL_ERROR;
1511 	}
1512 	if (result == TCL_RETURN) {
1513 	    return TCL_OK;
1514 	}
1515     } else {
1516 	result = Blt_GetOpenPrinter(interp, argv[2], &drawable);
1517     }
1518     if (result == TCL_OK) {
1519 	int oldMode;
1520 	HDC hDC;
1521 	double xRatio, yRatio;
1522 	TkWinDC *drawPtr;
1523 	double vportWidth, vportHeight;
1524 
1525 	drawPtr = (TkWinDC *) drawable;
1526 	hDC = drawPtr->hdc;
1527 	Blt_GetPrinterScale(hDC, &xRatio, &yRatio);
1528 	oldMode = SetMapMode(hDC, MM_ISOTROPIC);
1529 	if (oldMode == 0) {
1530 	    Tcl_AppendResult(interp, "can't set mode for printer DC: ",
1531 		Blt_LastError(), (char *)NULL);
1532 	    return TCL_ERROR;
1533 	}
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);
1538 
1539 	Blt_StartPrintJob(interp, drawable);
1540 	graphPtr->flags |= RESET_WORLD;
1541 	Blt_DrawGraph(graphPtr, drawable, noBackingStore);
1542 	Blt_EndPrintJob(interp, drawable);
1543     }
1544     return result;
1545 }
1546 
1547 #endif /* NO_PRINTER */
1548 
1549 /*
1550  *----------------------------------------------------------------------
1551  *
1552  * StringToFormat --
1553  *
1554  *	Convert a string represent a node number into its integer
1555  *	value.
1556  *
1557  * Results:
1558  *	The return value is a standard Tcl result.
1559  *
1560  *----------------------------------------------------------------------
1561  */
1562 /*ARGSUSED*/
1563 static int
StringToFormat(clientData,interp,switchName,string,record,offset)1564 StringToFormat(clientData, interp, switchName, string, record, offset)
1565     ClientData clientData;	/* Contains a pointer to the tabset containing
1566 				 * this image. */
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 */
1572 {
1573     int *formatPtr = (int *)(record + offset);
1574     char c;
1575 
1576     c = string[0];
1577     if ((c == 'p') && (strcmp(string, "photo") == 0)) {
1578 	*formatPtr = FORMAT_PHOTO;
1579 #ifdef WIN32
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;
1584 #endif /* WIN32 */
1585     } else {
1586 #ifdef WIN32
1587 	Tcl_AppendResult(interp, "bad format \"", string,
1588 		 "\": should be photo, emf, or wmf.", (char *)NULL);
1589 #else
1590 	Tcl_AppendResult(interp, "bad format \"", string,
1591 		 "\": should be photo.", (char *)NULL);
1592 #endif /* WIN32 */
1593 	return TCL_ERROR;
1594     }
1595     return TCL_OK;
1596 }
1597 
1598 #ifdef WIN32
InitMetaFileHeader(Tk_Window tkwin,int width,int height,APMHEADER * mfhPtr)1599 static int InitMetaFileHeader(
1600     Tk_Window tkwin,
1601     int width, int height,
1602     APMHEADER *mfhPtr)
1603 {
1604     unsigned int *p;
1605     unsigned int sum;
1606     Screen *screen;
1607 #define MM_INCH		25.4
1608     double dpiX, dpiY;
1609 
1610     mfhPtr->key = 0x9ac6cdd7L;
1611     mfhPtr->hmf = 0;
1612     mfhPtr->inch = 1440;
1613 
1614     screen = Tk_Screen(tkwin);
1615     dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
1616     dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
1617 
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;
1622     sum = 0;
1623     for (p = (unsigned int *)mfhPtr;
1624 	 p < (unsigned int *)&(mfhPtr->checksum); p++) {
1625 	sum ^= *p;
1626     }
1627     mfhPtr->checksum = sum;
1628     return TCL_OK;
1629 }
1630 
1631 static int
CreateAPMetaFile(Tcl_Interp * interp,HANDLE hMetaFile,HDC hDC,APMHEADER * mfhPtr,char * fileName)1632 CreateAPMetaFile(
1633     Tcl_Interp *interp,
1634     HANDLE hMetaFile,
1635     HDC hDC,
1636     APMHEADER *mfhPtr,
1637     char *fileName)
1638 {
1639     HANDLE hFile;
1640     HANDLE hMem;
1641     LPVOID buffer;
1642     int result;
1643     DWORD count, nBytes;
1644 
1645     result = TCL_ERROR;
1646     hMem = NULL;
1647     hFile = CreateFile(
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);
1658 	return TCL_ERROR;
1659     }
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);
1664 	goto error;
1665     }
1666     nBytes = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hDC);
1667     hMem = GlobalAlloc(GHND, nBytes);
1668     if (hMem == NULL) {
1669 	Tcl_AppendResult(interp, "can't create allocate global memory:",
1670 		Blt_LastError(), (char *)NULL);
1671 	goto error;
1672     }
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);
1677 	goto error;
1678     }
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);
1683 	goto error;
1684     }
1685     result = TCL_OK;
1686  error:
1687     CloseHandle(hFile);
1688     if (hMem != NULL) {
1689 	GlobalUnlock(hMem);
1690 	GlobalFree(hMem);
1691     }
1692     return result;
1693 }
1694 #endif /*WIN32*/
1695 
1696 /*
1697  * --------------------------------------------------------------------------
1698  *
1699  * SnapOp --
1700  *
1701  *	Snaps a picture of the graph and stores it in the specified image
1702  *
1703  * Results:
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.
1708  *
1709  * -------------------------------------------------------------------------
1710  */
1711 /*ARGSUSED*/
1712 static int
SnapOp(graphPtr,interp,argc,argv)1713 SnapOp(graphPtr, interp, argc, argv)
1714     Graph *graphPtr;		/* Graph widget record */
1715     Tcl_Interp *interp;
1716     int argc;			/* Not used. */
1717     char **argv;
1718 {
1719     int result;
1720     Pixmap drawable;
1721     int noBackingStore = 0;
1722     register int i;
1723     SnapData data;
1724 
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);
1732     if (i < 0) {
1733 	return TCL_ERROR;
1734     }
1735     i += 2;
1736     if (i >= argc) {
1737 	Tcl_AppendResult(interp, "missing name argument: should be \"",
1738 		 argv[0], "snap ?switches? name\"", (char *)NULL);
1739 	return TCL_ERROR;
1740     }
1741     data.name = argv[i];
1742     if (data.width < 2) {
1743 	data.width = 400;
1744     }
1745     if (data.height < 2) {
1746 	data.height = 400;
1747     }
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);
1752 
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));
1757 #ifdef WIN32
1758 	assert(drawable != None);
1759 #endif
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);
1765 #ifdef WIN32
1766     } else if ((data.format == FORMAT_WMF) || (data.format == FORMAT_EMF)) {
1767 	TkWinDC drawableDC;
1768 	TkWinDCState state;
1769 	HDC hRefDC, hDC;
1770 	HENHMETAFILE hMetaFile;
1771 	Tcl_DString dString;
1772 	char *title;
1773 
1774 	hRefDC = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
1775 
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);
1785 
1786 	if (hDC == NULL) {
1787 	    Tcl_AppendResult(interp, "can't create metafile: ",
1788 		     Blt_LastError(), (char *)NULL);
1789 	    return TCL_ERROR;
1790 	}
1791 
1792 	drawableDC.hdc = hDC;
1793 	drawableDC.type = TWD_WINDC;
1794 
1795 	Blt_LayoutGraph(graphPtr);
1796 	graphPtr->flags |= RESET_WORLD;
1797 	Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
1798 
1799 	hMetaFile = CloseEnhMetaFile(hDC);
1800 	if (strcmp(data.name, "CLIPBOARD") == 0) {
1801 	    HWND hWnd;
1802 
1803 	    hWnd = Tk_GetHWND(drawable);
1804 	    OpenClipboard(hWnd);
1805 	    EmptyClipboard();
1806 	    SetClipboardData(CF_ENHMETAFILE, hMetaFile);
1807 	    CloseClipboard();
1808 	    result = TCL_OK;
1809 	} else {
1810 	    result = TCL_ERROR;
1811 	    if (data.format == FORMAT_WMF) {
1812 		APMHEADER mfh;
1813 
1814 		assert(sizeof(mfh) == 22);
1815 		InitMetaFileHeader(graphPtr->tkwin, data.width, data.height,
1816 			&mfh);
1817 		result = CreateAPMetaFile(interp, hMetaFile, hRefDC, &mfh,
1818 			data.name);
1819 	    } else {
1820 		HENHMETAFILE hMetaFile2;
1821 
1822 		hMetaFile2 = CopyEnhMetaFile(hMetaFile, data.name);
1823 		if (hMetaFile2 != NULL) {
1824 		    result = TCL_OK;
1825 		    DeleteEnhMetaFile(hMetaFile2);
1826 		}
1827 	    }
1828 	    DeleteEnhMetaFile(hMetaFile);
1829 	}
1830 	TkWinReleaseDrawableDC(drawable, hRefDC, &state);
1831 #endif /*WIN32*/
1832     } else {
1833 	Tcl_AppendResult(interp, "bad snapshot format", (char *)NULL);
1834 	return TCL_ERROR;
1835     }
1836     graphPtr->flags = MAP_WORLD;
1837     Blt_EventuallyRedrawGraph(graphPtr);
1838     return result;
1839 }
1840 
1841 /*
1842  * --------------------------------------------------------------------------
1843  *
1844  * GraphWidgetCmd --
1845  *
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.
1849  *
1850  * Results:
1851  *	A standard Tcl result.
1852  *
1853  * Side effects:
1854  *	See the user documentation.
1855  *
1856  * --------------------------------------------------------------------------
1857  */
1858 static Blt_OpSpec graphOps[] =
1859 {
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?",},
1875 #ifndef NO_PRINTER
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?",},
1885 };
1886 static int nGraphOps = sizeof(graphOps) / sizeof(Blt_OpSpec);
1887 
1888 int
Blt_GraphInstCmdProc(clientData,interp,argc,argv)1889 Blt_GraphInstCmdProc(clientData, interp, argc, argv)
1890     ClientData clientData;
1891     Tcl_Interp *interp;
1892     int argc;
1893     char **argv;
1894 {
1895     Blt_Op proc;
1896     int result;
1897     Graph *graphPtr = (Graph *)clientData;
1898 
1899     proc = Blt_GetOp(interp, nGraphOps, graphOps, BLT_OP_ARG1, argc, argv, 0);
1900     if (proc == NULL) {
1901 	return TCL_ERROR;
1902     }
1903     Tcl_Preserve(graphPtr);
1904     result = (*proc) (graphPtr, interp, argc, argv);
1905     Tcl_Release(graphPtr);
1906     return result;
1907 }
1908 
1909 /*
1910  * --------------------------------------------------------------------------
1911  *
1912  * NewGraph --
1913  *
1914  *	Creates a new window and Tcl command representing an
1915  *	instance of a graph widget.
1916  *
1917  * Results:
1918  *	A standard Tcl result.
1919  *
1920  * Side effects:
1921  *	See the user documentation.
1922  *
1923  * --------------------------------------------------------------------------
1924  */
1925 static int
NewGraph(interp,argc,argv,classUid)1926 NewGraph(interp, argc, argv, classUid)
1927     Tcl_Interp *interp;
1928     int argc;
1929     CONST char **argv;
1930     Blt_Uid classUid;
1931 {
1932     Graph *graphPtr;
1933 
1934     if (argc < 2) {
1935 	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1936 	    " pathName ?option value?...\"", (char *)NULL);
1937 	return TCL_ERROR;
1938     }
1939     graphPtr = CreateGraph(interp, argc, argv, classUid);
1940     if (graphPtr == NULL) {
1941 	return TCL_ERROR;
1942     }
1943     Tcl_SetResult(interp, Tk_PathName(graphPtr->tkwin), TCL_VOLATILE);
1944     return TCL_OK;
1945 }
1946 
1947 /*
1948  * --------------------------------------------------------------------------
1949  *
1950  * GraphCmd --
1951  *
1952  *	Creates a new window and Tcl command representing an
1953  *	instance of a graph widget.
1954  *
1955  * Results:
1956  *	A standard Tcl result.
1957  *
1958  * Side effects:
1959  *	See the user documentation.
1960  *
1961  * --------------------------------------------------------------------------
1962  */
1963 /*ARGSUSED*/
1964 static int
GraphCmd(clientData,interp,argc,argv)1965 GraphCmd(clientData, interp, argc, argv)
1966     ClientData clientData;	/* Not used. */
1967     Tcl_Interp *interp;
1968     int argc;
1969     char **argv;
1970 {
1971     return NewGraph(interp, argc, (const char **)argv, bltLineElementUid);
1972 }
1973 
1974 /*
1975  *--------------------------------------------------------------
1976  *
1977  * BarchartCmd --
1978  *
1979  *	Creates a new window and Tcl command representing an
1980  *	instance of a barchart widget.
1981  *
1982  * Results:
1983  *	A standard Tcl result.
1984  *
1985  * Side effects:
1986  *	See the user documentation.
1987  *
1988  *--------------------------------------------------------------
1989  */
1990 /*ARGSUSED*/
1991 static int
BarchartCmd(clientData,interp,argc,argv)1992 BarchartCmd(clientData, interp, argc, argv)
1993     ClientData clientData;	/* Not used. */
1994     Tcl_Interp *interp;
1995     int argc;
1996     char **argv;
1997 {
1998     return NewGraph(interp, argc, (const char **)argv, bltBarElementUid);
1999 }
2000 
2001 /*
2002  *--------------------------------------------------------------
2003  *
2004  * StripchartCmd --
2005  *
2006  *	Creates a new window and Tcl command representing an
2007  *	instance of a barchart widget.
2008  *
2009  * Results:
2010  *	A standard Tcl result.
2011  *
2012  * Side effects:
2013  *	See the user documentation.
2014  *
2015  *--------------------------------------------------------------
2016  */
2017 /*ARGSUSED*/
2018 static int
StripchartCmd(clientData,interp,argc,argv)2019 StripchartCmd(clientData, interp, argc, argv)
2020     ClientData clientData;	/* Not used. */
2021     Tcl_Interp *interp;
2022     int argc;
2023     char **argv;
2024 {
2025     return NewGraph(interp, argc, (const char **)argv, bltStripElementUid);
2026 }
2027 
2028 /*
2029  * -----------------------------------------------------------------------
2030  *
2031  * DrawMargins --
2032  *
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.
2036  *
2037  *	---------------------------------
2038  *      |                               |
2039  *      |           rectArr[0]          |
2040  *      |                               |
2041  *	---------------------------------
2042  *      |     |top           right|     |
2043  *      |     |                   |     |
2044  *      |     |                   |     |
2045  *      | [1] |                   | [2] |
2046  *      |     |                   |     |
2047  *      |     |                   |     |
2048  *      |     |                   |     |
2049  *      |     |                   |     |
2050  *      |     |                   |     |
2051  *      |     |left         bottom|     |
2052  *	---------------------------------
2053  *      |                               |
2054  *      |          rectArr[3]           |
2055  *      |                               |
2056  *	---------------------------------
2057  *
2058  *		X coordinate axis
2059  *		Y coordinate axis
2060  *		legend
2061  *		interior border
2062  *		exterior border
2063  *		titles (X and Y axis, graph)
2064  *
2065  * Returns:
2066  *	None.
2067  *
2068  * Side Effects:
2069  *	Exterior of graph is displayed in its window.
2070  *
2071  * -----------------------------------------------------------------------
2072  */
2073 static void
DrawMargins(graphPtr,drawable)2074 DrawMargins(graphPtr, drawable)
2075     Graph *graphPtr;
2076     Drawable drawable;		/* Pixmap or window to draw into */
2077 {
2078     XRectangle rects[4];
2079     /*
2080      * Draw the four outer rectangles which encompass the plotting
2081      * surface. This clears the surrounding area and clips the plot.
2082      */
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;
2093 
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);
2097     } else {
2098 	XFillRectangles(graphPtr->display, drawable, graphPtr->fillGC, rects,
2099 			4);
2100     }
2101 
2102     /* Draw 3D border around the plotting area */
2103 
2104     if (graphPtr->plotBorderWidth > 0) {
2105 	int x, y, width, height;
2106 
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);
2115     }
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);
2119     }
2120     if (graphPtr->title != NULL) {
2121 	Blt_DrawText(graphPtr->tkwin, drawable, graphPtr->title,
2122 	    &graphPtr->titleTextStyle, graphPtr->titleX, graphPtr->titleY);
2123     }
2124     Blt_DrawAxes(graphPtr, drawable);
2125 
2126 }
2127 
2128 /*
2129  *----------------------------------------------------------------------
2130  *
2131  * DrawPlotRegion --
2132  *
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.
2138  *
2139  * Results:
2140  *	None.
2141  *
2142  *----------------------------------------------------------------------
2143  */
2144 static void
DrawPlotRegion(graphPtr,drawable)2145 DrawPlotRegion(graphPtr, drawable)
2146     Graph *graphPtr;
2147     Drawable drawable;		/* Pixmap or window to draw into */
2148 {
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);
2153 
2154     /* Draw the elements, markers, legend, and axis limits. */
2155 
2156     if (!graphPtr->gridPtr->hidden) {
2157 	Blt_DrawGrid(graphPtr, drawable);
2158     }
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);
2163     }
2164     Blt_DrawAxisLimits(graphPtr, drawable);
2165     Blt_DrawElements(graphPtr, drawable);
2166 }
2167 
2168 void
Blt_LayoutGraph(graphPtr)2169 Blt_LayoutGraph(graphPtr)
2170     Graph *graphPtr;
2171 {
2172     if (graphPtr->flags & RESET_AXES) {
2173 	Blt_ResetAxes(graphPtr);
2174     }
2175     if (graphPtr->flags & LAYOUT_NEEDED) {
2176 	Blt_LayoutMargins(graphPtr);
2177 	graphPtr->flags &= ~LAYOUT_NEEDED;
2178     }
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);
2183 	}
2184 	Blt_MapElements(graphPtr);
2185 	Blt_MapMarkers(graphPtr);
2186 	Blt_MapGrid(graphPtr);
2187 	graphPtr->flags &= ~(MAP_ALL);
2188     }
2189 }
2190 
2191 void
Blt_DrawGraph(graphPtr,drawable,backingStore)2192 Blt_DrawGraph(graphPtr, drawable, backingStore)
2193     Graph *graphPtr;
2194     Drawable drawable;		/* Pixmap or window to draw into */
2195     int backingStore;		/* If non-zero, use backing store for
2196 				 * plotting area. */
2197 {
2198     if (backingStore) {
2199 	/*
2200 	 * Create another pixmap to save elements if one doesn't
2201 	 * already exist or the size of the window has changed.
2202 	 */
2203 	if ((graphPtr->backPixmap == None) ||
2204 	    (graphPtr->backWidth != graphPtr->width) ||
2205 	    (graphPtr->backHeight != graphPtr->height)) {
2206 
2207 	    if (graphPtr->backPixmap != None) {
2208 		Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
2209 	    }
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;
2216 	}
2217 	if (graphPtr->flags & REDRAW_BACKING_STORE) {
2218 
2219 	    /* The backing store is new or out-of-date. */
2220 
2221 	    DrawPlotRegion(graphPtr, graphPtr->backPixmap);
2222 	    graphPtr->flags &= ~REDRAW_BACKING_STORE;
2223 	}
2224 
2225 	/* Copy the pixmap to the one used for drawing the entire graph. */
2226 
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);
2232     } else {
2233 	DrawPlotRegion(graphPtr, drawable);
2234     }
2235 
2236     /* Draw markers above elements */
2237     Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE);
2238     Blt_DrawActiveElements(graphPtr, drawable);
2239 
2240     if (graphPtr->flags & DRAW_MARGINS) {
2241 	DrawMargins(graphPtr, drawable);
2242     }
2243     if (graphPtr->gridPtr->hidden == 0 && graphPtr->gridPtr->raised) {
2244         Blt_DrawGrid(graphPtr, drawable);
2245     }
2246     if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
2247 	(Blt_LegendIsRaised(graphPtr->legend))) {
2248 	Blt_DrawLegend(graphPtr->legend, drawable);
2249     }
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);
2257     }
2258     /* Draw focus highlight ring. */
2259     if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & GRAPH_FOCUS)) {
2260 	GC gc;
2261 
2262 	gc = Tk_GCForColor(graphPtr->highlightColor, drawable);
2263 	Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth,
2264 	    drawable);
2265     }
2266 }
2267 
2268 static void
UpdateMarginTraces(graphPtr)2269 UpdateMarginTraces(graphPtr)
2270     Graph *graphPtr;
2271 {
2272     Margin *marginPtr;
2273     int size;
2274     register int i;
2275     char *oldVal, *newVal;
2276 
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;
2283 	    } else {
2284 		size = marginPtr->height;
2285 	    }
2286             newVal = Blt_Itoa(size);
2287             oldVal = Tcl_GetVar(graphPtr->interp, marginPtr->varName,
2288 		TCL_GLOBAL_ONLY);
2289             if (oldVal && !strcmp(oldVal,newVal)) continue;
2290 	    Tcl_SetVar(graphPtr->interp, marginPtr->varName, newVal,
2291 		TCL_GLOBAL_ONLY);
2292 	}
2293     }
2294 }
2295 
2296 /*
2297  *----------------------------------------------------------------------
2298  *
2299  * DisplayGraph --
2300  *
2301  *	This procedure is invoked to display a graph widget.
2302  *
2303  * Results:
2304  *	None.
2305  *
2306  * Side effects:
2307  *	Commands are output to X to display the graph in its
2308  *	current mode.
2309  *
2310  *----------------------------------------------------------------------
2311  */
2312 static void
DisplayGraph(clientData)2313 DisplayGraph(clientData)
2314     ClientData clientData;
2315 {
2316     Graph *graphPtr = (Graph *)clientData;
2317     Pixmap drawable;
2318 
2319     graphPtr->flags &= ~REDRAW_PENDING;
2320     if (graphPtr->tkwin == NULL) {
2321 	return;			/* Window destroyed (should not get here) */
2322     }
2323 #ifdef notdef
2324     fprintf(stderr, "Calling DisplayGraph(%s)\n", Tk_PathName(graphPtr->tkwin));
2325 #endif
2326     if (Blt_GraphUpdateNeeded(graphPtr)) {
2327 	/*
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.
2334 	 */
2335 	return;
2336     }
2337 
2338     /*
2339       Process the 'redrawcmd' callback as a REDRAW_BACKING_STORE
2340       event had been triggered.
2341 
2342       This callback can be used to generate a stream of postcript
2343       frames safely.
2344      */
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;
2350 
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 */
2356         }
2357     }
2358 
2359 
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.  */
2368 	return;
2369     }
2370 
2371     /* Disable crosshairs before redisplaying to the screen */
2372     Blt_DisableCrosshairs(graphPtr);
2373     /*
2374      * Create a pixmap the size of the window for double buffering.
2375      */
2376     if (graphPtr->doubleBuffer) {
2377 	drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
2378 		graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
2379     } else {
2380 	drawable = Tk_WindowId(graphPtr->tkwin);
2381     }
2382 #ifdef WIN32
2383     assert(drawable != None);
2384 #endif
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);
2390     } else {
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);
2396     }
2397     if (graphPtr->doubleBuffer) {
2398 	Tk_FreePixmap(graphPtr->display, drawable);
2399     }
2400     graphPtr->flags &= ~EXEC_REDRAWCMD;
2401     Blt_EnableCrosshairs(graphPtr);
2402     graphPtr->flags &= ~RESET_WORLD;
2403     UpdateMarginTraces(graphPtr);
2404 }
2405 
2406 /*LINTLIBRARY*/
2407 int
Blt_GraphInit(interp)2408 Blt_GraphInit(interp)
2409     Tcl_Interp *interp;
2410 {
2411     static Blt_CmdSpec cmdSpecs[] =
2412     {
2413 	{"graph", GraphCmd,},
2414 	{"barchart", BarchartCmd,},
2415 	{"stripchart", StripchartCmd,},
2416     };
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");
2421 
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");
2428 
2429     bltXAxisUid = (Blt_Uid)Tk_GetUid("X");
2430     bltYAxisUid = (Blt_Uid)Tk_GetUid("Y");
2431 
2432     return Blt_InitCmds(interp, "blt", cmdSpecs, 3);
2433 }
2434 
2435 Graph *
Blt_GetGraphFromWindowData(tkwin)2436 Blt_GetGraphFromWindowData(tkwin)
2437     Tk_Window tkwin;
2438 {
2439     Graph *graphPtr;
2440 
2441     while (tkwin != NULL) {
2442 	graphPtr = (Graph *)Blt_GetWindowInstanceData(tkwin);
2443 	if (graphPtr != NULL) {
2444 	    return graphPtr;
2445 	}
2446 	tkwin = Tk_Parent(tkwin);
2447     }
2448     return NULL;
2449 }
2450 
2451 int
Blt_GraphType(graphPtr)2452 Blt_GraphType(graphPtr)
2453     Graph *graphPtr;
2454 {
2455     if (graphPtr->classUid == bltLineElementUid) {
2456 	return GRAPH;
2457     } else if (graphPtr->classUid == bltBarElementUid) {
2458 	return BARCHART;
2459     } else if (graphPtr->classUid == bltStripElementUid) {
2460 	return STRIPCHART;
2461     }
2462     return 0;
2463 }
2464