WFDB Software Package 10.7.0

File: <base>/wave/modepan.c (15,178 bytes)
/* file: modepan.c	G. Moody        30 April 1990
			Last revised:	24 April 2020
Mode panel functions for WAVE

-------------------------------------------------------------------------------
WAVE: Waveform analyzer, viewer, and editor
Copyright (C) 1990-2009 George B. Moody

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, see <http://www.gnu.org/licenses/>.

You may contact the author by e-mail (wfdb@physionet.org) or postal mail
(MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
please visit PhysioNet (http://www.physionet.org/).
_______________________________________________________________________________

*/

#include "wave.h"
#include "xvwave.h"

Panel_item grid_item, opt_item, sig_item, ann_item, ov_item, tim_item, ts_item,
    vs_item, redraw_item;
Frame mode_frame;
Panel mode_panel;

/* Undo any mode changes. */
void mode_undo()
{
    xv_set(ts_item, PANEL_VALUE, tsa_index, NULL);
    xv_set(vs_item, PANEL_VALUE, vsa_index, NULL);
    xv_set(sig_item, PANEL_VALUE, sig_mode, NULL);
    xv_set(ann_item, PANEL_VALUE, ann_mode, NULL);
    xv_set(ov_item, PANEL_VALUE, overlap, NULL);
    xv_set(tim_item, PANEL_VALUE, time_mode, NULL);
    xv_set(grid_item, PANEL_VALUE, grid_mode, NULL);
    xv_set(opt_item, PANEL_VALUE,
	    (show_subtype & 1) |
	   ((show_chan    & 1) << 1) |
	   ((show_num     & 1) << 2) |
	   ((show_aux     & 1) << 3) |
	   ((show_marker  & 1) << 4) |
	   ((show_signame & 1) << 5) |
	   ((show_baseline& 1) << 6) |
	   ((show_level   & 1) << 7),
	   NULL);
}

/* Redraw and take down the window. */
static void redraw_proc(item, event)
Panel_item item;
Event *event;
{
    dismiss_mode();
    disp_proc(item, event);
}

int mode_popup_active = -1;
char *tchoice[] = {"0.25 mm/hour", "1 mm/hour", "5 mm/hour",
    "0.25 mm/min", "1 mm/min", "5 mm/min", "25 mm/min",
    "50 mm/min", "125 mm/min", "250 mm/min", "500 mm/min",
    "12.5 mm/sec", "25 mm/sec", "50 mm/sec", "125 mm/sec", "250 mm/sec",
    "500 mm/sec", "1000 mm/sec", "2000 mm/sec", "5000 mm/sec",
    "10 mm/ms", "20 mm/ms", "50 mm/ms", "100 mm/ms", "200 mm/ms", "500 mm/ms"};
char *vchoice[] = { "1 mm/mV", "2.5 mm/mV", "5 mm/mV", "10 mm/mV", "20 mm/mV",
   "40 mm/mV", "100 mm/mV" };

/* Set up popup window for adjusting display modes. */
void create_mode_popup()
{
    extern void save_defaults();	/* in xvwave.c */
    Icon icon;

    icon = xv_create(XV_NULL, ICON,
		     ICON_IMAGE, icon_image,
		     ICON_LABEL, "View",
		     NULL);
    mode_frame = xv_create(frame, FRAME_CMD,
	XV_LABEL, "View",
	FRAME_ICON, icon, 0);
    mode_panel = xv_get(mode_frame, FRAME_CMD_PANEL, 0);

    opt_item = xv_create(mode_panel, PANEL_CHOICE,
        PANEL_CHOOSE_ONE, FALSE,
	XV_HELP_DATA, "wave:view.show",
	PANEL_LABEL_STRING, "Show: ",
	PANEL_CHOICE_STRINGS,
	      "subtype", "`chan' field", "`num' field", "`aux' field",
	      "markers", "signal names", "baselines", "level", NULL,
	PANEL_VALUE, 0,
        0);
    ts_item = xv_create(mode_panel, PANEL_CHOICE_STACK,
	XV_HELP_DATA, "wave:view.time_scale",
	PANEL_LABEL_STRING, "Time scale: ",
	PANEL_CHOICE_STRINGS,
	      tchoice[0], tchoice[1], tchoice[2], tchoice[3], tchoice[4],
	      tchoice[5], tchoice[6], tchoice[7], tchoice[8], tchoice[9],
	      tchoice[10],tchoice[11],tchoice[12],tchoice[13],tchoice[14],
	      tchoice[15],tchoice[16],tchoice[17],tchoice[18],tchoice[19],
	      tchoice[20],tchoice[21],tchoice[22],tchoice[23],tchoice[24],
	      tchoice[25],
	      NULL,
	PANEL_VALUE, DEF_TSA_INDEX,
	PANEL_DEFAULT_VALUE, DEF_TSA_INDEX,
	0);
    vs_item = xv_create(mode_panel, PANEL_CHOICE_STACK,
	XV_HELP_DATA, "wave:view.amplitude_scale",
	PANEL_LABEL_STRING, "Amplitude scale: ",
	PANEL_CHOICE_STRINGS,
	      vchoice[0], vchoice[1], vchoice[2], vchoice[3], vchoice[4],
	      vchoice[5], vchoice[6], NULL,
	PANEL_VALUE, DEF_VSA_INDEX,
	PANEL_DEFAULT_VALUE, DEF_VSA_INDEX,
	0);
    sig_item = xv_create(mode_panel, PANEL_CHOICE_STACK,
	XV_HELP_DATA, "wave:view.draw",
	PANEL_LABEL_STRING, "Draw: ",
	PANEL_CHOICE_STRINGS,
	      "all signals", "listed signals only", "valid signals only", NULL,
	PANEL_VALUE, 0,
	PANEL_DEFAULT_VALUE, 0,
	0);
    grid_item = xv_create(mode_panel, PANEL_CHOICE_STACK,
	XV_HELP_DATA, "wave:view.grid",
	PANEL_LABEL_STRING, "Grid: ",
	PANEL_CHOICE_STRINGS,"None", "0.2 s", "0.5 mV", "0.2 s x 0.5 mV",
			     "0.04 s x 0.1 mV", "1 m x 0.5 mV", "1 m x 0.1 mV",
			     NULL,
	PANEL_VALUE, 0,
	PANEL_DEFAULT_VALUE, 3,
	0);
    ann_item = xv_create(mode_panel, PANEL_CHOICE_STACK,
	PANEL_NEXT_ROW, -1,
	XV_HELP_DATA, "wave:view.show_annotations",
	PANEL_LABEL_STRING, "Show annotations: ",
	PANEL_CHOICE_STRINGS,
	      "centered", "attached to signals", "as a signal", NULL,
	PANEL_VALUE, 0,
	PANEL_DEFAULT_VALUE, 0,
	0);
    ov_item = xv_create(mode_panel, PANEL_CHOICE_STACK,
	XV_HELP_DATA, "wave:view.overlap",
	PANEL_CHOICE_STRINGS,
	      "avoid overlap", "allow overlap", NULL,
        PANEL_VALUE, 0,
        PANEL_DEFAULT_VALUE, 0,
	0);
    tim_item = xv_create(mode_panel, PANEL_CHOICE_STACK,
	XV_HELP_DATA, "wave:view.time_display",
	PANEL_LABEL_STRING, "Time display: ",
	PANEL_CHOICE_STRINGS,
	      "elapsed", "absolute", "in sample intervals", NULL,
	PANEL_VALUE, 0,
	PANEL_DEFAULT_VALUE, 0,
	0);
    xv_create(mode_panel, PANEL_BUTTON,
	PANEL_NEXT_ROW, -1,
	XV_HELP_DATA, "wave:view.undo",
	PANEL_LABEL_STRING, "Undo changes",
	PANEL_NOTIFY_PROC, mode_undo,
	0);
    redraw_item = xv_create(mode_panel, PANEL_BUTTON,
	XV_HELP_DATA, "wave:view.redraw",
	PANEL_LABEL_STRING, "Redraw",
	PANEL_NOTIFY_PROC, redraw_proc,
	PANEL_CLIENT_DATA, (caddr_t) '.',
	0);
    xv_create(mode_panel, PANEL_BUTTON,
	XV_HELP_DATA, "wave:view.save_as_new_defaults",
	PANEL_LABEL_STRING, "Save as new defaults",
	PANEL_NOTIFY_PROC, save_defaults,
	0);

    xv_set(mode_panel, PANEL_DEFAULT_ITEM, redraw_item, NULL);

    window_fit(mode_panel);
    window_fit(mode_frame);
    mode_popup_active = 0;
}

/* Make the display mode popup window appear. */
void show_mode()
{
    if (mode_popup_active < 0) create_mode_popup();
    xv_set(mode_frame, WIN_SHOW, TRUE, 0);
    mode_popup_active = 1;
}

void set_modes()
{
    int i, otsai = tsa_index, ovsai = vsa_index;
    double osh = canvas_height_mv, osw = canvas_width_sec;
    static double vsa[] = { 1.0, 2.5, 5.0, 10.0, 20.0, 40.0, 100.0 };

    /* If the panel has never been instantiated, there is nothing to do. */
    if (mode_popup_active < 0) return;

    /* Read the current panel settings, beginning with the grid option. */
    switch (grid_mode = (int)xv_get(grid_item, PANEL_VALUE)) {
      case 0: ghflag = gvflag = visible = 0; break;
      case 1: ghflag = 0; gvflag = visible = 1; break;
      case 2: ghflag = visible = 1; gvflag = 0; break;
      case 3: ghflag = gvflag = visible = 1; break;
      case 4: ghflag = gvflag = visible = 2; break;
      case 5: ghflag = visible = 1; gvflag = 3; break;
      case 6: ghflag = visible = 2; gvflag = 3; break;
    }

    /* Next, check the annotation display options.  The bit mask assignments
       below are determined by the order of the PANEL_CHOICE_STRINGS for
       opt_item in create_mode_popup(), above. */
    i = (int)xv_get(opt_item, PANEL_VALUE);
    show_subtype =  i       & 1;
    show_chan    = (i >> 1) & 1;
    show_num     = (i >> 2) & 1;
    show_aux	 = (i >> 3) & 1;
    show_marker  = (i >> 4) & 1;
    show_signame = (i >> 5) & 1;
    show_baseline= (i >> 6) & 1;
    show_level	 = (i >> 7) & 1;

    /* Check the signal display options next. */
    i = sig_mode;
    sig_mode = (int)xv_get(sig_item, PANEL_VALUE);
    if (i != sig_mode || sig_mode == 2)
	set_baselines();

    /* Check the annotation display mode next. */
    ann_mode = (int)xv_get(ann_item, PANEL_VALUE);
    overlap = (int)xv_get(ov_item, PANEL_VALUE);

    /* Check the time display mode next. */
    i = time_mode;
    time_mode = (int)xv_get(tim_item, PANEL_VALUE);
    /* The `nsig > 0' test is a bit of a kludge -- we don't want to reset the
       time_mode before the record has been opened, because we don't know if
       absolute times are available until then. */
    if (nsig > 0 && time_mode == 1)
	(void)wtimstr(0L);	/* check if absolute times are available --
				   if not, time_mode is reset to 0 */
    if (i != time_mode) {
	set_start_time(wtimstr(display_start_time));
	set_end_time(wtimstr(display_start_time + nsamp));
	reset_start();
	reset_stop();
    }

    /* The purpose of the complex method of computing canvas_width_sec is to
       obtain a "rational" value for it even if the frame has been resized.
       First, we determine the number of 5 mm time-grid units in the window
       (note that the resize procedure in xvwave.c guarantees that this will
       be an integer;  the odd calculation is intended to take care of
       roundoff error in pixel-to-millimeter conversion).  For each scale,
       the multiplier of u is simply the number of seconds that would be
       represented by 5 mm.   Since u is usually a multiple of 5 (except on
       small displays, or if the frame has been resized to a small size),
       the calculated widths in seconds are usually integers, at worst
       expressible as an integral number of tenths of seconds. */
    if ((i = xv_get(ts_item, PANEL_VALUE)) >= 0) {
	int u = ((int)(canvas_width/dmmx(1) + 1)/5);	/* number of 5 mm
							   time-grid units */
	switch (tsa_index = i) {
	  case 0:	/* 0.25 mm/hour */
	    mmpersec = (0.25/3600.);
	    canvas_width_sec = 72000 * u; break;
	  case 1:	/* 1 mm/hour */
	    mmpersec = (1./3600.);
	    canvas_width_sec = 18000 * u; break;
	  case 2:	/* 5 mm/hour */
	    mmpersec = (5./3600.);
	    canvas_width_sec = 3600 * u; break;
	  case 3:	/* 0.25 mm/min */
	    mmpersec = (0.25/60.);
	    canvas_width_sec = 1200 * u; break;
	  case 4:	/* 1 mm/min */
	    mmpersec = (1./60.);
	    canvas_width_sec = 300 * u; break;
	  case 5:	/* 5 mm/min */
	    mmpersec = (5./60.);
	    canvas_width_sec = 60 * u; break;
	  case 6:	/* 25 mm/min */
	    mmpersec = (25./60.);
	    canvas_width_sec = 12 * u; break;
	  case 7:	/* 50 mm/min */
	    mmpersec = (50./60.);
	    canvas_width_sec = 6 * u; break;
	  case 8:	/* 125 mm/min */
	    mmpersec = (125./60.);
	    canvas_width_sec = (12 * u) / 5; break;
	  case 9:	/* 250 mm/min */
	    mmpersec = (250./60.);
	    canvas_width_sec = (6 * u) / 5; break;
	  case 10:	/* 500 mm/min */
	    mmpersec = (500./60.);
	    canvas_width_sec = (3 * u) / 5; break;
	  case 11:	/* 12.5 mm/sec */
	    mmpersec = 12.5;
	    canvas_width_sec = (2 * u) / 5; break;
	  case 12:	/* 25 mm/sec */
	    mmpersec = 25.;
	    canvas_width_sec = u / 5; break;
	  case 13:	/* 50 mm/sec */
	    mmpersec = 50.;
	    canvas_width_sec = u / 10; break;
	  case 14:	/* 125 mm/sec */
	    mmpersec = 125.;
	    canvas_width_sec = u / 25; break;
	  case 15:	/* 250 mm/sec */
	    mmpersec = 250.;
	    canvas_width_sec = u / 50.0; break;
	  case 16:	/* 500 mm/sec */
	    mmpersec = 500.;
	    canvas_width_sec = u / 100.0; break;
	  case 17:	/* 1000 mm/sec */
	    mmpersec = 1000.;
	    canvas_width_sec = u / 200.0; break;
	  case 18:	/* 2000 mm/sec */
	    mmpersec = 2000.;
	    canvas_width_sec = u / 400.0; break;
	  case 19:	/* 5000 mm/sec */
	    mmpersec = 5000.;
	    canvas_width_sec = u / 1000.0; break;
	  case 20:	/* 10 mm/ms */
	    mmpersec = 10000.;
	    canvas_width_sec = u / 2000.0; break;
	  case 21:	/* 20 mm/ms */
	    mmpersec = 20000.;
	    canvas_width_sec = u / 4000.0; break;
	  case 22:	/* 50 mm/ms */
	    mmpersec = 50000.;
	    canvas_width_sec = u / 10000.0; break;
	  case 23:	/* 100 mm/ms */
	    mmpersec = 100000.;
	    canvas_width_sec = u / 20000.0; break;
	  case 24:	/* 200 mm/ms */
	    mmpersec = 200000.;
	    canvas_width_sec = u / 40000.0; break;
	  case 25:	/* 500 mm/ms */
	    mmpersec = 500000.;
	    canvas_width_sec = u / 100000.0; break;
	}
    }

    /* Computation of canvas_height_mv could be as complex as above, but
       it doesn't seem so important to obtain a "rational" value here. */
    if ((i = xv_get(vs_item, PANEL_VALUE)) >= 0 && i < 7) {
	mmpermv = vsa[i];
	canvas_height_mv = canvas_height/dmmy(vsa[vsa_index = i]);
    }
    if (osh != canvas_height_mv || osw != canvas_width_sec ||
	otsai != tsa_index || ovsai != vsa_index) {
	vscale[0] = 0.0;
	calibrate();
    }
}

/* Effect any mode changes that were selected and make the popup disappear. */
void dismiss_mode()
{
    /* If the panel is currently visible, make it go away. */
    if (mode_popup_active > 0 &&
	(int)xv_get(mode_frame, FRAME_CMD_PUSHPIN_IN) == FALSE) {
	xv_set(mode_frame, WIN_SHOW, FALSE, 0);
	mode_popup_active = 0;
    }
    set_modes();
}

/* Time-to-string conversion functions.  These functions use those in the
   WFDB library, but ensure that (1) elapsed times are displayed if time_mode
   is 0, and (2) absolute times (if available) are displayed without brackets
   if time_mode is non-zero. */

WFDB_Time wstrtim(s)
char *s;
{
    WFDB_Time t;

    while (*s == ' ' || *s == '\t') s++;
    if (time_mode == 1 && *s != '[' && *s != 's' && *s != 'c' && *s != 'e') {
	char buf[80];

	sprintf(buf, "[%s]", s);
	s = buf;
    }
    t = strtim(s);
    if (*s == '[') {	/* absolute time specified - strtim returns a negated
			   sample number if s is valid */
	if (t > 0L) t = 0L;	/* invalid s (before sample 0) -- reset t */
	else t = -t;	/* valid s -- convert t to a positive sample number */
    }
    return (t);
}

char *wtimstr(t)
WFDB_Time t;
{
    switch (time_mode) {
      case 0:
      default:
	if (t == 0L) return ("0:00");
	else if (t < 0L) t = -t;
	return (timstr(t));
      case 1:
	{
	    char *p, *q;

	    if (t > 0L) t = -t;
	    p = timstr(t);
	    if (*p == '[') {
		p++;
		q = p + strlen(p) - 1;
		if (*q == ']') *q = '\0';
	    }
	    else {
		time_mode = 0;
		if (tim_item) xv_set(tim_item, PANEL_VALUE, time_mode, NULL);
	    }
	    return (p);
        }
      case 2:
	{
	    static char buf[100];

	    if (t < 0L) t = -t;
	    sprintf(buf, "s%"WFDB_Pd_TIME, t);
	    return (buf);
	}
    }
}

char *wmstimstr(t)
WFDB_Time t;
{
    switch (time_mode) {
      case 0:
      default:
	if (t == 0L) return ("0:00");
	else if (t < 0L) t = -t;
	return (mstimstr(t));
      case 1:
	{
	    char *p, *q;

	    if (t > 0L) t = -t;
	    p = mstimstr(t);
	    if (*p == '[') {
		p++;
		q = p + strlen(p) - 1;
		if (*q == ']') *q = '\0';
	    }
	    else {
		time_mode = 0;
		if (tim_item) xv_set(tim_item, PANEL_VALUE, time_mode, NULL);
	    }
	    return (p);
	}
      case 2:
	{
	    static char buf[100];

	    if (t < 0L) t = -t;
	    sprintf(buf, "s%"WFDB_Pd_TIME, t);
	    return (buf);
	}
    }
}