Contributor: MIKE PERRY
{
GG> Could somebody post a message with the Pascal 6.0 source for some
GG> sort of a scrolling menu system? I do NOT want TurboVision. I
GG> HATE OOP. I don't mind records and arrays, but i don't want OOP.
GG> I've done some programming for one myself....
}
UNIT MPMENU;
{
Written and designed by Michael Perry, (c) 1990 Progressive Computer Serv.
A basic, flexible, user-definable menu system using only the most basic
functions in Turbo Pascal. This unit is easily integratable into your
applications and gives you more versatility than most "pull down"-type
menu interfaces.
License: This unit should NOT be modified and redistributed in source
or object/TPU form. You can modify and use this in any non-
commercial program free-of-charge provided that "Mike Perry"
if credited either in the program or documentation. Use of
these routines in a commercially-sold package requires a
one-time registration fee of $30 to be sent to:
Progressive Computer Services
P.O. Box 7638
Metairie, LA 70010
Non-commercial users are also invited to register the code.
This insures that updates and future revisions are made
available and users are kept informed via mail.
Usage: Implementing menus using the MPMENU unit involves just a
few basic steps. At any point in your program, add code
to perform the following actions:
1. Define the menu by assigning values to the MENU_DATA
record.
2. Call the procedure MENU(MENU_DATA,RETURNCODE);
3. Implement a routine to evaluate the value of
RETURNCODE and act accordingly. The values of
RETURNCODE are as follows:
0 = ESC pressed (menu aborted)
1-x = The appropriate option was selected, with 1
being the first menu choice, 2 the second,
etc.
Example: Here is a sample main menu using the MENU procedure:
-----------------------------------------------------------------------------
Program DontDoMuch;
Uses Crt,MPMenu;
CONST HELL_FREEZES_OVER=FALSE;
VAR CHOICE:BYTE;
Begin
REPEAT
With Menu_Data Do Begin
Menu_Choices[1]:='1 - First Option '; - define menu choice onscreen
Row[1]:=10; Column[1]:=30; - where on screen displayed
Menu_Choices[2]:='2 - Second Option'; - same thing for 2nd choice
Row[2]:=12; Column[2]:=30; .
Menu_Choices[3]:='X - Exit Program '; .
Row[3]:=14; Column[3]:=30; .
Onekey:=TRUE; - enable 1-key execution
Num_Choices:=3; - number of menu choices
HiLighted:=112; - highlighted attribute
Normal:=7; - normal attribute
End;
MENU(MENU_DATA,CHOICE); - call the menu now and wait for user
Case Choice Of - evaluate user response and act
0:Halt; - ESC pressed
3:Halt; - option 3, Exit, selected
1:Begin
- put code here to do menu option 1
End;
2:Begin
- put code here to do menu option 2
End;
End
UNTIL HELL_FREEZES_OVER; - infinite loop - back to main menu
End.
-----------------------------------------------------------------------------
}
INTERFACE
USES Crt;
CONST
MAX_CHOICES = 10; { MAX_CHOICES can be changed
depending upon the highest
number of options you will
have on any given menu }
TYPE
MENU_ARRAY = RECORD { record structure for menu }
MENU_CHOICES : ARRAY[1..MAX_CHOICES] OF STRING[50]; { displayed option }
COLUMN : ARRAY[1..MAX_CHOICES] OF BYTE; { column location }
ROW : ARRAY[1..MAX_CHOICES] OF BYTE; { row location }
NUM_CHOICES : BYTE; { # choices on menu }
HILIGHTED : WORD; { attribute for hilight }
NORMAL : WORD; { attributed for normal }
ONEKEY : BOOLEAN; { TRUE for 1-key execution
}
END;
VAR
MENU_DATA : MENU_ARRAY; { global menu variable }
{
NOTE: You can define many menu variables simultaneously, but since you
can generally use only one menu at a time, you can conserve
memory and program space by re-defining this one MENU_DATA record
each time a menu is to be displayed.
}
{ internal procedures }
PROCEDURE SHOW_MENU(MENU_DATA:MENU_ARRAY);
PROCEDURE HILIGHT_CHOICE(MENU_DATA:MENU_ARRAY;CHOICENUM:BYTE);
PROCEDURE UNHILIGHT_CHOICE(MENU_DATA:MENU_ARRAY;CHOICENUM:BYTE);
FUNCTION GETKEY(VAR FUNCTIONKEY:BOOLEAN):CHAR;
FUNCTION FOUND_CHOICE(MENU_DATA:MENU_ARRAY;VAR EXITCODE:BYTE;CH:CHAR):BOOLEAN;
{ basically, the ONE callable procedure }
PROCEDURE MENU(MENU_DATA:MENU_ARRAY;VAR EXITCODE:BYTE);
IMPLEMENTATION
(*===========================================================================*)
PROCEDURE SHOW_MENU(MENU_DATA:MENU_ARRAY);
{ display defined menu array }
VAR I:BYTE;
BEGIN
TEXTATTR:=MENU_DATA.NORMAL;
FOR I:=0 TO (MENU_DATA.NUM_CHOICES-1) DO BEGIN
GOTOXY(MENU_DATA.COLUMN[I+1],MENU_DATA.ROW[I+1]);
WRITE(MENU_DATA.MENU_CHOICES[I+1]);
END;
END;
(*===========================================================================*)
PROCEDURE HILIGHT_CHOICE(MENU_DATA:MENU_ARRAY;CHOICENUM:BYTE);
{ highlight the appropriate menu choice }
BEGIN
GOTOXY(MENU_DATA.COLUMN[CHOICENUM],MENU_DATA.ROW[CHOICENUM]);
TEXTATTR:=MENU_DATA.HILIGHTED;
WRITE(MENU_DATA.MENU_CHOICES[CHOICENUM]);
{ below needed if direct screen writing not done }
GOTOXY(MENU_DATA.COLUMN[CHOICENUM],MENU_DATA.ROW[CHOICENUM]);
END;
(*===========================================================================*)
PROCEDURE UNHILIGHT_CHOICE(MENU_DATA:MENU_ARRAY;CHOICENUM:BYTE);
{ highlight the appropriate menu choice }
BEGIN
GOTOXY(MENU_DATA.COLUMN[CHOICENUM],MENU_DATA.ROW[CHOICENUM]);
TEXTATTR:=MENU_DATA.NORMAL;
WRITE(MENU_DATA.MENU_CHOICES[CHOICENUM]);
END;
(*===========================================================================*)
FUNCTION GETKEY(VAR FUNCTIONKEY:BOOLEAN):CHAR;
{ read keyboard and return character/function key }
VAR CH: CHAR;
BEGIN
CH:=ReadKey;
IF (CH=#0) THEN
BEGIN
CH:=ReadKey;
FUNCTIONKEY:=TRUE;
END
ELSE FUNCTIONKEY:=FALSE;
GETKEY:=CH;
END;
(*===========================================================================*)
FUNCTION FOUND_CHOICE(MENU_DATA:MENU_ARRAY;VAR EXITCODE:BYTE;CH:CHAR):BOOLEAN;
{ locate next occurance of menu choice starting with char CH }
VAR I:BYTE; TEMP:STRING;
BEGIN
CH:=UPCASE(CH);
IF EXITCODE=MENU_DATA.NUM_CHOICES THEN BEGIN
TEMP:=MENU_DATA.MENU_CHOICES[1];
IF UPCASE(TEMP[1])=CH THEN BEGIN
UNHILIGHT_CHOICE(MENU_DATA,EXITCODE);
EXITCODE:=1;
HILIGHT_CHOICE(MENU_DATA,EXITCODE);
FOUND_CHOICE:=TRUE;
EXIT;
END;
END;
FOR I:=EXITCODE+1 TO MENU_DATA.NUM_CHOICES DO BEGIN
TEMP:=MENU_DATA.MENU_CHOICES[I];
IF UPCASE(TEMP[1])=CH THEN BEGIN
UNHILIGHT_CHOICE(MENU_DATA,EXITCODE);
EXITCODE:=I;
HILIGHT_CHOICE(MENU_DATA,EXITCODE);
FOUND_CHOICE:=TRUE;
EXIT;
END;
END;
IF EXITCODE<>1 THEN BEGIN { KILLER RECURSION }
UNHILIGHT_CHOICE(MENU_DATA,EXITCODE);
EXITCODE:=1;
IF FOUND_CHOICE(MENU_DATA,EXITCODE,CH) THEN BEGIN
HILIGHT_CHOICE(MENU_DATA,EXITCODE);
FOUND_CHOICE:=TRUE;
EXIT;
END ELSE HILIGHT_CHOICE(MENU_DATA,EXITCODE);
END ELSE BEGIN
TEMP:=MENU_DATA.MENU_CHOICES[1];
IF UPCASE(TEMP[1])=CH THEN BEGIN
FOUND_CHOICE:=TRUE;
EXIT;
END;
END;
FOUND_CHOICE:=FALSE;
END;
(*===========================================================================*)
PROCEDURE MENU(MENU_DATA:MENU_ARRAY;VAR EXITCODE:BYTE);
{ display menu and return user's response:
0 = ESC pressed
1-x = appropriate choice selected
during operation, variable EXITCODE holds number of currently-selected
menu choice.
}
VAR
FNC:BOOLEAN; TEMPATTR:WORD;
CH:CHAR;
BEGIN
TEMPATTR:=TEXTATTR;
IF (EXITCODE=0) OR (EXITCODE>MENU_DATA.NUM_CHOICES) THEN
EXITCODE:=1;
SHOW_MENU(MENU_DATA);
HILIGHT_CHOICE(MENU_DATA,EXITCODE);
REPEAT
CH:=GETKEY(FNC);
IF FNC THEN BEGIN
IF CH=#77 THEN CH:=#80 ELSE
IF CH=#75 THEN CH:=#72;
CASE CH OF
#72:IF EXITCODE>1 THEN BEGIN { UP }
UNHILIGHT_CHOICE(MENU_DATA,EXITCODE);
EXITCODE:=EXITCODE-1;
END;
#80:IF EXITCODE1 THEN BEGIN { HOME }
UNHILIGHT_CHOICE(MENU_DATA,EXITCODE);
EXITCODE:=1;
END;
#79:IF EXITCODE