WFDB Software Package 10.7.0
(26,380 bytes)
/* file: logpan.c G. Moody 1 May 1990
Last revised: 24 April 2020
Log panel functions for WAVE
-------------------------------------------------------------------------------
WAVE: Waveform analyzer, viewer, and editor
Copyright (C) 1999 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"
#include <string.h>
#include <X11/Xos.h> /* for <sys/time.h> */
#include <xview/notice.h>
#include <xview/notify.h>
#include <xview/textsw.h>
#include <xview/defaults.h>
#define LLLMAX (RNLMAX+40+DSLMAX) /* max length of line in log file */
Frame log_frame;
/* A WAVE log file contains one-line entries. Each entry specifies a record
and a time or time interval in that record, and contains an associated
text string, which (typically) contains a user-entered description of
features of the signals at that time. The format is:
<record><whitespace><time_spec><whitespace><text>
where
<record> is the record name (if empty, use the record name given in the
previous entry, or the name of the currently displayed record if this
is the first entry)
<time_spec> is either the start and end times separated by a hyphen, or
the time of the center of the region of interest, in strtim format
<text> is the text of the log entry, which cannot begin with whitespace
but which may contain embedded whitespace (this field may be empty)
and <whitespace> is a sequence of one or more space or tab characters.
This format is compatible with `pschart' and `psfd' script file format.
The log is kept internally as a doubly-linked list of log_entry structures.
*/
struct log_entry {
struct log_entry *prev, *next;
char *record, *time_spec, *text;
} *first_entry, *current_entry, *last_entry;
/* This function selects an annotation at a specified time. If no such
annotation exists, it moves the log marker (an index mark pseudo-annotation)
to the specified time and selects the marker.
*/
void set_marker(t)
WFDB_Time t;
{
static struct ap *log_marker;
if (locate_annotation(t, 0))
attached = annp;
else if (log_marker) {
move_annotation(log_marker, t);
attached = log_marker;
}
else if (log_marker = get_ap()) {
log_marker->this.time = t;
log_marker->this.anntyp = INDEX_MARK;
insert_annotation(log_marker);
attached = log_marker;
}
}
/* add_entry allocates memory for a log_entry structure, fills it in
using the input arguments, and inserts it in the linked list at the
location after the current entry. (If current_entry is NULL, the
new entry is inserted at the head of the list.) Upon successful
(non-zero) return, current_entry points to the newly inserted
entry, and first_entry and last_entry may have been updated; if
memory cannot be allocated, add_entry returns 0, and the log entry
pointers are left unchanged. */
int add_entry(recp, timep, textp)
char *recp, *timep, *textp; /* record name, time specification, and text */
{
struct log_entry *new_entry;
char *p;
/* Allocate memory for structure and strings. */
if ((new_entry = (struct log_entry *)malloc(sizeof(struct log_entry)))
== NULL ||
(new_entry->record = (char *)malloc(strlen(recp)+1)) == NULL ||
(new_entry->time_spec = (char *)malloc(strlen(timep)+1)) == NULL ||
(textp &&
(new_entry->text = (char *)malloc(strlen(textp)+1)) == 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 log\n", 0,
NOTICE_BUTTON_YES, "Continue", 0);
#ifdef NOTICE
xv_destroy_safe(notice);
#endif
if (new_entry) {
if (new_entry->time_spec) free(new_entry->time_spec);
if (new_entry->record) free(new_entry->record);
free(new_entry);
}
return (0);
}
/* Fill in the log_entry structure. */
strcpy(new_entry->record, recp);
strcpy(new_entry->time_spec, timep);
if (textp) strcpy(new_entry->text, textp);
else new_entry->text = NULL;
/* Insert the new entry into the linked list. */
if (current_entry) {
if (current_entry->next) /* insert into middle of list */
(current_entry->next)->prev = new_entry;
else /* append at tail */
last_entry = new_entry;
new_entry->next = current_entry->next;
new_entry->prev = current_entry;
current_entry->next = new_entry;
}
else if (first_entry) { /* insert at head of list */
new_entry->next = first_entry;
new_entry->prev = NULL;
first_entry->prev = new_entry;
first_entry = new_entry;
}
else { /* list is empty, initialize it */
new_entry->next = new_entry->prev = NULL;
first_entry = last_entry = new_entry;
}
current_entry = new_entry;
p = strchr(timep, '-');
if (p) *p = '\0';
return (1);
}
/* delete_entry removes the current entry from the linked list, and resets the
current_entry pointer so that it points to the next entry in the linked
list if there is one, or to the previous entry otherwise. */
void delete_entry()
{
struct log_entry *p;
if (current_entry) {
if (current_entry->prev)
(current_entry->prev)->next = current_entry->next;
else /* deleting entry at head */
first_entry = current_entry->next;
if (p = current_entry->next)
(current_entry->next)->prev = current_entry->prev;
else /* deleting entry at tail */
p = last_entry = current_entry->prev;
free(current_entry->record);
free(current_entry->time_spec);
if (current_entry->text) free(current_entry->text);
free(current_entry);
current_entry = p;
}
}
/* read_log appends the contents of the log file named by its argument to
the linked list. It returns 1 if completely successful, 0 if the file
cannot be read or if not all properly formatted entries can be stored. */
int read_log(logfname)
char *logfname;
{
char buf[LLLMAX+1], *p, *recp = record, *timep, *textp = NULL, *strtok();
FILE *logfile;
int ignore;
if ((logfile = fopen(logfname, "r")) == NULL)
return (0);
while (fgets(buf, LLLMAX, logfile)) { /* read and parse an entry */
/* Check that entry is properly formatted -- if not, skip it. */
for (p = buf, ignore = 0; *p; p++) {
if (!isprint(*p) && !isspace(*p)) { ignore = 1; break; }
}
if (ignore) continue;
if (buf[0] != ' ' && buf[0] != '\t') {
recp = strtok(buf, " \t"); /* first token is record name */
timep = strtok(NULL, " \t\n");/* second token is time spec */
}
else /* record name missing, use previous value */
timep = strtok(buf, " \t"); /* first token is time spec */
if (recp == NULL || timep == NULL) continue;
/* skip improperly formatted entries */
textp = strtok(NULL, "\n"); /* remainder of line is text */
if (add_entry(recp, timep, textp) == 0) {
fclose(logfile);
return (0);
}
}
fclose(logfile);
return (1);
}
/* write_log copies the contents of the linked list to the log file named by
its argument. It returns 1 if successful, 0 otherwise. */
static int log_changes, save_log_backup;
int write_log(logfname)
char *logfname;
{
int result;
struct log_entry *p;
FILE *logfile;
/* Do we need to back up ? */
if (save_log_backup) {
char backfname[LNLMAX+2];
sprintf(backfname, "%s~", logfname);
if (rename(logfname, backfname)) {
#ifdef NOTICE
Xv_notice notice = xv_create((Frame)frame, NOTICE,
XV_SHOW, TRUE,
NOTICE_STATUS, &result,
#else
result = notice_prompt((Frame)frame, (Event *)NULL,
#endif
NOTICE_MESSAGE_STRINGS,
"Your log cannot be saved unless you remove the file named",
backfname,
"",
"You may attempt to correct this problem from",
"another window after pressing `Continue', or",
"you may exit immediately and discard your",
"changes by pressing `Exit'.", 0,
NOTICE_BUTTON_YES, "Continue",
NOTICE_BUTTON_NO, "Exit", NULL);
#ifdef NOTICE
xv_destroy_safe(notice);
#endif
if (result == NOTICE_YES) return (0);
else if (post_changes()) {
xv_destroy_safe(frame);
exit(1);
}
else return (0);
}
save_log_backup = 0;
}
if ((logfile = fopen(logfname, "w")) == NULL) {
#ifdef NOTICE
Xv_notice notice = xv_create((Frame)frame, NOTICE,
XV_SHOW, TRUE,
NOTICE_STATUS, &result,
#else
result = notice_prompt((Frame)frame, (Event *)NULL,
#endif
NOTICE_MESSAGE_STRINGS,
"Your log cannot be saved until you obtain",
"write permission for",
logfname,
"",
"You may attempt to correct this problem from",
"another window after pressing `Continue', or",
"you may exit immediately and discard your",
"changes by pressing `Exit'.", 0,
NOTICE_BUTTON_YES, "Continue",
NOTICE_BUTTON_NO, "Exit", NULL);
#ifdef NOTICE
xv_destroy_safe(notice);
#endif
if (result == NOTICE_YES) return (0);
else if (post_changes()) {
xv_destroy_safe(frame);
exit(1);
}
else return (0);
}
for (p = first_entry; p; p = p->next) {
fprintf(logfile, "%s %s", p->record, p->time_spec);
if (p->text) fprintf(logfile, " %s", p->text);
fprintf(logfile, "\n");
}
log_changes = 0;
fclose(logfile);
return (1);
}
Panel_item log_name_item, log_text_item, load_button,
add_button, replace_button, delete_button, edit_button, first_button,
rreview_button, prev_button, pause_button, next_button, review_button,
last_button;
struct itimerval timer;
Panel log_panel;
void show_current_entry()
{
char *p;
int record_changed = 0;
WFDB_Time t0;
if (current_entry) {
if (current_entry->text)
strncpy(description, current_entry->text, DSLMAX);
else
description[0] = '\0';
if (strcmp(record, current_entry->record)) {
set_record_item(current_entry->record);
record_changed = 1;
}
p = strchr(current_entry->time_spec, '-');
if (p) *p = '\0';
if ((t0 = strtim(current_entry->time_spec)) < 0L) t0 = -t0;
if (p) /* start and end times are given */
*p = '-';
else { /* only one time given -- place mark at that time */
set_marker(t0);
if ((t0 -= nsamp/2) < 0L) t0 = 0L;
}
if (!record_changed) {
set_frame_title();
(void)find_display_list(t0);
}
set_start_time(timstr(t0));
set_end_time(timstr(t0 + nsamp));
xv_set(log_text_item, PANEL_VALUE, description, NULL);
disp_proc(log_text_item, (Event *)NULL);
if (attached)
box((int)((attached->this.time - display_start_time)*tscale),
(ann_mode==1 && (unsigned)attached->this.chan < nsig) ?
(int)(base[(unsigned)attached->this.chan] + mmy(2)) : abase,
1);
}
}
Notify_value show_next_entry()
{
char *p;
WFDB_Time t0;
if (current_entry->next) current_entry = current_entry->next;
else current_entry = first_entry;
show_current_entry();
if (current_entry->next &&
strcmp(current_entry->next->record, current_entry->record) == 0) {
p = strchr(current_entry->next->time_spec, '-');
if (p) *p = '\0';
if ((t0 = strtim(current_entry->next->time_spec)) < 0L) t0 = -t0;
if (p) *p = '-'; /* t1 = strtim(p+1); */
else if ((t0 -= nsamp/2) < 0L) t0 = 0L;
(void)find_display_list(t0);
}
return (NOTIFY_DONE);
}
Notify_value show_prev_entry()
{
char *p;
WFDB_Time t0;
if (current_entry->prev) current_entry = current_entry->prev;
else current_entry = last_entry;
show_current_entry();
if (current_entry->prev &&
strcmp(current_entry->prev->record, current_entry->record) == 0) {
p = strchr(current_entry->prev->time_spec, '-');
if (p) *p = '\0';
if ((t0 = strtim(current_entry->prev->time_spec)) < 0L) t0 = -t0;
if (p) *p = '-'; /* t1 = strtim(p+1); */
else if ((t0 -= nsamp/2) < 0L) t0 = 0L;
(void)find_display_list(t0);
}
return (NOTIFY_DONE);
}
int review_delay;
int review_in_progress;
void log_review(direction)
int direction;
{
review_in_progress = direction;
timer.it_value.tv_sec = timer.it_interval.tv_sec = review_delay;
if (direction == 1)
notify_set_itimer_func(log_frame, show_next_entry, ITIMER_REAL,
&timer, NULL);
else
notify_set_itimer_func(log_frame, show_prev_entry, ITIMER_REAL,
&timer, NULL);
}
void pause_review()
{
review_in_progress = 0;
notify_set_itimer_func(log_frame,NOTIFY_FUNC_NULL,ITIMER_REAL,NULL,NULL);
}
/* Handle enabling/disabling log navigation buttons for non-review modes. */
void set_buttons()
{
xv_set(pause_button, PANEL_INACTIVE, TRUE, 0);
if (log_file_name) {
xv_set(load_button, PANEL_INACTIVE, FALSE, 0);
xv_set(add_button, PANEL_INACTIVE, FALSE, 0);
xv_set(edit_button, PANEL_INACTIVE, FALSE, 0);
}
if (current_entry) {
xv_set(replace_button, PANEL_INACTIVE, FALSE, 0);
xv_set(delete_button, PANEL_INACTIVE, FALSE, 0);
xv_set(first_button, PANEL_INACTIVE, FALSE, 0);
xv_set(last_button, PANEL_INACTIVE, FALSE, 0);
xv_set(next_button, PANEL_INACTIVE,
current_entry->next ? FALSE : TRUE, 0);
xv_set(prev_button, PANEL_INACTIVE,
current_entry->prev ? FALSE : TRUE, 0);
xv_set(review_button, PANEL_INACTIVE,
(current_entry->next || current_entry->prev) ? FALSE : TRUE, 0);
xv_set(rreview_button, PANEL_INACTIVE,
(current_entry->next || current_entry->prev) ? FALSE : TRUE, 0);
}
else {
xv_set(replace_button, PANEL_INACTIVE, TRUE, 0);
xv_set(delete_button, PANEL_INACTIVE, TRUE, 0);
xv_set(first_button, PANEL_INACTIVE, TRUE, 0);
xv_set(rreview_button, PANEL_INACTIVE, TRUE, 0);
xv_set(prev_button, PANEL_INACTIVE, TRUE, 0);
xv_set(next_button, PANEL_INACTIVE, TRUE, 0);
xv_set(review_button, PANEL_INACTIVE, TRUE, 0);
xv_set(last_button, PANEL_INACTIVE, TRUE, 0);
}
}
void disable_buttons()
{
xv_set(load_button, PANEL_INACTIVE, TRUE, 0);
xv_set(add_button, PANEL_INACTIVE, TRUE, 0);
xv_set(replace_button, PANEL_INACTIVE, TRUE, 0);
xv_set(delete_button, PANEL_INACTIVE, TRUE, 0);
xv_set(edit_button, PANEL_INACTIVE, TRUE, 0);
xv_set(first_button, PANEL_INACTIVE, TRUE, 0);
xv_set(rreview_button, PANEL_INACTIVE, TRUE, 0);
xv_set(prev_button, PANEL_INACTIVE, TRUE, 0);
xv_set(pause_button, PANEL_INACTIVE, TRUE, 0);
xv_set(next_button, PANEL_INACTIVE, TRUE, 0);
xv_set(review_button, PANEL_INACTIVE, TRUE, 0);
xv_set(last_button, PANEL_INACTIVE, TRUE, 0);
}
/* Edit the log file. */
void edit_log_file()
{
char *edit_command, *editor, *getenv();
int clen, elen, result;
if ((editor = getenv("EDITOR")) == NULL)
editor = defaults_get_string("wave.texteditor",
"Wave.TextEditor",
EDITOR);
elen = strlen(editor);
edit_command = malloc(elen + strlen(log_file_name) + 3);
if (edit_command) {
sprintf(edit_command, "%s %s\n", editor, log_file_name);
analyze_proc();
do_command(edit_command);
free(edit_command);
}
}
/* Handle selections in the log window. */
static void log_select(item, event)
Panel_item item;
Event *event;
{
int client_data = (int)xv_get(item, PANEL_CLIENT_DATA);
char timestring[25];
switch (client_data) {
case 'a': /* add an entry */
if (attached && display_start_time < attached->this.time &&
attached->this.time < display_start_time + nsamp)
strcpy(timestring, mstimstr(attached->this.time));
else {
strcpy(timestring, strtok(timstr(display_start_time), " "));
strcat(timestring, "-");
strcat(timestring, strtok(timstr(display_start_time+nsamp), " "));
}
if (add_entry(record, timestring, xv_get(log_text_item,PANEL_VALUE))) {
if (++log_changes > 10) write_log(log_file_name);
set_buttons();
}
break;
case 'd': /* delete the current entry */
delete_entry();
if (++log_changes > 10) write_log(log_file_name);
set_buttons();
show_current_entry();
break;
case 'e': /* edit the log file */
if (log_file_name) {
disable_buttons();
if (log_changes > 0) write_log(log_file_name);
edit_log_file();
/* Clear out the old entries. */
for (current_entry = first_entry; current_entry; )
delete_entry();
/* Reinitialize from the new log file. */
if (read_log(log_file_name))
save_log_backup = 1;
log_changes = 0;
current_entry = first_entry;
set_buttons();
}
break;
case 'f': /* specify log file name */
/* Do nothing unless the name has been changed. */
if (strncmp(log_file_name, (char *)xv_get(log_name_item,PANEL_VALUE),
LNLMAX)) {
/* If edits have been made, write the current log to the old file.
If the write fails, reset the file name (after write_log has
alerted the user). */
if (log_changes && write_log(log_file_name) == 0)
xv_set(log_name_item, PANEL_VALUE, log_file_name, 0);
else {
/* Clear out the old entries. */
for (current_entry = first_entry; current_entry; )
delete_entry();
strncpy(log_file_name,
(char *)xv_get(log_name_item, PANEL_VALUE), LNLMAX);
/* Reinitialize from the new log file. */
if (read_log(log_file_name))
save_log_backup = 1;
log_changes = 0;
current_entry = first_entry;
}
set_buttons();
show_current_entry();
}
break;
case 'l': /* force reload of log file */
/* If edits have been made, write the current log to the old file.
If the write fails, reset the file name (after write_log has
alerted the user). */
if (log_changes) {
char backfname[LNLMAX+2];
sprintf(backfname, "%s~", log_file_name);
save_log_backup = 0;
write_log(backfname);
}
/* Clear out the old entries. */
for (current_entry = first_entry; current_entry; )
delete_entry();
strncpy(log_file_name,
(char *)xv_get(log_name_item, PANEL_VALUE), LNLMAX);
/* Reinitialize from the new log file. */
if (read_log(log_file_name))
save_log_backup = 1;
log_changes = 0;
current_entry = first_entry;
set_buttons();
show_current_entry();
break;
case 'p': /* pause review */
pause_review();
set_buttons();
break;
case 'r': /* replace description field of current entry */
if (current_entry) {
char *newtext = (char *)xv_get(log_text_item, PANEL_VALUE);
char *newtextp;
if (strcmp(newtext, current_entry->text) &&
(newtextp = (char *)malloc(strlen(newtext)+1))) {
strcpy(newtextp, newtext);
free(current_entry->text);
current_entry->text = newtextp;
if (++log_changes > 10) write_log(log_file_name);
}
}
break;
case 'A': /* show first entry */
if (first_entry) {
current_entry = first_entry;
set_buttons();
show_current_entry();
}
break;
case '<': /* show previous entry */
if (current_entry->prev) {
current_entry = current_entry->prev;
set_buttons();
show_current_entry();
}
break;
case '>': /* show next entry */
if (current_entry->next) {
current_entry = current_entry->next;
set_buttons();
show_current_entry();
}
break;
case 'Z': /* show last entry */
if (last_entry) {
current_entry = last_entry;
set_buttons();
show_current_entry();
}
break;
case '+': /* review log entries */
case '-': /* review log entries in reverse order */
disable_buttons();
xv_set(pause_button, PANEL_INACTIVE, FALSE, 0);
log_review(client_data == '+' ? 1 : -1);
break;
}
}
static void adjust_delay(item, value)
Panel_item item;
int value;
{
review_delay = value;
if (review_in_progress) log_review(review_in_progress);
}
/* Set up log window. */
static void create_log_popup()
{
int dx;
Icon icon;
icon = xv_create(XV_NULL, ICON,
ICON_IMAGE, icon_image,
ICON_LABEL, "Log",
NULL);
log_frame = xv_create(frame, FRAME_CMD,
FRAME_LABEL, "WAVE log",
FRAME_ICON, icon, 0);
log_panel = xv_get(log_frame, FRAME_CMD_PANEL);
log_name_item = xv_create(log_panel, PANEL_TEXT,
XV_X, xv_col(log_panel, 0),
XV_Y, xv_row(log_panel, 0),
PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
PANEL_LABEL_STRING, "File: ",
XV_HELP_DATA, "wave:file.log.file",
PANEL_VALUE_DISPLAY_LENGTH, 60,
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'f',
0);
load_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Load",
XV_HELP_DATA, "wave:file.log.load",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'l',
PANEL_INACTIVE, TRUE,
0);
log_text_item = xv_create(log_panel, PANEL_TEXT,
XV_X, xv_col(log_panel, 0),
XV_Y, xv_row(log_panel, 1),
PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
PANEL_LABEL_STRING, "Description: ",
XV_HELP_DATA, "wave:file.log.description",
PANEL_VALUE_DISPLAY_LENGTH, 50,
PANEL_CLIENT_DATA, (caddr_t) '!', /* used by disp_proc, see annot.c */
0);
xv_create(log_panel, PANEL_SLIDER,
XV_HELP_DATA, "wave:file.log.review_delay",
PANEL_LABEL_STRING, "Delay:",
PANEL_DIRECTION, PANEL_HORIZONTAL,
PANEL_VALUE, 5,
PANEL_MAX_VALUE, 10,
PANEL_MIN_VALUE, 1,
PANEL_SHOW_RANGE, TRUE,
PANEL_SHOW_VALUE, FALSE,
PANEL_NOTIFY_PROC, adjust_delay,
NULL);
add_button = xv_create(log_panel, PANEL_BUTTON,
XV_X, xv_col(log_panel, 0),
XV_Y, xv_row(log_panel, 3),
PANEL_LABEL_STRING, "Add",
XV_HELP_DATA, "wave:file.log.add",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'a',
PANEL_INACTIVE, TRUE,
0);
replace_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Replace",
XV_HELP_DATA, "wave:file.log.replace",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'r',
PANEL_INACTIVE, TRUE,
0);
delete_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Delete",
XV_HELP_DATA, "wave:file.log.delete",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'd',
PANEL_INACTIVE, TRUE,
0);
edit_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Edit",
XV_HELP_DATA, "wave:file.log.edit",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'e',
PANEL_INACTIVE, TRUE,
0);
dx = xv_get(log_panel, PANEL_ITEM_X_GAP);
xv_set(log_panel, PANEL_ITEM_X_GAP, 4*dx, 0);
first_button = xv_create(log_panel, PANEL_BUTTON,
XV_X, xv_col(log_panel, 0),
XV_Y, xv_row(log_panel, 4),
PANEL_LABEL_STRING, "|<",
XV_HELP_DATA, "wave:file.log.|<",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'A',
PANEL_INACTIVE, TRUE,
0);
xv_set(log_panel, PANEL_ITEM_X_GAP, dx, 0);
rreview_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "<<",
XV_HELP_DATA, "wave:file.log.<<",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) '-',
PANEL_INACTIVE, TRUE,
0);
prev_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "<",
XV_HELP_DATA, "wave:file.log.<",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) '<',
PANEL_INACTIVE, TRUE,
0);
pause_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Pause",
XV_HELP_DATA, "wave:file.log.pause",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'p',
PANEL_INACTIVE, TRUE,
0);
next_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, ">",
XV_HELP_DATA, "wave:file.log.>",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) '>',
PANEL_INACTIVE, TRUE,
0);
review_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, ">>",
XV_HELP_DATA, "wave:file.log.>>",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) '+',
PANEL_INACTIVE, TRUE,
0);
last_button = xv_create(log_panel, PANEL_BUTTON,
PANEL_LABEL_STRING, ">|",
XV_HELP_DATA, "wave:file.log.>|",
PANEL_NOTIFY_PROC, log_select,
PANEL_CLIENT_DATA, (caddr_t) 'Z',
PANEL_INACTIVE, TRUE,
0);
window_fit(log_panel);
window_fit(log_frame);
xv_set(log_frame, FRAME_CMD_PUSHPIN_IN, TRUE, 0);
}
int log_popup_active = -1;
/* Make the log popup window appear. */
void show_log()
{
if (log_popup_active < 0) create_log_popup();
wmgr_top(log_frame);
xv_set(log_frame, WIN_MAP, TRUE, 0);
log_popup_active = 1;
}
/* Update and close the log file. */
void finish_log()
{
if (log_changes > 0) write_log(log_file_name);
}
/* Enter demonstration mode. */
void start_demo()
{
char *filename, *p, *title, *getenv();
int c, r, x, y;
Frame text_frame;
Textsw textsw;
Textsw_status status;
extern void mode_undo();
if (filename = malloc(strlen(helpdir) + strlen("wave/demo.txt") + 2)) {
if ((title = getenv("DEMOTITLE")) == NULL)
title = "Demonstration of WAVE";
if ((p = getenv("DEMOX")) == NULL)
x = 10;
else
x = atoi(p);
if ((p = getenv("DEMOY")) == NULL)
y = 700;
else
y = atoi(p);
if ((p = getenv("DEMOCOLS")) == NULL)
c = 80;
else
c = atoi(p);
if ((p = getenv("DEMOROWS")) == NULL)
r = 20;
else
r = atoi(p);
text_frame = xv_create(frame, FRAME,
XV_LABEL, title, XV_X, x, XV_Y, y,
WIN_COLUMNS, c, WIN_ROWS, r, 0);
textsw = (Textsw)xv_create(text_frame, TEXTSW, NULL);
sprintf(filename, "%s/wave/demo.txt", helpdir);
xv_set(textsw,
TEXTSW_STATUS, &status,
TEXTSW_FILE, filename,
TEXTSW_FIRST, 0,
TEXTSW_READ_ONLY, TRUE,
NULL);
if (status == TEXTSW_STATUS_OKAY)
xv_set(text_frame, WIN_MAP, TRUE, 0);
free(filename);
}
create_log_popup();
log_popup_active = 0;
xv_set(log_name_item, PANEL_VALUE, log_file_name, NULL);
show_mode();
ghflag = gvflag = visible = 1;
show_signame = 16;
mode_undo();
dismiss_mode();
if (read_log(log_file_name)) {
xv_set(pause_button, PANEL_INACTIVE, FALSE, 0);
log_review((Panel_item)NULL, (Event *)NULL);
}
}