home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_300
/
315_01
/
menu.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-16
|
22KB
|
715 lines
/* mgetch() and mgets() were added 9/89 by T Clune. */
/* clear_input_devices() made externally-visible 9/89 by T Clune */
/* print_headline() added 8/89, because I always use this construct anyway. */
/* added by T Clune. */
/* menu_mouse_config() added 8/89 by T. Clune to support user-configuration */
/* of mouse parameters, as per Frans VdV request. */
/* get_file() was added to menu.c [it was formerly in grabmain.c] and */
/* dir_menu() was eliminated 12/88, and get_dir() in dos_func.c was */
/* rewritten to use string pointers instead of character arrays. Now */
/* get_dir() is compatible with menu(), so dir_menu() is superfluous. */
/* Modified by T Clune. */
/* Modified by T Clune 12/88 to support mouse selection of menu items. */
/* menu.obj must be linked with mouselib.obj and sound.obj and the */
/* Microsoft library mouse.lib now. Sound.obj is used to generate the */
/* pause between mouse queries to give a pause between menu */
/* selections, analogous to the pause in keyboard repeats. Mouselib.obj */
/* is my mouse library front end. */
/* pathprint() added to menu.c 11/20/88 by T. Clune. Moved from */
/* tmbr_gp.c for conceptual unity. */
/* c_dir fname_unused() and get_dir() functions moved to dos_func.c */
/* 11/88 by T. Clune. */
/* c_dir.c functions added to menu.c 6/88 by T. Clune. */
/* the dir_menu() function supports a menu() -style selection of */
/* directory files. */
/* written by T. Clune for R. Webb, 5/18/88. */
/* Copyright (c) 1988, Eye Research Institute, 20 Staniford St., Boston, MA */
/* All rights reserved. */
/* menu.c is a menu-creation utility for use with large memory model */
/* Microsoft C routines. It expects to run on an IBM PC or At with */
/* either monchrome (including Hercules in text mode) or CGA adaptor */
/* menu() and reset_menu() are externally visible, highlight() is an */
/* internal routine called by menu() only. Written by Thomas Clune */
/* for the Eye Research Institute, 20 Staniford St., Boston, MA 02114 */
/* Copyright 1987, E.R.I. All rights reserved. */
#include "msc_hdrs.h" /* the usual gang of MS C headers */
#include "menu.h"
#include "ansi.h"
#include "mouselib.h"
#include "keys.h"
#include "sound.h"
#include "dos_func.h"
#define HERC_BASE 0XB0000000 /* base address of mono card */
#define CGA_BASE 0XB8000000 /* base address of color card */
static unsigned char off = 7; /* unlit character attribute code */
static unsigned char on = 112; /* lit character attribute code */
static int mouse_flag=KEYBOARD_ONLY; /* flag for whether mouse-based selection is */
/* active. Set to NO for compatibility with old version of menu.c */
/* See mouse_flag_toggle() for mouse_flag values */
static double off_time=OFF_TIME;
static double duty_time=DUTY_TIME;
static int mouse_sensitivity=MOUSE_SENSITIVITY;
static char *screen_ptr; /* base of screen memory to be named later */
static char *mode_ptr = (char *)0X00000410; /* mode memory address */
static void highlight(); /* local function for turning reverse */
/* video on and off */
static int get_response(); /* the operator-selection function, added 12/88 */
/* to support mouse input option as well as keyboard input */
static int mouse_read(); /* mouse support function for get_response() */
/* +++++++++++++++++ clear_input_devices() +++++++++++++++++++++++++ */
/* clear_input_devices() makes sure that no keypresses are pending */
/* or mouse buttons depressed before the menu selection is active */
void clear_input_devices()
{
int delay=0;
/* clear the keyboard bufffer */
if((mouse_flag != MOUSE_ONLY) && kbhit())
while(kbhit())
getch();
/* if the mouse is active, make sure the buttons are not depressed on entry */
if((mouse_flag !=KEYBOARD_ONLY) && (button_read().status != 0))
{
do
{
pause(0.1);
delay++;
}while((button_read().status != 0) && (delay<20));
/* after 2 secs, tell 'em to get their finger off the damn button */
if(delay>=20)
{
while(button_read().status !=0)
mouse_warning_sound();
}
}
}
/* +++++++++++++++++++++ get_file() +++++++++++++++++++++++++++++++++ */
/* get a filename by selecting from highlighting menu. Val returned is 0 */
/* unless there are too many files for this routine to process, in which case */
/* the f.error_flag is -1, or unless QUIT is the menu item selected, in which */
/* case f.error_flag is set to 1. The memory freed in get_file() was allocated */
/* in get_dir() (ind dos_func.c) except for the QUIT string area, which was */
/* allocated here. */
string_struc get_file()
{
int i,j,k;
int length;
static char *fnames[MAX_FILES+1];
static string_struc f;
static char dir_name[40]="";
int default_flag=0;
static char spec[14]="*.*";
char search_name[54];
static int last_choice=0;
char c;
f.error_flag=0;
CLS;
if(!strlen(dir_name))
{
strcpy(search_name, "Default directory");
default_flag=1;
}
else
strcpy(search_name, dir_name);
printf("Current search path is %s\n",search_name);
printf("Enter new path, or <CR> to keep current path\n");
if(mouse_flag != KEYBOARD_ONLY)
mouse_gets(dir_name);
else
gets(dir_name);
if((!strlen(dir_name)) && (default_flag == 0))
strcpy(dir_name, search_name);
printf("Enter file format for search, or <CR> to keep default %s\n", spec);
if(mouse_flag != KEYBOARD_ONLY)
mouse_gets(f.string);
else
gets(f.string);
if(strlen(f.string))
strcpy(spec, f.string);
strcpy(search_name, dir_name);
length=strlen(search_name);
if(length && (!(search_name[length-1]=='\\')))
strcat(search_name, "\\");
strcat(search_name, spec);
i=get_dir(search_name,fnames,MAX_FILES);
if(i>MAX_FILES)
{
printf("Too many files for array size\n");
printf("Maximum number of file entries supported=%d\n", MAX_FILES);
printf("Try using a more restrictive file format for the search\n");
printf("Press any key to return to menu\n");
if(mouse_flag != KEYBOARD_ONLY)
inpause();
else
getch();
f.error_flag=-1;
i=MAX_FILES-1; /* the number of buffers to free() */
}
else
{
k=MAX_FILES/20; /* 20 lines for display */
fnames[i]=malloc(5);
if(fnames[i]==NULL)
{
printf("Error allocating menu buffer memory. Program aborting.\n");
exit(-1);
}
strcpy(fnames[i], "Quit"); /* add an ABORT option to menu */
CLS;
printf("Number of matching files: %d\n",i);
/* 80/k=number of spaces per field width */
reset_menu(last_choice);
j=menu(2,0,80/k,k,1,i+1,0,fnames);
last_choice=j;
CLS;
if(j != i) /* if not QUIT */
{
strcpy(f.string, dir_name);
if((strlen(f.string))&&(!(f.string[strlen(f.string)-1]=='\\')))
strcat(f.string, "\\");
strcat(f.string, fnames[j]);
}
else
f.error_flag=1;
}
for(j=0;j<=i;j++)
free(fnames[j]);
return(f);
}
/* ++++++++++++++++ get_mouse_flag() +++++++++++++++++++++++++++ */
/* get_mouse_flag() returns the current status of the mouse flag. */
/* it is useful in making other i/o operations conform to the menu */
/* style, i.e., if the mouse is active for the menu, you may want */
/* other input options to support mouse input, etc. Any non-zero */
/* value means that the mouse is active. Any value >=0 means that */
/* the keyboard is active. */
int get_mouse_flag()
{
return mouse_flag;
}
/* +++++++++++++++++++ menu() +++++++++++++++++++++++++++++++++++++++++ */
/* */
/* top_mar = top margin spacing (0<=t_m<=24) */
/* left_mar = left margin spacing (0<=l_m<=79) */
/* tab = total spaces per column */
/* columns = number of columns in menu */
/* line_feed = number of spaces between lines in menu */
/* entries = number of items in menu array */
/* label = no auto numbering of menu items if 0, */
/* auto number but no number-key select if = 1 */
/* auto number and number-key select if > 1 */
/* MAX. ENTRIES FOR AUTO NUMBERING = 10 */
/* auto capital lettering but no select if = -1 */
/* auto lettering with keyboard select if < -1 */
/* MAX. ENTRIES FOR AUTO LETTERING = 26 */
/* str_ptr = pointer to menu entries array */
/* */
/* menu() returns the subscript of the menu item selected */
/* i.e., first item = 0, last item = ENTRIES - 1. */
/* */
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
int menu(top_mar,left_mar,tab,columns,line_feed,entries,label,str_ptr)
int top_mar, left_mar, tab, columns, line_feed, entries, label;
char *str_ptr[];
{
static int index = 0; /* menu array element currently active */
int x, y, i, j, disp_num; /* x,y screen coords, i,j menu array elements */
/* disp_num menu auto number */
unsigned char inchar; /* input key selection variable */
if(columns==0) /* reset index flag from reset_menu() */
{
index=line_feed;
return;
}
if(index>=entries) /* make sure index is not larger than the array */
index=0;
outp(0X3B4, 10); /* turn off cursor */
outp(0X3B5, 32);
i = *mode_ptr; /* is the system monitor monochrome? */
if((i & 48) == 48) /* if so, set mono screen base address */
screen_ptr = (char *)HERC_BASE;
else /* if not, set color screen base address */
screen_ptr = (char *)CGA_BASE;
for(i=0;i<entries;i++) /* print menu */
{
j = 0; /* first element in menu[i] entry */
x = left_mar + (i % columns) * tab; /* x-location for print */
y = top_mar + (i/columns)*line_feed; /* screen y for print */
if(label > 0) /* use numbers if appropriate */
{
disp_num = i + '1'; /* "ASCII-ize" the number */
if(disp_num > '9') /* >=10 prints as 0 */
disp_num = '0';
}
if(label < 0) /* use capital letters if appropriate */
{
disp_num = 'A' + i;
if(disp_num > 'Z')
disp_num = 'Z';
}
if(label != 0) /* for auto numbers or letters */
{
*(screen_ptr + 160*y + 2*x) = disp_num;
if(index == i) /* highlight first number */
*(screen_ptr + 160*y + 2*x + 1) = on;
else /* but not the others */
*(screen_ptr + 160*y + 2*x + 1) = off;
x += 1; /* go to next screen memory location */
*(screen_ptr + 160*y + 2*x) = 32; /* print space after # */
if(index == i) /* highlight first space */
*(screen_ptr + 160*y + 2*x + 1) = on;
else /* but not the others */
*(screen_ptr + 160*y + 2*x + 1) = off;
x += 1; /* go to next screen memory location */
}
while(str_ptr[i][j]!='\0') /* print the string to the end */
{
*(screen_ptr + 160*y + 2*x) = str_ptr[i][j];
if(index == i) /* highlight as appropriate */
*(screen_ptr + 160*y + 2*x + 1) = on;
else
*(screen_ptr + 160*y + 2*x + 1) = off;
x += 1; /* next screen memory location */
j += 1; /* next menu[i] character */
}
*(screen_ptr + 160*y + 2*x) = str_ptr[i][j]; /* use '\0' */
/* to mark end of menu entry on screen */
}
clear_input_devices();
while((inchar=get_response())!=CARRIAGE_RETURN) /* move highlight around selections */
{
inchar = toupper(inchar); /* caps only for alpha entries */
highlight(index,top_mar,left_mar,tab,columns,line_feed,off);
if(inchar==UP_MOVE) /* going up? */
if((index-columns)>=0)
index -= columns; /* increment selection */
if(inchar==RIGHT_MOVE) /* right? */
if(((index%columns)<(columns-1)) && ((index+1)<entries))
index++;
if(inchar==DOWN_MOVE) /* going down? */
if((index+columns)<entries)
index += columns;
if(inchar==LEFT_MOVE) /* going left? */
if((index%columns)>0)
index--;
highlight(index,top_mar,left_mar,tab,columns,line_feed,on);
if(label>1) /* are number keys active for selection? */
{
if(entries != 10)
j='1';
else
j='0';
if(inchar>=j && inchar <=(j+entries-1))
/* if num-key selection */
{
highlight(index,top_mar,left_mar,tab,columns,line_feed,off);
index=inchar-'1'; /* "Numberize" ASCII and */
/* subtract 1 for array starting at 0 */
if(index<0) /* menu 0 = array 9 */
index = 9;
highlight(index,top_mar,left_mar,tab,columns,line_feed,on);
break;
}
}
if(label<-1) /* are letter keys active? */
{
if(inchar>='A' && inchar<=('A'+entries-1))
/* if letter key selected */
{
highlight(index,top_mar,left_mar,tab,columns,line_feed,off);
index = inchar-'A';
highlight(index,top_mar,left_mar,tab,columns,line_feed,on);
break;
}
}
}
outp(0X3B4, 10); /* turn cursor back on */
outp(0X3B5, 11);
return index; /* return selected number */
}
/* +++++++++++++++++++ menu_mouse_config() ++++++++++++++++++++++++++++++ */
/* menu_mouse_config() sets the number of mickeys needed for a mouse movement */
/* to register as significant, and the duty cycle for evaluating the mouse */
/* motion. duty_on is the time in second when the mouse is accumulating */
/* data, and duty_off is the time when the data is ignored. The need for */
/* an "off" time is to give a positive feel to making a selection. */
void menu_mouse_config(sensitivity,duty_on,duty_off)
int sensitivity;
double duty_on, duty_off;
{
mouse_sensitivity=sensitivity;
duty_time=duty_on;
off_time=duty_off;
}
/* +++++++++++++++++++++++++ mgetch() +++++++++++++++++++++++++++++++++ */
/* mgetch() is the generic mouse-and-keyboard or just-keyboard getch() */
/* function. It decides whether to use the mouse on the basis of the status */
/* of mouse_flag. inpause() is in mouselib.c */
int mgetch()
{
int ret_val;
if(mouse_flag)
ret_val=inpause();
else
ret_val=getch();
return ret_val;
}
/* +++++++++++++++++++++ mgets() ++++++++++++++++++++++++++++++++++++ */
/* mgets() is the generic mouse-plus keuyboard or just-keyboard gets() */
/* function. The decision on whether to use the mouse is based on the */
/* status of mouse_flag. mouse_gets() is in mouselib.c */
char * mgets(strptr)
char *strptr;
{
clear_input_devices();
if(mouse_flag)
strptr=mouse_gets(strptr);
else
strptr=gets(strptr);
return strptr;
}
/* +++++++++++++++++++++++++ mouse_flag_toggle() +++++++++++++++++++++++++++++++ */
/* mouse_flag_toggle() sets the variable mouse_flag on or off to enable and disable */
/* mouse-based selection of menu items in menu() and dir_menu(). Added 12/88 */
/* by T Clune. if VAL=0, mouse is disabled. If VAL>0, mouse and keyboard */
/* are enabled. IF mouse<0, mouse only is enabled. */
void mouse_flag_toggle(val)
int val;
{
if(val>0)
mouse_flag=MOUSE_PLUS_KEYBOARD;
if(val==0)
mouse_flag=KEYBOARD_ONLY;
if(val<0)
mouse_flag=MOUSE_ONLY;
}
/* ++++++++++++++++++++++++++++ pathprint() ++++++++++++++++++++++++++++ */
/* pathprint() prints your current position in the menu structure on the */
/* bottom of the screen. Pathprint() must be called at each menu in the */
/* heirarchy in order to have the correct information. MENU_NAME is the */
/* current menu's pathprint name. All pathprint() does is determine whether */
/* that name is new to the list or not. If yes, it is added to the path, */
/* if no, the path list is truncated at that name. */
void pathprint(menu_name)
char menu_name[];
{
static char path_name[80]="";
static char divider[]="->";
char *strptr;
strptr=strstr(path_name, menu_name);
/* if the name is not currently on the printed path */
if(strptr==NULL)
{
if(strlen(path_name)) /* if not the first name, add name separator */
strcat(path_name, divider);
strcat(path_name, menu_name); /* add the name */
}
/* if the name is already on the path */
else
{
*strptr='\0'; /* delete everything from the name on in the path */
strcat(path_name, menu_name); /* and re-append the name at the end */
}
POSITION(1,24);
LINE_CLEAR;
POSITION(1,24);
printf("You are here: %s\n", path_name);
}
/* +++++++++++++++++++++++ print_headline() +++++++++++++++++++++++++++++ */
/* print_headline() writes an underlined string starting at x, y (1<=x<=80, */
/* 1<=y<=25). It is intended for use printing the title of a menu, which is */
/* passed as STRING. Added by T. Clune, 8/89. */
void print_headline(string, x, y)
char string[];
int x, y;
{
/* POSITION and CHAR_ATTRIBUTE are ANSI.H macros */
POSITION(x,y); /* center heading */
CHAR_ATTRIBUTE(UNDERSCORE);
printf("%s\n",string);
CHAR_ATTRIBUTE(NORMAL);
}
/* ++++++++++++++++ reset_menu() ++++++++++++++++++++++++++++++ */
/* reset_menu() resets the static variable that "remembers" which */
/* selection was last made on the current menu. It should be called */
/* whenever leaving a menu level for a new menu level that may call */
/* menu(), or an error condition can result if the next menu has */
/* fewer entries than the previous menu. SETTING may be set to the */
/* value that the menu being returned to last had or to 0 as a reset */
/* without "memory." */
void reset_menu(setting)
int setting;
{
char *dummy;
menu(0,0,0,0,setting,0,0,dummy); /* reset menu static variable to setting */
}
/* ++++++++++++++++++++++++++++++ get_extended_key() ++++++++++++++++++++++ */
/* get_extended_key() gets the ALT:KEY arrow codes and maps them onto single */
/* character values (The arrow keys are mapped onto four low-usage ASCII */
/* vals: CTL-A, CTL-B, CTL-D, and CTL-E.) */
static int get_extended_key()
{
int inchar;
inchar=getch(); /* get the second key */
switch(inchar)
{
case UPARROW: /* UP=CNTL-A */
inchar=UP_MOVE;
break;
case LEFTARROW: /* LEFT=CNTL-B */
inchar=LEFT_MOVE;
break;
case RIGHTARROW: /* RIGHT=CNTL-D */
inchar=RIGHT_MOVE;
break;
case DOWNARROW: /* DOWN=CNTL-E */
inchar=DOWN_MOVE;
break;
default: /* if it was anything else, "erase" it */
inchar=0;
}
return inchar;
}
/* ++++++++++++++++++++++++ get_response() +++++++++++++++++++++++++++ */
/* get_response() is the menu() and dir_menu() operator selection function */
/* that returns the character selected by keyboard or the mouse selection */
/* if mouse_flag is not zero */
static int get_response()
{
int ret_val= -1;
while(ret_val == -1)
{
if((mouse_flag != MOUSE_ONLY) && kbhit())
{
ret_val=getch();
if(ret_val==ALT)
ret_val=get_extended_key();
/* clear the keyboard buffer */
while(kbhit())
getch();
}
else
if(mouse_flag != KEYBOARD_ONLY)
ret_val=mouse_read();
}
return ret_val;
}
/* +++++++++++++++++++ highlight() +++++++++++++++++++++++++++ */
static void highlight(index,top_mar,left_mar,tab,columns,line_feed,value)
int index,top_mar,left_mar,tab,columns,line_feed;
unsigned char value;
{
/* Highlight selected entry and un-highlight deselected entry */
/* only menu attributes are modified */
int x, y;
x = left_mar + (index % columns) * tab;
y = top_mar + (index/columns)*line_feed;
while(*(screen_ptr + 160*y + 2*x) != '\0')
{
*(screen_ptr + 160*y + 2*x + 1) = value;
x += 1;
}
}
/* ++++++++++++++++++++++++++ mouse_read() +++++++++++++++++++++++++++++ */
/* mouse_read() reads the current condition of the mouse, and returns -1 */
/* if there is "nothing to report", the value is the keyboard equivalent */
/* to the mouse reading significance otherwise. */
static int mouse_read()
{
int ret_val= -1;
mstruc m;
if(button_read().status == RIGHT_BUTTON)
ret_val=CARRIAGE_RETURN;
else
{
/* clear mouse position counter, pause to let user feel positive lock */
/* on selected position, like keyboard repeat delay */
pause(off_time);
mdpos_read();
pause(duty_time);
m=mdpos_read();
m.dx /=mouse_sensitivity;
m.dy /=mouse_sensitivity;
/* the mouse will only respond left/right or up/down on a given read */
if(m.dx || m.dy)
{
if(abs(m.dx) >= abs(m.dy))
{
if(m.dx>0)
ret_val=RIGHT_MOVE;
else
ret_val=LEFT_MOVE;
}
else
{
if(m.dy>0)
ret_val=DOWN_MOVE;
else
ret_val=UP_MOVE;
}
}
}
return ret_val;
}