749N/A/* $XConsortium: StripChart.c,v 1.25 94/04/17 20:12:56 kaleb Exp $ */
749N/A
749N/A/***********************************************************
749N/A
749N/ACopyright (c) 1987, 1988, 1994 X Consortium
749N/A
749N/APermission is hereby granted, free of charge, to any person obtaining a copy
749N/Aof this software and associated documentation files (the "Software"), to deal
749N/Ain the Software without restriction, including without limitation the rights
749N/Ato use, copy, modify, merge, publish, distribute, sublicense, and/or sell
749N/Acopies of the Software, and to permit persons to whom the Software is
749N/Afurnished to do so, subject to the following conditions:
749N/A
749N/AThe above copyright notice and this permission notice shall be included in
749N/Aall copies or substantial portions of the Software.
749N/A
749N/ATHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
749N/AIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
749N/AFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
749N/AX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
749N/AAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
749N/ACONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
749N/A
749N/AExcept as contained in this notice, the name of the X Consortium shall not be
749N/Aused in advertising or otherwise to promote the sale, use or other dealings
749N/Ain this Software without prior written authorization from the X Consortium.
749N/A
749N/A
749N/ACopyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
749N/A
749N/A All Rights Reserved
749N/A
749N/APermission to use, copy, modify, and distribute this software and its
749N/Adocumentation for any purpose and without fee is hereby granted,
749N/Aprovided that the above copyright notice appear in all copies and that
749N/Aboth that copyright notice and this permission notice appear in
749N/Asupporting documentation, and that the name of Digital not be
749N/Aused in advertising or publicity pertaining to distribution of the
749N/Asoftware without specific, written prior permission.
749N/A
749N/ADIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
749N/AALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
749N/ADIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
749N/AANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
749N/AWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
749N/AARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
749N/ASOFTWARE.
749N/A
749N/A******************************************************************/
749N/A
749N/A#include <stdio.h>
749N/A#include <X11/IntrinsicP.h>
749N/A#include <X11/StringDefs.h>
749N/A#include <X11/Xaw/XawInit.h>
749N/A#include <X11/Xaw/StripCharP.h>
749N/A#include <X11/Xfuncs.h>
749N/A
749N/A#define MS_PER_SEC 1000
749N/A
749N/A/* Private Data */
749N/A
749N/A#define offset(field) XtOffsetOf(StripChartRec, field)
749N/A
749N/Astatic XtResource resources[] = {
749N/A {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
749N/A offset(core.width), XtRImmediate, (XtPointer) 120},
749N/A {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
749N/A offset(core.height), XtRImmediate, (XtPointer) 120},
749N/A {XtNupdate, XtCInterval, XtRInt, sizeof(int),
749N/A offset(strip_chart.update), XtRImmediate, (XtPointer) 10},
749N/A {XtNminScale, XtCScale, XtRInt, sizeof(int),
749N/A offset(strip_chart.min_scale), XtRImmediate, (XtPointer) 1},
749N/A {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
749N/A offset(strip_chart.fgpixel), XtRString, XtDefaultForeground},
749N/A {XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel),
749N/A offset(strip_chart.hipixel), XtRString, XtDefaultForeground},
749N/A {XtNgetValue, XtCCallback, XtRCallback, sizeof(XtPointer),
749N/A offset(strip_chart.get_value), XtRImmediate, (XtPointer) NULL},
749N/A {XtNjumpScroll, XtCJumpScroll, XtRInt, sizeof(int),
749N/A offset(strip_chart.jump_val), XtRImmediate, (XtPointer) DEFAULT_JUMP},
749N/A};
749N/A
749N/A#undef offset
749N/A
749N/Astatic void Initialize(), Destroy(), Redisplay(), MoveChart(), SetPoints();
749N/Astatic Boolean SetValues();
749N/Astatic int repaint_window();
749N/A
749N/AStripChartClassRec stripChartClassRec = {
749N/A { /* core fields */
749N/A /* superclass */ (WidgetClass) &simpleClassRec,
749N/A /* class_name */ "StripChart",
749N/A /* size */ sizeof(StripChartRec),
749N/A /* class_initialize */ XawInitializeWidgetSet,
749N/A /* class_part_initialize */ NULL,
749N/A /* class_inited */ FALSE,
749N/A /* initialize */ Initialize,
749N/A /* initialize_hook */ NULL,
749N/A /* realize */ XtInheritRealize,
749N/A /* actions */ NULL,
749N/A /* num_actions */ 0,
749N/A /* resources */ resources,
749N/A /* num_resources */ XtNumber(resources),
749N/A /* xrm_class */ NULLQUARK,
749N/A /* compress_motion */ TRUE,
749N/A /* compress_exposure */ XtExposeCompressMultiple |
749N/A XtExposeGraphicsExposeMerged,
749N/A /* compress_enterleave */ TRUE,
749N/A /* visible_interest */ FALSE,
749N/A /* destroy */ Destroy,
749N/A /* resize */ SetPoints,
749N/A /* expose */ Redisplay,
749N/A /* set_values */ SetValues,
749N/A /* set_values_hook */ NULL,
749N/A /* set_values_almost */ NULL,
749N/A /* get_values_hook */ NULL,
749N/A /* accept_focus */ NULL,
749N/A /* version */ XtVersion,
749N/A /* callback_private */ NULL,
749N/A /* tm_table */ NULL,
749N/A /* query_geometry */ XtInheritQueryGeometry,
749N/A /* display_accelerator */ XtInheritDisplayAccelerator,
749N/A /* extension */ NULL
749N/A },
749N/A { /* Simple class fields */
749N/A /* change_sensitive */ XtInheritChangeSensitive
749N/A }
749N/A};
749N/A
749N/AWidgetClass stripChartWidgetClass = (WidgetClass) &stripChartClassRec;
749N/A
749N/A/****************************************************************
749N/A *
749N/A * Private Procedures
749N/A *
749N/A ****************************************************************/
749N/A
749N/Astatic void draw_it();
749N/A
749N/A/* Function Name: CreateGC
749N/A * Description: Creates the GC's
749N/A * Arguments: w - the strip chart widget.
749N/A * which - which GC's to create.
749N/A * Returns: none
749N/A */
749N/A
749N/Astatic void
749N/ACreateGC(w, which)
749N/AStripChartWidget w;
749N/Aunsigned int which;
749N/A{
749N/A XGCValues myXGCV;
749N/A
749N/A if (which & FOREGROUND) {
749N/A myXGCV.foreground = w->strip_chart.fgpixel;
749N/A w->strip_chart.fgGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
749N/A }
749N/A
749N/A if (which & HIGHLIGHT) {
749N/A myXGCV.foreground = w->strip_chart.hipixel;
749N/A w->strip_chart.hiGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
749N/A }
749N/A}
749N/A
749N/A/* Function Name: DestroyGC
749N/A * Description: Destroys the GC's
749N/A * Arguments: w - the strip chart widget.
749N/A * which - which GC's to destroy.
749N/A * Returns: none
749N/A */
749N/A
749N/Astatic void
749N/ADestroyGC(w, which)
749N/AStripChartWidget w;
749N/Aunsigned int which;
749N/A{
749N/A if (which & FOREGROUND)
749N/A XtReleaseGC((Widget) w, w->strip_chart.fgGC);
749N/A
749N/A if (which & HIGHLIGHT)
749N/A XtReleaseGC((Widget) w, w->strip_chart.hiGC);
749N/A}
749N/A
749N/A/* ARGSUSED */
749N/Astatic void Initialize (greq, gnew, args, num_args)
749N/A Widget greq, gnew;
749N/A ArgList args;
749N/A Cardinal *num_args;
749N/A{
749N/A StripChartWidget w = (StripChartWidget)gnew;
749N/A
749N/A if (w->strip_chart.update > 0)
749N/A w->strip_chart.interval_id = XtAppAddTimeOut(
749N/A XtWidgetToApplicationContext(gnew),
749N/A w->strip_chart.update * MS_PER_SEC,
749N/A draw_it, (XtPointer) gnew);
749N/A CreateGC(w, (unsigned int) ALL_GCS);
749N/A
749N/A w->strip_chart.scale = w->strip_chart.min_scale;
749N/A w->strip_chart.interval = 0;
749N/A w->strip_chart.max_value = 0.0;
749N/A w->strip_chart.points = NULL;
749N/A SetPoints(w);
749N/A}
749N/A
749N/Astatic void Destroy (gw)
749N/A Widget gw;
749N/A{
749N/A StripChartWidget w = (StripChartWidget)gw;
749N/A
749N/A if (w->strip_chart.update > 0)
749N/A XtRemoveTimeOut (w->strip_chart.interval_id);
749N/A if (w->strip_chart.points)
749N/A XtFree((char *) w->strip_chart.points);
749N/A DestroyGC(w, (unsigned int) ALL_GCS);
749N/A}
749N/A
749N/A/*
749N/A * NOTE: This function really needs to recieve graphics exposure
749N/A * events, but since this is not easily supported until R4 I am
749N/A * going to hold off until then.
749N/A */
749N/A
749N/A/* ARGSUSED */
749N/Astatic void Redisplay(w, event, region)
749N/A Widget w;
749N/A XEvent *event;
749N/A Region region;
749N/A{
749N/A if (event->type == GraphicsExpose)
749N/A (void) repaint_window ((StripChartWidget)w, event->xgraphicsexpose.x,
749N/A event->xgraphicsexpose.width);
749N/A else
749N/A (void) repaint_window ((StripChartWidget)w, event->xexpose.x,
749N/A event->xexpose.width);
749N/A}
749N/A
749N/A/* ARGSUSED */
749N/Astatic void
749N/Adraw_it(client_data, id)
749N/AXtPointer client_data;
749N/AXtIntervalId *id; /* unused */
749N/A{
749N/A StripChartWidget w = (StripChartWidget)client_data;
749N/A double value;
749N/A
749N/A if (w->strip_chart.update > 0)
749N/A w->strip_chart.interval_id =
749N/A XtAppAddTimeOut(XtWidgetToApplicationContext( (Widget) w),
749N/A w->strip_chart.update * MS_PER_SEC,draw_it,client_data);
749N/A
749N/A if (w->strip_chart.interval >= (int)w->core.width)
749N/A MoveChart( (StripChartWidget) w, TRUE);
749N/A
749N/A /* Get the value, stash the point and draw corresponding line. */
749N/A
749N/A if (w->strip_chart.get_value == NULL)
749N/A return;
749N/A
749N/A XtCallCallbacks( (Widget)w, XtNgetValue, (XtPointer)&value );
749N/A
749N/A /*
749N/A * Keep w->strip_chart.max_value up to date, and if this data
749N/A * point is off the graph, change the scale to make it fit.
749N/A */
749N/A
749N/A if (value > w->strip_chart.max_value) {
749N/A w->strip_chart.max_value = value;
749N/A if (XtIsRealized((Widget)w) &&
749N/A w->strip_chart.max_value > w->strip_chart.scale) {
749N/A XClearWindow( XtDisplay (w), XtWindow (w));
749N/A w->strip_chart.interval = repaint_window(w, 0, (int) w->core.width);
749N/A }
749N/A }
749N/A
749N/A w->strip_chart.valuedata[w->strip_chart.interval] = value;
749N/A if (XtIsRealized((Widget)w)) {
749N/A int y = (int) (w->core.height
749N/A - (int)(w->core.height * value) / w->strip_chart.scale);
749N/A
749N/A XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC,
749N/A w->strip_chart.interval, y,
749N/A (unsigned int) 1, w->core.height - y);
749N/A /*
749N/A * Fill in the graph lines we just painted over.
749N/A */
749N/A
749N/A if (w->strip_chart.points != NULL) {
749N/A w->strip_chart.points[0].x = w->strip_chart.interval;
749N/A XDrawPoints(XtDisplay(w), XtWindow(w), w->strip_chart.hiGC,
749N/A w->strip_chart.points, w->strip_chart.scale - 1,
749N/A CoordModePrevious);
749N/A }
749N/A
749N/A XFlush(XtDisplay(w)); /* Flush output buffers */
749N/A }
749N/A w->strip_chart.interval++; /* Next point */
749N/A} /* draw_it */
749N/A
749N/A/* Blts data according to current size, then redraws the stripChart window.
749N/A * Next represents the number of valid points in data. Returns the (possibly)
749N/A * adjusted value of next. If next is 0, this routine draws an empty window
749N/A * (scale - 1 lines for graph). If next is less than the current window width,
749N/A * the returned value is identical to the initial value of next and data is
749N/A * unchanged. Otherwise keeps half a window's worth of data. If data is
749N/A * changed, then w->strip_chart.max_value is updated to reflect the
749N/A * largest data point.
749N/A */
749N/A
749N/Astatic int
749N/Arepaint_window(w, left, width)
749N/AStripChartWidget w;
749N/Aint left, width;
749N/A{
749N/A int i, j;
749N/A int next = w->strip_chart.interval;
749N/A int scale = w->strip_chart.scale;
749N/A int scalewidth = 0;
749N/A
749N/A /* Compute the minimum scale required to graph the data, but don't go
749N/A lower than min_scale. */
749N/A if (w->strip_chart.interval != 0 || scale <= (int)w->strip_chart.max_value)
749N/A scale = ((int) (w->strip_chart.max_value)) + 1;
749N/A if (scale < w->strip_chart.min_scale)
749N/A scale = w->strip_chart.min_scale;
749N/A
749N/A if (scale != w->strip_chart.scale) {
749N/A w->strip_chart.scale = scale;
749N/A left = 0;
749N/A width = next;
749N/A scalewidth = w->core.width;
749N/A
749N/A SetPoints(w);
749N/A
749N/A if (XtIsRealized ((Widget) w))
749N/A XClearWindow (XtDisplay (w), XtWindow (w));
749N/A
749N/A }
749N/A
749N/A if (XtIsRealized((Widget)w)) {
749N/A Display *dpy = XtDisplay(w);
749N/A Window win = XtWindow(w);
749N/A
749N/A width += left - 1;
749N/A if (!scalewidth) scalewidth = width;
749N/A
749N/A if (next < ++width) width = next;
749N/A
749N/A /* Draw data point lines. */
749N/A for (i = left; i < width; i++) {
749N/A int y = (int) (w->core.height -
749N/A (int)(w->core.height * w->strip_chart.valuedata[i]) /
749N/A w->strip_chart.scale);
749N/A
749N/A XFillRectangle(dpy, win, w->strip_chart.fgGC,
749N/A i, y, (unsigned int) 1,
749N/A (unsigned int) (w->core.height - y));
749N/A }
749N/A
749N/A /* Draw graph reference lines */
749N/A for (i = 1; i < w->strip_chart.scale; i++) {
749N/A j = i * ((int)w->core.height / w->strip_chart.scale);
749N/A XDrawLine(dpy, win, w->strip_chart.hiGC, left, j, scalewidth, j);
749N/A }
749N/A }
749N/A return(next);
749N/A}
749N/A
749N/A/* Function Name: MoveChart
749N/A * Description: moves the chart over when it would run off the end.
749N/A * Arguments: w - the load widget.
749N/A * blit - blit the bits? (TRUE/FALSE).
749N/A * Returns: none.
749N/A */
749N/A
749N/Astatic void
749N/AMoveChart(w, blit)
749N/AStripChartWidget w;
749N/ABoolean blit;
749N/A{
749N/A double old_max;
749N/A int left, i, j;
749N/A int next = w->strip_chart.interval;
749N/A
749N/A if (!XtIsRealized((Widget) w)) return;
749N/A
749N/A if (w->strip_chart.jump_val < 0) w->strip_chart.jump_val = DEFAULT_JUMP;
749N/A if (w->strip_chart.jump_val == DEFAULT_JUMP)
749N/A j = w->core.width >> 1; /* Half the window width. */
749N/A else {
749N/A j = w->core.width - w->strip_chart.jump_val;
749N/A if (j < 0) j = 0;
749N/A }
749N/A
749N/A (void) memmove((char *)(w->strip_chart.valuedata),
749N/A (char *)(w->strip_chart.valuedata + next - j),
749N/A j * sizeof(double));
749N/A next = w->strip_chart.interval = j;
749N/A
749N/A /*
749N/A * Since we just lost some data, recompute the
749N/A * w->strip_chart.max_value.
749N/A */
749N/A
749N/A old_max = w->strip_chart.max_value;
749N/A w->strip_chart.max_value = 0.0;
749N/A for (i = 0; i < next; i++) {
749N/A if (w->strip_chart.valuedata[i] > w->strip_chart.max_value)
749N/A w->strip_chart.max_value = w->strip_chart.valuedata[i];
749N/A }
749N/A
749N/A if (!blit) return; /* we are done... */
749N/A
749N/A if ( ((int) old_max) != ( (int) w->strip_chart.max_value) ) {
749N/A XClearWindow(XtDisplay(w), XtWindow(w));
749N/A repaint_window(w, 0, (int) w->core.width);
749N/A return;
749N/A }
749N/A
749N/A XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), XtWindow((Widget)w),
749N/A w->strip_chart.hiGC, (int) w->core.width - j, 0,
749N/A (unsigned int) j, (unsigned int) w->core.height,
749N/A 0, 0);
749N/A
749N/A XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
749N/A (int) j, 0,
749N/A (unsigned int) w->core.width - j, (unsigned int)w->core.height,
749N/A FALSE);
749N/A
749N/A /* Draw graph reference lines */
749N/A left = j;
749N/A for (i = 1; i < w->strip_chart.scale; i++) {
749N/A j = i * ((int)w->core.height / w->strip_chart.scale);
749N/A XDrawLine(XtDisplay((Widget) w), XtWindow( (Widget) w),
749N/A w->strip_chart.hiGC, left, j, (int)w->core.width, j);
749N/A }
749N/A return;
749N/A}
749N/A
749N/A/* ARGSUSED */
749N/Astatic Boolean SetValues (current, request, new, args, num_args)
749N/A Widget current, request, new;
749N/A ArgList args;
749N/A Cardinal *num_args;
749N/A{
749N/A StripChartWidget old = (StripChartWidget)current;
749N/A StripChartWidget w = (StripChartWidget)new;
749N/A Boolean ret_val = FALSE;
749N/A unsigned int new_gc = NO_GCS;
749N/A
749N/A if (w->strip_chart.update != old->strip_chart.update) {
749N/A if (old->strip_chart.update > 0)
749N/A XtRemoveTimeOut (old->strip_chart.interval_id);
749N/A if (w->strip_chart.update > 0)
749N/A w->strip_chart.interval_id =
749N/A XtAppAddTimeOut(XtWidgetToApplicationContext(new),
749N/A w->strip_chart.update * MS_PER_SEC,
749N/A draw_it, (XtPointer)w);
749N/A }
749N/A
749N/A if ( w->strip_chart.min_scale > (int) ((w->strip_chart.max_value) + 1) )
749N/A ret_val = TRUE;
749N/A
749N/A if ( w->strip_chart.fgpixel != old->strip_chart.fgpixel ) {
749N/A new_gc |= FOREGROUND;
749N/A ret_val = True;
749N/A }
749N/A
749N/A if ( w->strip_chart.hipixel != old->strip_chart.hipixel ) {
749N/A new_gc |= HIGHLIGHT;
749N/A ret_val = True;
749N/A }
749N/A
749N/A DestroyGC(old, new_gc);
749N/A CreateGC(w, new_gc);
749N/A
749N/A return( ret_val );
749N/A}
749N/A
749N/A/* Function Name: SetPoints
749N/A * Description: Sets up the polypoint that will be used to draw in
749N/A * the graph lines.
749N/A * Arguments: w - the StripChart widget.
749N/A * Returns: none.
749N/A */
749N/A
749N/A#define HEIGHT ( (unsigned int) w->core.height)
749N/A
749N/Astatic void
749N/ASetPoints(widget)
749N/AWidget widget;
749N/A{
749N/A StripChartWidget w = (StripChartWidget) widget;
749N/A XPoint * points;
749N/A Cardinal size;
749N/A int i;
749N/A
749N/A if (w->strip_chart.scale <= 1) { /* no scale lines. */
749N/A XtFree ((char *) w->strip_chart.points);
749N/A w->strip_chart.points = NULL;
749N/A return;
749N/A }
749N/A
749N/A size = sizeof(XPoint) * (w->strip_chart.scale - 1);
749N/A
749N/A points = (XPoint *) XtRealloc( (XtPointer) w->strip_chart.points, size);
749N/A w->strip_chart.points = points;
749N/A
749N/A /* Draw graph reference lines into clip mask */
749N/A
749N/A for (i = 1; i < w->strip_chart.scale; i++) {
749N/A points[i - 1].x = 0;
749N/A points[i - 1].y = HEIGHT / w->strip_chart.scale;
749N/A }
749N/A}