Logo Search packages:      
Sourcecode: vat version File versions  Download package

tkStripchart.c

/*
 * tkStripchart.c --
 *
 *      This module implements "Stripchart" widgets for the Tk
 *      toolkit.
 *
 * Copyright 1992 Regents of the University of Victoria, Wellington, NZ.
 * This code is derived from the tkScale widget.
 * Copyright 1990 Regents of the University of California.
 * 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.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 *
 *  BUGS:
 *   sometimes the callback procedure doesn't work (????)
 *    (it can often be made to work by setting a (different) value and
 *     restarting the callback procedure.)
 *
 *  CHANGES:
 *   would be nicer having tick interval instead of numticks.
 *
 */
static const char rcsid[] =
    "@(#) $Header: tkStripchart.c,v 1.7 96/03/16 13:13:59 van Exp $ (LBL)";

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "config.h"
#include "tk.h"

#if TK_MINOR_VERSION<1
#define Tk_Cursor Cursor
#endif

#define BLACK           "Black"
#define WHITE           "White"
#define GRAY            "#b0b0b0"
#define BISQUE1         "#ffe4c4"
#define MAROON          "#b03060"

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

/*
 *  Default stripchart configuration values
 */

#define DEF_STRIPCHART_BG_COLOR            BISQUE1
#define DEF_STRIPCHART_STRIP_COLOR         BLACK
#define DEF_STRIPCHART_TEXT_COLOR          BLACK
#define DEF_STRIPCHART_TICK_COLOR          GRAY
#define DEF_STRIPCHART_CURSOR              0

#define ALT_STRIPCHART_BG_COLOR            MAROON
#define ALT_STRIPCHART_STRIP_COLOR         WHITE
#define ALT_STRIPCHART_TEXT_COLOR          WHITE
#define ALT_STRIPCHART_TICK_COLOR          GRAY

/*
 * for monochrome displays
 */
#define DEF_STRIPCHART_BG_MONO             WHITE
#define DEF_STRIPCHART_STRIP_MONO          BLACK
#define DEF_STRIPCHART_TEXT_MONO           BLACK
#define DEF_STRIPCHART_TICK_MONO           BLACK

#define ALT_STRIPCHART_BG_MONO             BLACK
#define ALT_STRIPCHART_STRIP_MONO          WHITE
#define ALT_STRIPCHART_TEXT_MONO           WHITE
#define ALT_STRIPCHART_TICK_MONO           WHITE

#define DEF_STRIPCHART_NUMSTRIPS           "40"
#define DEF_STRIPCHART_NUMTICKS            "11"
#define DEF_STRIPCHART_BORDER_WIDTH        "3"
#define DEF_STRIPCHART_FONT                "*-Helvetica-Bold-R-Normal-*-120-*"
#define DEF_STRIPCHART_MINVALUE            "0"
#define DEF_STRIPCHART_MAXVALUE            "1000"
#define DEF_STRIPCHART_CALLBACK_INTERVAL   "500"
#define DEF_STRIPCHART_TITLE               0
#define DEF_STRIPCHART_HEIGHT              "80"
#define DEF_STRIPCHART_RELIEF              "flat"
#define DEF_STRIPCHART_TICK_INTERVAL       "50"
#define DEF_STRIPCHART_WIDTH               "1"
#define DEF_STRIPCHART_STRIPBORDERWIDTH    "0"
#define DEF_STRIPCHART_STRIPRELIEF         "flat"
#define DEF_STRIPCHART_SHOWTICKS           "true"
#define DEF_STRIPCHART_AUTOSCALE           "0"
#define DEF_STRIPCHART_HTICKS              "0"
#define DEF_USERBITS                       "0"
#define DEF_USERDATA                       0
#define DEF_GUARANTEE_DRAW                 "false"

#define  MAX_STRIPS              100
#define  MAX_TICKS               40
#define  PADDING                 2
#define  hasatitle(d)            (((d)->title != NULL) && \
                                  ((d)->title[0] != '\0'))
#ifndef abs
#define  abs(x)                  ( (x) < 0 ? -(x) : (x) )
#endif

/*
 * A data structure of the following type is kept for each
 * Stripchart that currently exists for this process:
 */

typedef struct strip_struct Stripchart;

struct strip_struct {
      Tk_Window tkwin;  /* Window that embodies the Stripchart.  NULL
                         * means that the window has been destroyed
                         * but the data structures haven't yet been
                         * cleaned up.*/
      Tcl_Interp *interp;     /* Interpreter associated with widget.  Used
                         * to delete widget command.  */
      Tk_Uid screenName;      /* If this window isn't a toplevel window
                         * then this is NULL;  otherwise it gives the
                         * name of the screen on which window is
                         * displayed. */
      Tk_3DBorder border;     /* Structure used to draw 3-D border and
                         * background. */
      int borderWidth;  /* Width of 3-D border (if any). */
      int relief;       /* 3-d effect: TK_RELIEF_RAISED etc. */
      Tk_Cursor cursor; /* Current cursor for window, or None. */
      Tk_3DBorder stripBorder;/* Structure used to draw the strips */
      int stripBorderWidth;
      int stripRelief;
      int strip_width;  /* width of a strip */
      int max_height;         /* maximum height of a strip */
      int num_strips;         /* the number of strips */
      int stripstodisplay;    /* how many of the num_strips should be
                         * displayed. */
      int num_ticks;          /* the number of ticks to display */
      double *value;          /* the data to be displayed in strip form. */
      double min_value;
      double max_value;
      double data_min;
      double data_max;
      int scrollrequired;
      int guarantee_draw;
      int grow_up;
      XFontStruct *fontPtr;   /* Information about text font, or NULL. */
      XColor *textColorPtr;   /* Color for drawing text. */
      GC textGC;        /* GC for drawing text. */
      XColor *tickColorPtr;   /* Color for drawing ticks. */
      GC tickGC;        /* GC for drawing ticks. */
      int showticks;
      int autoscale;
      int hticks;
      int lasthtick;

      /*
       * call back stuff
       */
      Tk_TimerToken timer;    /* tk timer used by callback */
      char *command;          /* Command used for callback. Malloc'ed. */
      char *rescale_command;  /* Command invoked on autorescale. Malloc'ed. */
      int continue_callback;  /* boolean flag used to terminate the
                         * callback */
      unsigned long interval; /* interval (mS) between callbacks */
      double (*callback_func)(Tcl_Interp* interp, Stripchart* stripPtr);
                          /* Function to be invoked on callback. */
      char *title;            /* Label to display above of stripchart; NULL
                         * means don't display a title.  Malloc'ed. */
      int displaybits;  /* Various flags;  see below for definitions. */
      int userbits;
      char *userdata;
      /*
       * Alternative colour scheme
       */
      Tk_3DBorder altborder;  /* Structure used to draw border  */
      Tk_3DBorder altstripBorder;/* structure used to draw strip  */
      XColor *a_textColor;    /* alternative text color         */
      XColor *a_tickColor;    /* alternative tick color         */

};

/*
 * displaybits for Stripcharts:
 *
 * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler
 *                              has already been queued to redraw
 *                              this window.
 * CLEAR_NEEDED:                Need to clear the window when redrawing.
 * DISPLAY_TITLE:               Draw the title, if there is one.
 * DISPLAY_STRIPS:              Draw the strips.
 * DISPLAY_BORDER:              Draw the border.
 * DISPLAY_TICKS:               Draw the ticks.
 * DISPLAY_ALL:                 Display everything.
 */

#define   REDRAW_PENDING            1
#define   CLEAR_NEEDED              2
#define   DISPLAY_TITLE                 4
#define   DISPLAY_STRIPS                8
#define   DISPLAY_BORDER               16
#define   DISPLAY_TICKS                32
#define   DISPLAY_ALL                  (DISPLAY_TITLE | DISPLAY_STRIPS | \
                              DISPLAY_BORDER | DISPLAY_TICKS)

static Tk_ConfigSpec configSpecs[] =
{
      {TK_CONFIG_BORDER, "-altbackground", "altbackground", "Background",
       ALT_STRIPCHART_BG_COLOR, Tk_Offset(Stripchart, altborder),
       TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_BORDER, "-altbackground", "altbackground", "Background",
       ALT_STRIPCHART_BG_MONO, Tk_Offset(Stripchart, altborder),
       TK_CONFIG_MONO_ONLY},
      {TK_CONFIG_BORDER, "-altstripcolor", "altstripcolor",
       "Foreground", ALT_STRIPCHART_STRIP_COLOR,
       Tk_Offset(Stripchart, altstripBorder), TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_BORDER, "-altstripcolor", "altstripcolor",
       "Foreground", ALT_STRIPCHART_STRIP_MONO,
       Tk_Offset(Stripchart, altstripBorder), TK_CONFIG_MONO_ONLY},
      {TK_CONFIG_COLOR, "-alttextcolor", "textcolor", "Foreground",
       ALT_STRIPCHART_TEXT_COLOR, Tk_Offset(Stripchart, a_textColor),
       TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_COLOR, "-alttextcolor", "textcolor", "Foreground",
       ALT_STRIPCHART_TEXT_MONO, Tk_Offset(Stripchart, a_textColor),
       TK_CONFIG_MONO_ONLY},
      {TK_CONFIG_COLOR, "-alttickcolor", "tickcolor", "Foreground",
       ALT_STRIPCHART_TICK_COLOR, Tk_Offset(Stripchart, a_tickColor),
       TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_COLOR, "-alttickcolor", "tickcolor", "Foreground",
       ALT_STRIPCHART_TICK_MONO, Tk_Offset(Stripchart, a_tickColor),
       TK_CONFIG_MONO_ONLY},

      {TK_CONFIG_BORDER, "-background", "background", "Background",
       DEF_STRIPCHART_BG_COLOR, Tk_Offset(Stripchart, border),
       TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_BORDER, "-background", "background", "Background",
       DEF_STRIPCHART_BG_MONO, Tk_Offset(Stripchart, border),
       TK_CONFIG_MONO_ONLY},
      {TK_CONFIG_SYNONYM, "-bd", "borderWidth", 0, 0, 0, 0},
      {TK_CONFIG_SYNONYM, "-bg", "background", 0, 0, 0, 0},
      {TK_CONFIG_INT, "-borderwidth", "borderWidth", "BorderWidth",
       DEF_STRIPCHART_BORDER_WIDTH, Tk_Offset(Stripchart, borderWidth), 0},
      {TK_CONFIG_STRING, "-command", "command", "Command",
       0, Tk_Offset(Stripchart, command), 0},
      {TK_CONFIG_STRING, "-rescale_command", "rescale_command", "RescaleCommand",
       0, Tk_Offset(Stripchart, rescale_command), 0},
      {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
       DEF_STRIPCHART_CURSOR, Tk_Offset(Stripchart, cursor),
       TK_CONFIG_NULL_OK},

      {TK_CONFIG_STRING, "-data", "data", "Data",
       DEF_USERDATA, Tk_Offset(Stripchart, userdata), 0},
      {TK_CONFIG_SYNONYM, "-fg", "stripcolor", 0,
       0, 0, 0},
      {TK_CONFIG_FONT, "-font", "font", "Font",
       DEF_STRIPCHART_FONT, Tk_Offset(Stripchart, fontPtr),
       0},
      {TK_CONFIG_BOOLEAN, "-guaranteedrawing", "guaranteedrawing",
       "Guaranteedrawing", DEF_GUARANTEE_DRAW,
       Tk_Offset(Stripchart, guarantee_draw), 0},
      {TK_CONFIG_INT, "-height", "height", "Height", DEF_STRIPCHART_HEIGHT,
       Tk_Offset(Stripchart, max_height), 0},
      {TK_CONFIG_INT, "-interval", "interval", "Interval",
      DEF_STRIPCHART_CALLBACK_INTERVAL, Tk_Offset(Stripchart, interval), 0},
      {TK_CONFIG_DOUBLE, "-max", "max", "Max",
       DEF_STRIPCHART_MAXVALUE, Tk_Offset(Stripchart, max_value), 0},
      {TK_CONFIG_DOUBLE, "-min", "min", "Min",
       DEF_STRIPCHART_MINVALUE, Tk_Offset(Stripchart, min_value), 0},
      {TK_CONFIG_INT, "-numstrips", "numstrips", "Numstrips",
       DEF_STRIPCHART_NUMSTRIPS, Tk_Offset(Stripchart, num_strips), 0},
      {TK_CONFIG_INT, "-numticks", "numticks", "Numticks",
       DEF_STRIPCHART_NUMTICKS, Tk_Offset(Stripchart, num_ticks), 0},
      {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
       DEF_STRIPCHART_RELIEF, Tk_Offset(Stripchart, relief), 0},
      {TK_CONFIG_BOOLEAN, "-showticks", "showticks", "Showticks",
       DEF_STRIPCHART_SHOWTICKS, Tk_Offset(Stripchart, showticks), 0},
      {TK_CONFIG_INT, "-autoscale", "autoscale", "Autoscale",
       DEF_STRIPCHART_AUTOSCALE, Tk_Offset(Stripchart, autoscale), 0},
      {TK_CONFIG_INT, "-hticks", "hticks", "Hticks",
       DEF_STRIPCHART_HTICKS, Tk_Offset(Stripchart, hticks), 0},
      {TK_CONFIG_INT, "-stripborderwidth", "stripborderwidth",
       "Stripborderwidth", DEF_STRIPCHART_STRIPBORDERWIDTH,
       Tk_Offset(Stripchart, stripBorderWidth), 0},
      {TK_CONFIG_BORDER, "-stripcolor", "stripcolor", "Stripcolor",
       DEF_STRIPCHART_STRIP_COLOR, Tk_Offset(Stripchart, stripBorder),
       TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_BORDER, "-stripcolor", "stripcolor", "Stripcolor",
       DEF_STRIPCHART_STRIP_MONO, Tk_Offset(Stripchart, stripBorder),
       TK_CONFIG_MONO_ONLY},
      {TK_CONFIG_RELIEF, "-striprelief", "striprelief", "Striprelief",
       DEF_STRIPCHART_STRIPRELIEF, Tk_Offset(Stripchart, stripRelief), 0},
      {TK_CONFIG_INT, "-stripwidth", "stripwidth", "Stripwidth",
       DEF_STRIPCHART_WIDTH, Tk_Offset(Stripchart, strip_width), 0},
      {TK_CONFIG_COLOR, "-textcolor", "textcolor", "Textcolor",
       DEF_STRIPCHART_TEXT_COLOR, Tk_Offset(Stripchart, textColorPtr),
       TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_COLOR, "-textcolor", "textcolor", "Textcolor",
       DEF_STRIPCHART_TEXT_MONO, Tk_Offset(Stripchart, textColorPtr),
       TK_CONFIG_MONO_ONLY},
      {TK_CONFIG_COLOR, "-tickcolor", "tickcolor", "Tickcolor",
       DEF_STRIPCHART_TICK_COLOR, Tk_Offset(Stripchart, tickColorPtr),
       TK_CONFIG_COLOR_ONLY},
      {TK_CONFIG_COLOR, "-tickcolor", "tickcolor", "Tickcolor",
       DEF_STRIPCHART_TICK_MONO, Tk_Offset(Stripchart, tickColorPtr),
       TK_CONFIG_MONO_ONLY},
      {TK_CONFIG_STRING, "-title", "title", "Title",
       DEF_STRIPCHART_TITLE, Tk_Offset(Stripchart, title), 0},
      {TK_CONFIG_BOOLEAN, "-up", "up", "Up", "true",
       Tk_Offset(Stripchart, grow_up), 0},
      {TK_CONFIG_INT, "-userbits", "userbits", "Userbits",
       DEF_USERBITS, Tk_Offset(Stripchart, userbits), 0},
      {TK_CONFIG_INT, "-width", "width", "Width", DEF_STRIPCHART_WIDTH,
       Tk_Offset(Stripchart, strip_width), 0},
      {TK_CONFIG_END, 0, 0, 0,
       0, 0, 0}
};

/*
 * Forward declarations for procedures defined later in this file:
 */
static void Callback(Stripchart* StripchartPtr);
static void ComputeStripchartGeometry(Stripchart* StripchartPtr);
static int ConfigureStripchart(Tcl_Interp* interp, Stripchart* StripchartPtr,
                        int argc, char** argv, int flags);
static void DestroyStripchart(ClientData clientData);
static void DisplayStripchart(ClientData clientData);
static void DrawStripi(Stripchart* StripchartPtr, int i);
static void EventuallyRedrawStripchart(Stripchart* StripchartPtr,
                               int displaybits);
static void ReplaceColours(Stripchart* StripchartPtr, int argc, char** argv);
static void ScrollStrips(Stripchart* StripchartPtr);
static void StripchartEventProc(ClientData clientData, XEvent* eventPtr);
static int StripchartWidgetCmd(ClientData clientData, Tcl_Interp* interp,
                        int argc, char** argv);
static void SetStripchartValue(Stripchart* StripchartPtr, double value);
static void SwapColours(Stripchart* StripchartPtr);

/*
 *--------------------------------------------------------------
 *
 * Tk_StripchartCmd --
 *
 *      This procedure is invoked to process the "Stripchart" and
 *      "toplevel" Tcl commands.  See the user documentation for
 *      details on what it does.
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *      See the user documentation.
 *
 *--------------------------------------------------------------
 */
int
Tk_StripchartCmd(ClientData clientData, Tcl_Interp *interp, int argc,
             char **argv)
 {
      Tk_Window tkwin = (Tk_Window) clientData;
      Tk_Window new;
      register Stripchart *StripchartPtr;

      if (argc < 2) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                      argv[0], " pathName ?options?\"", 0);
            return TCL_ERROR;
      }
      /*
       * Create the window.
       */
      new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], 0);
      if (new == NULL)
            return TCL_ERROR;

      Tk_SetClass(new, "Stripchart");
      StripchartPtr = (Stripchart*)calloc(1, sizeof(Stripchart));
      StripchartPtr->tkwin = new;
      StripchartPtr->interp = interp;

      Tk_CreateEventHandler(StripchartPtr->tkwin,
                        ExposureMask | StructureNotifyMask,
                        StripchartEventProc, (ClientData) StripchartPtr);
      Tcl_CreateCommand(interp, Tk_PathName(StripchartPtr->tkwin),
                    StripchartWidgetCmd, (ClientData)StripchartPtr,
                    0);

      if (ConfigureStripchart(interp, StripchartPtr, argc - 2, argv + 2, 0)
          != TCL_OK) {
            Tk_DestroyWindow(StripchartPtr->tkwin);
            return TCL_ERROR;
      }
      interp->result = Tk_PathName(StripchartPtr->tkwin);
      return TCL_OK;
}

/*
 * StripchartWidgetCmd --
 *
 *      This procedure is invoked to process the Tcl command
 *      that corresponds to a Stripchart widget.  See the user
 *      documentation for details on what it does.
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *      See the user documentation.
 */
static int
StripchartWidgetCmd(ClientData clientData, Tcl_Interp *interp,
                int argc, char **argv)
{
      register Stripchart *StripchartPtr = (Stripchart*)clientData;
      int result = TCL_OK;
      int length;
      char c;

      if (argc < 2) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    argv[0], " option ?arg arg ...?\"", 0);
            return TCL_ERROR;
      }
      Tk_Preserve((ClientData) StripchartPtr);
      c = argv[1][0];
      length = strlen(argv[1]);
      if (strncmp(argv[1], "configure", length) == 0) {
            if (argc == 2) {
                  result = Tk_ConfigureInfo(interp, StripchartPtr->tkwin,
                                      configSpecs,
                                      (char*)StripchartPtr,
                                      (char*)NULL, 0);
            } else if (argc == 3) {
                  result = Tk_ConfigureInfo(interp, StripchartPtr->tkwin,
                                      configSpecs,
                                      (char*)StripchartPtr,
                                      argv[2], 0);
            } else {
                  result = ConfigureStripchart(interp, StripchartPtr,
                                         argc - 2, argv + 2,
                                         TK_CONFIG_ARGV_ONLY);
            }
      } else if (strncmp(argv[1], "get", length) == 0) {
            if (argc != 2) {
                  Tcl_AppendResult(interp, "wrong # args: should be \"",
                               argv[0], " get\"", 0);
                  result = TCL_ERROR;
            } else
                  sprintf(interp->result, "%g",
                    StripchartPtr->value[
                          StripchartPtr->stripstodisplay - 1]);
      } else if (strncmp(argv[1], "set", length) == 0) {
            double value;

            if (argc != 3) {
                  Tcl_AppendResult(interp, "wrong # args: should be \"",
                             argv[0], " set value\"", 0);
                  result = TCL_ERROR;
            } else if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK)
                  result = TCL_ERROR;
             else
                  SetStripchartValue(StripchartPtr, value);
      } else if (strncmp(argv[1], "start", length) == 0) {
            if (!StripchartPtr->continue_callback) {
                  StripchartPtr->continue_callback = TRUE;
                  Callback(StripchartPtr);
            }
      } else if (strncmp(argv[1], "stop", length) == 0) {
            StripchartPtr->continue_callback = FALSE;
      } else if (strncmp(argv[1], "reset", length) == 0) {
            StripchartPtr->stripstodisplay = 0;
            StripchartPtr->scrollrequired = FALSE;
            EventuallyRedrawStripchart(StripchartPtr,
                                 DISPLAY_ALL | CLEAR_NEEDED);
      } else if (strncmp(argv[1], "swapcolours", length) == 0) {
            SwapColours(StripchartPtr);
      } else if (strncmp(argv[1], "replacecolours", length) == 0) {
            ReplaceColours(StripchartPtr, argc - 2, argv + 2);
      } else {
            Tcl_AppendResult(interp, "bad option \"", argv[1],
               "\":  must be configure, get, set, reset, start or stop",
                         0);
            result = TCL_ERROR;
      }
      Tk_Release((ClientData) StripchartPtr);
      return result;
}

/*
 *  Callback --
 *      Execute a Tcl command repeatedly until told to stop.  Involked
 *      with the start command and stopped with the stop command.
 *
 *  Results:
 *      None.
 *
 *  Side Effects:
 *      Timer queue is changed with the addition of a command to be
 *      executed periodically.
 */
static void 
Callback(Stripchart *StripchartPtr)
{
      int result;

      StripchartPtr->timer = 0;
      if (StripchartPtr->callback_func != NULL)
            SetStripchartValue(StripchartPtr,
                           (*(StripchartPtr->callback_func))
                              (StripchartPtr->interp, StripchartPtr));
      if (StripchartPtr->command != NULL &&
          StripchartPtr->command[0] != '\0') {
            result = Tcl_Eval(StripchartPtr->interp,
                          StripchartPtr->command);
            if (result == TCL_OK)
                  if (StripchartPtr->continue_callback)
                        StripchartPtr->timer =
                           Tk_CreateTimerHandler(
                                    StripchartPtr->interval,
                                    (void *)Callback,
                                    (ClientData)StripchartPtr);
      }
}

/*
 * DestroyStripchart --
 *
 *      This procedure is invoked by Tk_EventuallyFree or Tk_Release
 *      to clean up the internal structure of a Stripchart at a safe time
 *      (when no-one is using it anymore).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Everything associated with the Stripchart is freed up.
 */
static void
DestroyStripchart(ClientData clientData)
{
      Stripchart* StripchartPtr = (Stripchart*)clientData;

      if (StripchartPtr->timer)
            Tk_DeleteTimerHandler(StripchartPtr->timer);

      if (StripchartPtr->border != NULL)
            Tk_Free3DBorder(StripchartPtr->border);

      if (StripchartPtr->altborder != NULL)
            Tk_Free3DBorder(StripchartPtr->altborder);

      if (StripchartPtr->stripBorder != NULL)
            Tk_Free3DBorder(StripchartPtr->stripBorder);

      if (StripchartPtr->altstripBorder != NULL)
            Tk_Free3DBorder(StripchartPtr->altstripBorder);

      if (StripchartPtr->title != NULL)
            ckfree(StripchartPtr->title);

      if (StripchartPtr->command != NULL)
            ckfree(StripchartPtr->command);

      if (StripchartPtr->rescale_command != NULL)
            ckfree(StripchartPtr->rescale_command);

      if (StripchartPtr->value != NULL)
            free(StripchartPtr->value);

      if (StripchartPtr->fontPtr != NULL)
            Tk_FreeFontStruct(StripchartPtr->fontPtr);

      if (StripchartPtr->textColorPtr != NULL)
            Tk_FreeColor(StripchartPtr->textColorPtr);

      if (StripchartPtr->textGC != None && StripchartPtr->tkwin)
            Tk_FreeGC(Tk_Display(StripchartPtr->tkwin),
                    StripchartPtr->textGC);

      if (StripchartPtr->tickColorPtr != NULL)
            Tk_FreeColor(StripchartPtr->tickColorPtr);

      if (StripchartPtr->tickGC != None && StripchartPtr->tkwin)
            Tk_FreeGC(Tk_Display(StripchartPtr->tkwin),
                    StripchartPtr->tickGC);

      if (StripchartPtr->a_textColor != NULL)
            Tk_FreeColor(StripchartPtr->a_textColor);

      if (StripchartPtr->a_tickColor != NULL)
            Tk_FreeColor(StripchartPtr->a_tickColor);

      if (StripchartPtr->cursor != None && StripchartPtr->tkwin) {
            Tk_FreeCursor(Tk_Display(StripchartPtr->tkwin),
                        StripchartPtr->cursor);
      }
      ckfree((char *)StripchartPtr);
}

/*
 * ConfigureStripchart --
 *
 *      This procedure is called to process an argv/argc list, plus
 *      the Tk option database, in order to configure (or
 *      reconfigure) a Stripchart widget.
 *
 * Results:
 *      The return value is 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 StripchartPtr;  old resources get freed, if there
 *      were any.
 */
static int
ConfigureStripchart(Tcl_Interp *interp, Stripchart *StripchartPtr,
                int argc, char** argv, int flags)
{
      XGCValues gcValues;
      GC newGC;
      int result;

      result = Tk_ConfigureWidget(interp, StripchartPtr->tkwin, configSpecs,
                            argc, argv, (char *)StripchartPtr, flags);
      if (result != TCL_OK)
            return result;

      Tk_SetBackgroundFromBorder(StripchartPtr->tkwin, StripchartPtr->border);

      gcValues.font = StripchartPtr->fontPtr->fid;
      gcValues.foreground = StripchartPtr->textColorPtr->pixel;
      newGC = Tk_GetGC(StripchartPtr->tkwin, GCForeground|GCFont, &gcValues);
      if (StripchartPtr->textGC != None && StripchartPtr->tkwin) {
            Tk_FreeGC(Tk_Display(StripchartPtr->tkwin),
                    StripchartPtr->textGC);
      }
      StripchartPtr->textGC = newGC;

      gcValues.foreground = StripchartPtr->tickColorPtr->pixel;
      newGC = Tk_GetGC(StripchartPtr->tkwin, GCForeground, &gcValues);
      if (StripchartPtr->tickGC != None && StripchartPtr->tkwin) {
            Tk_FreeGC(Tk_Display(StripchartPtr->tkwin),
                    StripchartPtr->tickGC);
      }
      StripchartPtr->tickGC = newGC;

      if (StripchartPtr->value == 0)
            StripchartPtr->value = (double*)calloc(
                                    StripchartPtr->num_strips,
                                    sizeof(*StripchartPtr->value));
      else
            StripchartPtr->value = (double*)realloc(
                                    StripchartPtr->value,
                                    StripchartPtr->num_strips *
                                    sizeof(*StripchartPtr->value));
      if (StripchartPtr->num_ticks > MAX_TICKS)
            StripchartPtr->num_ticks = MAX_TICKS;

      if (StripchartPtr->min_value > StripchartPtr->max_value) {
            double temp = StripchartPtr->min_value;
            StripchartPtr->min_value = StripchartPtr->max_value;
            StripchartPtr->max_value = temp;
      }
      /*
       * Recompute display-related information, and let the geometry
       * manager know how much space is needed now.
       */
      ComputeStripchartGeometry(StripchartPtr);
      EventuallyRedrawStripchart(StripchartPtr, DISPLAY_ALL | CLEAR_NEEDED);
      return TCL_OK;
}

/*
 * ComputeStripchartGeometry --
 *
 *      This procedure is called to compute various geometrical
 *      information for a stripchart, such as where various things get
 *      displayed.  It's called when the window is reconfigured.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 */
static void
ComputeStripchartGeometry(Stripchart* StripchartPtr)
 {
      int tt = hasatitle(StripchartPtr);
      int bd = StripchartPtr->borderWidth;
      int lineHeight = StripchartPtr->fontPtr->ascent +
      StripchartPtr->fontPtr->descent;

      Tk_GeometryRequest(StripchartPtr->tkwin,
                     2 * (bd + PADDING) + StripchartPtr->num_strips *
                     StripchartPtr->strip_width,
                     2 * (bd + PADDING) + tt * (lineHeight + PADDING) +
                     StripchartPtr->max_height);
      Tk_SetInternalBorder(StripchartPtr->tkwin, StripchartPtr->borderWidth);
}

/*
 * DisplayStripchart --
 *
 *      This procedure is invoked to display a Stripchart widget.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Commands are output to X to display the Stripchart in its
 *      current mode.
 */
static void
DisplayStripchart(ClientData clientData)
 {
      Stripchart *StripchartPtr = (Stripchart *) clientData;
      Tk_Window tkwin = StripchartPtr->tkwin;
      int bd = StripchartPtr->borderWidth;
      int i, tt = hasatitle(StripchartPtr);

      /*
       * Variable declarations used in the title drawing routines
       */
      XFontStruct *fp = StripchartPtr->fontPtr;
      XCharStruct bbox;
      int x, dummy;
      int lineHeight = StripchartPtr->fontPtr->ascent +
      StripchartPtr->fontPtr->descent;

      StripchartPtr->displaybits &= ~REDRAW_PENDING;
      if ((StripchartPtr->tkwin == NULL) || !Tk_IsMapped(tkwin))
            return;

      /*
       * Clear the window if necessary
       */
      if (StripchartPtr->displaybits & CLEAR_NEEDED)
            XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));

      /*
       * Display the title, centered in the window if there is enough
       * space. Otherwise left justified and clipped on the right.
       */
      if (tt && StripchartPtr->displaybits & DISPLAY_TITLE) {
            XTextExtents(fp, StripchartPtr->title,
                       strlen(StripchartPtr->title),
                       &dummy, &dummy, &dummy, &bbox);
            if (bbox.lbearing + bbox.rbearing < Tk_Width(tkwin) - 2 * bd)
                  x = (Tk_Width(tkwin) - bbox.lbearing - bbox.rbearing)/2;
            else
                  x = bd + PADDING;

            XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin), bd, bd,
                 Tk_Width(tkwin) - 2 * bd, lineHeight + PADDING, False);
            XDrawString(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   StripchartPtr->textGC, x, fp->max_bounds.ascent + bd,
                  StripchartPtr->title, strlen(StripchartPtr->title));
      }
      /*
       * draw the strips
       */
      if (StripchartPtr->displaybits & CLEAR_NEEDED) {
            StripchartPtr->displaybits &= ~CLEAR_NEEDED;
            StripchartPtr->lasthtick = 0;
            for (i = 1; i <= StripchartPtr->stripstodisplay; i++)
                  DrawStripi(StripchartPtr, i);
      } else {
            if (StripchartPtr->stripstodisplay == StripchartPtr->num_strips) {
                  if (!StripchartPtr->scrollrequired)
                        StripchartPtr->scrollrequired = TRUE;
                  else
                        ScrollStrips(StripchartPtr);
            }
            DrawStripi(StripchartPtr, StripchartPtr->stripstodisplay);
      }

      /*
       * Display the border
       */
      if (StripchartPtr->displaybits & DISPLAY_BORDER) {
            Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
                         StripchartPtr->border, 0, 0, Tk_Width(tkwin),
                         Tk_Height(tkwin), StripchartPtr->borderWidth,
                           StripchartPtr->relief);
      }
}

/*
 * StripchartEventProc --
 *
 *      This procedure is invoked by the Tk dispatcher on
 *      structure changes to a Stripchart.  For Stripcharts with 3D
 *      borders, this procedure is also invoked for exposures.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      When the window gets deleted, internal structures get
 *      cleaned up.  When it gets exposed, it is redisplayed.
 */

static void
StripchartEventProc(ClientData clientData, XEvent *eventPtr)
 {
      Stripchart *StripchartPtr = (Stripchart*)clientData;

      if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
            if ((StripchartPtr->tkwin != NULL) &&
                !(StripchartPtr->displaybits & REDRAW_PENDING)) {
                  EventuallyRedrawStripchart(StripchartPtr,
                                       DISPLAY_ALL|CLEAR_NEEDED);
                  /*
                   * A clear isn't technically needed as the bitmap is
                   * clear when the window is exposed.  This flag is
                   * used to indicate that all strips need to be drawn,
                   * not just the most recent one.
                   */
            }
      } else if (eventPtr->type == DestroyNotify) {
            Tcl_DeleteCommand(StripchartPtr->interp,
                          Tk_PathName(StripchartPtr->tkwin));
            StripchartPtr->tkwin = NULL;
            if (StripchartPtr->displaybits & REDRAW_PENDING) {
                  Tk_CancelIdleCall(DisplayStripchart,
                                (ClientData)StripchartPtr);
            }
            Tk_EventuallyFree((ClientData)StripchartPtr, DestroyStripchart);
      } else if (eventPtr->type == ConfigureNotify) {
            int n = eventPtr->xconfigure.width;
            n -= 2 * (StripchartPtr->borderWidth + PADDING);
            n /= StripchartPtr->strip_width;
            if (n != StripchartPtr->num_strips) {
                  StripchartPtr->num_strips = n;
                  if (StripchartPtr->value == 0)
                        StripchartPtr->value =
                              (double*)calloc(n, sizeof(double));
                  else
                        StripchartPtr->value =
                              (double*)realloc(StripchartPtr->value,
                                           n * sizeof(double));
            }
      }
}

static double chose_range(const double* lim, int n, double range)
{
      double lr = pow(10., floor(log10(range)));
      double b = range / lr - 0.002;
      int i;
      for (i = 0; i < n; ++i)
            if (b < lim[i])
                  break;
      return ((lim[i]) * lr);
}

/*
 * choose a 'nice' lower limit for v (one on a 2/5/10 boundary)
 */
static int chose_scale(Stripchart* sp, double min, double max)
{
      const static double rlim[] = {
            1.0, 2.0, 5.0, 10.0
      };
      const static double blim[] = {
            1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0
      };
      double range = max - min;
      if (range < 1e-9)
            return (0);

      range = chose_range(blim, 9, range);
      min = (double)((int)(min / range)) * range;
      max = chose_range(rlim, 3, max - min) + min;
      if (max != sp->max_value || min != sp->min_value) {
            sp->min_value = min;
            sp->max_value = max;
            if (sp->rescale_command) {
                  char scmd[1024];
                  double n = sp->num_ticks - 1;
                  if (n <= 0.)
                        n = 1.;
                  sprintf(scmd, "%s %g %g %g", sp->rescale_command,
                        min, max, (max - min) / n);
                  (void)Tcl_Eval(sp->interp, scmd);
            }
            return (1);
      }
      return (0);
}

/*
 * SetStripchartValue --
 *
 *      This procedure changes the value of a Stripchart and invokes
 *      a Tcl command to reflect the current position of a Stripchart
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      (Re) Sets the scrollrequired flag.
 */
static void
SetStripchartValue(Stripchart *StripchartPtr, double value)
{
      int std = StripchartPtr->stripstodisplay;
      int maxvalues = StripchartPtr->num_strips;
      int flags = DISPLAY_STRIPS;

      if (std != maxvalues) {
            StripchartPtr->value[std] = value;
            StripchartPtr->stripstodisplay = ++std;
      } else {
            int n = maxvalues - 1;
            memcpy(StripchartPtr->value, StripchartPtr->value + 1,
                   n * sizeof(*StripchartPtr->value)); 
            StripchartPtr->value[n] = value;
      }
      if (StripchartPtr->autoscale) {
            double min = value; double max = value;
            double* vp = StripchartPtr->value;
            int i;
            for (i = 0; i < std; ++i) {
                  double v = vp[i];
                  if (v < min)
                        min = v;
                  if (v > max)
                        max = v;
            }
            if (StripchartPtr->autoscale == 1)
                  /* only adjust max */
                  min = StripchartPtr->min_value;

            if (max != StripchartPtr->data_max  ||
                min != StripchartPtr->data_min) {
                  StripchartPtr->data_min = min;
                  StripchartPtr->data_max = max;
                  if (chose_scale(StripchartPtr, min, max))
                        flags |= DISPLAY_ALL|CLEAR_NEEDED;
            }
      }
      EventuallyRedrawStripchart(StripchartPtr, flags);
}

/*
 * EventuallyRedrawStripchart --
 *
 *      Arrange for part or all of a stripchart widget to redrawn at
 *      the next convenient time in the future.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 */
static void
EventuallyRedrawStripchart(Stripchart *StripchartPtr, int displaybits)
{

      if (StripchartPtr->tkwin == NULL
          || !Tk_IsMapped(StripchartPtr->tkwin)
          || (StripchartPtr->displaybits & REDRAW_PENDING))
            return;

      StripchartPtr->displaybits = displaybits | REDRAW_PENDING;
      if (StripchartPtr->guarantee_draw)
            DisplayStripchart((ClientData) StripchartPtr);
      else
            Tk_DoWhenIdle(DisplayStripchart, (ClientData)StripchartPtr);
}

/*
 * SwapColours --
 *
 *     Save the current colour scheme so that it may be
 *     restored at some later stage.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     Colour scheme is swapped and Stripchart is redisplayed with
 *     the new colour scheme.
 */

static void 
SwapColours(Stripchart * StripchartPtr)
{
      Tk_3DBorder tempb;
      XColor *tempc;

#define SWAP(a,b,c) { (c) = (a); (a) = (b); (b) = (c); }

      SWAP(StripchartPtr->altborder, StripchartPtr->border, tempb);
      SWAP(StripchartPtr->altstripBorder, StripchartPtr->stripBorder, tempb);

      SWAP(StripchartPtr->a_textColor, StripchartPtr->textColorPtr, tempc);
      SWAP(StripchartPtr->a_tickColor, StripchartPtr->tickColorPtr, tempc);

#undef SWAP

      ConfigureStripchart(StripchartPtr->interp, StripchartPtr, 0, NULL,
                      TK_CONFIG_ARGV_ONLY);
}

/*
 * ReplaceColours --
 *
 *     Store the current colour scheme and replace it with
 *     the new one.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     Stripchart is displayed with the new colour scheme.
 */
static void 
ReplaceColours(Stripchart* StripchartPtr, int argc, char** argv)
{
      StripchartPtr->altborder =
            Tk_Get3DBorder(StripchartPtr->interp, StripchartPtr->tkwin,
                         Tk_NameOf3DBorder(StripchartPtr->border));
      StripchartPtr->altstripBorder =
            Tk_Get3DBorder(StripchartPtr->interp, StripchartPtr->tkwin,
                         Tk_NameOf3DBorder(StripchartPtr->stripBorder));
      StripchartPtr->a_textColor =
            Tk_GetColorByValue(StripchartPtr->tkwin,
                           StripchartPtr->textColorPtr);
      StripchartPtr->a_tickColor =
            Tk_GetColorByValue(StripchartPtr->tkwin,
                           StripchartPtr->tickColorPtr);

      ConfigureStripchart(StripchartPtr->interp, StripchartPtr, argc, argv,
                      TK_CONFIG_ARGV_ONLY);
}

/*
 * DrawStripi --
 *
 *     Draw the i-th strip of the stripchart.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     A new strip is drawn.
 */
static void 
DrawStripi(Stripchart* SPtr, int i)
{
      Tk_Window tkwin = SPtr->tkwin;
      int lineHeight = SPtr->fontPtr->ascent + SPtr->fontPtr->descent;
      int x = SPtr->borderWidth + PADDING + (i - 1) * SPtr->strip_width;
      int y = SPtr->borderWidth + PADDING +
            hasatitle(SPtr) * (lineHeight + PADDING);
      int w = SPtr->strip_width;
      int h;
      double maxv = SPtr->max_value;
      double minv = SPtr->min_value;

      if (i < 1 || i > SPtr->num_strips)
            return;

      /* Clear any strip that might be below this one */
      XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin), x, y, w,
               SPtr->max_height, FALSE);

      /* Calculate the height of the bar */
      if (maxv == minv)
            h = 0;
      else
            h = (int)((double)SPtr->max_height * (SPtr->value[i - 1] - minv)
                      / (maxv - minv));
      if (h > SPtr->max_height)
            h = SPtr->max_height;
      if (h == 0)
            h = 1;

      /* Adject the origin of the bars rectangle depending on its origin */
      if (SPtr->grow_up)
            y += (SPtr->max_height - h);

      /* draw x axis tick mark, if any */
      if (SPtr->hticks > 0 && ++SPtr->lasthtick >= SPtr->hticks) {
            SPtr->lasthtick = 0;
            XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), SPtr->tickGC,
                    x, SPtr->borderWidth + PADDING + 
                       hasatitle(SPtr) * (lineHeight + PADDING),
                    x, SPtr->max_height);
      }

      /* Draw the y axis ticks, if any */
      if (SPtr->showticks) {
            XSegment ticks[MAX_TICKS];

            for (i = 0; i < SPtr->num_ticks; i++) {
                  ticks[i].x1 = x;
                  ticks[i].x2 = x + SPtr->strip_width - 1;
                  ticks[i].y1 = ticks[i].y2 = SPtr->borderWidth +
                              PADDING + hasatitle(SPtr) *
                              (lineHeight + PADDING) + i *
                              SPtr->max_height / (SPtr->num_ticks-1);
            }
            XDrawSegments(Tk_Display(tkwin), Tk_WindowId(tkwin),
                        SPtr->tickGC, ticks, SPtr->num_ticks);
      }

      /* Draw the bar */
      Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
                     SPtr->stripBorder, x, y, w, h,
                     SPtr->stripBorderWidth,
                     SPtr->stripRelief);
}

/*
 * ScrollStrips --
 *
 *     Scroll the strips in the stripchart region to the left
 *     by the width of a strip pixels.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     Display changes.
 */
static void 
ScrollStrips(Stripchart* SPtr)
{
      Tk_Window tkwin = SPtr->tkwin;
      int lineHeight = SPtr->fontPtr->ascent + SPtr->fontPtr->descent;
      int src_x = SPtr->borderWidth + PADDING + SPtr->strip_width;
      int src_y = SPtr->borderWidth + PADDING +
                hasatitle(SPtr) * (lineHeight + PADDING);
      int dest_x = src_x - SPtr->strip_width;
      int dest_y = src_y;
      int w = (SPtr->num_strips - 1) * SPtr->strip_width;
      int h = SPtr->max_height;

      XCopyArea(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(tkwin),
                Tk_GetGC(tkwin, 0, NULL), src_x, src_y, w, h, dest_x, dest_y);
}

Generated by  Doxygen 1.6.0   Back to index