📄 wview.c
字号:
/* file: wview.c G. Moody 20 January 1993
Last revised: 11 June 2000 (but see *** below)
-------------------------------------------------------------------------------
wview: view WFDB-format signals and annotations under MS Windows
Copyright (C) 2000 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, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
You may contact the author by e-mail (george@mit.edu) or postal mail
(MIT Room E25-505A, Cambridge, MA 02139 USA). For updates to this software,
please visit PhysioNet (http://www.physionet.org/).
_______________________________________________________________________________
*/
#include <windows.h>
#include <string.h>
#include <wfdb/wfdb.h>
#include <wfdb/ecgcodes.h>
#include <stdlib.h>
#include "wview.h"
extern int sprintf();
#define SIGNAL_COLOR RGB(0, 0, 255) /* blue */
#define GRID_COLOR RGB(192, 192, 192) /* 25% grey */
#define PGRID_COLOR RGB(0, 0, 0) /* black */
#define MARKER_COLOR RGB(255, 0, 0) /* red */
/* Default scales and grid intervals. */
#define TSI 9 /* index (within tsi[], see below) of default time
scale */
#define VSI 3 /* index (within vsi[], see below) of default amplitude
scale */
#define GTICKSPERSEC 5 /* grid ticks per second */
#define GTICKSPERMV 2 /* grid ticks per millivolt */
/* Printing defaults. */
#define MAXCOPIES 10 /* maximum number of copies to be printed by a single
request */
/* Convert mm coordinates (origin at lower left of window) to window
coordinates (origin at upper left of window).*/
#define xtr(A) ((int)((A)*xppmm))
#define ytr(A) ((int)(wheight - (A)*yppmm))
/* Convert signal coordinates (origin on left edge of window at baseline
level yb, units sample intervals and A/D units) to window coordinates. */
#define xt(A) ((int)((A)*xppsi))
#define yt(A) ((int)(yb - (A)*ysc))
/* Similarly for printer coordinates. */
#define pxtr(A) ((int)((A)*pxppmm))
#define pytr(A) ((int)(pheight - (A)*pyppmm))
#define pxt(A) ((int)((A)*pxppsi))
#define pyt(A) ((int)(yb - (A)*pysc))
HANDLE hInst, hLibrary;
HPEN signal_pen;
HPEN grid_pen;
HPEN pgrid_pen;
HPEN marker_pen;
TEXTMETRIC tm;
static int cheight; /* y-spacing between rows of text */
static char record[20]; /* name of record to be browsed */
static char annotator[20] = "atr"; /* name of annotator to be browsed */
static char cfname[40]; /* name of calibration file */
static char wfdbpath[200]; /* search path for WFDB files */
static char title[80]; /* title of main window */
static double sg[WFDB_MAXSIG]; /* signal gain * calibration scale */
static double sps = WFDB_DEFFREQ; /* sampling frequency (Hz) */
static WFDB_Time tf = 100000L; /* time of end of record */
static WFDB_Time t0, t1; /* times of left, right window edges */
static double tscale; /* time scale (units are mm/sec) */
static double vscale; /* amplitude scale (units are mm/mV) */
static WFDB_Siginfo si[WFDB_MAXSIG]; /* signal specifications */
static int nsig; /* number of valid signals in si[] */
static int nann; /* number of open annotation files */
static int tmode; /* 0: show elapsed time; 1: show absolute time */
/* User-selectable time and amplitude scales. */
static double tsc[] = { 0.25/60., 1./60., 5./60., 25./60., 50./60., 125./60.,
250./60., 500./60., 12.5, 25., 50., 125., 250. };
static char *tst[] = { "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" };
static int tsi = TSI; /* index (within tsc[] and tst[]) of current tscale */
#define MAXTSI (sizeof(tsc)/sizeof(double) - 1)
static double vsc[] = { 1., 2.5, 5., 10., 20., 40., 100. };
static char *vst[] = { "1 mm/mV", "2.5 mm/mV", "5 mm/mV", "10 mm/mV",
"20 mm/mV", "40 mm/mV", "100 mm/mV" };
static int vsi = VSI; /* index (within vsc[] and vst[]) of current vscale */
#define MAXVSI (sizeof(vsc)/sizeof(double) - 1)
int find_next();
int find_previous();
int open_record();
int open_annotation_file();
void paint_main_window();
void print_window();
void set_title();
int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
static char *p, *q, rtemp[80], pstart[40], pdate[20];
MSG msg;
extern int sscanf();
/* If specified, read the calibration file to get standard scales. */
if (cfname[0] == '\0' && (p = getenv("WFDBCAL"))) {
strncpy(cfname, p, sizeof(cfname)-1);
calopen(cfname);
}
/* Set the default display scales. */
tscale = tsc[TSI];
vscale = vsc[VSI];
rtemp[0] = '\0';
if (lpCmdLine)
sscanf(lpCmdLine, "%s%s%s%s", rtemp, annotator, pstart, pdate);
/* Open the signal file(s) for the record to be browsed. */
if (*rtemp) {
/* Convert upper case characters to lower case. */
for (p = rtemp; *p; p++)
if ('A' <= *p && *p <= 'Z') *p += 'a' - 'A';
/* Was the first argument actually a header file name? */
if (strlen(rtemp) > 4 &&
strcmp(rtemp+strlen(rtemp)-4, ".hea") == 0) { /* yes */
/* Locate any drive/path prefix in the first argument. */
for (p = rtemp, q = rtemp-1; *p != '.'; p++)
if (*p == ':' || *p == '\\') q = p;
/* Replace the '.' with a null to strip the '.hea' suffix. */
*p = '\0';
/* Extract the record name. */
strncpy(record, q+1, sizeof(record));
/* If there was a drive/path prefix, add it to the WFDB path. */
if (q >= rtemp) {
size_t l = (size_t)(q-rtemp+1);
strncpy(wfdbpath, rtemp, l);
wfdbpath[l] = ';';
strncpy(wfdbpath+l+1, getwfdb(), sizeof(wfdbpath)-l);
setwfdb(wfdbpath);
}
}
else /* no */
strncpy(record, rtemp, sizeof(record));
open_record();
}
/* If specified, open the annotation file for the record. */
if (*record && *annotator && *annotator != '-')
open_annotation_file();
/* If specified, set the start time. */
if (*record && *pstart) {
if (*pstart == '[') { /* absolute time */
tmode = 1;
if (*pdate) {
strcat(pstart, " ");
strcat(pstart, pdate);
}
if ((t0 = -strtim(pstart)) < 0L)
t0 = 0L; /* pstart precedes start of record; go to 0 */
}
else
t0 = strtim(pstart);
}
if (!hPrevInstance)
if (!InitApplication(hInstance))
return (FALSE);
if (!InitInstance(hInstance, nCmdShow))
return (FALSE);
while (GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wfdbquit();
return (msg.wParam);
}
BOOL InitApplication(hInstance)
HANDLE hInstance;
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, "ecgicon");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "wviewmenu";
wc.lpszClassName = "wviewclass";
return (RegisterClass(&wc));
}
BOOL InitInstance(hInstance, nCmdShow)
HANDLE hInstance;
int nCmdShow;
{
HWND hWnd;
hInst = hInstance;
set_title();
hWnd = CreateWindow(
"wviewclass",
title,
WS_OVERLAPPEDWINDOW | WS_HSCROLL,
0,
0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
return (FALSE);
if (*record == '\0') {
static FARPROC lpProcChoose;
lpProcChoose = MakeProcInstance(Choose, hInst);
DialogBox(hInst, "ChooseBox", hWnd, lpProcChoose);
FreeProcInstance(lpProcChoose);
set_title();
SetWindowText(hWnd, title);
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return (TRUE);
}
static double xppmm, yppmm; /* display resolution */
static double wwsec, whmv; /* window size in seconds (x) and mV (y) */
static int bflag = 0; /* draw signal baselines if non-zero */
static int gtflag = 1; /* draw time grid if non-zero */
static int gvflag = 1; /* draw amplitude grid if non-zero */
static int mflag = 0; /* draw marker bars if non-zero */
static int nflag = 1; /* show signal names if non-zero */
static int show_subtyp = 0, show_chan = 0, show_num = 0, show_aux = 0;
/* display respective fields of annotations if non-zero */
static int wwidth, wheight; /* window size in pixels */
long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
static FARPROC lpProcAbout;
static FARPROC lpProcChoose;
static FARPROC lpProcFind;
static FARPROC lpProcPrint;
static FARPROC lpProcPrintOptions;
static FARPROC lpProcViewOptions;
HDC hDC;
HMENU hMenu;
static int scrollpos = 0; /* scroll position (0 - 2048) */
switch (message) {
case WM_COMMAND:
hMenu = GetMenu(hWnd);
switch (wParam) {
case IDM_OPEN:
lpProcChoose = MakeProcInstance(Choose, hInst);
if (DialogBox(hInst, "ChooseBox", hWnd, lpProcChoose))
InvalidateRect(hWnd, NULL, TRUE);
FreeProcInstance(lpProcChoose);
set_title();
SetWindowText(hWnd, title);
break;
case IDM_FIND:
lpProcFind = MakeProcInstance(Find, hInst);
if (DialogBox(hInst, "FindBox", hWnd, lpProcFind)) {
scrollpos = (int)(2048.*t0/tf);
SetScrollPos(hWnd, SB_HORZ, scrollpos, TRUE);
InvalidateRect(hWnd, NULL, TRUE);
}
FreeProcInstance(lpProcFind);
break;
case IDM_NEW:
case IDM_SAVE:
case IDM_SAVEAS:
case IDM_UNDO:
case IDM_CUT:
case IDM_COPY:
case IDM_PASTE:
case IDM_DEL:
MessageBox(hWnd, "Sorry, this command is not yet implemented!",
"WVIEW", MB_ICONEXCLAMATION | MB_OK);
break;
case IDM_EXIT:
SendMessage(hWnd, WM_CLOSE, 0, 0L);
break;
case IDM_HELP_INDEX:
WinHelp(hWnd, "wview.hlp", HELP_CONTENTS, 0L);
break;
case IDM_HELP_CHOOSING:
WinHelp(hWnd, "wview.hlp", HELP_CONTEXT, (long)Choosing_Topic);
break;
case IDM_HELP_BROWSING:
WinHelp(hWnd, "wview.hlp", HELP_CONTEXT, (long)Browsing_Topic);
break;
case IDM_HELP_OPTIONS:
WinHelp(hWnd, "wview.hlp", HELP_CONTEXT, (long)Options_Topic);
break;
case IDM_HELP_SEARCHING:
WinHelp(hWnd, "wview.hlp", HELP_CONTEXT, (long)Searching_Topic);
break;
case IDM_HELP_PRINTING:
WinHelp(hWnd, "wview.hlp", HELP_CONTEXT, (long)Printing_Topic);
break;
case IDM_ABOUT:
lpProcAbout = MakeProcInstance(About, hInst);
DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
FreeProcInstance(lpProcAbout);
break;
case IDM_PRINT:
lpProcPrint = MakeProcInstance(Print, hInst);
DialogBox(hInst, "PrintBox", hWnd, lpProcPrint);
FreeProcInstance(lpProcPrint);
break;
case IDM_VIEW:
lpProcViewOptions = MakeProcInstance(ViewOptions, hInst);
DialogBox(hInst, "ViewOptionsBox", hWnd, lpProcViewOptions);
FreeProcInstance(lpProcViewOptions);
InvalidateRect(hWnd, NULL, TRUE);
break;
case IDM_PROPT:
lpProcPrintOptions = MakeProcInstance(PrintOptions, hInst);
DialogBox(hInst, "PrintOptionsBox", hWnd, lpProcPrintOptions);
FreeProcInstance(lpProcPrintOptions);
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
break;
case WM_CREATE:
/* Create the pen objects. */
signal_pen = CreatePen(PS_SOLID, 0, SIGNAL_COLOR);
grid_pen = CreatePen(PS_SOLID, 0, GRID_COLOR);
pgrid_pen = CreatePen(PS_DOT, 0, PGRID_COLOR);
marker_pen = CreatePen(PS_DOT, 0, MARKER_COLOR);
hDC = GetDC(hWnd);
/* Determine display resolution (pixels per millimeter in x and y). */
xppmm = GetDeviceCaps(hDC,HORZRES)/(double)GetDeviceCaps(hDC,HORZSIZE);
yppmm = GetDeviceCaps(hDC,VERTRES)/(double)GetDeviceCaps(hDC,VERTSIZE);
/* Get the size characteristics of the current font. */
GetTextMetrics(hDC, &tm);
cheight = tm.tmExternalLeading + tm.tmHeight;
ReleaseDC(hWnd, hDC);
break;
case WM_SIZE:
wwidth = LOWORD(lParam);
wheight = HIWORD(lParam);
SetScrollRange(hWnd, SB_HORZ, 0, 2048, FALSE);
scrollpos = (int)(2048.*t0/tf);
SetScrollPos(hWnd, SB_HORZ, scrollpos, TRUE);
break;
case WM_HSCROLL:
switch (wParam) {
case SB_TOP:
t0 = 0L;
break;
case SB_BOTTOM:
t0 = tf - (WFDB_Time)(wwsec*sps);
break;
case SB_LINEUP:
if ((t0 -= (WFDB_Time)sps) < 0L) t0 = 0L;
break;
case SB_LINEDOWN:
t0 += (WFDB_Time)sps;
break;
case SB_PAGEUP:
if ((t0 -= (WFDB_Time)(sps*(int)wwsec)) < 0L) t0 = 0L;
break;
case SB_PAGEDOWN:
t0 += (WFDB_Time)(sps*(int)wwsec);
break;
case SB_THUMBTRACK: /* events occur while dragging "thumb" */
/* Show the time corresponding to the current thumb position. */
{
char ts[30];
int x, y;
hDC = GetDC(hWnd);
SetBkMode(hDC, OPAQUE);
SetTextColor(hDC, MARKER_COLOR);
sprintf(ts, "<< %20s >>",
timstr(-(WFDB_Time)(tf*(LOWORD(lParam)/2048.))));
x = 200;
y = ytr(5.) - tm.tmExternalLeading;
TextOut(hDC, x, y, ts, strlen(ts));
ReleaseDC(hWnd, hDC);
}
return (NULL); /* do not redraw the entire window */
case SB_THUMBPOSITION: /* event occurs when "thumb" is dropped */
t0 = (WFDB_Time)(tf * (LOWORD(lParam)/2048.));
if (t0 >= tf && tf > 0L)
t0 = tf - (WFDB_Time)(wwsec*sps);
break;
default:
return (NULL);
}
scrollpos = (int)(2048.*t0/tf);
SetScrollPos(hWnd, SB_HORZ, scrollpos, TRUE);
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_KEYDOWN:
switch (wParam) {
case VK_HOME: /* <Home> key */
SendMessage(hWnd, WM_HSCROLL, SB_TOP, 0L);
break;
case VK_END: /* <End> key */
SendMessage(hWnd, WM_HSCROLL, SB_BOTTOM, 0L);
break;
case VK_LEFT: /* <left arrow> */
SendMessage(hWnd, WM_HSCROLL, SB_LINEUP, 0L);
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -