/* file: scope.c G. Moody 31 July 1991 Last revised: 10 June 2005 Scope window functions for WAVE ------------------------------------------------------------------------------- WAVE: Waveform analyzer, viewer, and editor Copyright (C) 1991-2005 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 . 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" #include #include #include #include #include #include #include #include #define SQRTMAXSPEED (30) #define MAXSPEED (SQRTMAXSPEED*SQRTMAXSPEED) static int scope_use_overlays, use_color, grey; static void scan(), create_scope_panel(), set_dt(); static void scope_proc(Panel_item item, Event *event); void save_scope_params(a, b, c) int a, b, c; { scope_use_overlays = a; use_color = b; grey = c; } static void scope_repaint(canvas, paint_window, repaint_area) Canvas canvas; Xv_Window paint_window; Rectlist *repaint_area; { ; } static unsigned int scope_height, scope_width; static XPoint *sbuf; static int v0n, v0f, v0v, xt, yt; static long scope_dt; static Display *scope_display; static XID scope_xid; static GC clear_plane[4], plot_sig; static void scope_resize(canvas, w, h) Canvas canvas; int w, h; { int i; xt = mmx(2); if (w > scope_width) { XPoint *sbt; if (sbuf == NULL && (sbt = (XPoint *)malloc(w * sizeof(XPoint))) == NULL) { #ifdef NOTICE Xv_notice notice = xv_create((Frame)frame, NOTICE, XV_SHOW, TRUE, #else (void)notice_prompt((Frame)frame, (Event *)NULL, #endif NOTICE_MESSAGE_STRINGS, "Error in allocating memory for scope\n", 0, NOTICE_BUTTON_YES, "Continue", 0); #ifdef NOTICE xv_destroy_safe(notice); #endif return; } else if (sbuf != NULL && (sbt = (XPoint *)realloc(sbuf, w * sizeof(XPoint))) == NULL) { #ifdef NOTICE Xv_notice notice = xv_create((Frame)frame, NOTICE, XV_SHOW, TRUE, #else (void)notice_prompt((Frame)frame, (Event *)NULL, #endif NOTICE_MESSAGE_STRINGS, "Error in allocating memory for scope\n", 0, NOTICE_BUTTON_YES, "Continue", 0); #ifdef NOTICE xv_destroy_safe(notice); #endif return; } sbuf = sbt; } if (w != scope_width-1) { if (scope_width == 0) scope_dt = strtim("0.5"); scope_width = w-1; if (tscale <= 1.0) /* resolution limited by display */ for (i = 0; i <= scope_width; i++) sbuf[i].x = i; else { /* resolution limited by input data */ for (i = 0; i <= scope_width; i++) sbuf[i].x = i*tscale; } } if (h != scope_height) { scope_height = h; yt = scope_height - mmy(2); v0n = scope_height/3; v0f = scope_height/2; v0v = 2*scope_height/3; } if (scope_xid) { if (scope_use_overlays) { static XPoint tbuf[2]; tbuf[0].y = 0; tbuf[1].y = scope_height-1; for (i = 0; i on numeric keypad: simulate middle mouse button */ case MS_MIDDLE: if (event_is_down(event)) { if (event_ctrl_is_down(event)) set_dt(x); else scope_proc(XV_NULL, (Event *) '*'); /* interrupt scan */ } break; case KEY_RIGHT(12): /* right-arrow: simulate right mouse button */ case MS_RIGHT: if (event_is_down(event)) { if (event_ctrl_is_down(event)) scope_proc(XV_NULL, (Event *) ']'); /* scan forward */ else scope_proc(XV_NULL, (Event *) '>'); /* single step */ } break; } handling_event = 0; } static Colormap colormap; static XColor color[16]; static unsigned long pixel_table[16]; static Canvas canvas; static Frame scope_frame; static Panel scope_panel; #ifndef USE_OLC_PLUS extern Server_image cursor_image; #endif void create_scope_popup(overlay_flag, use_color, grey) int overlay_flag, use_color, grey; { char *bgcname, *fgcname; int i, ncolors; static unsigned long mask[4]; static XGCValues gcvalues; Icon icon; Xv_Cursor scope_c; Xv_singlecolor scope_c_bg, scope_c_fg; Xv_Window window; icon = xv_create(frame, ICON, ICON_IMAGE, icon_image, ICON_LABEL, "Scope", NULL); scope_frame = xv_create(frame, FRAME, XV_LABEL, "Scope", XV_WIDTH, mmx(25)+4, XV_HEIGHT, mmy(150), FRAME_ICON, icon, NULL); create_scope_panel(); window_fit_height(scope_panel); canvas = xv_create(scope_frame, CANVAS, CANVAS_REPAINT_PROC, scope_repaint, CANVAS_RESIZE_PROC, scope_resize, CANVAS_WIDTH, mmx(25), CANVAS_HEIGHT, mmy(100), CANVAS_MIN_PAINT_WIDTH, mmx(25), CANVAS_MIN_PAINT_HEIGHT, mmy(20), CANVAS_AUTO_CLEAR, TRUE, WIN_X, 0, WIN_BELOW, scope_panel, WIN_DYNAMIC_VISUAL, overlay_flag, 0); scope_resize(canvas, (int)xv_get(canvas, CANVAS_WIDTH), (int)xv_get(canvas, CANVAS_HEIGHT)); window_fit(canvas); window_fit(scope_frame); scope_display = (Display *)xv_get(canvas, XV_DISPLAY); window = (Xv_Window)xv_get(canvas, CANVAS_NTH_PAINT_WINDOW, 0); scope_xid = (Window)xv_get(window, XV_XID); mask[0] = mask[1] = mask[2] = mask[3] = ~0; /* Get the appropriate foreground and background colors. */ if (use_color && !grey) { bgcname = defaults_get_string("wave.scope.color.background", "Wave.Scope.Color.background", "white"); fgcname = defaults_get_string("wave.scope.color.foreground", "Wave.Scope.Color.Foreground", "blue"); } else if (grey) { bgcname = defaults_get_string("wave.scope.grey.background", "Wave.Scope.Grey.background", "white"); fgcname = defaults_get_string("wave.scope.grey.foreground", "Wave.Scope.Grey.Foreground", "black"); } else { bgcname = defaults_get_string("wave.scope.mono.background", "Wave.Scope.Mono.background", "white"); if (strcmp(bgcname, "black") && strcmp(bgcname, "Black")) fgcname = "black"; else fgcname = "white"; } /* Allocate a colormap for the scope display area. */ colormap = DefaultColormap(scope_display, DefaultScreen(scope_display)); /* Determine how to "fade" the display (i.e., how to make the oldest data disappear into the background). There are two techniques: A. If 16 read/write color cells in 4 plane groups are available, we number them 0 to 15. Color 0 is pure background, and color 15 is pure foreground. The remainder are blends of foreground and background, and the amount of foreground in the mix is determined by the number of non-zero bits in the color number. Before writing new data, one of the four planes is cleared (the choice of which plane is made by circulating through the 4 in a fixed order). New data are always written in color 15. Thus new data are 100% foreground, data one cycle old are 75%, data two cycles old are 50%, data three cycles old are 25%, and all older data are erased (i.e., set to the background color). B. Otherwise, before writing new data, we set one of every four pixels in the window to the background color using one of four bitmasks (each of which includes one-fourth of the pixels, with no overlap between the bitmasks). Thus 100% of all the pixels in new data are in the foreground color, data one cycle old are 75% visible (on average), data two cycles old are 50% visible, data three cycles old are 25% visible, and all older data are erased. It might be even better to combine these techniques if both can be used on a given display, to give 16 levels of fading; this would also require 16 GCs, however, which might pose a problem for the server. */ if (overlay_flag) { /* Try to get read/write color cells if possible. */ if (!XAllocColorCells(scope_display, colormap, 0, mask, 4, pixel_table, 1)) overlay_flag = 0; /* impossible -- use plan B */ else { /* execute plan A */ /* Color 0: background */ XParseColor(scope_display, colormap, bgcname, &color[0]); /* Color 15: foreground */ XParseColor(scope_display, colormap, fgcname, &color[15]); /* Color numbers with one non-zero bit: 1, 2, 4, 8 */ color[1].red = color[2].red = color[4].red = color[8].red = (color[15].red + 3*color[0].red ) / 4; color[1].green = color[2].green = color[4].green = color[8].green = (color[15].green + 3*color[0].green) / 4; color[1].blue = color[2].blue = color[4].blue = color[8].blue = (color[15].blue + 3*color[0].blue ) / 4; /* Color numbers with two non-zero bits: 3, 5, 6, 9, 10, 12 */ color[3].red = color[5].red = color[6].red = color[9].red = color[10].red = color[12].red = (color[15].red + color[0].red ) / 2; color[3].green = color[5].green = color[6].green = color[9].green = color[10].green = color[12].green = (color[15].green + color[0].green) / 2; color[3].blue = color[5].blue = color[6].blue = color[9].blue = color[10].blue = color[12].blue = (color[15].blue + color[0].blue ) / 2; /* Color numbers with three non-zero bits: 7, 11, 13, 14 */ color[7].red = color[11].red = color[13].red = color[14].red = (3*color[15].red + color[0].red )/4; color[7].green = color[11].green = color[13].green = color[14].green = (3*color[15].green + color[0].green)/4; color[7].blue = color[11].blue = color[13].blue = color[14].blue = (3*color[15].blue + color[0].blue )/4; for (i = 0; i < 16; i++) { color[i].pixel = pixel_table[0]; if (i & 1) color[i].pixel |= mask[0]; if (i & 2) color[i].pixel |= mask[1]; if (i & 4) color[i].pixel |= mask[2]; if (i & 8) color[i].pixel |= mask[3]; color[i].flags = DoRed | DoGreen | DoBlue; pixel_table[i] = color[i].pixel; } ncolors = 16; XStoreColors(scope_display, colormap, color, ncolors); /* Create plan A graphics contexts. */ gcvalues.foreground = pixel_table[15]; gcvalues.background = pixel_table[0]; plot_sig = XCreateGC(scope_display, scope_xid, GCForeground | GCBackground, &gcvalues); gcvalues.foreground = gcvalues.background; for (i = 0; i < 4; i++) { gcvalues.plane_mask = mask[i]; clear_plane[i] = XCreateGC(scope_display, scope_xid, GCBackground | GCForeground | GCPlaneMask, &gcvalues); } } } if (!overlay_flag) { /* execute plan B */ int j; unsigned int stipple_height, stipple_width; GC set_stipple, clear_stipple; Pixmap stipple; XParseColor(scope_display, colormap, bgcname, &color[0]); XParseColor(scope_display, colormap, fgcname, &color[1]); (void)XAllocColor(scope_display, colormap, &color[0]); (void)XAllocColor(scope_display, colormap, &color[1]); pixel_table[0] = color[0].pixel; pixel_table[1] = color[1].pixel; ncolors = 2; /* Create plan B graphics contexts. */ XQueryBestStipple(scope_display, scope_xid, scope_width, scope_height, &stipple_width, &stipple_height); stipple = XCreatePixmap(scope_display, scope_xid, stipple_width, stipple_height, 1); gcvalues.foreground = gcvalues.background = BlackPixel(scope_display, DefaultScreen(scope_display)); clear_stipple = XCreateGC(scope_display, stipple, GCForeground, &gcvalues); gcvalues.foreground = WhitePixel(scope_display, DefaultScreen(scope_display)); set_stipple = XCreateGC(scope_display, stipple, GCForeground | GCBackground, &gcvalues); for (j = 0; j < stipple_height-1; j += 2) for (i = 0; i < stipple_width-1; i += 2) { XDrawPoint(scope_display, stipple, set_stipple, i, j); XDrawPoint(scope_display, stipple, clear_stipple, i+1, j); XDrawPoint(scope_display, stipple, clear_stipple, i+1, j+1); XDrawPoint(scope_display, stipple, clear_stipple, i, j+1); } gcvalues.stipple = stipple; gcvalues.fill_style = FillStippled; for (i = 0; i < 4; i++) { gcvalues.ts_x_origin = (i & 1); gcvalues.ts_y_origin = (i == 1 || i == 2); clear_plane[i] = XCreateGC(scope_display, scope_xid, GCForeground | GCStipple | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin, &gcvalues); } gcvalues.foreground = pixel_table[1]; gcvalues.background = pixel_table[0]; plot_sig = XCreateGC(scope_display, scope_xid, GCForeground | GCBackground, &gcvalues); gcvalues.foreground = gcvalues.background; } /* Create and install a cursor for the scope window. Record the cursor colors in terms of the XView color model (8 bits for each of red, green, blue, rather than 16 as in the X model). */ scope_c_fg.red = color[ncolors - 1].red >> 8; scope_c_fg.green = color[ncolors - 1].green >> 8; scope_c_fg.blue = color[ncolors - 1].blue >> 8; scope_c_bg.red = color[0].red >> 8; scope_c_bg.green = color[0].green >> 8; scope_c_bg.blue = color[0].blue >> 8; scope_c = (Xv_Cursor)xv_create(scope_frame, CURSOR, #ifndef USE_OLC_PLUS /* See xvwave.c for details on the alternative cursors. */ CURSOR_IMAGE, cursor_image, CURSOR_XHOT, 7, CURSOR_YHOT, 7, #else CURSOR_SRC_CHAR, OLC_PLUS, #endif CURSOR_FOREGROUND_COLOR, &scope_c_fg, CURSOR_BACKGROUND_COLOR, &scope_c_bg, NULL); xv_set(window, WIN_CURSOR, scope_c, NULL); /* Register the event handler for the scope window. */ xv_set(window, WIN_EVENT_PROC, scope_window_event_proc, WIN_CONSUME_EVENTS, WIN_NO_EVENTS, LOC_WINENTER, ACTION_HELP, WIN_ASCII_EVENTS, WIN_MOUSE_BUTTONS, LOC_DRAG, NULL, NULL); xv_set(window, WIN_IGNORE_EVENTS, WIN_UP_ASCII_EVENTS, NULL, NULL); for (i = 0; i < 4; i++) XFillRectangle(scope_display, scope_xid, clear_plane[i], 0, 0, scope_width, scope_height); scope_resize(canvas, (int)xv_get(canvas, CANVAS_WIDTH), (int)xv_get(canvas, CANVAS_HEIGHT)); } static int show_this_frame() { static char plane = 3, first_frame = 1; int i, i0, tt, tt0 = 0, v0; long t; if (first_frame && scope_use_overlays) { first_frame = 0; scope_resize(canvas, (int)xv_get(canvas, CANVAS_WIDTH), (int)xv_get(canvas, CANVAS_HEIGHT)); } if (scope_annp == NULL) return(0); if ((t = scope_annp->this.time - scope_dt) < 0L) { tt0 = (int)(-t); t = 0L; } if (isigsettime(t) < 0 || getvec(scope_v) < 0) return (0); switch (map2(scope_annp->this.anntyp)) { case NORMAL: case LEARN: v0 = scope_v[signal_choice] * vscale[signal_choice] - v0n; break; case FUSION: v0 = scope_v[signal_choice] * vscale[signal_choice] - v0f; break; case PVC: v0 = scope_v[signal_choice] * vscale[signal_choice] - v0v; break; } if (tscale >= 1.0) { /* resolution limited by input data */ for (i = i0 = tt0; i < scope_width; i++) { if (getvec(scope_v) <= 0) break; sbuf[i].y = scope_v[signal_choice] * vscale[signal_choice] - v0; } i--; } else { /* resolution limited by display */ int vmax, vmin, vv, x; (void)getvec(scope_v); vmax = vmin = scope_v[signal_choice]; i = i0 = tt0*tscale; if (i < scope_width) sbuf[i].y = scope_v[signal_choice] * vscale[signal_choice] - v0; for (tt = tt0 + 1; i < scope_width && getvec(scope_v) > 0; tt++) { if (scope_v[signal_choice] > vmax) vmax = scope_v[signal_choice]; else if (scope_v[signal_choice] i) { i = x; if (vmax - vv > vv - vmin) vv = vmin = vmax; else vv = vmax = vmin; sbuf[i].y = vv * vscale[signal_choice] - v0; } } } XFillRectangle(scope_display, scope_xid, clear_plane[plane], 0, 0, scope_width, scope_height); if (++plane > 3) { char *tp; plane = 0; tp = wtimstr(t); XDrawString(scope_display,scope_xid,plot_sig,xt,yt,tp,strlen(tp)); } if (i > i0) XDrawLines(scope_display, scope_xid, plot_sig, sbuf + i0, i - i0, CoordModeOrigin); return (1); } static void refresh_time() { char *tp; int i, ytt; long t; ytt = scope_height - mmy(5); for (i = 0; i < 4; i++) XFillRectangle(scope_display, scope_xid, clear_plane[i], 0, ytt, scope_width, scope_height); if (scope_annp) { t = scope_annp->this.time - scope_width/2; tp = wtimstr(t); XDrawString(scope_display,scope_xid,plot_sig,xt,yt,tp,strlen(tp)); } } static int show_next_frame() { if (scope_annp == NULL) { scan(0); return (0); } while (scope_annp->this.time < begin_analysis_time) { if (scope_annp->next == NULL) break; scope_annp = scope_annp->next; } do { if (((scope_annp = scope_annp->next) == NULL) || (scope_annp->this.anntyp == INDEX_MARK) || (end_analysis_time > 0L && scope_annp->this.time > end_analysis_time)) { if (scope_annp == NULL) scope_annp = ap_end; else if (scope_annp->this.anntyp == INDEX_MARK && scope_annp->next != NULL) scope_annp = scope_annp->next; else scope_annp = scope_annp->previous; scan(0); return (0); } } while (!isqrs(scope_annp->this.anntyp) || (ann_mode == 1 && scope_annp->this.chan != signal_choice)); return (show_this_frame()); } static int show_prev_frame() { if (scope_annp == NULL) { scan(0); return (0); } while (end_analysis_time > 0L && scope_annp->this.time > end_analysis_time) { if (scope_annp->previous == NULL) break; scope_annp = scope_annp->previous; } do { if (((scope_annp = scope_annp->previous) == NULL) || (scope_annp->this.anntyp == INDEX_MARK) || (scope_annp->this.time < begin_analysis_time)) { if (scope_annp == NULL) scope_annp = ap_start; else if (scope_annp->this.anntyp == INDEX_MARK && scope_annp->previous != NULL) scope_annp = scope_annp->previous; else scope_annp = scope_annp->next; scan(0); return (0); } } while (!isqrs(scope_annp->this.anntyp) || (ann_mode == 1 && scope_annp->this.chan != signal_choice)); return (show_this_frame()); } static int speed = MAXSPEED; static Notify_value show_next_n_frames() { int i; for (i = speed/10 + 1; i > 0 ; i--) if (show_next_frame() == 0) break; refresh_time(); return (NOTIFY_DONE); } static Notify_value show_prev_n_frames() { int i; for (i = speed/10 + 1; i > 0 ; i--) if (show_prev_frame() == 0) break; refresh_time(); return (NOTIFY_DONE); } static struct itimerval sc_timer; static void scan(speed) int speed; { if (speed > 0) { if (speed > MAXSPEED) speed = MAXSPEED; sc_timer.it_value.tv_usec = sc_timer.it_interval.tv_usec = 1000000L/speed; notify_set_itimer_func(scope_frame, show_next_n_frames, ITIMER_REAL, &sc_timer, NULL); scan_active = 1; } else if (speed == 0) { notify_set_itimer_func(scope_frame, NOTIFY_FUNC_NULL, ITIMER_REAL, NULL, NULL); scan_active = 0; } else { if (speed < -MAXSPEED) speed = -MAXSPEED; sc_timer.it_value.tv_usec = sc_timer.it_interval.tv_usec = -1000000L/speed; notify_set_itimer_func(scope_frame, show_prev_n_frames, ITIMER_REAL, &sc_timer, NULL); scan_active = -1; } } static void scope_proc(Panel_item item, Event *event) { int client_data; long t0; if (item) client_data = (int)xv_get(item, PANEL_CLIENT_DATA); else client_data = (int)event; if (ap_start == NULL) { #ifdef NOTICE Xv_notice notice = xv_create((Frame)frame, NOTICE, XV_SHOW, TRUE, #else (void)notice_prompt((Frame)frame, (Event *)NULL, #endif NOTICE_MESSAGE_STRINGS, "Scope functions cannot be used while the", "annotation list is empty.", 0, NOTICE_BUTTON_YES, "Continue", 0); #ifdef NOTICE xv_destroy_safe(notice); #endif return; } if (attached && (begin_analysis_time <= attached->this.time && (attached->this.time <= end_analysis_time || end_analysis_time<0L))) { scope_annp = attached; attached = NULL; } else if (scope_annp == NULL) { (void)locate_annotation(display_start_time, -128); scope_annp = annp; } switch (client_data) { case '[': /* scan backwards */ box(0, 0, 0); scan(-speed); break; case '<': /* single-step backwards */ show_prev_frame(); refresh_time(); if (display_start_time < scope_annp->this.time && scope_annp->this.time < display_start_time + nsamp) box((int)((scope_annp->this.time - display_start_time)*tscale), (ann_mode==1 && (unsigned)scope_annp->this.chan < nsig) ? (int)(base[(unsigned)scope_annp->this.chan] + mmy(2)) : abase, 1); else box(0, 0, 0); break; case '*': /* interrupt scan */ scan(0); refresh_time(); if ((t0 = scope_annp->this.time - nsamp/2) < 0L) t0 = 0L; find_display_list(t0); set_start_time(wtimstr(t0)); set_end_time(wtimstr(t0 + nsamp)); if (item) disp_proc(item, (Event *)NULL); else disp_proc(XV_NULL, event); box((int)((scope_annp->this.time - display_start_time)*tscale), (ann_mode==1 && (unsigned)scope_annp->this.chan < nsig) ? (int)(base[(unsigned)scope_annp->this.chan] + mmy(2)) : abase, 1); break; case '>': /* single-step forwards */ show_next_frame(); refresh_time(); if (display_start_time < scope_annp->this.time && scope_annp->this.time < display_start_time + nsamp) box((int)((scope_annp->this.time - display_start_time)*tscale), (ann_mode==1 && (unsigned)scope_annp->this.chan < nsig) ? (int)(base[(unsigned)scope_annp->this.chan] + mmy(2)) : abase, 1); else box(0, 0, 0); break; case ']': /* scan forwards */ default: box(0, 0, 0); scan(speed); break; } } static void adjust_speed(item, value) Panel_item item; int value; { speed = value*value; if (scan_active) scan(scan_active*speed); } static char *lmstimstr(t) long t; { char *p, *p0; if (t == 0L) return ("0"); else if (t > 0L) p = mstimstr(t); else p = p0 = mstimstr(-t); while (*p == ' ' || *p == '0' || *p == ':') p++; if (*p == '.') p--; if (t < 0L && p > p0) *(--p) = '-'; return (p); } Panel_item dt_item; static void adjust_dt(item, value) Panel_item item; int value; { char *dt_string = (char *) xv_get(item, PANEL_VALUE); while (*dt_string == ' ' || *dt_string == '\t') dt_string++; if (*dt_string != '-') scope_dt = strtim(dt_string); else scope_dt = -strtim(dt_string+1); xv_set(item, PANEL_VALUE, lmstimstr(scope_dt)); } static void set_dt(x) int x; { scope_dt = x; scope_dt /= tscale; xv_set(dt_item, PANEL_VALUE, lmstimstr(scope_dt)); } static void create_scope_panel() { scope_panel = xv_create(scope_frame, PANEL, XV_X, 0, XV_HELP_DATA, "wave:scope_panel", 0); xv_create(scope_panel, PANEL_SLIDER, XV_HELP_DATA, "wave:scope_panel.speed", PANEL_LABEL_STRING, "Speed", PANEL_DIRECTION, PANEL_VERTICAL, PANEL_VALUE, SQRTMAXSPEED, PANEL_MAX_VALUE, SQRTMAXSPEED, PANEL_SHOW_RANGE, FALSE, PANEL_SHOW_VALUE, FALSE, PANEL_NOTIFY_PROC, adjust_speed, NULL); dt_item = xv_create(scope_panel, PANEL_TEXT, XV_HELP_DATA, "wave:scope_panel.dt", PANEL_LABEL_STRING, "dt: ", PANEL_VALUE_DISPLAY_LENGTH, 6, PANEL_VALUE, "0.500", PANEL_NOTIFY_PROC, adjust_dt, NULL); xv_create(scope_panel, PANEL_BUTTON, XV_HELP_DATA, "wave:scope_panel.<<", PANEL_LABEL_STRING, "<<", PANEL_NOTIFY_PROC, scope_proc, PANEL_CLIENT_DATA, (caddr_t) '[', NULL); xv_create(scope_panel, PANEL_BUTTON, XV_HELP_DATA, "wave:scope_panel.<", PANEL_LABEL_STRING, "<", PANEL_NOTIFY_PROC, scope_proc, PANEL_CLIENT_DATA, (caddr_t) '<', NULL); xv_create(scope_panel, PANEL_BUTTON, XV_HELP_DATA, "wave:scope_panel.pause", PANEL_LABEL_STRING, " Pause ", PANEL_NOTIFY_PROC, scope_proc, PANEL_CLIENT_DATA, (caddr_t) '*', NULL); xv_create(scope_panel, PANEL_BUTTON, XV_HELP_DATA, "wave:scope_panel.>", PANEL_LABEL_STRING, ">", PANEL_NOTIFY_PROC, scope_proc, PANEL_CLIENT_DATA, (caddr_t) '>', NULL); xv_create(scope_panel, PANEL_BUTTON, XV_HELP_DATA, "wave:scope_panel.>>", PANEL_LABEL_STRING, ">>", PANEL_NOTIFY_PROC, scope_proc, PANEL_CLIENT_DATA, (caddr_t) ']', NULL); } static int scope_popup_active = -1; void show_scope_window() { if (scope_popup_active < 0) create_scope_popup(scope_use_overlays, use_color, grey); wmgr_top(scope_frame); xv_set(scope_frame, WIN_MAP, TRUE, 0); scope_popup_active = 1; }