source: trunk/kitgen/8.x/blt/generic/bltGraph.c@ 175

Last change on this file since 175 was 175, checked in by demin, 12 years ago

initial commit

File size: 72.2 KB
Line 
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
49Blt_Uid bltXAxisUid;
50Blt_Uid bltYAxisUid;
51Blt_Uid bltBarElementUid;
52Blt_Uid bltLineElementUid;
53Blt_Uid bltStripElementUid;
54Blt_Uid bltContourElementUid;
55Blt_Uid bltLineMarkerUid;
56Blt_Uid bltBitmapMarkerUid;
57Blt_Uid bltImageMarkerUid;
58Blt_Uid bltTextMarkerUid;
59Blt_Uid bltPolygonMarkerUid;
60Blt_Uid bltWindowMarkerUid;
61
62extern Tk_CustomOption bltLinePenOption;
63extern Tk_CustomOption bltBarPenOption;
64extern Tk_CustomOption bltDistanceOption;
65extern Tk_CustomOption bltBarModeOption;
66extern Tk_CustomOption bltPadOption;
67extern Tk_CustomOption bltTileOption;
68extern 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 "2"
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
111static 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, "-datacommand", "dataCommand", "DataCommand",
153 (char *)NULL, Tk_Offset(Graph, dataCmd), 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
253static Blt_SwitchParseProc StringToFormat;
254static Blt_SwitchCustom formatSwitch =
255{
256 StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
257};
258
259typedef struct {
260 char *name;
261 int width, height;
262 int format;
263} SnapData;
264
265enum SnapFormats { FORMAT_PHOTO, FORMAT_EMF, FORMAT_WMF };
266
267static 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
276static Tcl_IdleProc DisplayGraph;
277static Tcl_FreeProc DestroyGraph;
278static Tk_EventProc GraphEventProc;
279Tcl_CmdProc Blt_GraphInstCmdProc;
280
281static Blt_BindPickProc PickEntry;
282static Tcl_CmdProc StripchartCmd;
283static Tcl_CmdProc BarchartCmd;
284static Tcl_CmdProc GraphCmd;
285static Tcl_CmdDeleteProc GraphInstCmdDeleteProc;
286static Blt_TileChangedProc TileChangedProc;
287
288/*
289 *--------------------------------------------------------------
290 *
291 * Blt_EventuallyRedrawGraph --
292 *
293 * Tells the Tk dispatcher to call the graph display routine at
294 * the next idle point. This request is made only if the window
295 * is displayed and no other redraw request is pending.
296 *
297 * Results: None.
298 *
299 * Side effects:
300 * The window is eventually redisplayed.
301 *
302 *--------------------------------------------------------------
303 */
304void
305Blt_EventuallyRedrawGraph(graphPtr)
306 Graph *graphPtr; /* Graph widget record */
307{
308 if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) {
309 Tcl_DoWhenIdle(DisplayGraph, graphPtr);
310 graphPtr->flags |= REDRAW_PENDING;
311 }
312}
313
314/*
315 *--------------------------------------------------------------
316 *
317 * GraphEventProc --
318 *
319 * This procedure is invoked by the Tk dispatcher for various
320 * events on graphs.
321 *
322 * Results:
323 * None.
324 *
325 * Side effects:
326 * When the window gets deleted, internal structures get
327 * cleaned up. When it gets exposed, the graph is eventually
328 * redisplayed.
329 *
330 *--------------------------------------------------------------
331 */
332static void
333GraphEventProc(clientData, eventPtr)
334 ClientData clientData; /* Graph widget record */
335 register XEvent *eventPtr; /* Event which triggered call to routine */
336{
337 Graph *graphPtr = clientData;
338
339 if (eventPtr->type == Expose) {
340 if (eventPtr->xexpose.count == 0) {
341 graphPtr->flags |= REDRAW_WORLD;
342 Blt_EventuallyRedrawGraph(graphPtr);
343 }
344 } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
345 if (eventPtr->xfocus.detail != NotifyInferior) {
346 if (eventPtr->type == FocusIn) {
347 graphPtr->flags |= GRAPH_FOCUS;
348 } else {
349 graphPtr->flags &= ~GRAPH_FOCUS;
350 }
351 graphPtr->flags |= REDRAW_WORLD;
352 Blt_EventuallyRedrawGraph(graphPtr);
353 }
354 } else if (eventPtr->type == DestroyNotify) {
355 if (graphPtr->tkwin != NULL) {
356 Blt_DeleteWindowInstanceData(graphPtr->tkwin);
357 graphPtr->tkwin = NULL;
358 Tcl_DeleteCommandFromToken(graphPtr->interp, graphPtr->cmdToken);
359 }
360 if (graphPtr->flags & REDRAW_PENDING) {
361 Tcl_CancelIdleCall(DisplayGraph, graphPtr);
362 }
363 Tcl_EventuallyFree(graphPtr, DestroyGraph);
364 } else if (eventPtr->type == ConfigureNotify) {
365 graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
366 Blt_EventuallyRedrawGraph(graphPtr);
367 }
368}
369
370/*
371 *----------------------------------------------------------------------
372 *
373 * GraphInstCmdDeleteProc --
374 *
375 * This procedure is invoked when a widget command is deleted. If
376 * the widget isn't already in the process of being destroyed,
377 * this command destroys it.
378 *
379 * Results:
380 * None.
381 *
382 * Side effects:
383 * The widget is destroyed.
384 *
385 *---------------------------------------------------------------------- */
386static void
387GraphInstCmdDeleteProc(clientData)
388 ClientData clientData; /* Pointer to widget record. */
389{
390 Graph *graphPtr = clientData;
391
392 if (graphPtr->tkwin != NULL) { /* NULL indicates window has
393 * already been destroyed. */
394 Tk_Window tkwin;
395
396 tkwin = graphPtr->tkwin;
397 graphPtr->tkwin = NULL;
398#ifdef ITCL_NAMESPACES
399 Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
400#endif /* ITCL_NAMESPACES */
401 Blt_DeleteWindowInstanceData(tkwin);
402 Tk_DestroyWindow(tkwin);
403 }
404}
405
406/*
407 *----------------------------------------------------------------------
408 *
409 * TileChangedProc
410 *
411 * Rebuilds the designated GC with the new tile pixmap.
412 *
413 * Results:
414 * None.
415 *
416 *----------------------------------------------------------------------
417 */
418/*ARGSUSED*/
419static void
420TileChangedProc(clientData, tile)
421 ClientData clientData;
422 Blt_Tile tile; /* Not used. */
423{
424 Graph *graphPtr = clientData;
425
426 if (graphPtr->tkwin != NULL) {
427 graphPtr->flags |= REDRAW_WORLD;
428 Blt_EventuallyRedrawGraph(graphPtr);
429 }
430}
431
432/*
433 *--------------------------------------------------------------
434 *
435 * AdjustAxisPointers --
436 *
437 * Sets the axis pointers according to whether the axis is
438 * inverted on not. The axis sites are also reset.
439 *
440 * Results:
441 * None.
442 *
443 *--------------------------------------------------------------
444 */
445static void
446AdjustAxisPointers(graphPtr)
447 Graph *graphPtr; /* Graph widget record */
448{
449 if (graphPtr->inverted) {
450 graphPtr->leftMargin.axes = graphPtr->axisChain[0];
451 graphPtr->bottomMargin.axes = graphPtr->axisChain[1];
452 graphPtr->rightMargin.axes = graphPtr->axisChain[2];
453 graphPtr->topMargin.axes = graphPtr->axisChain[3];
454 } else {
455 graphPtr->leftMargin.axes = graphPtr->axisChain[1];
456 graphPtr->bottomMargin.axes = graphPtr->axisChain[0];
457 graphPtr->rightMargin.axes = graphPtr->axisChain[3];
458 graphPtr->topMargin.axes = graphPtr->axisChain[2];
459 }
460}
461
462static int
463InitPens(graphPtr)
464 Graph *graphPtr;
465{
466 Blt_InitHashTable(&graphPtr->penTable, BLT_STRING_KEYS);
467 if (Blt_CreatePen(graphPtr, "activeLine", bltLineElementUid, 0,
468 (char **)NULL) == NULL) {
469 return TCL_ERROR;
470 }
471 if (Blt_CreatePen(graphPtr, "activeBar", bltBarElementUid, 0,
472 (char **)NULL) == NULL) {
473 return TCL_ERROR;
474 }
475 return TCL_OK;
476}
477
478/*
479 *----------------------------------------------------------------------
480 *
481 * Blt_GraphTags --
482 *
483 * Sets the binding tags for a graph object. This routine is
484 * called by Tk when an event occurs in the graph. It fills
485 * an array of pointers with bind tag addresses.
486 *
487 * The object addresses are strings hashed in one of two tag
488 * tables: one for elements and the another for markers. Note
489 * that there's only one binding table for elements and markers.
490 * [We don't want to trigger both a marker and element bind
491 * command for the same event.] But we don't want a marker and
492 * element with the same tag name to activate the others
493 * bindings. A tag "all" for markers should mean all markers, not
494 * all markers and elements. As a result, element and marker
495 * tags are stored in separate hash tables, which means we can't
496 * generate the same tag address for both an elements and marker,
497 * even if they have the same name.
498 *
499 * Results:
500 * None.
501 *
502 * Side effects:
503 * This information will be used by the binding code in bltUtil.c
504 * to determine what graph objects match the current event. The
505 * tags are placed in tagArr and *nTagsPtr is set with the
506 * number of tags found.
507 *
508 *----------------------------------------------------------------------
509 */
510/*ARGSUSED*/
511void
512Blt_GraphTags(table, object, context, list)
513 Blt_BindTable table;
514 ClientData object;
515 ClientData context; /* Not used. */
516 Blt_List list;
517{
518 Element *elemPtr;
519 MakeTagProc *tagProc;
520 Graph *graphPtr;
521
522 graphPtr = (Graph *)Blt_GetBindingData(table);
523 /*
524 * Trick: Markers, elements, and axes have the same first few
525 * fields in their structures, such as "type", "name", or
526 * "tags". This is so we can look at graph objects
527 * interchangably. It doesn't matter what we cast the
528 * object to.
529 */
530 elemPtr = (Element *)object;
531
532 if ((elemPtr->classUid == bltLineElementUid) ||
533 (elemPtr->classUid == bltStripElementUid) ||
534 (elemPtr->classUid == bltBarElementUid)) {
535 tagProc = Blt_MakeElementTag;
536 } else if ((elemPtr->classUid == bltXAxisUid) ||
537 (elemPtr->classUid == bltYAxisUid)) {
538 tagProc = Blt_MakeAxisTag;
539 } else {
540 tagProc = Blt_MakeMarkerTag;
541 }
542 /*
543 * Always add the name of the object to the tag array.
544 */
545 Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->name), 0);
546 Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->classUid), 0);
547 if (elemPtr->tags != NULL) {
548 register char **p;
549
550 for (p = elemPtr->tags; *p != NULL; p++) {
551 Blt_ListAppend(list, (*tagProc) (graphPtr, *p), 0);
552 }
553 }
554}
555
556/*
557 * Find the closest point from the set of displayed elements,
558 * searching the display list from back to front. That way, if
559 * the points from two different elements overlay each other exactly,
560 * the one that's on top (visible) is picked.
561 */
562/*ARGSUSED*/
563static ClientData
564PickEntry(clientData, x, y, contextPtr)
565 ClientData clientData;
566 int x, y;
567 ClientData *contextPtr; /* Not used. */
568{
569 Graph *graphPtr = clientData;
570 Blt_ChainLink *linkPtr;
571 Element *elemPtr;
572 Marker *markerPtr;
573 Extents2D exts;
574
575 if (graphPtr->flags & MAP_ALL) {
576 return NULL; /* Can't pick anything until the next
577 * redraw occurs. */
578 }
579 Blt_GraphExtents(graphPtr, &exts);
580
581 if ((x > exts.right) || (x < exts.left) || (y > exts.bottom) ||
582 (y < exts.top)) {
583 /*
584 * Sample coordinate is in one of the graph margins. Can only
585 * pick an axis.
586 */
587 return Blt_NearestAxis(graphPtr, x, y);
588 }
589
590 /*
591 * From top-to-bottom check:
592 * 1. markers drawn on top (-under false).
593 * 2. elements using its display list back to front.
594 * 3. markers drawn under element (-under true).
595 */
596 markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, FALSE);
597 if (markerPtr != NULL) {
598 return markerPtr; /* Found a marker (-under false). */
599 }
600 {
601 ClosestSearch search;
602
603 search.along = SEARCH_BOTH;
604 search.halo = graphPtr->halo + 1;
605 search.index = -1;
606 search.x = x;
607 search.y = y;
608 search.dist = (double)(search.halo + 1);
609 search.mode = SEARCH_AUTO;
610
611 for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
612 linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
613 elemPtr = Blt_ChainGetValue(linkPtr);
614 if ((elemPtr->flags & MAP_ITEM) ||
615 (Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
616 (Blt_VectorNotifyPending(elemPtr->y.clientId))) {
617 continue;
618 }
619 if ((!elemPtr->hidden) && (elemPtr->state == STATE_NORMAL)) {
620 (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
621 }
622 }
623 if (search.dist <= (double)search.halo) {
624 return search.elemPtr; /* Found an element within the
625 * minimum halo distance. */
626 }
627 }
628 markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, TRUE);
629 if (markerPtr != NULL) {
630 return markerPtr; /* Found a marker (-under true) */
631 }
632 return NULL; /* Nothing found. */
633}
634
635/*
636 *----------------------------------------------------------------------
637 *
638 * ConfigureGraph --
639 *
640 * Allocates resources for the graph.
641 *
642 * Results:
643 * None.
644 *
645 * Side effects:
646 * Configuration information, such as text string, colors, font,
647 * etc. get set for graphPtr; old resources get freed, if there
648 * were any. The graph is redisplayed.
649 *
650 *----------------------------------------------------------------------
651 */
652static void
653ConfigureGraph(graphPtr)
654 Graph *graphPtr; /* Graph widget record */
655{
656 XColor *colorPtr;
657 GC newGC;
658 XGCValues gcValues;
659 unsigned long gcMask;
660
661 /* Don't allow negative bar widths. Reset to an arbitrary value (0.1) */
662 if (graphPtr->barWidth <= 0.0) {
663 graphPtr->barWidth = 0.1;
664 }
665 graphPtr->inset = graphPtr->borderWidth + graphPtr->highlightWidth + 1;
666 if ((graphPtr->reqHeight != Tk_ReqHeight(graphPtr->tkwin)) ||
667 (graphPtr->reqWidth != Tk_ReqWidth(graphPtr->tkwin))) {
668 Tk_GeometryRequest(graphPtr->tkwin, graphPtr->reqWidth,
669 graphPtr->reqHeight);
670 }
671 Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
672 colorPtr = Tk_3DBorderColor(graphPtr->border);
673
674 if (graphPtr->title != NULL) {
675 int w, h;
676
677 Blt_GetTextExtents(&graphPtr->titleTextStyle, graphPtr->title, &w, &h);
678 graphPtr->titleTextStyle.height = h + 10;
679 } else {
680 graphPtr->titleTextStyle.width = graphPtr->titleTextStyle.height = 0;
681 }
682
683 /*
684 * Create GCs for interior and exterior regions, and a background
685 * GC for clearing the margins with XFillRectangle
686 */
687
688 /* Margin GC */
689
690 gcValues.foreground = graphPtr->titleTextStyle.color->pixel;
691 gcValues.background = colorPtr->pixel;
692 gcMask = (GCForeground | GCBackground);
693 newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
694 if (graphPtr->drawGC != NULL) {
695 Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
696 }
697 graphPtr->drawGC = newGC;
698
699 /* Plot fill GC (Background = Foreground) */
700
701 gcValues.foreground = graphPtr->plotBg->pixel;
702 newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
703 if (graphPtr->plotFillGC != NULL) {
704 Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
705 }
706 graphPtr->plotFillGC = newGC;
707
708 /* Margin fill GC (Background = Foreground) */
709
710 gcValues.foreground = colorPtr->pixel;
711 gcValues.background = graphPtr->titleTextStyle.color->pixel;
712 newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
713 if (graphPtr->fillGC != NULL) {
714 Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
715 }
716 graphPtr->fillGC = newGC;
717 if (graphPtr->tile != NULL) {
718 Blt_SetTileChangedProc(graphPtr->tile, TileChangedProc, graphPtr);
719 }
720
721 Blt_ResetTextStyle(graphPtr->tkwin, &graphPtr->titleTextStyle);
722
723 if (Blt_ConfigModified(configSpecs, "-invertxy", (char *)NULL)) {
724
725 /*
726 * If the -inverted option changed, we need to readjust the pointers
727 * to the axes and recompute the their scales.
728 */
729
730 AdjustAxisPointers(graphPtr);
731 graphPtr->flags |= RESET_AXES;
732 }
733 if ((!graphPtr->backingStore) && (graphPtr->backPixmap != None)) {
734
735 /*
736 * Free the pixmap if we're not buffering the display of elements
737 * anymore.
738 */
739
740 Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
741 graphPtr->backPixmap = None;
742 }
743 /*
744 * Reconfigure the crosshairs, just in case the background color of
745 * the plotarea has been changed.
746 */
747 Blt_ConfigureCrosshairs(graphPtr);
748
749 /*
750 * Update the layout of the graph (and redraw the elements) if
751 * any of the following graph options which affect the size of
752 * the plotting area has changed.
753 *
754 * -aspect
755 * -borderwidth, -plotborderwidth
756 * -font, -title
757 * -width, -height
758 * -invertxy
759 * -bottommargin, -leftmargin, -rightmargin, -topmargin,
760 * -barmode, -barwidth
761 */
762 if (Blt_ConfigModified(configSpecs, "-invertxy", "-title", "-font",
763 "-*margin", "-*width", "-height", "-barmode", "-*pad*", "-aspect",
764 (char *)NULL)) {
765 graphPtr->flags |= RESET_WORLD;
766 }
767 if (Blt_ConfigModified(configSpecs, "-plotbackground", (char *)NULL)) {
768 graphPtr->flags |= REDRAW_BACKING_STORE;
769 }
770 graphPtr->flags |= REDRAW_WORLD;
771 Blt_EventuallyRedrawGraph(graphPtr);
772}
773
774/*
775 *----------------------------------------------------------------------
776 *
777 * DestroyGraph --
778 *
779 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
780 * to clean up the internal structure of a graph at a safe time
781 * (when no-one is using it anymore).
782 *
783 * Results:
784 * None.
785 *
786 * Side effects:
787 * Everything associated with the widget is freed up.
788 *
789 *----------------------------------------------------------------------
790 */
791static void
792DestroyGraph(dataPtr)
793 DestroyData dataPtr;
794{
795 Graph *graphPtr = (Graph *)dataPtr;
796
797 Tk_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display, 0);
798 /*
799 * Destroy the individual components of the graph: elements, markers,
800 * X and Y axes, legend, display lists etc.
801 */
802 Blt_DestroyMarkers(graphPtr);
803 Blt_DestroyElements(graphPtr);
804
805 Blt_DestroyAxes(graphPtr);
806 Blt_DestroyPens(graphPtr);
807
808 if (graphPtr->legend != NULL) {
809 Blt_DestroyLegend(graphPtr);
810 }
811 if (graphPtr->postscript != NULL) {
812 Blt_DestroyPostScript(graphPtr);
813 }
814 if (graphPtr->crosshairs != NULL) {
815 Blt_DestroyCrosshairs(graphPtr);
816 }
817 if (graphPtr->gridPtr != NULL) {
818 Blt_DestroyGrid(graphPtr);
819 }
820 if (graphPtr->bindTable != NULL) {
821 Blt_DestroyBindingTable(graphPtr->bindTable);
822 }
823
824 /* Release allocated X resources and memory. */
825 if (graphPtr->drawGC != NULL) {
826 Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
827 }
828 if (graphPtr->fillGC != NULL) {
829 Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
830 }
831 if (graphPtr->plotFillGC != NULL) {
832 Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
833 }
834 Blt_FreeTextStyle(graphPtr->display, &graphPtr->titleTextStyle);
835 if (graphPtr->backPixmap != None) {
836 Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
837 }
838 if (graphPtr->freqArr != NULL) {
839 Blt_Free(graphPtr->freqArr);
840 }
841 if (graphPtr->nStacks > 0) {
842 Blt_DeleteHashTable(&graphPtr->freqTable);
843 }
844 if (graphPtr->tile != NULL) {
845 Blt_FreeTile(graphPtr->tile);
846 }
847 Blt_Free(graphPtr);
848}
849
850/*
851 *----------------------------------------------------------------------
852 *
853 * CreateGraph --
854 *
855 * This procedure creates and initializes a new widget.
856 *
857 * Results:
858 * The return value is a pointer to a structure describing
859 * the new widget. If an error occurred, then the return
860 * value is NULL and an error message is left in interp->result.
861 *
862 * Side effects:
863 * Memory is allocated, a Tk_Window is created, etc.
864 *
865 *----------------------------------------------------------------------
866 */
867
868static Graph *
869CreateGraph(interp, argc, argv, classUid)
870 Tcl_Interp *interp;
871 int argc;
872 char **argv;
873 Blt_Uid classUid;
874{
875 Graph *graphPtr;
876 Tk_Window tkwin;
877
878 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
879 (char *)NULL);
880 if (tkwin == NULL) {
881 return NULL;
882 }
883 graphPtr = Blt_Calloc(1, sizeof(Graph));
884 assert(graphPtr);
885
886 /* Initialize the graph data structure. */
887
888 graphPtr->tkwin = tkwin;
889 graphPtr->display = Tk_Display(tkwin);
890 graphPtr->interp = interp;
891 graphPtr->classUid = classUid;
892 graphPtr->backingStore = TRUE;
893 graphPtr->doubleBuffer = TRUE;
894 graphPtr->highlightWidth = 2;
895 graphPtr->plotRelief = TK_RELIEF_SUNKEN;
896 graphPtr->relief = TK_RELIEF_FLAT;
897 graphPtr->flags = (RESET_WORLD);
898 graphPtr->nextMarkerId = 1;
899 graphPtr->padLeft = graphPtr->padRight = 8;
900 graphPtr->padTop = graphPtr->padBottom = 8;
901 graphPtr->bottomMargin.site = MARGIN_BOTTOM;
902 graphPtr->leftMargin.site = MARGIN_LEFT;
903 graphPtr->topMargin.site = MARGIN_TOP;
904 graphPtr->rightMargin.site = MARGIN_RIGHT;
905 Blt_InitTextStyle(&graphPtr->titleTextStyle);
906
907 Blt_InitHashTable(&graphPtr->axes.table, BLT_STRING_KEYS);
908 Blt_InitHashTable(&graphPtr->axes.tagTable, BLT_STRING_KEYS);
909 Blt_InitHashTable(&graphPtr->elements.table, BLT_STRING_KEYS);
910 Blt_InitHashTable(&graphPtr->elements.tagTable, BLT_STRING_KEYS);
911 Blt_InitHashTable(&graphPtr->markers.table, BLT_STRING_KEYS);
912 Blt_InitHashTable(&graphPtr->markers.tagTable, BLT_STRING_KEYS);
913 graphPtr->elements.displayList = Blt_ChainCreate();
914 graphPtr->markers.displayList = Blt_ChainCreate();
915 graphPtr->axes.displayList = Blt_ChainCreate();
916
917 if (classUid == bltLineElementUid) {
918 Tk_SetClass(tkwin, "Graph");
919 } else if (classUid == bltBarElementUid) {
920 Tk_SetClass(tkwin, "Barchart");
921 } else if (classUid == bltStripElementUid) {
922 Tk_SetClass(tkwin, "Stripchart");
923 }
924 Blt_SetWindowInstanceData(tkwin, graphPtr);
925
926 if (InitPens(graphPtr) != TCL_OK) {
927 goto error;
928 }
929 if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc - 2, argv + 2,
930 (char *)graphPtr, 0) != TCL_OK) {
931 goto error;
932 }
933 if (Blt_DefaultAxes(graphPtr) != TCL_OK) {
934 goto error;
935 }
936 AdjustAxisPointers(graphPtr);
937
938 if (Blt_CreatePostScript(graphPtr) != TCL_OK) {
939 goto error;
940 }
941 if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
942 goto error;
943 }
944 if (Blt_CreateLegend(graphPtr) != TCL_OK) {
945 goto error;
946 }
947 if (Blt_CreateGrid(graphPtr) != TCL_OK) {
948 goto error;
949 }
950 Tk_CreateEventHandler(graphPtr->tkwin,
951 ExposureMask | StructureNotifyMask | FocusChangeMask, GraphEventProc,
952 graphPtr);
953
954 graphPtr->cmdToken = Tcl_CreateCommand(interp, argv[1],
955 Blt_GraphInstCmdProc, graphPtr, GraphInstCmdDeleteProc);
956#ifdef ITCL_NAMESPACES
957 Itk_SetWidgetCommand(graphPtr->tkwin, graphPtr->cmdToken);
958#endif
959 ConfigureGraph(graphPtr);
960 graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr,
961 PickEntry, Blt_GraphTags);
962 return graphPtr;
963
964 error:
965 DestroyGraph((DestroyData)graphPtr);
966 return NULL;
967}
968
969
970/* Widget sub-commands */
971
972/*ARGSUSED*/
973static int
974XAxisOp(graphPtr, interp, argc, argv)
975 Graph *graphPtr;
976 Tcl_Interp *interp; /* Not used. */
977 int argc;
978 char **argv;
979{
980 int margin;
981
982 margin = (graphPtr->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM;
983 return Blt_AxisOp(graphPtr, margin, argc, argv);
984}
985
986/*ARGSUSED*/
987static int
988X2AxisOp(graphPtr, interp, argc, argv)
989 Graph *graphPtr;
990 Tcl_Interp *interp; /* Not used. */
991 int argc;
992 char **argv;
993{
994 int margin;
995
996 margin = (graphPtr->inverted) ? MARGIN_RIGHT : MARGIN_TOP;
997 return Blt_AxisOp(graphPtr, margin, argc, argv);
998}
999
1000/*ARGSUSED*/
1001static int
1002YAxisOp(graphPtr, interp, argc, argv)
1003 Graph *graphPtr;
1004 Tcl_Interp *interp; /* Not used. */
1005 int argc;
1006 char **argv;
1007{
1008 int margin;
1009
1010 margin = (graphPtr->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT;
1011 return Blt_AxisOp(graphPtr, margin, argc, argv);
1012}
1013
1014/*ARGSUSED*/
1015static int
1016Y2AxisOp(graphPtr, interp, argc, argv)
1017 Graph *graphPtr;
1018 Tcl_Interp *interp; /* Not used. */
1019 int argc;
1020 char **argv;
1021{
1022 int margin;
1023
1024 margin = (graphPtr->inverted) ? MARGIN_TOP : MARGIN_RIGHT;
1025 return Blt_AxisOp(graphPtr, margin, argc, argv);
1026}
1027
1028/*ARGSUSED*/
1029static int
1030BarOp(graphPtr, interp, argc, argv)
1031 Graph *graphPtr;
1032 Tcl_Interp *interp; /* Not used. */
1033 int argc;
1034 char **argv;
1035{
1036 return Blt_ElementOp(graphPtr, interp, argc, argv, bltBarElementUid);
1037}
1038
1039/*ARGSUSED*/
1040static int
1041LineOp(graphPtr, interp, argc, argv)
1042 Graph *graphPtr;
1043 Tcl_Interp *interp; /* Not used. */
1044 int argc;
1045 char **argv;
1046{
1047 return Blt_ElementOp(graphPtr, interp, argc, argv, bltLineElementUid);
1048}
1049
1050/*ARGSUSED*/
1051static int
1052ElementOp(graphPtr, interp, argc, argv)
1053 Graph *graphPtr;
1054 Tcl_Interp *interp; /* Not used. */
1055 int argc;
1056 char **argv;
1057{
1058 return Blt_ElementOp(graphPtr, interp, argc, argv, graphPtr->classUid);
1059}
1060
1061static int
1062ConfigureOp(graphPtr, interp, argc, argv)
1063 Graph *graphPtr;
1064 Tcl_Interp *interp;
1065 int argc;
1066 char **argv;
1067{
1068 int flags;
1069
1070 flags = TK_CONFIG_ARGV_ONLY;
1071 if (argc == 2) {
1072 return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1073 (char *)graphPtr, (char *)NULL, flags);
1074 } else if (argc == 3) {
1075 return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
1076 (char *)graphPtr, argv[2], flags);
1077 } else {
1078 if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 2,
1079 argv + 2, (char *)graphPtr, flags) != TCL_OK) {
1080 return TCL_ERROR;
1081 }
1082 ConfigureGraph(graphPtr);
1083 return TCL_OK;
1084 }
1085}
1086
1087/* ARGSUSED*/
1088static int
1089CgetOp(graphPtr, interp, argc, argv)
1090 Graph *graphPtr;
1091 Tcl_Interp *interp;
1092 int argc; /* Not used. */
1093 char **argv;
1094{
1095 return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
1096 (char *)graphPtr, argv[2], 0);
1097}
1098
1099/*
1100 *--------------------------------------------------------------
1101 *
1102 * ExtentsOp --
1103 *
1104 * Reports the size of one of several items within the graph.
1105 * The following are valid items:
1106 *
1107 * "bottommargin" Height of the bottom margin
1108 * "leftmargin" Width of the left margin
1109 * "legend" x y w h of the legend
1110 * "plotarea" x y w h of the plotarea
1111 * "plotheight" Height of the plot area
1112 * "rightmargin" Width of the right margin
1113 * "topmargin" Height of the top margin
1114 * "plotwidth" Width of the plot area
1115 *
1116 * Results:
1117 * Always returns TCL_OK.
1118 *
1119 *--------------------------------------------------------------
1120 */
1121/* ARGSUSED*/
1122static int
1123ExtentsOp(graphPtr, interp, argc, argv)
1124 Graph *graphPtr;
1125 Tcl_Interp *interp;
1126 int argc; /* Not used. */
1127 char **argv;
1128{
1129 char c;
1130 unsigned int length;
1131 char string[200];
1132
1133 c = argv[2][0];
1134 length = strlen(argv[2]);
1135 if ((c == 'p') && (length > 4) &&
1136 (strncmp("plotheight", argv[2], length) == 0)) {
1137 Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottom - graphPtr->top + 1),
1138 TCL_VOLATILE);
1139 } else if ((c == 'p') && (length > 4) &&
1140 (strncmp("plotwidth", argv[2], length) == 0)) {
1141 Tcl_SetResult(interp, Blt_Itoa(graphPtr->right - graphPtr->left + 1),
1142 TCL_VOLATILE);
1143 } else if ((c == 'p') && (length > 4) &&
1144 (strncmp("plotarea", argv[2], length) == 0)) {
1145 sprintf(string, "%d %d %d %d", graphPtr->left, graphPtr->top,
1146 graphPtr->right - graphPtr->left + 1,
1147 graphPtr->bottom - graphPtr->top + 1);
1148 Tcl_SetResult(interp, string, TCL_VOLATILE);
1149 } else if ((c == 'l') && (length > 2) &&
1150 (strncmp("legend", argv[2], length) == 0)) {
1151 sprintf(string, "%d %d %d %d", Blt_LegendX(graphPtr->legend),
1152 Blt_LegendY(graphPtr->legend),
1153 Blt_LegendWidth(graphPtr->legend),
1154 Blt_LegendHeight(graphPtr->legend));
1155 Tcl_SetResult(interp, string, TCL_VOLATILE);
1156 } else if ((c == 'l') && (length > 2) &&
1157 (strncmp("leftmargin", argv[2], length) == 0)) {
1158 Tcl_SetResult(interp, Blt_Itoa(graphPtr->leftMargin.width),
1159 TCL_VOLATILE);
1160 } else if ((c == 'r') && (length > 1) &&
1161 (strncmp("rightmargin", argv[2], length) == 0)) {
1162 Tcl_SetResult(interp, Blt_Itoa(graphPtr->rightMargin.width),
1163 TCL_VOLATILE);
1164 } else if ((c == 't') && (length > 1) &&
1165 (strncmp("topmargin", argv[2], length) == 0)) {
1166 Tcl_SetResult(interp, Blt_Itoa(graphPtr->topMargin.height), TCL_VOLATILE);
1167 } else if ((c == 'b') && (length > 1) &&
1168 (strncmp("bottommargin", argv[2], length) == 0)) {
1169 Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottomMargin.height),
1170 TCL_VOLATILE);
1171 } else {
1172 Tcl_AppendResult(interp, "bad extent item \"", argv[2],
1173 "\": should be plotheight, plotwidth, leftmargin, rightmargin, \
1174topmargin, bottommargin, plotarea, or legend", (char *)NULL);
1175 return TCL_ERROR;
1176 }
1177 return TCL_OK;
1178}
1179
1180/*
1181 *--------------------------------------------------------------
1182 *
1183 * InsideOp --
1184 *
1185 * Returns true of false whether the given point is inside
1186 * the plotting area (defined by left,bottom right, top).
1187 *
1188 * Results:
1189 * Always returns TCL_OK. interp->result will contain
1190 * the boolean string representation.
1191 *
1192 *--------------------------------------------------------------
1193 */
1194/* ARGSUSED*/
1195static int
1196InsideOp(graphPtr, interp, argc, argv)
1197 Graph *graphPtr;
1198 Tcl_Interp *interp;
1199 int argc; /* Not used. */
1200 char **argv;
1201{
1202 int x, y;
1203 Extents2D exts;
1204 int result;
1205
1206 if (Tk_GetPixels(interp, graphPtr->tkwin, argv[2], &x) != TCL_OK) {
1207 return TCL_ERROR;
1208 }
1209 if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &y) != TCL_OK) {
1210 return TCL_ERROR;
1211 }
1212 Blt_GraphExtents(graphPtr, &exts);
1213 result = PointInRegion(&exts, x, y);
1214 Blt_SetBooleanResult(interp, result);
1215 return TCL_OK;
1216}
1217
1218/*
1219 * -------------------------------------------------------------------------
1220 *
1221 * InvtransformOp --
1222 *
1223 * This procedure returns a list of the graph coordinate
1224 * values corresponding with the given window X and Y
1225 * coordinate positions.
1226 *
1227 * Results:
1228 * Returns a standard Tcl result. If an error occurred while
1229 * parsing the window positions, TCL_ERROR is returned, and
1230 * interp->result will contain the error message. Otherwise
1231 * interp->result will contain a Tcl list of the x and y
1232 * coordinates.
1233 *
1234 * ------------------------------------------------------------------------
1235 */
1236/*ARGSUSED*/
1237static int
1238InvtransformOp(graphPtr, interp, argc, argv)
1239 Graph *graphPtr; /* Graph widget record */
1240 Tcl_Interp *interp;
1241 int argc; /* Not used. */
1242 char **argv;
1243{
1244 double x, y;
1245 Point2D point;
1246 Axis2D axes;
1247
1248 if (Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK ||
1249 Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK) {
1250 return TCL_ERROR;
1251 }
1252 if (graphPtr->flags & RESET_AXES) {
1253 Blt_ResetAxes(graphPtr);
1254 }
1255 /* Perform the reverse transformation, converting from window
1256 * coordinates to graph data coordinates. Note that the point is
1257 * always mapped to the bottom and left axes (which may not be
1258 * what the user wants). */
1259
1260 /* Pick the first pair of axes */
1261 axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
1262 axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
1263 point = Blt_InvMap2D(graphPtr, x, y, &axes);
1264
1265 Tcl_AppendElement(interp, Blt_Dtoa(interp, point.x));
1266 Tcl_AppendElement(interp, Blt_Dtoa(interp, point.y));
1267 return TCL_OK;
1268}
1269
1270/*
1271 * --------------------------------------------------------------------------
1272 *
1273 * TransformOp --
1274 *
1275 * This procedure returns a list of the window coordinates
1276 * corresponding with the given graph x and y coordinates.
1277 *
1278 * Results:
1279 * Returns a standard Tcl result. interp->result contains
1280 * the list of the graph coordinates. If an error occurred
1281 * while parsing the window positions, TCL_ERROR is returned,
1282 * then interp->result will contain an error message.
1283 *
1284 * -------------------------------------------------------------------------
1285 */
1286/*ARGSUSED*/
1287static int
1288TransformOp(graphPtr, interp, argc, argv)
1289 Graph *graphPtr; /* Graph widget record */
1290 Tcl_Interp *interp;
1291 int argc; /* Not used. */
1292 char **argv;
1293{
1294 double x, y;
1295 Point2D point;
1296 Axis2D axes;
1297
1298 if ((Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK) ||
1299 (Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK)) {
1300 return TCL_ERROR;
1301 }
1302 if (graphPtr->flags & RESET_AXES) {
1303 Blt_ResetAxes(graphPtr);
1304 }
1305 /*
1306 * Perform the transformation from window to graph coordinates.
1307 * Note that the points are always mapped onto the bottom and left
1308 * axes (which may not be the what the user wants).
1309 */
1310 axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
1311 axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
1312
1313 point = Blt_Map2D(graphPtr, x, y, &axes);
1314 Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.x)));
1315 Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.y)));
1316 return TCL_OK;
1317}
1318
1319#ifndef NO_PRINTER
1320
1321/*
1322 * --------------------------------------------------------------------------
1323 *
1324 * Print1Op --
1325 *
1326 * Prints the equivalent of a screen snapshot of the graph
1327 * to the designated printer.
1328 *
1329 * Results:
1330 * Returns a standard Tcl result. If an error occurred
1331 * TCL_ERROR is returned and interp->result will contain an
1332 * error message.
1333 *
1334 * -------------------------------------------------------------------------
1335 */
1336/*ARGSUSED*/
1337static int
1338Print1Op(graphPtr, interp, argc, argv)
1339 Graph *graphPtr; /* Graph widget record */
1340 Tcl_Interp *interp;
1341 int argc; /* Not used. */
1342 char **argv;
1343{
1344 int noBackingStore = 0;
1345 BITMAPINFO info;
1346 void *data;
1347 TkWinDCState state;
1348 TkWinBitmap bd;
1349 DIBSECTION ds;
1350 Drawable drawable;
1351 HBITMAP hBitmap;
1352 HDC hDC;
1353 DOCINFO di;
1354 double pageWidth, pageHeight;
1355 int result;
1356 double scale, sx, sy;
1357 int jobId;
1358
1359 graphPtr->width = Tk_Width(graphPtr->tkwin);
1360 graphPtr->height = Tk_Height(graphPtr->tkwin);
1361 if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
1362 graphPtr->width = graphPtr->reqWidth;
1363 }
1364 if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
1365 graphPtr->height = graphPtr->reqHeight;
1366 }
1367 if (argc == 2) {
1368 result = Blt_PrintDialog(interp, &drawable);
1369 if (result == TCL_ERROR) {
1370 return TCL_ERROR;
1371 }
1372 if (result == TCL_RETURN) {
1373 return TCL_OK;
1374 }
1375 } else {
1376 if (Blt_GetOpenPrinter(interp, argv[2], &drawable) != TCL_OK) {
1377 return TCL_ERROR;
1378 }
1379 }
1380 /*
1381 * This is a taken from Blt_SnapPhoto. The difference is that
1382 * here we're using the DIBSection directly, without converting
1383 * the section into a ColorImage.
1384 */
1385 ZeroMemory(&info, sizeof(info));
1386 info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1387 info.bmiHeader.biWidth = graphPtr->width;
1388 info.bmiHeader.biHeight = graphPtr->height;
1389 info.bmiHeader.biPlanes = 1;
1390 info.bmiHeader.biBitCount = 32;
1391 info.bmiHeader.biCompression = BI_RGB;
1392 hDC = TkWinGetDrawableDC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
1393 &state);
1394 hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
1395 TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hDC, &state);
1396
1397 /*
1398 * Create our own drawable by hand using the DIB we just created.
1399 * We'll then draw into it using the standard drawing functions.
1400 */
1401 bd.type = TWD_BITMAP;
1402 bd.handle = hBitmap;
1403 bd.colormap = DefaultColormap(graphPtr->display,
1404 DefaultScreen(graphPtr->display));
1405 bd.depth = Tk_Depth(graphPtr->tkwin);
1406
1407 graphPtr->flags |= RESET_WORLD;
1408 Blt_DrawGraph(graphPtr, (Drawable)&bd, noBackingStore);
1409
1410 /*
1411 * Now that the DIB contains the image of the graph, get the the
1412 * data bits and write them to the printer device, stretching the
1413 * image to the fit the printer's resolution.
1414 */
1415 result = TCL_ERROR;
1416 if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
1417 Tcl_AppendResult(interp, "can't get object: ", Blt_LastError(),
1418 (char *)NULL);
1419 goto done;
1420 }
1421 hDC = ((TkWinDC *) drawable)->hdc;
1422 /* Get the resolution of the printer device. */
1423 sx = (double)GetDeviceCaps(hDC, HORZRES) / (double)graphPtr->width;
1424 sy = (double)GetDeviceCaps(hDC, VERTRES) / (double)graphPtr->height;
1425 scale = MIN(sx, sy);
1426 pageWidth = scale * graphPtr->width;
1427 pageHeight = scale * graphPtr->height;
1428
1429 ZeroMemory(&di, sizeof(di));
1430 di.cbSize = sizeof(di);
1431 di.lpszDocName = "Graph Contents";
1432 jobId = StartDoc(hDC, &di);
1433 if (jobId <= 0) {
1434 Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(),
1435 (char *)NULL);
1436 goto done;
1437 }
1438 if (StartPage(hDC) <= 0) {
1439 Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(),
1440 (char *)NULL);
1441 goto done;
1442 }
1443 StretchDIBits(hDC, 0, 0, ROUND(pageWidth), ROUND(pageHeight), 0, 0,
1444 graphPtr->width, graphPtr->height, ds.dsBm.bmBits,
1445 (LPBITMAPINFO)&ds.dsBmih, DIB_RGB_COLORS, SRCCOPY);
1446 EndPage(hDC);
1447 EndDoc(hDC);
1448 result = TCL_OK;
1449 done:
1450 DeleteBitmap(hBitmap);
1451 return result;
1452}
1453
1454/*
1455 * --------------------------------------------------------------------------
1456 *
1457 * Print2Op --
1458 *
1459 * Prints directly to the designated printer device.
1460 *
1461 * Results:
1462 * Returns a standard Tcl result. If an error occurred,
1463 * TCL_ERROR is returned and interp->result will contain an
1464 * error message.
1465 *
1466 * -------------------------------------------------------------------------
1467 */
1468/*ARGSUSED*/
1469static int
1470Print2Op(graphPtr, interp, argc, argv)
1471 Graph *graphPtr; /* Graph widget record */
1472 Tcl_Interp *interp;
1473 int argc; /* Not used. */
1474 char **argv;
1475{
1476 Drawable drawable;
1477 int noBackingStore = 0;
1478 int result;
1479
1480 graphPtr->width = Tk_Width(graphPtr->tkwin);
1481 graphPtr->height = Tk_Height(graphPtr->tkwin);
1482 if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
1483 graphPtr->width = graphPtr->reqWidth;
1484 }
1485 if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
1486 graphPtr->height = graphPtr->reqHeight;
1487 }
1488 if (argc == 2) {
1489 result = Blt_PrintDialog(interp, &drawable);
1490 if (result == TCL_ERROR) {
1491 return TCL_ERROR;
1492 }
1493 if (result == TCL_RETURN) {
1494 return TCL_OK;
1495 }
1496 } else {
1497 result = Blt_GetOpenPrinter(interp, argv[2], &drawable);
1498 }
1499 if (result == TCL_OK) {
1500 int oldMode;
1501 HDC hDC;
1502 double xRatio, yRatio;
1503 TkWinDC *drawPtr;
1504 double vportWidth, vportHeight;
1505
1506 drawPtr = (TkWinDC *) drawable;
1507 hDC = drawPtr->hdc;
1508 Blt_GetPrinterScale(hDC, &xRatio, &yRatio);
1509 oldMode = SetMapMode(hDC, MM_ISOTROPIC);
1510 if (oldMode == 0) {
1511 Tcl_AppendResult(interp, "can't set mode for printer DC: ",
1512 Blt_LastError(), (char *)NULL);
1513 return TCL_ERROR;
1514 }
1515 vportWidth = graphPtr->width * xRatio;
1516 vportHeight = graphPtr->height * yRatio;
1517 SetViewportExtEx(hDC, ROUND(vportWidth), ROUND(vportHeight), NULL);
1518 SetWindowExtEx(hDC, graphPtr->width, graphPtr->height, NULL);
1519
1520 Blt_StartPrintJob(interp, drawable);
1521 graphPtr->flags |= RESET_WORLD;
1522 Blt_DrawGraph(graphPtr, drawable, noBackingStore);
1523 Blt_EndPrintJob(interp, drawable);
1524 }
1525 return result;
1526}
1527
1528#endif /* NO_PRINTER */
1529
1530/*
1531 *----------------------------------------------------------------------
1532 *
1533 * StringToFormat --
1534 *
1535 * Convert a string represent a node number into its integer
1536 * value.
1537 *
1538 * Results:
1539 * The return value is a standard Tcl result.
1540 *
1541 *----------------------------------------------------------------------
1542 */
1543/*ARGSUSED*/
1544static int
1545StringToFormat(clientData, interp, switchName, string, record, offset)
1546 ClientData clientData; /* Contains a pointer to the tabset containing
1547 * this image. */
1548 Tcl_Interp *interp; /* Interpreter to send results back to */
1549 char *switchName; /* Not used. */
1550 char *string; /* String representation */
1551 char *record; /* Structure record */
1552 int offset; /* Offset to field in structure */
1553{
1554 int *formatPtr = (int *)(record + offset);
1555 char c;
1556
1557 c = string[0];
1558 if ((c == 'p') && (strcmp(string, "photo") == 0)) {
1559 *formatPtr = FORMAT_PHOTO;
1560#ifdef WIN32
1561 } else if ((c == 'e') && (strcmp(string, "emf") == 0)) {
1562 *formatPtr = FORMAT_EMF;
1563 } else if ((c == 'w') && (strcmp(string, "wmf") == 0)) {
1564 *formatPtr = FORMAT_WMF;
1565#endif /* WIN32 */
1566 } else {
1567#ifdef WIN32
1568 Tcl_AppendResult(interp, "bad format \"", string,
1569 "\": should be photo, emf, or wmf.", (char *)NULL);
1570#else
1571 Tcl_AppendResult(interp, "bad format \"", string,
1572 "\": should be photo.", (char *)NULL);
1573#endif /* WIN32 */
1574 return TCL_ERROR;
1575 }
1576 return TCL_OK;
1577}
1578
1579#ifdef WIN32
1580static int InitMetaFileHeader(
1581 Tk_Window tkwin,
1582 int width, int height,
1583 APMHEADER *mfhPtr)
1584{
1585 unsigned int *p;
1586 unsigned int sum;
1587 Screen *screen;
1588#define MM_INCH 25.4
1589 double dpiX, dpiY;
1590
1591 mfhPtr->key = 0x9ac6cdd7L;
1592 mfhPtr->hmf = 0;
1593 mfhPtr->inch = 1440;
1594
1595 screen = Tk_Screen(tkwin);
1596 dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
1597 dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
1598
1599 mfhPtr->bbox.Left = mfhPtr->bbox.Top = 0;
1600 mfhPtr->bbox.Bottom = (SHORT)((width * 1440)/ dpiX);
1601 mfhPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
1602 mfhPtr->reserved = 0;
1603 sum = 0;
1604 for (p = (unsigned int *)mfhPtr;
1605 p < (unsigned int *)&(mfhPtr->checksum); p++) {
1606 sum ^= *p;
1607 }
1608 mfhPtr->checksum = sum;
1609 return TCL_OK;
1610}
1611
1612static int
1613CreateAPMetaFile(
1614 Tcl_Interp *interp,
1615 HANDLE hMetaFile,
1616 HDC hDC,
1617 APMHEADER *mfhPtr,
1618 char *fileName)
1619{
1620 HANDLE hFile;
1621 HANDLE hMem;
1622 LPVOID buffer;
1623 int result;
1624 DWORD count, nBytes;
1625
1626 result = TCL_ERROR;
1627 hMem = NULL;
1628 hFile = CreateFile(
1629 fileName, /* File path */
1630 GENERIC_WRITE, /* Access mode */
1631 0, /* No sharing. */
1632 NULL, /* Security attributes */
1633 CREATE_ALWAYS, /* Overwrite any existing file */
1634 FILE_ATTRIBUTE_NORMAL,
1635 NULL); /* No template file */
1636 if (hFile == INVALID_HANDLE_VALUE) {
1637 Tcl_AppendResult(interp, "can't create metafile \"", fileName,
1638 "\":", Blt_LastError(), (char *)NULL);
1639 return TCL_ERROR;
1640 }
1641 if ((!WriteFile(hFile, (LPVOID)mfhPtr, sizeof(APMHEADER), &count,
1642 NULL)) || (count != sizeof(APMHEADER))) {
1643 Tcl_AppendResult(interp, "can't create metafile header to \"",
1644 fileName, "\":", Blt_LastError(), (char *)NULL);
1645 goto error;
1646 }
1647 nBytes = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hDC);
1648 hMem = GlobalAlloc(GHND, nBytes);
1649 if (hMem == NULL) {
1650 Tcl_AppendResult(interp, "can't create allocate global memory:",
1651 Blt_LastError(), (char *)NULL);
1652 goto error;
1653 }
1654 buffer = (LPVOID)GlobalLock(hMem);
1655 if (!GetWinMetaFileBits(hMetaFile, nBytes, buffer, MM_ANISOTROPIC, hDC)) {
1656 Tcl_AppendResult(interp, "can't get metafile bits:",
1657 Blt_LastError(), (char *)NULL);
1658 goto error;
1659 }
1660 if ((!WriteFile(hFile, buffer, nBytes, &count, NULL)) ||
1661 (count != nBytes)) {
1662 Tcl_AppendResult(interp, "can't write metafile bits:",
1663 Blt_LastError(), (char *)NULL);
1664 goto error;
1665 }
1666 result = TCL_OK;
1667 error:
1668 CloseHandle(hFile);
1669 if (hMem != NULL) {
1670 GlobalUnlock(hMem);
1671 GlobalFree(hMem);
1672 }
1673 return result;
1674}
1675#endif /*WIN32*/
1676
1677/*
1678 * --------------------------------------------------------------------------
1679 *
1680 * SnapOp --
1681 *
1682 * Snaps a picture of the graph and stores it in the specified image
1683 *
1684 * Results:
1685 * Returns a standard Tcl result. interp->result contains
1686 * the list of the graph coordinates. If an error occurred
1687 * while parsing the window positions, TCL_ERROR is returned,
1688 * then interp->result will contain an error message.
1689 *
1690 * -------------------------------------------------------------------------
1691 */
1692/*ARGSUSED*/
1693static int
1694SnapOp(graphPtr, interp, argc, argv)
1695 Graph *graphPtr; /* Graph widget record */
1696 Tcl_Interp *interp;
1697 int argc; /* Not used. */
1698 char **argv;
1699{
1700 int result;
1701 Pixmap drawable;
1702 int noBackingStore = 0;
1703 register int i;
1704 SnapData data;
1705
1706 /* .g snap ?switches? name */
1707 data.height = Tk_Height(graphPtr->tkwin);
1708 data.width = Tk_Width(graphPtr->tkwin);
1709 data.format = FORMAT_PHOTO;
1710 /* Process switches */
1711 i = Blt_ProcessSwitches(interp, snapSwitches, argc - 2, argv + 2,
1712 (char *)&data, BLT_SWITCH_OBJV_PARTIAL);
1713 if (i < 0) {
1714 return TCL_ERROR;
1715 }
1716 i += 2;
1717 if (i >= argc) {
1718 Tcl_AppendResult(interp, "missing name argument: should be \"",
1719 argv[0], "snap ?switches? name\"", (char *)NULL);
1720 return TCL_ERROR;
1721 }
1722 data.name = argv[i];
1723 if (data.width < 2) {
1724 data.width = 400;
1725 }
1726 if (data.height < 2) {
1727 data.height = 400;
1728 }
1729 /* Always re-compute the layout of the graph before snapping the photo. */
1730 graphPtr->width = data.width;
1731 graphPtr->height = data.height;
1732 Blt_LayoutGraph(graphPtr);
1733
1734 drawable = Tk_WindowId(graphPtr->tkwin);
1735 if (data.format == FORMAT_PHOTO) {
1736 drawable = Tk_GetPixmap(graphPtr->display, drawable, graphPtr->width,
1737 graphPtr->height, Tk_Depth(graphPtr->tkwin));
1738#ifdef WIN32
1739 assert(drawable != None);
1740#endif
1741 graphPtr->flags |= RESET_WORLD;
1742 Blt_DrawGraph(graphPtr, drawable, noBackingStore);
1743 result = Blt_SnapPhoto(interp, graphPtr->tkwin, drawable, 0, 0,
1744 data.width, data.height, data.width, data.height, data.name, 1.0);
1745 Tk_FreePixmap(graphPtr->display, drawable);
1746#ifdef WIN32
1747 } else if ((data.format == FORMAT_WMF) || (data.format == FORMAT_EMF)) {
1748 TkWinDC drawableDC;
1749 TkWinDCState state;
1750 HDC hRefDC, hDC;
1751 HENHMETAFILE hMetaFile;
1752 Tcl_DString dString;
1753 char *title;
1754
1755 hRefDC = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
1756
1757 Tcl_DStringInit(&dString);
1758 Tcl_DStringAppend(&dString, "BLT Graph ", -1);
1759 Tcl_DStringAppend(&dString, BLT_VERSION, -1);
1760 Tcl_DStringAppend(&dString, "\0", -1);
1761 Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
1762 Tcl_DStringAppend(&dString, "\0", -1);
1763 title = Tcl_DStringValue(&dString);
1764 hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, title);
1765 Tcl_DStringFree(&dString);
1766
1767 if (hDC == NULL) {
1768 Tcl_AppendResult(interp, "can't create metafile: ",
1769 Blt_LastError(), (char *)NULL);
1770 return TCL_ERROR;
1771 }
1772
1773 drawableDC.hdc = hDC;
1774 drawableDC.type = TWD_WINDC;
1775
1776 Blt_LayoutGraph(graphPtr);
1777 graphPtr->flags |= RESET_WORLD;
1778 Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
1779
1780 hMetaFile = CloseEnhMetaFile(hDC);
1781 if (strcmp(data.name, "CLIPBOARD") == 0) {
1782 HWND hWnd;
1783
1784 hWnd = Tk_GetHWND(drawable);
1785 OpenClipboard(hWnd);
1786 EmptyClipboard();
1787 SetClipboardData(CF_ENHMETAFILE, hMetaFile);
1788 CloseClipboard();
1789 result = TCL_OK;
1790 } else {
1791 result = TCL_ERROR;
1792 if (data.format == FORMAT_WMF) {
1793 APMHEADER mfh;
1794
1795 assert(sizeof(mfh) == 22);
1796 InitMetaFileHeader(graphPtr->tkwin, data.width, data.height,
1797 &mfh);
1798 result = CreateAPMetaFile(interp, hMetaFile, hRefDC, &mfh,
1799 data.name);
1800 } else {
1801 HENHMETAFILE hMetaFile2;
1802
1803 hMetaFile2 = CopyEnhMetaFile(hMetaFile, data.name);
1804 if (hMetaFile2 != NULL) {
1805 result = TCL_OK;
1806 DeleteEnhMetaFile(hMetaFile2);
1807 }
1808 }
1809 DeleteEnhMetaFile(hMetaFile);
1810 }
1811 TkWinReleaseDrawableDC(drawable, hRefDC, &state);
1812#endif /*WIN32*/
1813 } else {
1814 Tcl_AppendResult(interp, "bad snapshot format", (char *)NULL);
1815 return TCL_ERROR;
1816 }
1817 graphPtr->flags = MAP_WORLD;
1818 Blt_EventuallyRedrawGraph(graphPtr);
1819 return result;
1820}
1821
1822/*
1823 * --------------------------------------------------------------------------
1824 *
1825 * GraphWidgetCmd --
1826 *
1827 * This procedure is invoked to process the Tcl command that
1828 * corresponds to a widget managed by this module. See the user
1829 * documentation for details on what it does.
1830 *
1831 * Results:
1832 * A standard Tcl result.
1833 *
1834 * Side effects:
1835 * See the user documentation.
1836 *
1837 * --------------------------------------------------------------------------
1838 */
1839static Blt_OpSpec graphOps[] =
1840{
1841 {"axis", 1, (Blt_Op)Blt_VirtualAxisOp, 2, 0, "oper ?args?",},
1842 {"bar", 2, (Blt_Op)BarOp, 2, 0, "oper ?args?",},
1843 {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
1844 {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
1845 {"crosshairs", 2, (Blt_Op)Blt_CrosshairsOp, 2, 0, "oper ?args?",},
1846 {"element", 2, (Blt_Op)ElementOp, 2, 0, "oper ?args?",},
1847 {"extents", 2, (Blt_Op)ExtentsOp, 3, 3, "item",},
1848 {"grid", 1, (Blt_Op)Blt_GridOp, 2, 0, "oper ?args?",},
1849 {"inside", 3, (Blt_Op)InsideOp, 4, 4, "winX winY",},
1850 {"invtransform", 3, (Blt_Op)InvtransformOp, 4, 4, "winX winY",},
1851 {"legend", 2, (Blt_Op)Blt_LegendOp, 2, 0, "oper ?args?",},
1852 {"line", 2, (Blt_Op)LineOp, 2, 0, "oper ?args?",},
1853 {"marker", 2, (Blt_Op)Blt_MarkerOp, 2, 0, "oper ?args?",},
1854 {"pen", 2, (Blt_Op)Blt_PenOp, 2, 0, "oper ?args?",},
1855 {"postscript", 2, (Blt_Op)Blt_PostScriptOp, 2, 0, "oper ?args?",},
1856#ifndef NO_PRINTER
1857 {"print1", 2, (Blt_Op)Print1Op, 2, 3, "?printerName?",},
1858 {"print2", 2, (Blt_Op)Print2Op, 2, 3, "?printerName?",},
1859#endif /*NO_PRINTER*/
1860 {"snap", 1, (Blt_Op)SnapOp, 3, 0, "?switches? name",},
1861 {"transform", 1, (Blt_Op)TransformOp, 4, 4, "x y",},
1862 {"x2axis", 2, (Blt_Op)X2AxisOp, 2, 0, "oper ?args?",},
1863 {"xaxis", 2, (Blt_Op)XAxisOp, 2, 0, "oper ?args?",},
1864 {"y2axis", 2, (Blt_Op)Y2AxisOp, 2, 0, "oper ?args?",},
1865 {"yaxis", 2, (Blt_Op)YAxisOp, 2, 0, "oper ?args?",},
1866};
1867static int nGraphOps = sizeof(graphOps) / sizeof(Blt_OpSpec);
1868
1869int
1870Blt_GraphInstCmdProc(clientData, interp, argc, argv)
1871 ClientData clientData;
1872 Tcl_Interp *interp;
1873 int argc;
1874 char **argv;
1875{
1876 Blt_Op proc;
1877 int result;
1878 Graph *graphPtr = clientData;
1879
1880 proc = Blt_GetOp(interp, nGraphOps, graphOps, BLT_OP_ARG1, argc, argv, 0);
1881 if (proc == NULL) {
1882 return TCL_ERROR;
1883 }
1884 Tcl_Preserve(graphPtr);
1885 result = (*proc) (graphPtr, interp, argc, argv);
1886 Tcl_Release(graphPtr);
1887 return result;
1888}
1889
1890/*
1891 * --------------------------------------------------------------------------
1892 *
1893 * NewGraph --
1894 *
1895 * Creates a new window and Tcl command representing an
1896 * instance of a graph widget.
1897 *
1898 * Results:
1899 * A standard Tcl result.
1900 *
1901 * Side effects:
1902 * See the user documentation.
1903 *
1904 * --------------------------------------------------------------------------
1905 */
1906static int
1907NewGraph(interp, argc, argv, classUid)
1908 Tcl_Interp *interp;
1909 int argc;
1910 char **argv;
1911 Blt_Uid classUid;
1912{
1913 Graph *graphPtr;
1914
1915 if (argc < 2) {
1916 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1917 " pathName ?option value?...\"", (char *)NULL);
1918 return TCL_ERROR;
1919 }
1920 graphPtr = CreateGraph(interp, argc, argv, classUid);
1921 if (graphPtr == NULL) {
1922 return TCL_ERROR;
1923 }
1924 Tcl_SetResult(interp, Tk_PathName(graphPtr->tkwin), TCL_VOLATILE);
1925 return TCL_OK;
1926}
1927
1928/*
1929 * --------------------------------------------------------------------------
1930 *
1931 * GraphCmd --
1932 *
1933 * Creates a new window and Tcl command representing an
1934 * instance of a graph widget.
1935 *
1936 * Results:
1937 * A standard Tcl result.
1938 *
1939 * Side effects:
1940 * See the user documentation.
1941 *
1942 * --------------------------------------------------------------------------
1943 */
1944/*ARGSUSED*/
1945static int
1946GraphCmd(clientData, interp, argc, argv)
1947 ClientData clientData; /* Not used. */
1948 Tcl_Interp *interp;
1949 int argc;
1950 char **argv;
1951{
1952 return NewGraph(interp, argc, argv, bltLineElementUid);
1953}
1954
1955/*
1956 *--------------------------------------------------------------
1957 *
1958 * BarchartCmd --
1959 *
1960 * Creates a new window and Tcl command representing an
1961 * instance of a barchart widget.
1962 *
1963 * Results:
1964 * A standard Tcl result.
1965 *
1966 * Side effects:
1967 * See the user documentation.
1968 *
1969 *--------------------------------------------------------------
1970 */
1971/*ARGSUSED*/
1972static int
1973BarchartCmd(clientData, interp, argc, argv)
1974 ClientData clientData; /* Not used. */
1975 Tcl_Interp *interp;
1976 int argc;
1977 char **argv;
1978{
1979 return NewGraph(interp, argc, argv, bltBarElementUid);
1980}
1981
1982/*
1983 *--------------------------------------------------------------
1984 *
1985 * StripchartCmd --
1986 *
1987 * Creates a new window and Tcl command representing an
1988 * instance of a barchart widget.
1989 *
1990 * Results:
1991 * A standard Tcl result.
1992 *
1993 * Side effects:
1994 * See the user documentation.
1995 *
1996 *--------------------------------------------------------------
1997 */
1998/*ARGSUSED*/
1999static int
2000StripchartCmd(clientData, interp, argc, argv)
2001 ClientData clientData; /* Not used. */
2002 Tcl_Interp *interp;
2003 int argc;
2004 char **argv;
2005{
2006 return NewGraph(interp, argc, argv, bltStripElementUid);
2007}
2008
2009/*
2010 * -----------------------------------------------------------------------
2011 *
2012 * DrawMargins --
2013 *
2014 * Draws the exterior region of the graph (axes, ticks, titles, etc)
2015 * onto a pixmap. The interior region is defined by the given
2016 * rectangle structure.
2017 *
2018 * ---------------------------------
2019 * | |
2020 * | rectArr[0] |
2021 * | |
2022 * ---------------------------------
2023 * | |top right| |
2024 * | | | |
2025 * | | | |
2026 * | [1] | | [2] |
2027 * | | | |
2028 * | | | |
2029 * | | | |
2030 * | | | |
2031 * | | | |
2032 * | |left bottom| |
2033 * ---------------------------------
2034 * | |
2035 * | rectArr[3] |
2036 * | |
2037 * ---------------------------------
2038 *
2039 * X coordinate axis
2040 * Y coordinate axis
2041 * legend
2042 * interior border
2043 * exterior border
2044 * titles (X and Y axis, graph)
2045 *
2046 * Returns:
2047 * None.
2048 *
2049 * Side Effects:
2050 * Exterior of graph is displayed in its window.
2051 *
2052 * -----------------------------------------------------------------------
2053 */
2054static void
2055DrawMargins(graphPtr, drawable)
2056 Graph *graphPtr;
2057 Drawable drawable; /* Pixmap or window to draw into */
2058{
2059 XRectangle rects[4];
2060 /*
2061 * Draw the four outer rectangles which encompass the plotting
2062 * surface. This clears the surrounding area and clips the plot.
2063 */
2064 rects[0].x = rects[0].y = rects[3].x = rects[1].x = 0;
2065 rects[0].width = rects[3].width = (short int)graphPtr->width;
2066 rects[0].height = (short int)graphPtr->top;
2067 rects[3].y = graphPtr->bottom;
2068 rects[3].height = graphPtr->height - graphPtr->bottom;
2069 rects[2].y = rects[1].y = graphPtr->top;
2070 rects[1].width = graphPtr->left;
2071 rects[2].height = rects[1].height = graphPtr->bottom - graphPtr->top;
2072 rects[2].x = graphPtr->right;
2073 rects[2].width = graphPtr->width - graphPtr->right;
2074
2075 if (graphPtr->tile != NULL) {
2076 Blt_SetTileOrigin(graphPtr->tkwin, graphPtr->tile, 0, 0);
2077 Blt_TileRectangles(graphPtr->tkwin, drawable, graphPtr->tile, rects, 4);
2078 } else {
2079 XFillRectangles(graphPtr->display, drawable, graphPtr->fillGC, rects,
2080 4);
2081 }
2082
2083 /* Draw 3D border around the plotting area */
2084
2085 if (graphPtr->plotBorderWidth > 0) {
2086 int x, y, width, height;
2087
2088 x = graphPtr->left - graphPtr->plotBorderWidth;
2089 y = graphPtr->top - graphPtr->plotBorderWidth;
2090 width = (graphPtr->right - graphPtr->left) +
2091 (2 * graphPtr->plotBorderWidth);
2092 height = (graphPtr->bottom - graphPtr->top) +
2093 (2 * graphPtr->plotBorderWidth);
2094 Blt_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border, x, y,
2095 width, height, graphPtr->plotBorderWidth, graphPtr->plotRelief);
2096 }
2097 if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
2098 /* Legend is drawn on one of the graph margins */
2099 Blt_DrawLegend(graphPtr->legend, drawable);
2100 }
2101 if (graphPtr->title != NULL) {
2102 Blt_DrawText(graphPtr->tkwin, drawable, graphPtr->title,
2103 &graphPtr->titleTextStyle, graphPtr->titleX, graphPtr->titleY);
2104 }
2105 Blt_DrawAxes(graphPtr, drawable);
2106
2107}
2108
2109/*
2110 *----------------------------------------------------------------------
2111 *
2112 * DrawPlotRegion --
2113 *
2114 * Draws the contents of the plotting area. This consists of
2115 * the elements, markers (draw under elements), axis limits,
2116 * grid lines, and possibly the legend. Typically, the output
2117 * will be cached into a backing store pixmap, so that redraws
2118 * can occur quickly.
2119 *
2120 * Results:
2121 * None.
2122 *
2123 *----------------------------------------------------------------------
2124 */
2125static void
2126DrawPlotRegion(graphPtr, drawable)
2127 Graph *graphPtr;
2128 Drawable drawable; /* Pixmap or window to draw into */
2129{
2130 /* Clear the background of the plotting area. */
2131 XFillRectangle(graphPtr->display, drawable, graphPtr->plotFillGC,
2132 graphPtr->left, graphPtr->top, graphPtr->right - graphPtr->left + 1,
2133 graphPtr->bottom - graphPtr->top + 1);
2134
2135 /* Draw the elements, markers, legend, and axis limits. */
2136
2137 if (!graphPtr->gridPtr->hidden) {
2138 Blt_DrawGrid(graphPtr, drawable);
2139 }
2140 Blt_DrawMarkers(graphPtr, drawable, MARKER_UNDER);
2141 if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
2142 (!Blt_LegendIsRaised(graphPtr->legend))) {
2143 Blt_DrawLegend(graphPtr->legend, drawable);
2144 }
2145 Blt_DrawAxisLimits(graphPtr, drawable);
2146 Blt_DrawElements(graphPtr, drawable);
2147}
2148
2149void
2150Blt_LayoutGraph(graphPtr)
2151 Graph *graphPtr;
2152{
2153 if (graphPtr->flags & RESET_AXES) {
2154 Blt_ResetAxes(graphPtr);
2155 }
2156 if (graphPtr->flags & LAYOUT_NEEDED) {
2157 Blt_LayoutMargins(graphPtr);
2158 graphPtr->flags &= ~LAYOUT_NEEDED;
2159 }
2160 /* Compute coordinate transformations for graph components */
2161 if ((graphPtr->vRange > 1) && (graphPtr->hRange > 1)) {
2162 if (graphPtr->flags & MAP_WORLD) {
2163 Blt_MapAxes(graphPtr);
2164 }
2165 Blt_MapElements(graphPtr);
2166 Blt_MapMarkers(graphPtr);
2167 Blt_MapGrid(graphPtr);
2168 graphPtr->flags &= ~(MAP_ALL);
2169 }
2170}
2171
2172void
2173Blt_DrawGraph(graphPtr, drawable, backingStore)
2174 Graph *graphPtr;
2175 Drawable drawable; /* Pixmap or window to draw into */
2176 int backingStore; /* If non-zero, use backing store for
2177 * plotting area. */
2178{
2179 if (backingStore) {
2180 /*
2181 * Create another pixmap to save elements if one doesn't
2182 * already exist or the size of the window has changed.
2183 */
2184 if ((graphPtr->backPixmap == None) ||
2185 (graphPtr->backWidth != graphPtr->width) ||
2186 (graphPtr->backHeight != graphPtr->height)) {
2187
2188 if (graphPtr->backPixmap != None) {
2189 Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
2190 }
2191 graphPtr->backPixmap = Tk_GetPixmap(graphPtr->display,
2192 Tk_WindowId(graphPtr->tkwin), graphPtr->width,
2193 graphPtr->height, Tk_Depth(graphPtr->tkwin));
2194 graphPtr->backWidth = graphPtr->width;
2195 graphPtr->backHeight = graphPtr->height;
2196 graphPtr->flags |= REDRAW_BACKING_STORE;
2197 }
2198 if (graphPtr->flags & REDRAW_BACKING_STORE) {
2199
2200 /* The backing store is new or out-of-date. */
2201
2202 DrawPlotRegion(graphPtr, graphPtr->backPixmap);
2203 graphPtr->flags &= ~REDRAW_BACKING_STORE;
2204 }
2205
2206 /* Copy the pixmap to the one used for drawing the entire graph. */
2207
2208 XCopyArea(graphPtr->display, graphPtr->backPixmap, drawable,
2209 graphPtr->drawGC, graphPtr->left, graphPtr->top,
2210 (graphPtr->right - graphPtr->left + 1),
2211 (graphPtr->bottom - graphPtr->top + 1),
2212 graphPtr->left, graphPtr->top);
2213 } else {
2214 DrawPlotRegion(graphPtr, drawable);
2215 }
2216
2217 /* Draw markers above elements */
2218 Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE);
2219 Blt_DrawActiveElements(graphPtr, drawable);
2220
2221 if (graphPtr->flags & DRAW_MARGINS) {
2222 DrawMargins(graphPtr, drawable);
2223 }
2224 if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
2225 (Blt_LegendIsRaised(graphPtr->legend))) {
2226 Blt_DrawLegend(graphPtr->legend, drawable);
2227 }
2228 /* Draw 3D border just inside of the focus highlight ring. */
2229 if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) {
2230 Blt_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border,
2231 graphPtr->highlightWidth, graphPtr->highlightWidth,
2232 graphPtr->width - 2 * graphPtr->highlightWidth,
2233 graphPtr->height - 2 * graphPtr->highlightWidth,
2234 graphPtr->borderWidth, graphPtr->relief);
2235 }
2236 /* Draw focus highlight ring. */
2237 if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & GRAPH_FOCUS)) {
2238 GC gc;
2239
2240 gc = Tk_GCForColor(graphPtr->highlightColor, drawable);
2241 Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth,
2242 drawable);
2243 }
2244}
2245
2246static void
2247UpdateMarginTraces(graphPtr)
2248 Graph *graphPtr;
2249{
2250 Margin *marginPtr;
2251 int size;
2252 register int i;
2253
2254 for (i = 0; i < 4; i++) {
2255 marginPtr = graphPtr->margins + i;
2256 if (marginPtr->varName != NULL) { /* Trigger variable traces */
2257 if ((marginPtr->site == MARGIN_LEFT) ||
2258 (marginPtr->site == MARGIN_RIGHT)) {
2259 size = marginPtr->width;
2260 } else {
2261 size = marginPtr->height;
2262 }
2263 Tcl_SetVar(graphPtr->interp, marginPtr->varName, Blt_Itoa(size),
2264 TCL_GLOBAL_ONLY);
2265 }
2266 }
2267}
2268
2269/*
2270 *----------------------------------------------------------------------
2271 *
2272 * DisplayGraph --
2273 *
2274 * This procedure is invoked to display a graph widget.
2275 *
2276 * Results:
2277 * None.
2278 *
2279 * Side effects:
2280 * Commands are output to X to display the graph in its
2281 * current mode.
2282 *
2283 *----------------------------------------------------------------------
2284 */
2285static void
2286DisplayGraph(clientData)
2287 ClientData clientData;
2288{
2289 Graph *graphPtr = clientData;
2290 Pixmap drawable;
2291
2292 graphPtr->flags &= ~REDRAW_PENDING;
2293 if (graphPtr->tkwin == NULL) {
2294 return; /* Window destroyed (should not get here) */
2295 }
2296#ifdef notdef
2297 fprintf(stderr, "Calling DisplayGraph(%s)\n", Tk_PathName(graphPtr->tkwin));
2298#endif
2299 if (Blt_GraphUpdateNeeded(graphPtr)) {
2300 /*
2301 * One of the elements of the graph has a vector notification
2302 * pending. This means that the vector will eventually notify
2303 * the graph that its data has changed. Since the graph uses
2304 * the actual vector (not a copy) we need to keep in-sync.
2305 * Therefore don't draw right now but wait until we've been
2306 * notified before redrawing.
2307 */
2308 return;
2309 }
2310 graphPtr->width = Tk_Width(graphPtr->tkwin);
2311 graphPtr->height = Tk_Height(graphPtr->tkwin);
2312 Blt_LayoutGraph(graphPtr);
2313 Blt_UpdateCrosshairs(graphPtr);
2314 if (!Tk_IsMapped(graphPtr->tkwin)) {
2315 /* The graph's window isn't displayed, so don't bother
2316 * drawing anything. By getting this far, we've at least
2317 * computed the coordinates of the graph's new layout. */
2318 return;
2319 }
2320
2321 /* Disable crosshairs before redisplaying to the screen */
2322 Blt_DisableCrosshairs(graphPtr);
2323 /*
2324 * Create a pixmap the size of the window for double buffering.
2325 */
2326 if (graphPtr->doubleBuffer) {
2327 drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
2328 graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
2329 } else {
2330 drawable = Tk_WindowId(graphPtr->tkwin);
2331 }
2332#ifdef WIN32
2333 assert(drawable != None);
2334#endif
2335 Blt_DrawGraph(graphPtr, drawable,
2336 graphPtr->backingStore && graphPtr->doubleBuffer);
2337 if (graphPtr->flags & DRAW_MARGINS) {
2338 XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
2339 graphPtr->drawGC, 0, 0, graphPtr->width, graphPtr->height, 0, 0);
2340 } else {
2341 XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
2342 graphPtr->drawGC, graphPtr->left, graphPtr->top,
2343 (graphPtr->right - graphPtr->left + 1),
2344 (graphPtr->bottom - graphPtr->top + 1),
2345 graphPtr->left, graphPtr->top);
2346 }
2347 if (graphPtr->doubleBuffer) {
2348 Tk_FreePixmap(graphPtr->display, drawable);
2349 }
2350 Blt_EnableCrosshairs(graphPtr);
2351 graphPtr->flags &= ~RESET_WORLD;
2352 UpdateMarginTraces(graphPtr);
2353}
2354
2355/*LINTLIBRARY*/
2356int
2357Blt_GraphInit(interp)
2358 Tcl_Interp *interp;
2359{
2360 static Blt_CmdSpec cmdSpecs[] =
2361 {
2362 {"graph", GraphCmd,},
2363 {"barchart", BarchartCmd,},
2364 {"stripchart", StripchartCmd,},
2365 };
2366 bltBarElementUid = (Blt_Uid)Tk_GetUid("BarElement");
2367 bltLineElementUid = (Blt_Uid)Tk_GetUid("LineElement");
2368 bltStripElementUid = (Blt_Uid)Tk_GetUid("StripElement");
2369 bltContourElementUid = (Blt_Uid)Tk_GetUid("ContourElement");
2370
2371 bltLineMarkerUid = (Blt_Uid)Tk_GetUid("LineMarker");
2372 bltBitmapMarkerUid = (Blt_Uid)Tk_GetUid("BitmapMarker");
2373 bltImageMarkerUid = (Blt_Uid)Tk_GetUid("ImageMarker");
2374 bltTextMarkerUid = (Blt_Uid)Tk_GetUid("TextMarker");
2375 bltPolygonMarkerUid = (Blt_Uid)Tk_GetUid("PolygonMarker");
2376 bltWindowMarkerUid = (Blt_Uid)Tk_GetUid("WindowMarker");
2377
2378 bltXAxisUid = (Blt_Uid)Tk_GetUid("X");
2379 bltYAxisUid = (Blt_Uid)Tk_GetUid("Y");
2380
2381 return Blt_InitCmds(interp, "blt", cmdSpecs, 3);
2382}
2383
2384Graph *
2385Blt_GetGraphFromWindowData(tkwin)
2386 Tk_Window tkwin;
2387{
2388 Graph *graphPtr;
2389
2390 while (tkwin != NULL) {
2391 graphPtr = (Graph *)Blt_GetWindowInstanceData(tkwin);
2392 if (graphPtr != NULL) {
2393 return graphPtr;
2394 }
2395 tkwin = Tk_Parent(tkwin);
2396 }
2397 return NULL;
2398}
2399
2400int
2401Blt_GraphType(graphPtr)
2402 Graph *graphPtr;
2403{
2404 if (graphPtr->classUid == bltLineElementUid) {
2405 return GRAPH;
2406 } else if (graphPtr->classUid == bltBarElementUid) {
2407 return BARCHART;
2408 } else if (graphPtr->classUid == bltStripElementUid) {
2409 return STRIPCHART;
2410 }
2411 return 0;
2412}
Note: See TracBrowser for help on using the repository browser.