--- /dev/null
+
+/*
+ * bltGrMarker.c --
+ *
+ * This module implements markers for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltChain.h"
+#include "bltGrElem.h"
+
+#define GETBITMAP(b) \
+ (((b)->destBitmap == None) ? (b)->srcBitmap : (b)->destBitmap)
+
+#define MAX_OUTLINE_POINTS 12
+
+/* Map graph coordinates to normalized coordinates [0..1] */
+#define NORMALIZE(A,x) (((x) - (A)->axisRange.min) / (A)->axisRange.range)
+
+#define DEF_MARKER_ANCHOR "center"
+#define DEF_MARKER_BACKGROUND RGB_WHITE
+#define DEF_MARKER_BG_MONO RGB_WHITE
+#define DEF_MARKER_BITMAP (char *)NULL
+#define DEF_MARKER_CAP_STYLE "butt"
+#define DEF_MARKER_COORDS (char *)NULL
+#define DEF_MARKER_DASHES (char *)NULL
+#define DEF_MARKER_DASH_OFFSET "0"
+#define DEF_MARKER_ELEMENT (char *)NULL
+#define DEF_MARKER_FOREGROUND RGB_BLACK
+#define DEF_MARKER_FG_MONO RGB_BLACK
+#define DEF_MARKER_FILL_COLOR RGB_RED
+#define DEF_MARKER_FILL_MONO RGB_WHITE
+#define DEF_MARKER_FONT STD_FONT
+#define DEF_MARKER_GAP_COLOR RGB_PINK
+#define DEF_MARKER_GAP_MONO RGB_BLACK
+#define DEF_MARKER_HEIGHT "0"
+#define DEF_MARKER_HIDE "no"
+#define DEF_MARKER_JOIN_STYLE "miter"
+#define DEF_MARKER_JUSTIFY "left"
+#define DEF_MARKER_LINE_WIDTH "1"
+#define DEF_MARKER_MAP_X "x"
+#define DEF_MARKER_MAP_Y "y"
+#define DEF_MARKER_NAME (char *)NULL
+#define DEF_MARKER_OUTLINE_COLOR RGB_BLACK
+#define DEF_MARKER_OUTLINE_MONO RGB_BLACK
+#define DEF_MARKER_PAD "4"
+#define DEF_MARKER_ROTATE "0.0"
+#define DEF_MARKER_SCALE "1.0"
+#define DEF_MARKER_SHADOW_COLOR (char *)NULL
+#define DEF_MARKER_SHADOW_MONO (char *)NULL
+#define DEF_MARKER_STATE "normal"
+#define DEF_MARKER_STIPPLE (char *)NULL
+#define DEF_MARKER_TEXT (char *)NULL
+#define DEF_MARKER_UNDER "no"
+#define DEF_MARKER_WIDTH "0"
+#define DEF_MARKER_WINDOW (char *)NULL
+#define DEF_MARKER_XOR "no"
+#define DEF_MARKER_X_OFFSET "0"
+#define DEF_MARKER_Y_OFFSET "0"
+
+#define DEF_MARKER_TEXT_TAGS "Text all"
+#define DEF_MARKER_IMAGE_TAGS "Image all"
+#define DEF_MARKER_BITMAP_TAGS "Bitmap all"
+#define DEF_MARKER_WINDOW_TAGS "Window all"
+#define DEF_MARKER_POLYGON_TAGS "Polygon all"
+#define DEF_MARKER_LINE_TAGS "Line all"
+
+static Tk_OptionParseProc StringToCoordinates;
+static Tk_OptionPrintProc CoordinatesToString;
+static Tk_CustomOption coordsOption =
+{
+ StringToCoordinates, CoordinatesToString, (ClientData)0
+};
+extern Tk_CustomOption bltColorPairOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltStateOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+
+typedef Marker *(MarkerCreateProc) _ANSI_ARGS_((void));
+typedef void (MarkerDrawProc) _ANSI_ARGS_((Marker *markerPtr,
+ Drawable drawable));
+typedef void (MarkerFreeProc) _ANSI_ARGS_((Graph *graphPtr, Marker *markerPtr));
+typedef int (MarkerConfigProc) _ANSI_ARGS_((Marker *markerPtr));
+typedef void (MarkerMapProc) _ANSI_ARGS_((Marker *markerPtr));
+typedef void (MarkerPostScriptProc) _ANSI_ARGS_((Marker *markerPtr,
+ PsToken psToken));
+typedef int (MarkerPointProc) _ANSI_ARGS_((Marker *markerPtr,
+ Point2D *samplePtr));
+typedef int (MarkerRegionProc) _ANSI_ARGS_((Marker *markerPtr,
+ Extents2D *extsPtr, int enclosed));
+
+typedef struct {
+ Tk_ConfigSpec *configSpecs; /* Marker configuration specifications */
+
+ MarkerConfigProc *configProc;
+ MarkerDrawProc *drawProc;
+ MarkerFreeProc *freeProc;
+ MarkerMapProc *mapProc;
+ MarkerPointProc *pointProc;
+ MarkerRegionProc *regionProc;
+ MarkerPostScriptProc *postscriptProc;
+
+} MarkerClass;
+
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Marker --
+ *
+ * Structure defining the generic marker. In C++ parlance this
+ * would be the base type from which all markers are derived.
+ *
+ * This structure corresponds with the specific types of markers.
+ * Don't change this structure without changing the individual
+ * marker structures of each type below.
+ *
+ * -------------------------------------------------------------------
+ */
+struct MarkerStruct {
+ char *name; /* Identifier for marker in list */
+
+ Blt_Uid classUid; /* Type of marker. */
+
+ Graph *graphPtr; /* Graph widget of marker. */
+
+ unsigned int flags;
+
+ char **tags;
+
+ int hidden; /* If non-zero, don't display the marker. */
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Coordinate array to position marker */
+ int nWorldPts; /* Number of points in above array */
+
+ char *elemName; /* Element associated with marker */
+
+ Axis2D axes;
+
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can
+ * be a performance penalty because
+ * the graph must be redraw entirely
+ * each time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ MarkerClass *classPtr;
+
+ int state;
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * TextMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Blt_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* The graph this marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* If non-zero, don't display the
+ * marker. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (1 X-Y coordinate) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* pixel offset from anchor */
+
+ MarkerClass *classPtr;
+
+ int state;
+ /*
+ * Text specific fields and attributes
+ */
+#ifdef notdef
+ char *textVarName; /* Name of variable (malloc'ed) or
+ * NULL. If non-NULL, graph displays
+ * the contents of this variable. */
+#endif
+ char *string; /* Text string to be display. The string
+ * make contain newlines. */
+
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+
+ Point2D anchorPos; /* Translated anchor point. */
+
+ int width, height; /* Dimension of bounding box. */
+
+ TextStyle style; /* Text attributes (font, fg, anchor, etc) */
+
+ TextLayout *textPtr; /* Contains information about the layout
+ * of the text. */
+ Point2D outline[5];
+ XColor *fillColor;
+ GC fillGC;
+} TextMarker;
+
+
+static Tk_ConfigSpec textConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(TextMarker, anchor), 0},
+ {TK_CONFIG_COLOR, "-background", "background", "MarkerBackground",
+ (char *)NULL, Tk_Offset(TextMarker, fillColor),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", "Background",
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_TEXT_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", "Foreground",
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_MARKER_FONT, Tk_Offset(TextMarker, style.font), 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FOREGROUND, Tk_Offset(TextMarker, style.color),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FG_MONO, Tk_Offset(TextMarker, style.color),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_MARKER_JUSTIFY, Tk_Offset(TextMarker, style.justify),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+ DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+ DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+ DEF_MARKER_ROTATE, Tk_Offset(TextMarker, style.theta),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_MARKER_SHADOW_COLOR, Tk_Offset(TextMarker, style.shadow),
+ TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_MARKER_SHADOW_MONO, Tk_Offset(TextMarker, style.shadow),
+ TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_MARKER_STATE, Tk_Offset(Marker, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_MARKER_TEXT, Tk_Offset(TextMarker, string), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * WindowMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Blt_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is
+ * currently hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (1 X-Y coordinate) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset from anchor. */
+
+ MarkerClass *classPtr;
+
+ int state;
+
+ /*
+ * Window specific attributes
+ */
+ char *pathName; /* Name of child widget to be displayed. */
+ Tk_Window tkwin; /* Window to display. */
+ int reqWidth, reqHeight; /* If non-zero, this overrides the size
+ * requested by the child widget. */
+
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+
+ Point2D anchorPos; /* Translated anchor point. */
+ int width, height; /* Current size of the child window. */
+
+} WindowMarker;
+
+static Tk_ConfigSpec windowConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(WindowMarker, anchor), 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_WINDOW_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(WindowMarker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_MARKER_HEIGHT, Tk_Offset(WindowMarker, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_MARKER_STATE, Tk_Offset(Marker, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_MARKER_WIDTH, Tk_Offset(WindowMarker, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+ {TK_CONFIG_STRING, "-window", "window", "Window",
+ DEF_MARKER_WINDOW, Tk_Offset(WindowMarker, pathName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * BitmapMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Blt_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is currently
+ * hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker in world (graph)
+ * coordinates. If 2 pairs of X-Y
+ * coordinates are specified, then the
+ * bitmap is resized to fit this area.
+ * Otherwise if 1 pair, then the bitmap
+ * is positioned at the coordinate at its
+ * normal size. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ int xOffset, yOffset; /* Pixel offset from origin of bitmap */
+
+ MarkerClass *classPtr;
+
+ int state;
+
+ /* Bitmap specific attributes */
+ Pixmap srcBitmap; /* Original bitmap. May be further
+ * scaled or rotated. */
+ double rotate; /* Requested rotation of the bitmap */
+ double theta; /* Normalized rotation (0..360
+ * degrees) */
+ Tk_Anchor anchor; /* If only one X-Y coordinate is
+ * given, indicates how to translate
+ * the given marker position. Otherwise,
+ * if there are two X-Y coordinates, then
+ * this value is ignored. */
+ Point2D anchorPos; /* Translated anchor point. */
+
+ XColor *outlineColor; /* Foreground color */
+ XColor *fillColor; /* Background color */
+
+ GC gc; /* Private graphic context */
+ GC fillGC; /* Shared graphic context */
+ Pixmap destBitmap; /* Bitmap to be drawn. */
+ int destWidth, destHeight; /* Dimensions of the final bitmap */
+
+ Point2D outline[MAX_OUTLINE_POINTS];
+ /* Polygon representing the background
+ * of the bitmap. */
+ int nOutlinePts;
+} BitmapMarker;
+
+static Tk_ConfigSpec bitmapConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(BitmapMarker, anchor), 0},
+ {TK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MARKER_BACKGROUND, Tk_Offset(BitmapMarker, fillColor),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MARKER_BG_MONO, Tk_Offset(BitmapMarker, fillColor),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_BITMAP_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
+ DEF_MARKER_BITMAP, Tk_Offset(BitmapMarker, srcBitmap),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FOREGROUND, Tk_Offset(BitmapMarker, outlineColor),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FG_MONO, Tk_Offset(BitmapMarker, outlineColor),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+ DEF_MARKER_ROTATE, Tk_Offset(BitmapMarker, rotate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_MARKER_STATE, Tk_Offset(Marker, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * ImageMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Blt_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is
+ * currently hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+ Point2D *worldPts; /* Position of marker in world (graph)
+ * coordinates. If 2 pairs of X-Y
+ * coordinates are specified, then the
+ * image is resized to fit this area.
+ * Otherwise if 1 pair, then the image
+ * is positioned at the coordinate at
+ * its normal size. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset from anchor */
+
+ MarkerClass *classPtr;
+
+ int state;
+
+ /* Image specific attributes */
+ char *imageName; /* Name of image to be displayed. */
+ Tk_Image tkImage; /* Tk image to be displayed. */
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+ Point2D anchorPos; /* Translated anchor point. */
+ int width, height; /* Dimensions of the image */
+ Tk_Image tmpImage;
+ Pixmap pixmap; /* Pixmap containing the scaled image */
+ ColorTable colorTable; /* Pointer to color table */
+ Blt_ColorImage srcImage;
+ GC gc;
+
+} ImageMarker;
+
+static Tk_ConfigSpec imageConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(ImageMarker, anchor), 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_IMAGE_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-image", "image", "Image",
+ (char *)NULL, Tk_Offset(ImageMarker, imageName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_MARKER_STATE, Tk_Offset(Marker, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * LineMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Blt_Uid classUid; /* Type is "linemarker" */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is currently
+ * hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (X-Y coordinates) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset */
+
+ MarkerClass *classPtr;
+
+ int state;
+
+ /* Line specific attributes */
+ XColor *fillColor;
+ XColor *outlineColor; /* Foreground and background colors */
+
+ int lineWidth; /* Line width. */
+ int capStyle; /* Cap style. */
+ int joinStyle; /* Join style.*/
+ Blt_Dashes dashes; /* Dash list values (max 11) */
+
+ GC gc; /* Private graphic context */
+
+ Segment2D *segments; /* Malloc'ed array of points.
+ * Represents individual line segments
+ * (2 points per segment) comprising
+ * the mapped line. The segments may
+ * not necessarily be connected after
+ * clipping. */
+ int nSegments; /* # segments in the above array. */
+
+ int xor;
+ int xorState; /* State of the XOR drawing. Indicates
+ * if the marker is currently drawn. */
+} LineMarker;
+
+static Tk_ConfigSpec lineConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_LINE_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap",
+ DEF_MARKER_CAP_STYLE, Tk_Offset(LineMarker, capStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_MARKER_DASHES, Tk_Offset(LineMarker, dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-dashoffset", "dashOffset", "DashOffset",
+ DEF_MARKER_DASH_OFFSET, Tk_Offset(LineMarker, dashes.offset),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-fill", "fill", "Fill",
+ (char *)NULL, Tk_Offset(LineMarker, fillColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join",
+ DEF_MARKER_JOIN_STYLE, Tk_Offset(LineMarker, joinStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_MARKER_LINE_WIDTH, Tk_Offset(LineMarker, lineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_COLOR, Tk_Offset(LineMarker, outlineColor),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_MONO, Tk_Offset(LineMarker, outlineColor),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_MARKER_STATE, Tk_Offset(Marker, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor",
+ DEF_MARKER_XOR, Tk_Offset(LineMarker, xor), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * PolygonMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Blt_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is currently
+ * hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (X-Y coordinates) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset */
+
+ MarkerClass *classPtr;
+
+ int state;
+
+ /* Polygon specific attributes and fields */
+
+ Point2D *screenPts; /* Array of points representing the
+ * polygon in screen coordinates. It's
+ * not used for drawing, but to
+ * generate the outlinePts and fillPts
+ * arrays that are the coordinates of
+ * the possibly clipped outline and
+ * filled polygon. */
+
+ ColorPair outline;
+ ColorPair fill;
+
+ Pixmap stipple; /* Stipple pattern to fill the polygon. */
+ int lineWidth; /* Width of polygon outline. */
+ int capStyle;
+ int joinStyle;
+ Blt_Dashes dashes; /* List of dash values. Indicates how
+ * draw the dashed line. If no dash
+ * values are provided, or the first value
+ * is zero, then the line is drawn solid. */
+
+ GC outlineGC; /* Graphics context to draw the outline of
+ * the polygon. */
+ GC fillGC; /* Graphics context to draw the filled
+ * polygon. */
+
+ Point2D *fillPts; /* Malloc'ed array of points used to draw
+ * the filled polygon. These points may
+ * form a degenerate polygon after clipping.
+ */
+
+ int nFillPts; /* # points in the above array. */
+
+ Segment2D *outlinePts; /* Malloc'ed array of points.
+ * Represents individual line segments
+ * (2 points per segment) comprising
+ * the outline of the polygon. The
+ * segments may not necessarily be
+ * closed or connected after clipping. */
+
+ int nOutlinePts; /* # points in the above array. */
+
+ int xor;
+ int xorState; /* State of the XOR drawing. Indicates
+ * if the marker is visible. We have
+ * to drawn it again to erase it. */
+} PolygonMarker;
+
+static Tk_ConfigSpec polygonConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_POLYGON_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap",
+ DEF_MARKER_CAP_STYLE, Tk_Offset(PolygonMarker, capStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_MARKER_DASHES, Tk_Offset(PolygonMarker, dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_MARKER_FILL_COLOR, Tk_Offset(PolygonMarker, fill),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_MARKER_FILL_MONO, Tk_Offset(PolygonMarker, fill),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join",
+ DEF_MARKER_JOIN_STYLE, Tk_Offset(PolygonMarker, joinStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_MARKER_LINE_WIDTH, Tk_Offset(PolygonMarker, lineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_COLOR, Tk_Offset(PolygonMarker, outline),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_MONO, Tk_Offset(PolygonMarker, outline),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_MARKER_STATE, Tk_Offset(Marker, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+ DEF_MARKER_STIPPLE, Tk_Offset(PolygonMarker, stipple),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor",
+ DEF_MARKER_XOR, Tk_Offset(PolygonMarker, xor),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerCreateProc CreateBitmapMarker, CreateLineMarker, CreateImageMarker,
+ CreatePolygonMarker, CreateTextMarker, CreateWindowMarker;
+
+static MarkerDrawProc DrawBitmapMarker, DrawLineMarker, DrawImageMarker,
+ DrawPolygonMarker, DrawTextMarker, DrawWindowMarker;
+
+static MarkerFreeProc FreeBitmapMarker, FreeLineMarker, FreeImageMarker,
+ FreePolygonMarker, FreeTextMarker, FreeWindowMarker;
+
+static MarkerConfigProc ConfigureBitmapMarker, ConfigureLineMarker,
+ ConfigureImageMarker, ConfigurePolygonMarker, ConfigureTextMarker,
+ ConfigureWindowMarker;
+
+static MarkerMapProc MapBitmapMarker, MapLineMarker, MapImageMarker,
+ MapPolygonMarker, MapTextMarker, MapWindowMarker;
+
+static MarkerPostScriptProc BitmapMarkerToPostScript, LineMarkerToPostScript,
+ ImageMarkerToPostScript, PolygonMarkerToPostScript,
+ TextMarkerToPostScript, WindowMarkerToPostScript;
+
+static MarkerPointProc PointInBitmapMarker, PointInLineMarker,
+ PointInImageMarker, PointInPolygonMarker, PointInTextMarker,
+ PointInWindowMarker;
+
+static MarkerRegionProc RegionInBitmapMarker, RegionInLineMarker,
+ RegionInImageMarker, RegionInPolygonMarker, RegionInTextMarker,
+ RegionInWindowMarker;
+
+static Tk_ImageChangedProc ImageChangedProc;
+
+static MarkerClass bitmapMarkerClass = {
+ bitmapConfigSpecs,
+ ConfigureBitmapMarker,
+ DrawBitmapMarker,
+ FreeBitmapMarker,
+ MapBitmapMarker,
+ PointInBitmapMarker,
+ RegionInBitmapMarker,
+ BitmapMarkerToPostScript,
+};
+
+static MarkerClass imageMarkerClass = {
+ imageConfigSpecs,
+ ConfigureImageMarker,
+ DrawImageMarker,
+ FreeImageMarker,
+ MapImageMarker,
+ PointInImageMarker,
+ RegionInImageMarker,
+ ImageMarkerToPostScript,
+};
+
+static MarkerClass lineMarkerClass = {
+ lineConfigSpecs,
+ ConfigureLineMarker,
+ DrawLineMarker,
+ FreeLineMarker,
+ MapLineMarker,
+ PointInLineMarker,
+ RegionInLineMarker,
+ LineMarkerToPostScript,
+};
+
+static MarkerClass polygonMarkerClass = {
+ polygonConfigSpecs,
+ ConfigurePolygonMarker,
+ DrawPolygonMarker,
+ FreePolygonMarker,
+ MapPolygonMarker,
+ PointInPolygonMarker,
+ RegionInPolygonMarker,
+ PolygonMarkerToPostScript,
+};
+
+static MarkerClass textMarkerClass = {
+ textConfigSpecs,
+ ConfigureTextMarker,
+ DrawTextMarker,
+ FreeTextMarker,
+ MapTextMarker,
+ PointInTextMarker,
+ RegionInTextMarker,
+ TextMarkerToPostScript,
+};
+
+static MarkerClass windowMarkerClass = {
+ windowConfigSpecs,
+ ConfigureWindowMarker,
+ DrawWindowMarker,
+ FreeWindowMarker,
+ MapWindowMarker,
+ PointInWindowMarker,
+ RegionInWindowMarker,
+ WindowMarkerToPostScript,
+};
+
+#ifdef notdef
+static MarkerClass rectangleMarkerClass = {
+ rectangleConfigSpecs,
+ ConfigureRectangleMarker,
+ DrawRectangleMarker,
+ FreeRectangleMarker,
+ MapRectangleMarker,
+ PointInRectangleMarker,
+ RegionInRectangleMarker,
+ RectangleMarkerToPostScript,
+};
+
+static MarkerClass ovalMarkerClass = {
+ ovalConfigSpecs,
+ ConfigureOvalMarker,
+ DrawOvalMarker,
+ FreeOvalMarker,
+ MapOvalMarker,
+ PointInOvalMarker,
+ RegionInOvalMarker,
+ OvalMarkerToPostScript,
+};
+#endif
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BoxesDontOverlap --
+ *
+ * Tests if the bounding box of a marker overlaps the plotting
+ * area in any way. If so, the marker will be drawn. Just do a
+ * min/max test on the extents of both boxes.
+ *
+ * Note: It's assumed that the extents of the bounding box lie
+ * within the area. So for a 10x10 rectangle, bottom and
+ * left would be 9.
+ *
+ * Results:
+ * Returns 0 is the marker is visible in the plotting area, and
+ * 1 otherwise (marker is clipped).
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+BoxesDontOverlap(graphPtr, extsPtr)
+ Graph *graphPtr;
+ Extents2D *extsPtr;
+{
+ /*if(extsPtr->right >= extsPtr->left || extsPtr->bottom >= extsPtr->top ||
+ graphPtr->right >= graphPtr->left || graphPtr->bottom >= graphPtr->top)*/
+ if(extsPtr->right <= extsPtr->left || extsPtr->bottom <= extsPtr->top ||
+ graphPtr->right <= graphPtr->left || graphPtr->bottom <= graphPtr->top)
+ {
+ return 1;
+ }
+ assert(extsPtr->right >= extsPtr->left);
+ assert(extsPtr->bottom >= extsPtr->top);
+ assert(graphPtr->right >= graphPtr->left);
+ assert(graphPtr->bottom >= graphPtr->top);
+
+ return (((double)graphPtr->right < extsPtr->left) ||
+ ((double)graphPtr->bottom < extsPtr->top) ||
+ (extsPtr->right < (double)graphPtr->left) ||
+ (extsPtr->bottom < (double)graphPtr->top));
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetCoordinate --
+ *
+ * Convert the expression string into a floating point value. The
+ * only reason we use this routine instead of Blt_ExprDouble is to
+ * handle "elastic" bounds. That is, convert the strings "-Inf",
+ * "Inf" into -(DBL_MAX) and DBL_MAX respectively.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The value of the
+ * expression is passed back via valuePtr.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+GetCoordinate(interp, expr, valuePtr)
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *expr; /* Numeric expression string to parse */
+ double *valuePtr; /* Real-valued result of expression */
+{
+ char c;
+
+ c = expr[0];
+ if ((c == 'I') && (strcmp(expr, "Inf") == 0)) {
+ *valuePtr = DBL_MAX; /* Elastic upper bound */
+ } else if ((c == '-') && (expr[1] == 'I') && (strcmp(expr, "-Inf") == 0)) {
+ *valuePtr = -DBL_MAX; /* Elastic lower bound */
+ } else if ((c == '+') && (expr[1] == 'I') && (strcmp(expr, "+Inf") == 0)) {
+ *valuePtr = DBL_MAX; /* Elastic upper bound */
+ } else if (Tcl_ExprDouble(interp, expr, valuePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PrintCoordinate --
+ *
+ * Convert the floating point value into its string
+ * representation. The only reason this routine is used in
+ * instead of sprintf, is to handle the "elastic" bounds. That
+ * is, convert the values DBL_MAX and -(DBL_MAX) into "+Inf" and
+ * "-Inf" respectively.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The string of the
+ * expression is passed back via string.
+ *
+ * ---------------------------------------------------------------------- */
+static char *
+PrintCoordinate(interp, x)
+ Tcl_Interp *interp;
+ double x; /* Numeric value */
+{
+ if (x == DBL_MAX) {
+ return "+Inf";
+ } else if (x == -DBL_MAX) {
+ return "-Inf";
+ } else {
+ static char string[TCL_DOUBLE_SPACE + 1];
+
+ Tcl_PrintDouble(interp, (double)x, string);
+ return string;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ParseCoordinates --
+ *
+ * The Tcl coordinate list is converted to their floating point
+ * values. It will then replace the current marker coordinates.
+ *
+ * Since different marker types require different number of
+ * coordinates this must be checked here.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side effects:
+ * If the marker coordinates are reset, the graph is eventually
+ * redrawn with at the new marker coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ParseCoordinates(interp, markerPtr, nExprs, exprArr)
+ Tcl_Interp *interp;
+ Marker *markerPtr;
+ int nExprs;
+ char **exprArr;
+{
+ int nWorldPts;
+ int minArgs, maxArgs;
+ Point2D *worldPts;
+ register int i;
+ register Point2D *pointPtr;
+ double x, y;
+
+ if (nExprs == 0) {
+ return TCL_OK;
+ }
+ if (nExprs & 1) {
+ Tcl_AppendResult(interp, "odd number of marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (markerPtr->classUid == bltLineMarkerUid) {
+ minArgs = 4, maxArgs = 0;
+ } else if (markerPtr->classUid == bltPolygonMarkerUid) {
+ minArgs = 6, maxArgs = 0;
+ } else if ((markerPtr->classUid == bltWindowMarkerUid) ||
+ (markerPtr->classUid == bltTextMarkerUid)) {
+ minArgs = 2, maxArgs = 2;
+ } else if ((markerPtr->classUid == bltImageMarkerUid) ||
+ (markerPtr->classUid == bltBitmapMarkerUid)) {
+ minArgs = 2, maxArgs = 4;
+ } else {
+ Tcl_AppendResult(interp, "unknown marker type", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if (nExprs < minArgs) {
+ Tcl_AppendResult(interp, "too few marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((maxArgs > 0) && (nExprs > maxArgs)) {
+ Tcl_AppendResult(interp, "too many marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ nWorldPts = nExprs / 2;
+ worldPts = Blt_Malloc(nWorldPts * sizeof(Point2D));
+ if (worldPts == NULL) {
+ Tcl_AppendResult(interp, "can't allocate new coordinate array",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /* Don't free the old coordinate array until we've parsed the new
+ * coordinates without errors. */
+ pointPtr = worldPts;
+ for (i = 0; i < nExprs; i += 2) {
+ if ((GetCoordinate(interp, exprArr[i], &x) != TCL_OK) ||
+ (GetCoordinate(interp, exprArr[i + 1], &y) != TCL_OK)) {
+ Blt_Free(worldPts);
+ return TCL_ERROR;
+ }
+ pointPtr->x = x, pointPtr->y = y;
+ pointPtr++;
+ }
+ if (markerPtr->worldPts != NULL) {
+ Blt_Free(markerPtr->worldPts);
+ }
+ markerPtr->worldPts = worldPts;
+ markerPtr->nWorldPts = nWorldPts;
+ markerPtr->flags |= MAP_ITEM;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToCoordinates --
+ *
+ * Given a Tcl list of numeric expression representing the
+ * element values, convert into an array of floating point
+ * values. In addition, the minimum and maximum values are saved.
+ * Since elastic values are allow (values which translate to the
+ * min/max of the graph), we must try to get the non-elastic
+ * minimum and maximum.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The vector is
+ * passed back via the vecPtr.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToCoordinates(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Tcl list of numeric expressions */
+ char *widgRec; /* Marker record */
+ int offset; /* Not used. */
+{
+ Marker *markerPtr = (Marker *)widgRec;
+ int nExprs;
+ char **exprArr;
+ int result;
+
+ nExprs = 0;
+ if ((string != NULL) &&
+ (Tcl_SplitList(interp, string, &nExprs, &exprArr) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (nExprs == 0) {
+ if (markerPtr->worldPts != NULL) {
+ Blt_Free(markerPtr->worldPts);
+ markerPtr->worldPts = NULL;
+ }
+ markerPtr->nWorldPts = 0;
+ return TCL_OK;
+ }
+ result = ParseCoordinates(interp, markerPtr, nExprs, exprArr);
+ Blt_Free(exprArr);
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CoordinatesToString --
+ *
+ * Convert the vector of floating point values into a Tcl list.
+ *
+ * Results:
+ * The string representation of the vector is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+CoordinatesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Marker record */
+ int offset; /* Not used. */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Marker *markerPtr = (Marker *)widgRec;
+ Tcl_Interp *interp;
+ Tcl_DString dString;
+ char *result;
+ register int i;
+ register Point2D *p;
+
+ if (markerPtr->nWorldPts < 1) {
+ return "";
+ }
+ interp = markerPtr->graphPtr->interp;
+
+ Tcl_DStringInit(&dString);
+ p = markerPtr->worldPts;
+ for (i = 0; i < markerPtr->nWorldPts; i++) {
+ Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->x));
+ Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->y));
+ p++;
+ }
+ result = Tcl_DStringValue(&dString);
+
+ /*
+ * If memory wasn't allocated for the dynamic string, do it here (it's
+ * currently on the stack), so that Tcl can free it normally.
+ */
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * HMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a
+ * window position.
+ *
+ * Results:
+ * Returns a floating point number representing the window
+ * coordinate position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+HMap(graphPtr, axisPtr, x)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double x;
+{
+ register double norm;
+
+ if (x == DBL_MAX) {
+ norm = 1.0;
+ } else if (x == -DBL_MAX) {
+ norm = 0.0;
+ } else {
+ if (axisPtr->logScale) {
+ if (x > 0.0) {
+ x = log10(x);
+ } else if (x < 0.0) {
+ x = 0.0;
+ }
+ }
+ norm = NORMALIZE(axisPtr, x);
+ }
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ /* Horizontal transformation */
+ return ((norm * graphPtr->hRange) + graphPtr->hOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a
+ * window position.
+ *
+ * Results:
+ * Returns a double precision number representing the window
+ * coordinate position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+VMap(graphPtr, axisPtr, y)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double y;
+{
+ register double norm;
+
+ if (y == DBL_MAX) {
+ norm = 1.0;
+ } else if (y == -DBL_MAX) {
+ norm = 0.0;
+ } else {
+ if (axisPtr->logScale) {
+ if (y > 0.0) {
+ y = log10(y);
+ } else if (y < 0.0) {
+ y = 0.0;
+ }
+ }
+ norm = NORMALIZE(axisPtr, y);
+ }
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ /* Vertical transformation */
+ return (((1.0 - norm) * graphPtr->vRange) + graphPtr->vOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapPoint --
+ *
+ * Maps the given graph x,y coordinate values to a window position.
+ *
+ * Results:
+ * Returns a XPoint structure containing the window coordinates
+ * of the given graph x,y coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Point2D
+MapPoint(graphPtr, pointPtr, axesPtr)
+ Graph *graphPtr;
+ Point2D *pointPtr; /* Graph X-Y coordinate. */
+ Axis2D *axesPtr; /* Specifies which axes to use */
+{
+ Point2D result;
+
+ if (graphPtr->inverted) {
+ result.x = HMap(graphPtr, axesPtr->y, pointPtr->y);
+ result.y = VMap(graphPtr, axesPtr->x, pointPtr->x);
+ } else {
+ result.x = HMap(graphPtr, axesPtr->x, pointPtr->x);
+ result.y = VMap(graphPtr, axesPtr->y, pointPtr->y);
+ }
+ return result; /* Result is screen coordinate. */
+}
+
+static Marker *
+CreateMarker(graphPtr, name, classUid)
+ Graph *graphPtr;
+ char *name;
+ Blt_Uid classUid;
+{
+ Marker *markerPtr;
+
+ /* Create the new marker based upon the given type */
+ if (classUid == bltBitmapMarkerUid) {
+ markerPtr = CreateBitmapMarker(); /* bitmap */
+ } else if (classUid == bltLineMarkerUid) {
+ markerPtr = CreateLineMarker(); /* line */
+ } else if (classUid == bltImageMarkerUid) {
+ markerPtr = CreateImageMarker(); /* image */
+ } else if (classUid == bltTextMarkerUid) {
+ markerPtr = CreateTextMarker(); /* text */
+ } else if (classUid == bltPolygonMarkerUid) {
+ markerPtr = CreatePolygonMarker(); /* polygon */
+ } else if (classUid == bltWindowMarkerUid) {
+ markerPtr = CreateWindowMarker(); /* window */
+ } else {
+ return NULL;
+ }
+ assert(markerPtr);
+ markerPtr->graphPtr = graphPtr;
+ markerPtr->hidden = markerPtr->drawUnder = FALSE;
+ markerPtr->flags |= MAP_ITEM;
+ markerPtr->name = Blt_Strdup(name);
+ markerPtr->classUid = classUid;
+ return markerPtr;
+}
+
+static void
+DestroyMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ /* Free the resources allocated for the particular type of marker */
+ (*markerPtr->classPtr->freeProc) (graphPtr, markerPtr);
+ if (markerPtr->worldPts != NULL) {
+ Blt_Free(markerPtr->worldPts);
+ }
+ Blt_DeleteBindings(graphPtr->bindTable, markerPtr);
+ Tk_FreeOptions(markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ graphPtr->display, 0);
+ if (markerPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&graphPtr->markers.table, markerPtr->hashPtr);
+ }
+ if (markerPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(graphPtr->markers.displayList, markerPtr->linkPtr);
+ }
+ if (markerPtr->name != NULL) {
+ Blt_Free(markerPtr->name);
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_Free(markerPtr->elemName);
+ }
+ if (markerPtr->tags != NULL) {
+ Blt_Free(markerPtr->tags);
+ }
+ Blt_Free(markerPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureBitmapMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a bitmap marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as bitmap pixmap, colors,
+ * rotation, etc. get set for markerPtr; old resources get freed,
+ * if there were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ConfigureBitmapMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ if (bmPtr->srcBitmap == None) {
+ return TCL_OK;
+ }
+ bmPtr->theta = FMOD(bmPtr->rotate, 360.0);
+ if (bmPtr->theta < 0.0) {
+ bmPtr->theta += 360.0;
+ }
+ gcMask = 0;
+ if (bmPtr->outlineColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = bmPtr->outlineColor->pixel;
+ }
+ if (bmPtr->fillColor != NULL) {
+ gcValues.background = bmPtr->fillColor->pixel;
+ gcMask |= GCBackground;
+ } else {
+ gcValues.clip_mask = bmPtr->srcBitmap;
+ gcMask |= GCClipMask;
+ }
+
+ /* Note that while this is a "shared" GC, we're going to change
+ * the clip origin right before the bitmap is drawn anyways. This
+ * assumes that any drawing code using this GC (with GCClipMask
+ * set) is going to want to set the clip origin anyways. */
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bmPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->gc);
+ }
+ bmPtr->gc = newGC;
+
+ /* Create background GC color */
+
+ if (bmPtr->fillColor != NULL) {
+ gcValues.foreground = bmPtr->fillColor->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+ }
+ bmPtr->fillGC = newGC;
+ }
+ bmPtr->flags |= MAP_ITEM;
+ if (bmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapBitmapMarker --
+ *
+ * This procedure gets called each time the layout of the graph
+ * changes. The x, y window coordinates of the bitmap marker are
+ * saved in the marker structure.
+ *
+ * Additionly, if no background color was specified, the
+ * GCTileStipXOrigin and GCTileStipYOrigin attributes are set in
+ * the private GC.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window coordinates are saved and if no background color was
+ * set, the GC stipple origins are changed to calculated window
+ * coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapBitmapMarker(markerPtr)
+ Marker *markerPtr;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ Extents2D exts;
+ Graph *graphPtr = markerPtr->graphPtr;
+ Point2D anchorPos;
+ Point2D corner1, corner2;
+ int destWidth, destHeight;
+ int srcWidth, srcHeight;
+ register int i;
+
+ if (bmPtr->srcBitmap == None) {
+ return;
+ }
+ if (bmPtr->destBitmap != None) {
+ Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+ bmPtr->destBitmap = None;
+ }
+ /*
+ * Collect the coordinates. The number of coordinates will determine
+ * the calculations to be made.
+ *
+ * x1 y1 A single pair of X-Y coordinates. They represent
+ * the anchor position of the bitmap.
+ *
+ * x1 y1 x2 y2 Two pairs of X-Y coordinates. They represent
+ * two opposite corners of a bounding rectangle. The
+ * bitmap is possibly rotated and scaled to fit into
+ * this box.
+ *
+ */
+ Tk_SizeOfBitmap(graphPtr->display, bmPtr->srcBitmap, &srcWidth,
+ &srcHeight);
+ corner1 = MapPoint(graphPtr, bmPtr->worldPts, &bmPtr->axes);
+ if (bmPtr->nWorldPts > 1) {
+ double hold;
+
+ corner2 = MapPoint(graphPtr, bmPtr->worldPts + 1, &bmPtr->axes);
+ /* Flip the corners if necessary */
+ if (corner1.x > corner2.x) {
+ hold = corner1.x, corner1.x = corner2.x, corner2.x = hold;
+ }
+ if (corner1.y > corner2.y) {
+ hold = corner1.y, corner1.y = corner2.y, corner2.y = hold;
+ }
+ } else {
+ corner2.x = corner1.x + srcWidth - 1;
+ corner2.y = corner1.y + srcHeight - 1;
+ }
+ destWidth = (int)(corner2.x - corner1.x) + 1;
+ destHeight = (int)(corner2.y - corner1.y) + 1;
+
+ if (bmPtr->nWorldPts == 1) {
+ anchorPos = Blt_TranslatePoint(&corner1, destWidth, destHeight,
+ bmPtr->anchor);
+ } else {
+ anchorPos = corner1;
+ }
+ anchorPos.x += bmPtr->xOffset;
+ anchorPos.y += bmPtr->yOffset;
+
+ /* Check if the bitmap sits at least partially in the plot area. */
+ exts.left = anchorPos.x;
+ exts.top = anchorPos.y;
+ exts.right = anchorPos.x + destWidth - 1;
+ exts.bottom = anchorPos.y + destHeight - 1;
+
+ bmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ if (bmPtr->clipped) {
+ return; /* Bitmap is offscreen. Don't generate
+ * rotated or scaled bitmaps. */
+ }
+
+ /*
+ * Scale the bitmap if necessary. It's a little tricky because we
+ * only want to scale what's visible on the screen, not the entire
+ * bitmap.
+ */
+ if ((bmPtr->theta != 0.0) || (destWidth != srcWidth) ||
+ (destHeight != srcHeight)) {
+ int regionWidth, regionHeight;
+ Region2D region; /* Indicates the portion of the scaled
+ * bitmap that we want to display. */
+ double left, right, top, bottom;
+
+ /*
+ * Determine the region of the bitmap visible in the plot area.
+ */
+ left = MAX(graphPtr->left, exts.left);
+ right = MIN(graphPtr->right, exts.right);
+ top = MAX(graphPtr->top, exts.top);
+ bottom = MIN(graphPtr->bottom, exts.bottom);
+
+ region.left = region.top = 0;
+ if (graphPtr->left > exts.left) {
+ region.left = (int)(graphPtr->left - exts.left);
+ }
+ if (graphPtr->top > exts.top) {
+ region.top = (int)(graphPtr->top - exts.top);
+ }
+ regionWidth = (int)(right - left) + 1;
+ regionHeight = (int)(bottom - top) + 1;
+ region.right = region.left + (int)(right - left);
+ region.bottom = region.top + (int)(bottom - top);
+
+ anchorPos.x = left;
+ anchorPos.y = top;
+ bmPtr->destBitmap = Blt_ScaleRotateBitmapRegion(graphPtr->tkwin,
+ bmPtr->srcBitmap, srcWidth, srcHeight,
+ region.left, region.top, regionWidth, regionHeight,
+ destWidth, destHeight, bmPtr->theta);
+ bmPtr->destWidth = regionWidth;
+ bmPtr->destHeight = regionHeight;
+ } else {
+ bmPtr->destWidth = srcWidth;
+ bmPtr->destHeight = srcHeight;
+ bmPtr->destBitmap = None;
+ }
+ bmPtr->anchorPos = anchorPos;
+ {
+ double xScale, yScale;
+ double tx, ty;
+ double rotWidth, rotHeight;
+ Point2D polygon[5];
+ int n;
+
+ /*
+ * Compute a polygon to represent the background area of the bitmap.
+ * This is needed for backgrounds of arbitrarily rotated bitmaps.
+ * We also use it to print a background in PostScript.
+ */
+ Blt_GetBoundingBox(srcWidth, srcHeight, bmPtr->theta, &rotWidth,
+ &rotHeight, polygon);
+ xScale = (double)destWidth / rotWidth;
+ yScale = (double)destHeight / rotHeight;
+
+ /*
+ * Adjust each point of the polygon. Both scale it to the new size
+ * and translate it to the actual screen position of the bitmap.
+ */
+ tx = exts.left + destWidth * 0.5;
+ ty = exts.top + destHeight * 0.5;
+ for (i = 0; i < 4; i++) {
+ polygon[i].x = (polygon[i].x * xScale) + tx;
+ polygon[i].y = (polygon[i].y * yScale) + ty;
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+ n = Blt_PolyRectClip(&exts, polygon, 4, bmPtr->outline);
+ assert(n <= MAX_OUTLINE_POINTS);
+ if (n < 3) {
+ memcpy(&bmPtr->outline, polygon, sizeof(Point2D) * 4);
+ bmPtr->nOutlinePts = 4;
+ } else {
+ bmPtr->nOutlinePts = n;
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInBitmapMarker --
+ *
+ * Indicates if the given point is over the bitmap marker. The
+ * area of the bitmap is the rectangle.
+ *
+ * Results:
+ * Returns 1 is the point is over the bitmap marker, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInBitmapMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->srcBitmap == None) {
+ return 0;
+ }
+ if (bmPtr->theta != 0.0) {
+ Point2D points[MAX_OUTLINE_POINTS];
+ register int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap
+ * and see if the point is inside of it.
+ */
+ for (i = 0; i < bmPtr->nOutlinePts; i++) {
+ points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x;
+ points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y;
+ }
+ return Blt_PointInPolygon(samplePtr, points, bmPtr->nOutlinePts);
+ }
+ return ((samplePtr->x >= bmPtr->anchorPos.x) &&
+ (samplePtr->x < (bmPtr->anchorPos.x + bmPtr->destWidth)) &&
+ (samplePtr->y >= bmPtr->anchorPos.y) &&
+ (samplePtr->y < (bmPtr->anchorPos.y + bmPtr->destHeight)));
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInBitmapMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInBitmapMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (bmPtr->theta != 0.0) {
+ Point2D points[MAX_OUTLINE_POINTS];
+ register int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap
+ * and see if the point is inside of it.
+ */
+ for (i = 0; i < bmPtr->nOutlinePts; i++) {
+ points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x;
+ points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y;
+ }
+ return Blt_RegionInPolygon(extsPtr, points, bmPtr->nOutlinePts,
+ enclosed);
+ }
+ if (enclosed) {
+ return ((bmPtr->anchorPos.x >= extsPtr->left) &&
+ (bmPtr->anchorPos.y >= extsPtr->top) &&
+ ((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->right) &&
+ ((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->bottom));
+ }
+ return !((bmPtr->anchorPos.x >= extsPtr->right) ||
+ (bmPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->left) ||
+ ((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawBitmapMarker --
+ *
+ * Draws the bitmap marker that have a transparent of filled
+ * background.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * GC stipple origins are changed to current window coordinates.
+ * Commands are output to X to draw the marker in its current
+ * mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawBitmapMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ double theta;
+ Pixmap bitmap;
+
+ bitmap = GETBITMAP(bmPtr);
+ if ((bitmap == None) || (bmPtr->destWidth < 1) || (bmPtr->destHeight < 1)) {
+ return;
+ }
+ theta = FMOD(bmPtr->theta, (double)90.0);
+ if ((bmPtr->fillColor == NULL) || (theta != 0.0)) {
+
+ /*
+ * If the bitmap is rotated and a filled background is
+ * required, then a filled polygon is drawn before the
+ * bitmap.
+ */
+
+ if (bmPtr->fillColor != NULL) {
+ int i;
+ XPoint polygon[MAX_OUTLINE_POINTS];
+
+ for (i = 0; i < bmPtr->nOutlinePts; i++) {
+ polygon[i].x = (short int)bmPtr->outline[i].x;
+ polygon[i].y = (short int)bmPtr->outline[i].y;
+ }
+ XFillPolygon(graphPtr->display, drawable, bmPtr->fillGC,
+ polygon, bmPtr->nOutlinePts, Convex, CoordModeOrigin);
+ }
+ XSetClipMask(graphPtr->display, bmPtr->gc, bitmap);
+ XSetClipOrigin(graphPtr->display, bmPtr->gc, (int)bmPtr->anchorPos.x,
+ (int)bmPtr->anchorPos.y);
+ } else {
+ XSetClipMask(graphPtr->display, bmPtr->gc, None);
+ XSetClipOrigin(graphPtr->display, bmPtr->gc, 0, 0);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable, bmPtr->gc, 0, 0,
+ bmPtr->destWidth, bmPtr->destHeight, (int)bmPtr->anchorPos.x,
+ (int)bmPtr->anchorPos.y, 1);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BitmapMarkerToPostScript --
+ *
+ * Generates PostScript to print a bitmap marker.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+BitmapMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr; /* Marker to be printed */
+ PsToken psToken;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ Pixmap bitmap;
+
+ bitmap = GETBITMAP(bmPtr);
+ if (bitmap == None) {
+ return;
+ }
+ if (bmPtr->fillColor != NULL) {
+ Blt_BackgroundToPostScript(psToken, bmPtr->fillColor);
+ Blt_PolygonToPostScript(psToken, bmPtr->outline, 4);
+ }
+ Blt_ForegroundToPostScript(psToken, bmPtr->outlineColor);
+
+ Blt_FormatToPostScript(psToken,
+ " gsave\n %g %g translate\n %d %d scale\n",
+ bmPtr->anchorPos.x, bmPtr->anchorPos.y + bmPtr->destHeight,
+ bmPtr->destWidth, -bmPtr->destHeight);
+ Blt_FormatToPostScript(psToken, " %d %d true [%d 0 0 %d 0 %d] {",
+ bmPtr->destWidth, bmPtr->destHeight, bmPtr->destWidth,
+ -bmPtr->destHeight, bmPtr->destHeight);
+ Blt_BitmapDataToPostScript(psToken, graphPtr->display, bitmap,
+ bmPtr->destWidth, bmPtr->destHeight);
+ Blt_AppendToPostScript(psToken, " } imagemask\n",
+ "grestore\n", (char *)NULL);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeBitmapMarker --
+ *
+ * Releases the memory and attributes of the bitmap marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Bitmap attributes (GCs, colors, bitmap, etc) get destroyed.
+ * Memory is released, X resources are freed, and the graph is
+ * redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeBitmapMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->gc);
+ }
+ if (bmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+ }
+ if (bmPtr->destBitmap != None) {
+ Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateBitmapMarker --
+ *
+ * Allocate memory and initialize methods for the new bitmap marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the bitmap marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateBitmapMarker()
+{
+ BitmapMarker *bmPtr;
+
+ bmPtr = Blt_Calloc(1, sizeof(BitmapMarker));
+ if (bmPtr != NULL) {
+ bmPtr->classPtr = &bitmapMarkerClass;
+ }
+ return (Marker *)bmPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight; /* Not used. */
+{
+ ImageMarker *imPtr = clientData;
+ Tk_PhotoHandle photo;
+
+ photo = Blt_FindPhoto(imPtr->graphPtr->interp, imPtr->imageName);
+ if (photo != NULL) {
+ if (imPtr->srcImage != NULL) {
+ Blt_FreeColorImage(imPtr->srcImage);
+ }
+ /* Convert the latest incarnation of the photo image back to a
+ * color image that we can scale. */
+ imPtr->srcImage = Blt_PhotoToColorImage(photo);
+ }
+ imPtr->graphPtr->flags |= REDRAW_BACKING_STORE;
+ imPtr->flags |= MAP_ITEM;
+ Blt_EventuallyRedrawGraph(imPtr->graphPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureImageMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a image marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as image pixmap, colors,
+ * rotation, etc. get set for markerPtr; old resources get freed,
+ * if there were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureImageMarker(markerPtr)
+ Marker *markerPtr;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ if (Blt_ConfigModified(markerPtr->classPtr->configSpecs, graphPtr->interp, "-image",
+ (char *)NULL)) {
+ Tcl_Interp *interp = graphPtr->interp;
+
+ if (imPtr->tkImage != NULL) {
+ Tk_FreeImage(imPtr->tkImage);
+ imPtr->tkImage = NULL;
+ }
+ if (imPtr->imageName[0] != '\0') {
+ GC newGC;
+ Tk_PhotoHandle photo;
+
+ imPtr->tkImage = Tk_GetImage(interp, graphPtr->tkwin,
+ imPtr->imageName, ImageChangedProc, imPtr);
+ if (imPtr->tkImage == NULL) {
+ Blt_Free(imPtr->imageName);
+ imPtr->imageName = NULL;
+ return TCL_ERROR;
+ }
+ photo = Blt_FindPhoto(interp, imPtr->imageName);
+ if (photo != NULL) {
+ if (imPtr->srcImage != NULL) {
+ Blt_FreeColorImage(imPtr->srcImage);
+ }
+ /* Convert the photo into a color image */
+ imPtr->srcImage = Blt_PhotoToColorImage(photo);
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, 0L, (XGCValues *)NULL);
+ if (imPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, imPtr->gc);
+ }
+ imPtr->gc = newGC;
+ }
+ }
+ imPtr->flags |= MAP_ITEM;
+ if (imPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapImageMarker --
+ *
+ * This procedure gets called each time the layout of the graph
+ * changes. The x, y window coordinates of the image marker are
+ * saved in the marker structure.
+ *
+ * Additionly, if no background color was specified, the
+ * GCTileStipXOrigin and GCTileStipYOrigin attributes are set in
+ * the private GC.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window coordinates are saved and if no background color was *
+ * set, the GC stipple origins are changed to calculated window
+ * coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapImageMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Extents2D exts;
+ Graph *graphPtr;
+ ImageMarker *imPtr;
+ Point2D anchorPos;
+ Point2D corner1, corner2;
+ int scaledWidth, scaledHeight;
+ int srcWidth, srcHeight;
+
+ imPtr = (ImageMarker *)markerPtr;
+ if (imPtr->tkImage == NULL) {
+ return;
+ }
+ graphPtr = imPtr->graphPtr;
+ corner1 = MapPoint(graphPtr, imPtr->worldPts, &imPtr->axes);
+ if (imPtr->srcImage == NULL) {
+ /*
+ * Don't scale or rotate non-photo images.
+ */
+ Tk_SizeOfImage(imPtr->tkImage, &srcWidth, &srcHeight);
+ imPtr->width = srcWidth;
+ imPtr->height = srcHeight;
+ imPtr->anchorPos = Blt_TranslatePoint(&corner1,
+ srcWidth, srcHeight, imPtr->anchor);
+ imPtr->anchorPos.x += imPtr->xOffset;
+ imPtr->anchorPos.y += imPtr->yOffset;
+ exts.left = imPtr->anchorPos.x;
+ exts.top = imPtr->anchorPos.y;
+ exts.right = exts.left + srcWidth - 1;
+ exts.bottom = exts.top + srcHeight - 1;
+ imPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ return;
+ }
+
+ imPtr->width = srcWidth = Blt_ColorImageWidth(imPtr->srcImage);
+ imPtr->height = srcHeight = Blt_ColorImageHeight(imPtr->srcImage);
+ if ((srcWidth == 0) && (srcHeight == 0)) {
+ imPtr->clipped = TRUE;
+ return; /* Empty image. */
+ }
+ if (imPtr->nWorldPts > 1) {
+ double hold;
+
+ corner2 = MapPoint(graphPtr, imPtr->worldPts + 1, &imPtr->axes);
+ /* Flip the corners if necessary */
+ if (corner1.x > corner2.x) {
+ hold = corner1.x, corner1.x = corner2.x, corner2.x = hold;
+ }
+ if (corner1.y > corner2.y) {
+ hold = corner1.y, corner1.y = corner2.y, corner2.y = hold;
+ }
+ } else {
+ corner2.x = corner1.x + srcWidth - 1;
+ corner2.y = corner1.y + srcHeight - 1;
+ }
+ scaledWidth = (int)(corner2.x - corner1.x) + 1;
+ scaledHeight = (int)(corner2.y - corner1.y) + 1;
+
+ if (imPtr->nWorldPts == 1) {
+ anchorPos = Blt_TranslatePoint(&corner1, scaledWidth, scaledHeight,
+ imPtr->anchor);
+ } else {
+ anchorPos = corner1;
+ }
+ anchorPos.x += imPtr->xOffset;
+ anchorPos.y += imPtr->yOffset;
+
+ /* Check if the image sits at least partially in the plot area. */
+ exts.left = anchorPos.x;
+ exts.top = anchorPos.y;
+ exts.right = anchorPos.x + scaledWidth - 1;
+ exts.bottom = anchorPos.y + scaledHeight - 1;
+
+ imPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ if (imPtr->clipped) {
+ return; /* Image is offscreen. Don't generate
+ * rotated or scaled images. */
+ }
+ if ((scaledWidth != srcWidth) || (scaledHeight != srcHeight)) {
+ Tk_PhotoHandle photo;
+ Blt_ColorImage destImage;
+ int x, y, width, height;
+ int left, right, top, bottom;
+
+ /* Determine the region of the subimage inside of the
+ * destination image. */
+ left = MAX((int)exts.left, graphPtr->left);
+ top = MAX((int)exts.top, graphPtr->top);
+ right = MIN((int)exts.right, graphPtr->right);
+ bottom = MIN((int)exts.bottom, graphPtr->bottom);
+
+ /* Reset image location and coordinates to that of the region */
+ anchorPos.x = left;
+ anchorPos.y = top;
+
+ x = y = 0;
+ if (graphPtr->left > (int)exts.left) {
+ x = graphPtr->left - (int)exts.left;
+ }
+ if (graphPtr->top > (int)exts.top) {
+ y = graphPtr->top - (int)exts.top;
+ }
+ width = (int)(right - left + 1);
+ height = (int)(bottom - top + 1);
+
+ destImage = Blt_ResizeColorSubimage(imPtr->srcImage, x, y, width,
+ height, scaledWidth, scaledHeight);
+#ifdef notyet
+ /* Now convert the color image into a pixmap */
+ if (imPtr->pixmap != None) {
+ Blt_FreeColorTable(imPtr->colorTable);
+ Tk_FreePixmap(Tk_Display(graphPtr->tkwin), imPtr->pixmap);
+ imPtr->colorTable = NULL;
+ }
+ imPtr->pixmap = Blt_ColorImageToPixmap(graphPtr->interp,
+ graphPtr->tkwin, destImage, &imPtr->colorTable);
+#else
+ imPtr->pixmap = None;
+ if (imPtr->tmpImage == NULL) {
+ imPtr->tmpImage = Blt_CreateTemporaryImage(graphPtr->interp,
+ graphPtr->tkwin, imPtr);
+ if (imPtr->tmpImage == NULL) {
+ return;
+ }
+ }
+ /* Put the scaled colorimage into the photo. */
+ photo = Blt_FindPhoto(graphPtr->interp,
+ Blt_NameOfImage(imPtr->tmpImage));
+ Blt_ColorImageToPhoto(destImage, photo);
+#endif
+ Blt_FreeColorImage(destImage);
+ imPtr->width = width;
+ imPtr->height = height;
+ }
+ imPtr->anchorPos = anchorPos;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInWindowMarker --
+ *
+ * Indicates if the given point is over the window marker. The
+ * area of the window is the rectangle.
+ *
+ * Results:
+ * Returns 1 is the point is over the window marker, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInImageMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+ return ((samplePtr->x >= imPtr->anchorPos.x) &&
+ (samplePtr->x < (imPtr->anchorPos.x + imPtr->width)) &&
+ (samplePtr->y >= imPtr->anchorPos.y) &&
+ (samplePtr->y < (imPtr->anchorPos.y + imPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInImageMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInImageMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+ if (imPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (enclosed) {
+ return ((imPtr->anchorPos.x >= extsPtr->left) &&
+ (imPtr->anchorPos.y >= extsPtr->top) &&
+ ((imPtr->anchorPos.x + imPtr->width) <= extsPtr->right) &&
+ ((imPtr->anchorPos.y + imPtr->height) <= extsPtr->bottom));
+ }
+ return !((imPtr->anchorPos.x >= extsPtr->right) ||
+ (imPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((imPtr->anchorPos.x + imPtr->width) <= extsPtr->left) ||
+ ((imPtr->anchorPos.y + imPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawImageMarker --
+ *
+ * This procedure is invoked to draw a image marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * GC stipple origins are changed to current window coordinates.
+ * Commands are output to X to draw the marker in its current mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawImageMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ int width, height;
+
+ if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) {
+ return;
+ }
+ if (imPtr->pixmap == None) {
+ Pixmap pixmap;
+ Tk_Image tkImage;
+
+ tkImage = (imPtr->tmpImage != NULL) ? imPtr->tmpImage : imPtr->tkImage;
+ Tk_SizeOfImage(tkImage, &width, &height);
+ /* pixmap = Tk_ImageGetPhotoPixmap(tkImage); */
+ pixmap = None;
+ if (pixmap == None) { /* May not be a "photo" image. */
+ Tk_RedrawImage(tkImage, 0, 0, width, height, drawable,
+ (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y);
+ } else {
+ XCopyArea(imPtr->graphPtr->display, pixmap, drawable,
+ imPtr->gc, 0, 0, width, height, (int)imPtr->anchorPos.x,
+ (int)imPtr->anchorPos.y);
+ }
+ } else {
+ XCopyArea(imPtr->graphPtr->display, imPtr->pixmap, drawable,
+ imPtr->gc, 0, 0, imPtr->width, imPtr->height,
+ (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ImageMarkerToPostScript --
+ *
+ * This procedure is invoked to print a image marker.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ImageMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr; /* Marker to be printed */
+ PsToken psToken;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ char *imageName;
+ Tk_PhotoHandle photo;
+
+ if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) {
+ return; /* Image doesn't exist anymore */
+ }
+ imageName = (imPtr->tmpImage == NULL)
+ ? Blt_NameOfImage(imPtr->tkImage) : Blt_NameOfImage(imPtr->tmpImage);
+ photo = Blt_FindPhoto(markerPtr->graphPtr->interp, imageName);
+ if (photo == NULL) {
+ return; /* Image isn't a photo image */
+ }
+ Blt_PhotoToPostScript(psToken, photo, imPtr->anchorPos.x,
+ imPtr->anchorPos.y);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeImageMarker --
+ *
+ * Destroys the structure containing the attributes of the image
+ * marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Image attributes (GCs, colors, image, etc) get destroyed.
+ * Memory is released, X resources are freed, and the graph is
+ * redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeImageMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+ if (imPtr->pixmap != None) {
+ Tk_FreePixmap(graphPtr->display, imPtr->pixmap);
+ }
+ if (imPtr->tkImage != NULL) {
+ Tk_FreeImage(imPtr->tkImage);
+ }
+ if (imPtr->tmpImage != NULL) {
+ Blt_DestroyTemporaryImage(graphPtr->interp, imPtr->tmpImage);
+ }
+ if (imPtr->srcImage != NULL) {
+ Blt_FreeColorImage(imPtr->srcImage);
+ }
+ if (imPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, imPtr->gc);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateImageMarker --
+ *
+ * Allocate memory and initialize methods for the new image marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the image marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateImageMarker()
+{
+ ImageMarker *imPtr;
+
+ imPtr = Blt_Calloc(1, sizeof(ImageMarker));
+ if (imPtr != NULL) {
+ imPtr->classPtr = &imageMarkerClass;
+ }
+ return (Marker *)imPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureTextMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a text marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for markerPtr; old resources get freed, if there
+ * were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureTextMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ tmPtr->style.theta = FMOD(tmPtr->style.theta, 360.0);
+ if (tmPtr->style.theta < 0.0) {
+ tmPtr->style.theta += 360.0;
+ }
+ newGC = NULL;
+ if (tmPtr->fillColor != NULL) {
+ gcMask = GCForeground;
+ gcValues.foreground = tmPtr->fillColor->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ }
+ if (tmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, tmPtr->fillGC);
+ }
+ tmPtr->fillGC = newGC;
+ Blt_ResetTextStyle(graphPtr->tkwin, &tmPtr->style);
+
+ if (Blt_ConfigModified(tmPtr->classPtr->configSpecs, graphPtr->interp, "-text",
+ (char *)NULL)) {
+ if (tmPtr->textPtr != NULL) {
+ Blt_Free(tmPtr->textPtr);
+ tmPtr->textPtr = NULL;
+ }
+ tmPtr->width = tmPtr->height = 0;
+ if (tmPtr->string != NULL) {
+ register int i;
+ double rotWidth, rotHeight;
+
+ tmPtr->textPtr = Blt_GetTextLayout(tmPtr->string, &tmPtr->style);
+ Blt_GetBoundingBox(tmPtr->textPtr->width, tmPtr->textPtr->height,
+ tmPtr->style.theta, &rotWidth, &rotHeight, tmPtr->outline);
+ tmPtr->width = ROUND(rotWidth);
+ tmPtr->height = ROUND(rotHeight);
+ for (i = 0; i < 4; i++) {
+ tmPtr->outline[i].x += ROUND(rotWidth * 0.5);
+ tmPtr->outline[i].y += ROUND(rotHeight * 0.5);
+ }
+ tmPtr->outline[4].x = tmPtr->outline[0].x;
+ tmPtr->outline[4].y = tmPtr->outline[0].y;
+ }
+ }
+ tmPtr->flags |= MAP_ITEM;
+ if (tmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapTextMarker --
+ *
+ * Calculate the layout position for a text marker. Positional
+ * information is saved in the marker. If the text is rotated,
+ * a bitmap containing the text is created.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If no background color has been specified, the GC stipple
+ * origins are changed to current window coordinates. For both
+ * rotated and non-rotated text, if any old bitmap is leftover,
+ * it is freed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapTextMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ Extents2D exts;
+ Point2D anchorPos;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ anchorPos = MapPoint(graphPtr, tmPtr->worldPts, &tmPtr->axes);
+ anchorPos = Blt_TranslatePoint(&anchorPos, tmPtr->width, tmPtr->height,
+ tmPtr->anchor);
+ anchorPos.x += tmPtr->xOffset;
+ anchorPos.y += tmPtr->yOffset;
+ /*
+ * Determine the bounding box of the text and test to see if it
+ * is at least partially contained within the plotting area.
+ */
+ exts.left = anchorPos.x;
+ exts.top = anchorPos.y;
+ exts.right = anchorPos.x + tmPtr->width - 1;
+ exts.bottom = anchorPos.y + tmPtr->height - 1;
+ tmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ tmPtr->anchorPos = anchorPos;
+
+}
+
+static int
+PointInTextMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->string == NULL) {
+ return 0;
+ }
+ if (tmPtr->style.theta != 0.0) {
+ Point2D points[5];
+ register int i;
+
+ /*
+ * Figure out the bounding polygon (isolateral) for the text
+ * and see if the point is inside of it.
+ */
+
+ for (i = 0; i < 5; i++) {
+ points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+ points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+ }
+ return Blt_PointInPolygon(samplePtr, points, 5);
+ }
+ return ((samplePtr->x >= tmPtr->anchorPos.x) &&
+ (samplePtr->x < (tmPtr->anchorPos.x + tmPtr->width)) &&
+ (samplePtr->y >= tmPtr->anchorPos.y) &&
+ (samplePtr->y < (tmPtr->anchorPos.y + tmPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInTextMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInTextMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (tmPtr->style.theta != 0.0) {
+ Point2D points[5];
+ register int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap
+ * and see if the point is inside of it.
+ */
+ for (i = 0; i < 4; i++) {
+ points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+ points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+ }
+ return Blt_RegionInPolygon(extsPtr, points, 4, enclosed);
+ }
+ if (enclosed) {
+ return ((tmPtr->anchorPos.x >= extsPtr->left) &&
+ (tmPtr->anchorPos.y >= extsPtr->top) &&
+ ((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->right) &&
+ ((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->bottom));
+ }
+ return !((tmPtr->anchorPos.x >= extsPtr->right) ||
+ (tmPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->left) ||
+ ((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawTextMarker --
+ *
+ * Draws the text marker on the graph.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to draw the marker in its current
+ * mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawTextMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ if (tmPtr->fillGC != NULL) {
+ XPoint pointArr[4];
+ register int i;
+
+ /*
+ * Simulate the rotated background of the bitmap by
+ * filling a bounding polygon with the background color.
+ */
+ for (i = 0; i < 4; i++) {
+ pointArr[i].x = (short int)
+ (tmPtr->outline[i].x + tmPtr->anchorPos.x);
+ pointArr[i].y = (short int)
+ (tmPtr->outline[i].y + tmPtr->anchorPos.y);
+ }
+ XFillPolygon(graphPtr->display, drawable, tmPtr->fillGC, pointArr, 4,
+ Convex, CoordModeOrigin);
+ }
+ if (tmPtr->style.color != NULL) {
+ Blt_DrawTextLayout(graphPtr->tkwin, drawable, tmPtr->textPtr,
+ &tmPtr->style, (int)tmPtr->anchorPos.x, (int)tmPtr->anchorPos.y);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextMarkerToPostScript --
+ *
+ * Outputs PostScript commands to draw a text marker at a given
+ * x,y coordinate, rotation, anchor, and font.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript font and color settings are changed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+TextMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ if (tmPtr->fillGC != NULL) {
+ Point2D polygon[4];
+ register int i;
+
+ /*
+ * Simulate the rotated background of the bitmap by
+ * filling a bounding polygon with the background color.
+ */
+ for (i = 0; i < 4; i++) {
+ polygon[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+ polygon[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+ }
+ Blt_BackgroundToPostScript(psToken, tmPtr->fillColor);
+ Blt_PolygonToPostScript(psToken, polygon, 4);
+ }
+ Blt_TextToPostScript(psToken, tmPtr->string, &tmPtr->style,
+ tmPtr->anchorPos.x, tmPtr->anchorPos.y);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeTextMarker --
+ *
+ * Destroys the structure containing the attributes of the text
+ * marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Text attributes (GCs, colors, stipple, font, etc) get destroyed.
+ * Memory is released, X resources are freed, and the graph is
+ * redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeTextMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ Blt_FreeTextStyle(graphPtr->display, &tmPtr->style);
+ if (tmPtr->textPtr != NULL) {
+ Blt_Free(tmPtr->textPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTextMarker --
+ *
+ * Allocate memory and initialize methods for the new text marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the text marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateTextMarker()
+{
+ TextMarker *tmPtr;
+
+ tmPtr = Blt_Calloc(1, sizeof(TextMarker));
+ assert(tmPtr);
+
+ tmPtr->classPtr = &textMarkerClass;
+ Blt_InitTextStyle(&tmPtr->style);
+ tmPtr->style.anchor = TK_ANCHOR_NW;
+ tmPtr->style.padLeft = tmPtr->style.padRight = 4;
+ tmPtr->style.padTop = tmPtr->style.padBottom = 4;
+
+ return (Marker *)tmPtr;
+}
+
+
+static void ChildEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void ChildGeometryProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+
+static void ChildCustodyProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+
+static Tk_GeomMgr winMarkerMgrInfo =
+{
+ "graph", /* Name of geometry manager used by winfo */
+ ChildGeometryProc, /* Procedure to for new geometry requests */
+ ChildCustodyProc, /* Procedure when window is taken away */
+};
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureWindowMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a window marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as window pathname, placement,
+ * etc. get set for markerPtr; old resources get freed, if there
+ * were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureWindowMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+ Tk_Window tkwin;
+
+ if (wmPtr->pathName == NULL) {
+ return TCL_OK;
+ }
+ tkwin = Tk_NameToWindow(graphPtr->interp, wmPtr->pathName,
+ graphPtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_Parent(tkwin) != graphPtr->tkwin) {
+ Tcl_AppendResult(graphPtr->interp, "\"", wmPtr->pathName,
+ "\" is not a child of \"", Tk_PathName(graphPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (tkwin != wmPtr->tkwin) {
+ if (wmPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask,
+ ChildEventProc, wmPtr);
+ Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0);
+ Tk_UnmapWindow(wmPtr->tkwin);
+ }
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildEventProc,
+ wmPtr);
+ Tk_ManageGeometry(tkwin, &winMarkerMgrInfo, wmPtr);
+ }
+ wmPtr->tkwin = tkwin;
+
+ wmPtr->flags |= MAP_ITEM;
+ if (wmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapWindowMarker --
+ *
+ * Calculate the layout position for a window marker. Positional
+ * information is saved in the marker.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapWindowMarker(markerPtr)
+ Marker *markerPtr;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->graphPtr;
+ Extents2D exts;
+ int width, height;
+
+ if (wmPtr->tkwin == (Tk_Window)NULL) {
+ return;
+ }
+ wmPtr->anchorPos = MapPoint(graphPtr, wmPtr->worldPts, &wmPtr->axes);
+
+ width = Tk_ReqWidth(wmPtr->tkwin);
+ height = Tk_ReqHeight(wmPtr->tkwin);
+ if (wmPtr->reqWidth > 0) {
+ width = wmPtr->reqWidth;
+ }
+ if (wmPtr->reqHeight > 0) {
+ height = wmPtr->reqHeight;
+ }
+ wmPtr->anchorPos = Blt_TranslatePoint(&wmPtr->anchorPos, width, height,
+ wmPtr->anchor);
+ wmPtr->anchorPos.x += wmPtr->xOffset;
+ wmPtr->anchorPos.y += wmPtr->yOffset;
+ wmPtr->width = width;
+ wmPtr->height = height;
+
+ /*
+ * Determine the bounding box of the window and test to see if it
+ * is at least partially contained within the plotting area.
+ */
+ exts.left = wmPtr->anchorPos.x;
+ exts.top = wmPtr->anchorPos.y;
+ exts.right = wmPtr->anchorPos.x + wmPtr->width - 1;
+ exts.bottom = wmPtr->anchorPos.y + wmPtr->height - 1;
+ wmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInWindowMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ return ((samplePtr->x >= wmPtr->anchorPos.x) &&
+ (samplePtr->x < (wmPtr->anchorPos.x + wmPtr->width)) &&
+ (samplePtr->y >= wmPtr->anchorPos.y) &&
+ (samplePtr->y < (wmPtr->anchorPos.y + wmPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInWindowMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (enclosed) {
+ return ((wmPtr->anchorPos.x >= extsPtr->left) &&
+ (wmPtr->anchorPos.y >= extsPtr->top) &&
+ ((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->right) &&
+ ((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->bottom));
+ }
+ return !((wmPtr->anchorPos.x >= extsPtr->right) ||
+ (wmPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->left) ||
+ ((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DrawWindowMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->tkwin == NULL) {
+ return;
+ }
+ if ((wmPtr->height != Tk_Height(wmPtr->tkwin)) ||
+ (wmPtr->width != Tk_Width(wmPtr->tkwin)) ||
+ ((int)wmPtr->anchorPos.x != Tk_X(wmPtr->tkwin)) ||
+ ((int)wmPtr->anchorPos.y != Tk_Y(wmPtr->tkwin))) {
+ Tk_MoveResizeWindow(wmPtr->tkwin, (int)wmPtr->anchorPos.x,
+ (int)wmPtr->anchorPos.y, wmPtr->width, wmPtr->height);
+ }
+ if (!Tk_IsMapped(wmPtr->tkwin)) {
+ Tk_MapWindow(wmPtr->tkwin);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * WindowMarkerToPostScript --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+WindowMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->tkwin == NULL) {
+ return;
+ }
+ if (Tk_IsMapped(wmPtr->tkwin)) {
+ Blt_WindowToPostScript(psToken, wmPtr->tkwin, wmPtr->anchorPos.x,
+ wmPtr->anchorPos.y);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeWindowMarker --
+ *
+ * Destroys the structure containing the attributes of the window
+ * marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window is destroyed and removed from the screen.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreeWindowMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask,
+ ChildEventProc, wmPtr);
+ Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0);
+ Tk_DestroyWindow(wmPtr->tkwin);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateWindowMarker --
+ *
+ * Allocate memory and initialize methods for the new window marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the window marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateWindowMarker()
+{
+ WindowMarker *wmPtr;
+
+ wmPtr = Blt_Calloc(1, sizeof(WindowMarker));
+ if (wmPtr != NULL) {
+ wmPtr->classPtr = &windowMarkerClass;
+ }
+ return (Marker *)wmPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildEventProc --
+ *
+ * This procedure is invoked whenever StructureNotify events
+ * occur for a window that's managed as part of a graph window
+ * marker. This procedure's only purpose is to clean up when
+ * windows are deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is disassociated from the window item when it is
+ * deleted.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ChildEventProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to record describing window item. */
+ XEvent *eventPtr; /* Describes what just happened. */
+{
+ WindowMarker *wmPtr = clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ wmPtr->tkwin = NULL;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildGeometryProc --
+ *
+ * This procedure is invoked whenever a window that's associated
+ * with a window item changes its requested dimensions.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The size and location on the window of the window may change,
+ * depending on the options specified for the window item.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ChildGeometryProc(clientData, tkwin)
+ ClientData clientData; /* Pointer to record for window item. */
+ Tk_Window tkwin; /* Window that changed its desired size. */
+{
+ WindowMarker *wmPtr = clientData;
+
+ if (wmPtr->reqWidth == 0) {
+ wmPtr->width = Tk_ReqWidth(tkwin);
+ }
+ if (wmPtr->reqHeight == 0) {
+ wmPtr->height = Tk_ReqHeight(tkwin);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildCustodyProc --
+ *
+ * This procedure is invoked when an embedded window has been
+ * stolen by another geometry manager. The information and
+ * memory associated with the widget is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the graph to be redrawn without the embedded
+ * widget at the next idle point.
+ *
+ * ----------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+ChildCustodyProc(clientData, tkwin)
+ ClientData clientData; /* Window marker to be destroyed. */
+ Tk_Window tkwin; /* Not used. */
+{
+ Marker *markerPtr = clientData;
+ Graph *graphPtr;
+
+ graphPtr = markerPtr->graphPtr;
+ DestroyMarker(markerPtr);
+ /*
+ * Not really needed. We should get an Expose event when the
+ * child window is unmapped.
+ */
+ Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapLineMarker --
+ *
+ * Calculate the layout position for a line marker. Positional
+ * information is saved in the marker. The line positions are
+ * stored in an array of points (malloc'ed).
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapLineMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+ Point2D *srcPtr, *endPtr;
+ Segment2D *segments, *segPtr;
+ Point2D p, q, next;
+ Extents2D exts;
+
+ lmPtr->nSegments = 0;
+ if (lmPtr->segments != NULL) {
+ Blt_Free(lmPtr->segments);
+ }
+ if (lmPtr->nWorldPts < 2) {
+ return; /* Too few points */
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+
+ /*
+ * Allow twice the number of world coordinates. The line will
+ * represented as series of line segments, not one continous
+ * polyline. This is because clipping against the plot area may
+ * chop the line into several disconnected segments.
+ */
+ segments = Blt_Malloc(lmPtr->nWorldPts * sizeof(Segment2D));
+ srcPtr = lmPtr->worldPts;
+ p = MapPoint(graphPtr, srcPtr, &lmPtr->axes);
+ p.x += lmPtr->xOffset;
+ p.y += lmPtr->yOffset;
+
+ segPtr = segments;
+ for (srcPtr++, endPtr = lmPtr->worldPts + lmPtr->nWorldPts;
+ srcPtr < endPtr; srcPtr++) {
+ next = MapPoint(graphPtr, srcPtr, &lmPtr->axes);
+ next.x += lmPtr->xOffset;
+ next.y += lmPtr->yOffset;
+ q = next;
+ if (Blt_LineRectClip(&exts, &p, &q)) {
+ segPtr->p = p;
+ segPtr->q = q;
+ segPtr++;
+ }
+ p = next;
+ }
+ lmPtr->nSegments = segPtr - segments;
+ lmPtr->segments = segments;
+ lmPtr->clipped = (lmPtr->nSegments == 0);
+}
+
+static int
+PointInLineMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ return Blt_PointInSegments(samplePtr, lmPtr->segments, lmPtr->nSegments,
+ (double)lmPtr->graphPtr->halo);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInLineMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInLineMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nWorldPts < 2) {
+ return FALSE;
+ }
+ if (enclosed) {
+ Point2D p;
+ Point2D *pointPtr, *endPtr;
+
+ for (pointPtr = lmPtr->worldPts,
+ endPtr = lmPtr->worldPts + lmPtr->nWorldPts;
+ pointPtr < endPtr; pointPtr++) {
+ p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes);
+ if ((p.x < extsPtr->left) && (p.x > extsPtr->right) &&
+ (p.y < extsPtr->top) && (p.y > extsPtr->bottom)) {
+ return FALSE;
+ }
+ }
+ return TRUE; /* All points inside bounding box. */
+ } else {
+ Point2D p, q;
+ int count;
+ Point2D *pointPtr, *endPtr;
+
+ count = 0;
+ for (pointPtr = lmPtr->worldPts,
+ endPtr = lmPtr->worldPts + (lmPtr->nWorldPts - 1);
+ pointPtr < endPtr; pointPtr++) {
+ p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes);
+ q = MapPoint(lmPtr->graphPtr, pointPtr + 1, &lmPtr->axes);
+ if (Blt_LineRectClip(extsPtr, &p, &q)) {
+ count++;
+ }
+ }
+ return (count > 0); /* At least 1 segment passes through region. */
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawLineMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawLineMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nSegments > 0) {
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ Blt_Draw2DSegments(graphPtr->display, drawable, lmPtr->gc,
+ lmPtr->segments, lmPtr->nSegments);
+ if (lmPtr->xor) { /* Toggle the drawing state */
+ lmPtr->xorState = (lmPtr->xorState == 0);
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureLineMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a line marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as line width, colors, dashes,
+ * etc. get set for markerPtr; old resources get freed, if there
+ * were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLineMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Drawable drawable;
+
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle);
+ if (lmPtr->outlineColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = lmPtr->outlineColor->pixel;
+ }
+ if (lmPtr->fillColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = lmPtr->fillColor->pixel;
+ }
+ gcValues.cap_style = lmPtr->capStyle;
+ gcValues.join_style = lmPtr->joinStyle;
+ gcValues.line_width = LineWidth(lmPtr->lineWidth);
+ gcValues.line_style = LineSolid;
+ if (LineIsDashed(lmPtr->dashes)) {
+ gcValues.line_style =
+ (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash;
+ }
+ if (lmPtr->xor) {
+ unsigned long pixel;
+ gcValues.function = GXxor;
+
+ gcMask |= GCFunction;
+ if (graphPtr->plotBg == NULL) {
+ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ pixel = graphPtr->plotBg->pixel;
+ }
+ if (gcMask & GCBackground) {
+ gcValues.background ^= pixel;
+ }
+ gcValues.foreground ^= pixel;
+ if (drawable != None) {
+ DrawLineMarker(markerPtr, drawable);
+ }
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lmPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+ }
+ if (LineIsDashed(lmPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &lmPtr->dashes);
+ }
+ lmPtr->gc = newGC;
+ if (lmPtr->xor) {
+ if (drawable != None) {
+ MapLineMarker(markerPtr);
+ DrawLineMarker(markerPtr, drawable);
+ }
+ return TCL_OK;
+ }
+ lmPtr->flags |= MAP_ITEM;
+ if (lmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LineMarkerToPostScript --
+ *
+ * Prints postscript commands to display the connect line.
+ * Dashed lines need to be handled specially, especially if a
+ * background color is designated.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript output commands are saved in the interpreter
+ * (infoPtr->interp) result field.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+LineMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nSegments > 0) {
+ Blt_LineAttributesToPostScript(psToken, lmPtr->outlineColor,
+ lmPtr->lineWidth, &lmPtr->dashes, lmPtr->capStyle,
+ lmPtr->joinStyle);
+ if ((LineIsDashed(lmPtr->dashes)) && (lmPtr->fillColor != NULL)) {
+ Blt_AppendToPostScript(psToken, "/DashesProc {\n gsave\n ",
+ (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, lmPtr->fillColor);
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ Blt_AppendToPostScript(psToken,
+ "stroke\n",
+ " grestore\n",
+ "} def\n", (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "/DashesProc {} def\n",
+ (char *)NULL);
+ }
+ Blt_2DSegmentsToPostScript(psToken, lmPtr->segments, lmPtr->nSegments);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeLineMarker --
+ *
+ * Destroys the structure and attributes of a line marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Line attributes (GCs, colors, stipple, etc) get released.
+ * Memory is deallocated, X resources are freed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeLineMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+ }
+ if (lmPtr->segments != NULL) {
+ Blt_Free(lmPtr->segments);
+ }
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateLineMarker --
+ *
+ * Allocate memory and initialize methods for a new line marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the line marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateLineMarker()
+{
+ LineMarker *lmPtr;
+
+ lmPtr = Blt_Calloc(1, sizeof(LineMarker));
+ if (lmPtr != NULL) {
+ lmPtr->classPtr = &lineMarkerClass;
+ lmPtr->xor = FALSE;
+ lmPtr->capStyle = CapButt;
+ lmPtr->joinStyle = JoinMiter;
+ }
+ return (Marker *)lmPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapPolygonMarker --
+ *
+ * Calculate the layout position for a polygon marker. Positional
+ * information is saved in the polygon in an array of points
+ * (malloc'ed).
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapPolygonMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+ Point2D *srcPtr, *destPtr, *endPtr;
+ Point2D *screenPts;
+ Extents2D exts;
+ int nScreenPts;
+
+ if (pmPtr->outlinePts != NULL) {
+ Blt_Free(pmPtr->outlinePts);
+ pmPtr->outlinePts = NULL;
+ pmPtr->nOutlinePts = 0;
+ }
+ if (pmPtr->fillPts != NULL) {
+ Blt_Free(pmPtr->fillPts);
+ pmPtr->fillPts = NULL;
+ pmPtr->nFillPts = 0;
+ }
+ if (pmPtr->screenPts != NULL) {
+ Blt_Free(pmPtr->screenPts);
+ pmPtr->screenPts = NULL;
+ }
+ if (pmPtr->nWorldPts < 3) {
+ return; /* Too few points */
+ }
+
+ /*
+ * Allocate and fill a temporary array to hold the screen
+ * coordinates of the polygon.
+ */
+ nScreenPts = pmPtr->nWorldPts + 1;
+ screenPts = Blt_Malloc((nScreenPts + 1) * sizeof(Point2D));
+ endPtr = pmPtr->worldPts + pmPtr->nWorldPts;
+ destPtr = screenPts;
+ for (srcPtr = pmPtr->worldPts; srcPtr < endPtr; srcPtr++) {
+ *destPtr = MapPoint(graphPtr, srcPtr, &pmPtr->axes);
+ destPtr->x += pmPtr->xOffset;
+ destPtr->y += pmPtr->yOffset;
+ destPtr++;
+ }
+ *destPtr = screenPts[0];
+
+ Blt_GraphExtents(graphPtr, &exts);
+ pmPtr->clipped = TRUE;
+ if (pmPtr->fill.fgColor != NULL) { /* Polygon fill required. */
+ Point2D *fillPts;
+ int n;
+
+ fillPts = Blt_Malloc(sizeof(Point2D) * nScreenPts * 3);
+ assert(fillPts);
+ n = Blt_PolyRectClip(&exts, screenPts, pmPtr->nWorldPts, fillPts);
+ if (n < 3) {
+ Blt_Free(fillPts);
+ } else {
+ pmPtr->nFillPts = n;
+ pmPtr->fillPts = fillPts;
+ pmPtr->clipped = FALSE;
+ }
+ }
+ if ((pmPtr->outline.fgColor != NULL) && (pmPtr->lineWidth > 0)) {
+ Segment2D *outlinePts;
+ register Segment2D *segPtr;
+ /*
+ * Generate line segments representing the polygon outline.
+ * The resulting outline may or may not be closed from
+ * viewport clipping.
+ */
+ outlinePts = Blt_Malloc(nScreenPts * sizeof(Segment2D));
+ if (outlinePts == NULL) {
+ return; /* Can't allocate point array */
+ }
+ /*
+ * Note that this assumes that the point array contains an
+ * extra point that closes the polygon.
+ */
+ segPtr = outlinePts;
+ for (srcPtr = screenPts, endPtr = screenPts + (nScreenPts - 1);
+ srcPtr < endPtr; srcPtr++) {
+ segPtr->p = srcPtr[0];
+ segPtr->q = srcPtr[1];
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ }
+ }
+ pmPtr->nOutlinePts = segPtr - outlinePts;
+ pmPtr->outlinePts = outlinePts;
+ if (pmPtr->nOutlinePts > 0) {
+ pmPtr->clipped = FALSE;
+ }
+ }
+ pmPtr->screenPts = screenPts;
+}
+
+static int
+PointInPolygonMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+ return Blt_PointInPolygon(samplePtr, pmPtr->screenPts,
+ pmPtr->nWorldPts + 1);
+ }
+ return FALSE;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInPolygonMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInPolygonMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+ return Blt_RegionInPolygon(extsPtr, pmPtr->screenPts, pmPtr->nWorldPts,
+ enclosed);
+ }
+ return FALSE;
+}
+
+static void
+DrawPolygonMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ /* Draw polygon fill region */
+ if ((pmPtr->nFillPts > 0) && (pmPtr->fill.fgColor != NULL)) {
+ XPoint *destPtr, *pointArr;
+ Point2D *srcPtr, *endPtr;
+
+ pointArr = Blt_Malloc(pmPtr->nFillPts * sizeof(XPoint));
+ if (pointArr == NULL) {
+ return;
+ }
+ destPtr = pointArr;
+ for (srcPtr = pmPtr->fillPts,
+ endPtr = pmPtr->fillPts + pmPtr->nFillPts; srcPtr < endPtr;
+ srcPtr++) {
+ destPtr->x = (short int)srcPtr->x;
+ destPtr->y = (short int)srcPtr->y;
+ destPtr++;
+ }
+ XFillPolygon(graphPtr->display, drawable, pmPtr->fillGC,
+ pointArr, pmPtr->nFillPts, Complex, CoordModeOrigin);
+ Blt_Free(pointArr);
+ }
+ /* and then the outline */
+ if ((pmPtr->nOutlinePts > 0) && (pmPtr->lineWidth > 0) &&
+ (pmPtr->outline.fgColor != NULL)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable, pmPtr->outlineGC,
+ pmPtr->outlinePts, pmPtr->nOutlinePts);
+ }
+}
+
+
+static void
+PolygonMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if (pmPtr->fill.fgColor != NULL) {
+
+ /*
+ * Options: fg bg
+ * Draw outline only.
+ * x Draw solid or stipple.
+ * x x Draw solid or stipple.
+ */
+
+ /* Create a path to use for both the polygon and its outline. */
+ Blt_PathToPostScript(psToken, pmPtr->fillPts, pmPtr->nFillPts);
+ Blt_AppendToPostScript(psToken, "closepath\n", (char *)NULL);
+
+ /* If the background fill color was specified, draw the
+ * polygon in a solid fashion with that color. */
+ if (pmPtr->fill.bgColor != NULL) {
+ Blt_BackgroundToPostScript(psToken, pmPtr->fill.bgColor);
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ Blt_ForegroundToPostScript(psToken, pmPtr->fill.fgColor);
+ if (pmPtr->stipple != None) {
+ /* Draw the stipple in the foreground color. */
+ Blt_StippleToPostScript(psToken, graphPtr->display,
+ pmPtr->stipple);
+ } else {
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ }
+
+ /* Draw the outline in the foreground color. */
+ if ((pmPtr->lineWidth > 0) && (pmPtr->outline.fgColor != NULL)) {
+
+ /* Set up the line attributes. */
+ Blt_LineAttributesToPostScript(psToken, pmPtr->outline.fgColor,
+ pmPtr->lineWidth, &pmPtr->dashes, pmPtr->capStyle,
+ pmPtr->joinStyle);
+
+ /*
+ * Define on-the-fly a PostScript macro "DashesProc" that
+ * will be executed for each call to the Polygon drawing
+ * routine. If the line isn't dashed, simply make this an
+ * empty definition.
+ */
+ if ((pmPtr->outline.bgColor != NULL) && (LineIsDashed(pmPtr->dashes))) {
+ Blt_AppendToPostScript(psToken,
+ "/DashesProc {\n",
+ "gsave\n ", (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, pmPtr->outline.bgColor);
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ Blt_AppendToPostScript(psToken,
+ "stroke\n",
+ " grestore\n",
+ "} def\n", (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "/DashesProc {} def\n",
+ (char *)NULL);
+ }
+ Blt_2DSegmentsToPostScript(psToken, pmPtr->outlinePts,
+ pmPtr->nOutlinePts);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigurePolygonMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a polygon marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as polygon color, dashes,
+ * fillstyle, etc. get set for markerPtr; old resources get
+ * freed, if there were any. The marker is eventually
+ * redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigurePolygonMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Drawable drawable;
+
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ gcMask = (GCLineWidth | GCLineStyle);
+ if (pmPtr->outline.fgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = pmPtr->outline.fgColor->pixel;
+ }
+ if (pmPtr->outline.bgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = pmPtr->outline.bgColor->pixel;
+ }
+ gcMask |= (GCCapStyle | GCJoinStyle);
+ gcValues.cap_style = pmPtr->capStyle;
+ gcValues.join_style = pmPtr->joinStyle;
+ gcValues.line_style = LineSolid;
+ gcValues.dash_offset = 0;
+ gcValues.line_width = LineWidth(pmPtr->lineWidth);
+ if (LineIsDashed(pmPtr->dashes)) {
+ gcValues.line_style = (pmPtr->outline.bgColor == NULL)
+ ? LineOnOffDash : LineDoubleDash;
+ }
+ if (pmPtr->xor) {
+ unsigned long pixel;
+ gcValues.function = GXxor;
+
+ gcMask |= GCFunction;
+ if (graphPtr->plotBg == NULL) {
+ /* The graph's color option may not have been set yet */
+ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ pixel = graphPtr->plotBg->pixel;
+ }
+ if (gcMask & GCBackground) {
+ gcValues.background ^= pixel;
+ }
+ gcValues.foreground ^= pixel;
+ if (drawable != None) {
+ DrawPolygonMarker(markerPtr, drawable);
+ }
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(pmPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &pmPtr->dashes);
+ }
+ if (pmPtr->outlineGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+ }
+ pmPtr->outlineGC = newGC;
+
+ gcMask = 0;
+ if (pmPtr->fill.fgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = pmPtr->fill.fgColor->pixel;
+ }
+ if (pmPtr->fill.bgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = pmPtr->fill.bgColor->pixel;
+ }
+ if (pmPtr->stipple != None) {
+ gcValues.stipple = pmPtr->stipple;
+ gcValues.fill_style = (pmPtr->fill.bgColor != NULL)
+ ? FillOpaqueStippled : FillStippled;
+ gcMask |= (GCStipple | GCFillStyle);
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (pmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+ }
+ pmPtr->fillGC = newGC;
+
+ if ((gcMask == 0) && !(graphPtr->flags & RESET_AXES) && (pmPtr->xor)) {
+ if (drawable != None) {
+ MapPolygonMarker(markerPtr);
+ DrawPolygonMarker(markerPtr, drawable);
+ }
+ return TCL_OK;
+ }
+ pmPtr->flags |= MAP_ITEM;
+ if (pmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreePolygonMarker --
+ *
+ * Release memory and resources allocated for the polygon element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the polygon element is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreePolygonMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if (pmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+ }
+ if (pmPtr->outlineGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+ }
+ if (pmPtr->fillPts != NULL) {
+ Blt_Free(pmPtr->fillPts);
+ }
+ if (pmPtr->outlinePts != NULL) {
+ Blt_Free(pmPtr->outlinePts);
+ }
+ if (pmPtr->screenPts != NULL) {
+ Blt_Free(pmPtr->screenPts);
+ }
+ Blt_FreeColorPair(&pmPtr->outline);
+ Blt_FreeColorPair(&pmPtr->fill);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreatePolygonMarker --
+ *
+ * Allocate memory and initialize methods for the new polygon
+ * marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is
+ * returned.
+ *
+ * Side effects:
+ * Memory is allocated for the polygon marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreatePolygonMarker()
+{
+ PolygonMarker *pmPtr;
+
+ pmPtr = Blt_Calloc(1, sizeof(PolygonMarker));
+ if (pmPtr != NULL) {
+ pmPtr->classPtr = &polygonMarkerClass;
+ pmPtr->capStyle = CapButt;
+ pmPtr->joinStyle = JoinMiter;
+
+ }
+ return (Marker *)pmPtr;
+}
+
+static int
+NameToMarker(graphPtr, name, markerPtrPtr)
+ Graph *graphPtr;
+ char *name;
+ Marker **markerPtrPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&graphPtr->markers.table, name);
+ if (hPtr != NULL) {
+ *markerPtrPtr = (Marker *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+ }
+ Tcl_AppendResult(graphPtr->interp, "can't find marker \"", name,
+ "\" in \"", Tk_PathName(graphPtr->tkwin), (char *)NULL);
+ return TCL_ERROR;
+}
+
+
+static int
+RenameMarker(graphPtr, markerPtr, oldName, newName)
+ Graph *graphPtr;
+ Marker *markerPtr;
+ char *oldName, *newName;
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ /* Rename the marker only if no marker already exists by that name */
+ hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, newName, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(graphPtr->interp, "can't rename marker: \"", newName,
+ "\" already exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ markerPtr->name = Blt_Strdup(newName);
+ markerPtr->hashPtr = hPtr;
+ Blt_SetHashValue(hPtr, (char *)markerPtr);
+
+ /* Delete the old hash entry */
+ hPtr = Blt_FindHashEntry(&graphPtr->markers.table, oldName);
+ Blt_DeleteHashEntry(&graphPtr->markers.table, hPtr);
+ if (oldName != NULL) {
+ Blt_Free(oldName);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Returns a list of marker identifiers in interp->result;
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+NamesOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ Blt_ChainLink *linkPtr;
+ register int i;
+
+ Tcl_ResetResult(interp);
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if (argc == 3) {
+ Tcl_AppendElement(interp, markerPtr->name);
+ continue;
+ }
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(markerPtr->name, argv[i])) {
+ Tcl_AppendElement(interp, markerPtr->name);
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+ClientData
+Blt_MakeMarkerTag(graphPtr, tagName)
+ Graph *graphPtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&graphPtr->markers.tagTable, tagName, &isNew);
+ assert(hPtr);
+ return Blt_GetHashKey(&graphPtr->markers.tagTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .g element bind elemName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tag;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->markers.tagTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tag = Blt_GetHashKey(&graphPtr->markers.tagTable, hPtr);
+ Tcl_AppendElement(interp, tag);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+ Blt_MakeMarkerTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+
+ if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tk_ConfigureValue(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr, argv[4], 0)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ int flags = TK_CONFIG_ARGV_ONLY;
+ char *oldName;
+ int nNames, nOpts;
+ char **options;
+ register int i;
+ int under;
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (NameToMarker(graphPtr, argv[i], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nNames = i; /* Number of element names specified */
+ nOpts = argc - i; /* Number of options specified */
+ options = argv + nNames; /* Start of options in argv */
+
+ for (i = 0; i < nNames; i++) {
+ NameToMarker(graphPtr, argv[i], &markerPtr);
+ if (nOpts == 0) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ (char *)NULL, flags);
+ } else if (nOpts == 1) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ options[0], flags);
+ }
+ /* Save the old marker. */
+ oldName = markerPtr->name;
+ under = markerPtr->drawUnder;
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, nOpts, options,
+ (char *)markerPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (oldName != markerPtr->name) {
+ if (RenameMarker(graphPtr, markerPtr, oldName, markerPtr->name)
+ != TCL_OK) {
+ markerPtr->name = oldName;
+ return TCL_ERROR;
+ }
+ }
+ if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (markerPtr->drawUnder != under) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * This procedure creates and initializes a new marker.
+ *
+ * Results:
+ * The return value is a pointer to a structure describing
+ * the new element. If an error occurred, then the return
+ * value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated, etc.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+CreateOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ Blt_Uid classUid;
+ register int i;
+ char *name;
+ char string[200];
+ unsigned int length;
+ char c;
+
+ c = argv[3][0];
+ /* Create the new marker based upon the given type */
+ if ((c == 't') && (strcmp(argv[3], "text") == 0)) {
+ classUid = bltTextMarkerUid;
+ } else if ((c == 'l') && (strcmp(argv[3], "line") == 0)) {
+ classUid = bltLineMarkerUid;
+ } else if ((c == 'p') && (strcmp(argv[3], "polygon") == 0)) {
+ classUid = bltPolygonMarkerUid;
+ } else if ((c == 'i') && (strcmp(argv[3], "image") == 0)) {
+ classUid = bltImageMarkerUid;
+ } else if ((c == 'b') && (strcmp(argv[3], "bitmap") == 0)) {
+ classUid = bltBitmapMarkerUid;
+ } else if ((c == 'w') && (strcmp(argv[3], "window") == 0)) {
+ classUid = bltWindowMarkerUid;
+ } else {
+ Tcl_AppendResult(interp, "unknown marker type \"", argv[3],
+ "\": should be \"text\", \"line\", \"polygon\", \"bitmap\", \"image\", or \
+\"window\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Scan for "-name" option. We need it for the component name */
+ name = NULL;
+ for (i = 4; i < argc; i += 2) {
+ length = strlen(argv[i]);
+ if ((length > 1) && (strncmp(argv[i], "-name", length) == 0)) {
+ name = argv[i + 1];
+ break;
+ }
+ }
+ /* If no name was given for the marker, make up one. */
+ if (name == NULL) {
+ sprintf(string, "marker%d", graphPtr->nextMarkerId++);
+ name = string;
+ } else if (name[0] == '-') {
+ Tcl_AppendResult(interp, "name of marker \"", name,
+ "\" can't start with a '-'", (char *)NULL);
+ return TCL_ERROR;
+ }
+ markerPtr = CreateMarker(graphPtr, name, classUid);
+ if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, name,
+ markerPtr->classUid, markerPtr->classPtr->configSpecs,
+ argc - 4, argv + 4, (char *)markerPtr, 0) != TCL_OK) {
+ DestroyMarker(markerPtr);
+ return TCL_ERROR;
+ }
+ if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+ DestroyMarker(markerPtr);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, name, &isNew);
+ if (!isNew) {
+ Marker *oldMarkerPtr;
+ /*
+ * Marker by the same name already exists. Delete the old
+ * marker and it's list entry. But save the hash entry.
+ */
+ oldMarkerPtr = (Marker *)Blt_GetHashValue(hPtr);
+ oldMarkerPtr->hashPtr = NULL;
+ DestroyMarker(oldMarkerPtr);
+ }
+ Blt_SetHashValue(hPtr, markerPtr);
+ markerPtr->hashPtr = hPtr;
+ markerPtr->linkPtr =
+ Blt_ChainAppend(graphPtr->markers.displayList, markerPtr);
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ Tcl_SetResult(interp, name, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the marker given by markerId.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ register int i;
+
+ for (i = 3; i < argc; i++) {
+ if (NameToMarker(graphPtr, argv[i], &markerPtr) == TCL_OK) {
+ DestroyMarker(markerPtr);
+ }
+ }
+ Tcl_ResetResult(interp);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Find the legend entry from the given argument. The argument
+ * can be either a screen position "@x,y" or the name of an
+ * element.
+ *
+ * I don't know how useful it is to test with the name of an
+ * element.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ register Marker *markerPtr;
+
+ if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) {
+ markerPtr = (Marker *)Blt_GetCurrentItem(graphPtr->bindTable);
+ /* Report only on markers. */
+ if (markerPtr == NULL) {
+ return TCL_OK;
+ }
+ if ((markerPtr->classUid == bltBitmapMarkerUid) ||
+ (markerPtr->classUid == bltLineMarkerUid) ||
+ (markerPtr->classUid == bltWindowMarkerUid) ||
+ (markerPtr->classUid == bltPolygonMarkerUid) ||
+ (markerPtr->classUid == bltTextMarkerUid) ||
+ (markerPtr->classUid == bltImageMarkerUid)) {
+ Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RelinkOp --
+ *
+ * Reorders the marker (given by the first name) before/after
+ * the another marker (given by the second name) in the
+ * marker display list. If no second name is given, the
+ * marker is placed at the beginning/end of the list.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RelinkOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Blt_ChainLink *linkPtr, *placePtr;
+ Marker *markerPtr;
+
+ /* Find the marker to be raised or lowered. */
+ if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Right now it's assumed that all markers are always in the
+ display list. */
+ linkPtr = markerPtr->linkPtr;
+ Blt_ChainUnlinkLink(graphPtr->markers.displayList, markerPtr->linkPtr);
+
+ placePtr = NULL;
+ if (argc == 5) {
+ if (NameToMarker(graphPtr, argv[4], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ placePtr = markerPtr->linkPtr;
+ }
+
+ /* Link the marker at its new position. */
+ if (argv[2][0] == 'a') {
+ Blt_ChainLinkAfter(graphPtr->markers.displayList, linkPtr, placePtr);
+ } else {
+ Blt_ChainLinkBefore(graphPtr->markers.displayList, linkPtr, placePtr);
+ }
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ * Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FindOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_ChainLink *linkPtr;
+ Extents2D exts;
+ Marker *markerPtr;
+ int mode;
+ int left, right, top, bottom;
+ int enclosed;
+
+#define FIND_ENCLOSED (1<<0)
+#define FIND_OVERLAPPING (1<<1)
+ if (strcmp(argv[3], "enclosed") == 0) {
+ mode = FIND_ENCLOSED;
+ } else if (strcmp(argv[3], "overlapping") == 0) {
+ mode = FIND_OVERLAPPING;
+ } else {
+ Tcl_AppendResult(interp, "bad search type \"", argv[3],
+ ": should be \"enclosed\", or \"overlapping\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if ((Tcl_GetInt(interp, argv[4], &left) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[5], &top) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[6], &right) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[7], &bottom) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (left < right) {
+ exts.left = (double)left;
+ exts.right = (double)right;
+ } else {
+ exts.left = (double)right;
+ exts.right = (double)left;
+ }
+ if (top < bottom) {
+ exts.top = (double)top;
+ exts.bottom = (double)bottom;
+ } else {
+ exts.top = (double)bottom;
+ exts.bottom = (double)top;
+ }
+ enclosed = (mode == FIND_ENCLOSED);
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if (markerPtr->hidden) {
+ continue;
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&graphPtr->elements.table,
+ markerPtr->elemName);
+ if (hPtr != NULL) {
+ Element *elemPtr;
+
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ }
+ }
+ if ((*markerPtr->classPtr->regionProc)(markerPtr, &exts, enclosed)) {
+ Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE);
+ return TCL_OK;
+ }
+ }
+ Tcl_SetResult(interp, "", TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ * Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExistsOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&graphPtr->markers.table, argv[3]);
+ Blt_SetBooleanResult(interp, (hPtr != NULL));
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * Returns a symbolic name for the type of the marker whose ID is
+ * given.
+ *
+ * Results:
+ * A standard Tcl result. interp->result will contain the symbolic
+ * type of the marker.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+
+ if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, markerPtr->classUid, TCL_STATIC);
+ return TCL_OK;
+}
+
+/* Public routines */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_MarkerOp --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec markerOps[] =
+{
+ {"after", 1, (Blt_Op)RelinkOp, 4, 5, "marker ?afterMarker?",},
+ {"before", 2, (Blt_Op)RelinkOp, 4, 5, "marker ?beforeMarker?",},
+ {"bind", 2, (Blt_Op)BindOp, 3, 6, "marker sequence command",},
+ {"cget", 2, (Blt_Op)CgetOp, 5, 5, "marker option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
+ "marker ?marker?... ?option value?...",},
+ {"create", 2, (Blt_Op)CreateOp, 4, 0,
+ "type ?option value?...",},
+ {"delete", 1, (Blt_Op)DeleteOp, 3, 0, "?marker?...",},
+ {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "marker",},
+ {"find", 1, (Blt_Op)FindOp, 8, 8, "enclosed|overlapping x1 y1 x2 y2",},
+ {"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
+ {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
+ {"type", 1, (Blt_Op)TypeOp, 4, 4, "marker",},
+};
+static int nMarkerOps = sizeof(markerOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+int
+Blt_MarkerOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nMarkerOps, markerOps, BLT_OP_ARG2, argc, argv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, argc, argv);
+ return result;
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_MarkersToPostScript --
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_MarkersToPostScript(graphPtr, psToken, under)
+ Graph *graphPtr;
+ PsToken psToken;
+ int under;
+{
+ Blt_ChainLink *linkPtr;
+ register Marker *markerPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if ((markerPtr->classPtr->postscriptProc == NULL) ||
+ (markerPtr->nWorldPts == 0)) {
+ continue;
+ }
+ if (markerPtr->drawUnder != under) {
+ continue;
+ }
+ if (markerPtr->hidden) {
+ continue;
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&graphPtr->elements.table,
+ markerPtr->elemName);
+ if (hPtr != NULL) {
+ Element *elemPtr;
+
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ }
+ }
+ Blt_AppendToPostScript(psToken, "\n% Marker \"", markerPtr->name,
+ "\" is a ", markerPtr->classUid, " marker\n", (char *)NULL);
+ (*markerPtr->classPtr->postscriptProc) (markerPtr, psToken);
+ }
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_DrawMarkers --
+ *
+ * Calls the individual drawing routines (based on marker type)
+ * for each marker in the display list.
+ *
+ * A marker will not be drawn if
+ *
+ * 1) An element linked to the marker (indicated by elemName)
+ * is currently hidden.
+ *
+ * 2) No coordinates have been specified for the marker.
+ *
+ * 3) The marker is requesting to be drawn at a different level
+ * (above/below the elements) from the current mode.
+ *
+ * 4) The marker is configured as hidden (-hide option).
+ *
+ * 5) The marker isn't visible in the current viewport
+ * (i.e. clipped).
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Markers are drawn into the drawable (pixmap) which will eventually
+ * be displayed in the graph window.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_DrawMarkers(graphPtr, drawable, under)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+ int under;
+{
+ Blt_ChainLink *linkPtr;
+ Marker *markerPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+
+ if ((markerPtr->nWorldPts == 0) ||
+ (markerPtr->drawUnder != under) ||
+ (markerPtr->hidden) ||
+ (markerPtr->clipped)) {
+ continue;
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_HashEntry *hPtr;
+
+ /* Look up the named element and see if it's hidden */
+ hPtr = Blt_FindHashEntry(&graphPtr->elements.table,
+ markerPtr->elemName);
+ if (hPtr != NULL) {
+ Element *elemPtr;
+
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ }
+ }
+
+ (*markerPtr->classPtr->drawProc) (markerPtr, drawable);
+ }
+}
+
+void
+Blt_MapMarkers(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Marker *markerPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if ((markerPtr->nWorldPts == 0) || (markerPtr->hidden)) {
+ continue;
+ }
+ if ((graphPtr->flags & MAP_ALL) || (markerPtr->flags & MAP_ITEM)) {
+ (*markerPtr->classPtr->mapProc) (markerPtr);
+ markerPtr->flags &= ~MAP_ITEM;
+ }
+ }
+}
+
+
+void
+Blt_DestroyMarkers(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Marker *markerPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->markers.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ markerPtr = (Marker *)Blt_GetHashValue(hPtr);
+ /*
+ * Dereferencing the pointer to the hash table prevents the
+ * hash table entry from being automatically deleted.
+ */
+ markerPtr->hashPtr = NULL;
+ DestroyMarker(markerPtr);
+ }
+ Blt_DeleteHashTable(&graphPtr->markers.table);
+ Blt_DeleteHashTable(&graphPtr->markers.tagTable);
+ Blt_ChainDestroy(graphPtr->markers.displayList);
+}
+
+Marker *
+Blt_NearestMarker(graphPtr, x, y, under)
+ Graph *graphPtr;
+ int x, y; /* Screen coordinates */
+ int under;
+{
+ Blt_ChainLink *linkPtr;
+ Marker *markerPtr;
+ Point2D point;
+
+ point.x = (double)x;
+ point.y = (double)y;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->markers.displayList);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ /*
+ * Don't consider markers that are pending to be mapped. Even
+ * if the marker has already been mapped, the coordinates
+ * could be invalid now. Better to pick no marker than the
+ * wrong marker.
+ */
+ if ((markerPtr->drawUnder == under) && (markerPtr->nWorldPts > 0) &&
+ ((markerPtr->flags & MAP_ITEM) == 0) &&
+ (!markerPtr->hidden) && (markerPtr->state == STATE_NORMAL)) {
+ if ((*markerPtr->classPtr->pointProc) (markerPtr, &point)) {
+ return markerPtr;
+ }
+ }
+ }
+ return NULL;
+}