! DOMENU A replacement for Inform's standard DoMenu
! version 6.2 built for Inform 6 by L. Ross Raszewski
! (
[email protected])
!
!
! Requires version 3.1 or greater of the Utility.h library
! To install:
! Add the following lines BEFORE the inclusion of the parser header file:
! Replace DoMenu;
! Replace LowKey_Menu;
! Include this library AFTER the inclusion of parser.h and utility.h
! New in 6.2: Fixed a bug that sometimes causes odd behavior when removing
! options from a menu. Also revised some of the comments.
! New in 6.1: It is now legal for the first option on a menu to be a
! skipped-option. The menu will behave properly.
! NOTE::: A menu consisting of ONLY separators is ILLEGAL
! So don't do it.
! new in 6.0: Once-Mode-Emulation! Domenu can now create menus in the
! style of Chris Klimas's "Once"
! See details below!
!
! Other features:
! Custom Menu Marker (v.5)
! User-defined top-bar tags (v.5)
! V6lib support (Thanks to Jason C. Penny) (v.5)
! Complete code revision (v.5)
! User-Defined MENU_MARKER to indicate current choice (v.5)
! Multi-page support (v.4)
! Multilingual (Inform 6.3+) support (v.3)
! Null-menu objects (v.3)
! dynamic menu support (v.2)
! multi-line descriptions (v.1)
! Second title bar line (v.1)
! Suppression of terminating <look> (v.1)
! Automatic centering of titles (v.1)
!
! This menu system was designed to support the Altmenu object based
! menu system. It does still, and always will support old style domenus.
!
! Differences in usage from standard Domenu:
! Supression of terminating <look>:
! to prevent the library from executing a <<look>> command upon exiting
! the menu, add ,1 to the end of the argument list:
! DoMenu(menu_choices,entryr,choicer,1);
! Null-objects:
! When EntryR is called with a menu_item of -5,
! the global skipitem will contain the number equivalent to the current
! selection. To force a skip of that selection, make EntryR return SKIP:
! (from EntryR:)
! if (menu_item==-5 && skipitem==3) return skip;
! will skip the third option.
! Multi-line descriptions:
! When EntryR() is called with menu_item=-1, return the number of lines
! in the description:
! (from EntryR:)
! if (menu_item==-1) return x;
! where x is the number of lines of description
! Title Bars:
! When EntryR() is called with menu_item=-1, set item_name to the
! title bar string:
! (from EntryR)
! if (menu_item==-1) { item_name="Second Line"; return 4;}
! (where 4 is the number of lines of description)
!
! Long menus:
! menu_choices (which must be a function for multipage menus) is now
! called with a parameter. This parameter is the number of the menu
! item to start with. Descriptive text should only be printed if
! the parameter is zero:
! [ Menu_choices doFrom;
! if (doFrom==0) print "Introductory text";
! if (doFrom<=1) print "Item 1";
! if (doFrom<=2) print "item 2";
! ...
! ]
! Inserts the word [More] at the top and bottom to
! indicate additional selections.
! Tagarray:
! to change the default messages displayed on the domenu top
! banner, pass an array containing replacement tags as the 5th argument:
! DoMenu(menu_choices,entryr,choicer,0,MY_TAGS);
! the structure of the tag array is as follows:
! -->0: top-left tag
! -->1: top-right tag
! -->2: bottom-left tag
! -->3: bottom-right tag: unnested menu
! -->4: bottom-right tag: nested menu
! -->5: right-margin offset for top-right tag
! -->6: right-margin offset for bottom-right tag
! Custom menu-marker
! Define MENU_MARKER before inclusion to a single character.
! ex: Constant MENU_MARKER '*';
! Replaces the traditional > with a *
!
! NEW FEATURE -- ONCE-MODE-EMULATION
! Domenu now uses the global variable "Menu_Mode" to determine the menu style
! A Menu_Mode of TRADITIONAL activates "Traditional" menus (the default)
! A menu_mode of ONCE activates full Once-Emulation-Mode. the menu
! will dynamically resize to fit the menu, up to the screen height-5
! lines. Banner tags are not supported, nor are menu titles.
! An integer value of Menu_Mode, greater than 1 will create a menu
! in partial Once-Mode-Emulation, which will resize to a maximum of
! menu_mode lines. Multipage is still supported in Once-Mode
!
!
!
! Comments? e-mail me!
!
! For maximum enjoyment, add on (not required)
! AltMenu.H -> An alternative to menus, object oriented menu system
! (inspired by Graham Nelson's attempt to do the same
! thing. Mine makes use of the nifty abilities of this
! library)
Iffalse UTILITY_LIBRARY>=31;
message error "DoMenu 6.2 requires version 3.1 or greater of the Utility.h library.";
endif;
ifndef DOMENU_LIBRARY;
Constant DOMENU_LIBRARY 62;
! Global Skipitem, used for specifying items to be skipped.
global Skipitem;
! SKIP The return value for skip objects.
constant SKIP = -55;
! tagarray: holds the text-tags used in the domenu banner
global tagarray;
! DM_TAG is the default tagarray
Array DM_TAG--> NKEY__TX PKEY__TX RKEY__TX QKEY1__TX QKEY2__TX 12 17;
! Language Block
Default MORE__TX "[More]"; ! Message displayed to indicate multipage
Default MENU_MARKER '>'; ! Marks the current selection
Constant TRADITIONAL 0;
Constant ONCE 1;
Global Menu_Mode=TRADITIONAL;
! Loykey version of Domenu. NOTE: skipped options do not work in lowkey
! mode.
[ LowKey_Menu menu_choices EntryR ChoiceR inflag lines main_title i j;
menu_nesting++;
.LKRD;
menu_item=0;
lines=indirect(EntryR);
main_title=item_name;
print "--- "; print (string) main_title; print " ---^";
menu_item=-1;
item_name=-1;
indirect(EntryR);
if (item_name ofclass string) print (string) item_name;
if (menu_choices ofclass Routine) menu_choices.call();
else print (string) menu_choices;
for (::)
{ L__M(##Miscellany, 52, lines);
print "> ";
#IFV3; read buffer parse;
#IFNOT; read buffer parse DrawStatusLine;
#ENDIF;
i=parse-->1;
if (i==QUIT1__WD or QUIT2__WD || parse->1==0)
{ menu_nesting--; if (menu_nesting>0) rfalse;
if (deadflag==0 && inflag==0) <<Look>>;
rfalse;
}
i=TryNumber(1);
if (i==0) jump LKRD;
if (i<1 || i>lines) continue;
menu_item=i;
j=indirect(ChoiceR);
if (j==2) jump LKRD;
if (j==3) rfalse;
}
];
#IFV3;
[ DoMenu menu_choices EntryR ChoiceR inflag;
LowKey_Menu(menu_choices,EntryR,ChoiceR,inflag);
];
#ENDIF;
! Domenu: syntax is the same as always
[ DoMenu Menu_choices EntryR ChoiceR inflag D_tagarray cl;
#ifdef V6DEFS_H;
give ActiveZWinStyle ~general;
StatusWin.HideCursor();
#Endif;
if (D_tagarray==0) D_tagarray=DM_TAG;
menu_nesting++;
cl=1;
while(cl~=-1)
{
tagarray=D_Tagarray;
cl=DM_Menu(Menu_choices,EntryR,ChoiceR,cl);
}
menu_nesting--;
if (menu_nesting==0)
{
if (Menu_Mode==traditional) @erase_window -1;
else
#ifdef V6DEFS_H;
StatusWin.Erase();
#ifnot;
@erase_window 1;
#endif;
#ifdef V6DEFS_H;
MainWin.Activate();
ActiveZWinStyle.Activate();
#ifnot;
font on; @set_cursor 1 1;
#endif;
if (deadflag==0 && inflag==0)
{
DrawStatusline();
<<Look>>;
}
}
];
! Domenu internal functions: DO NOT CALL SEPARATELY
[ DM_Menu Menu_choices EntryR ChoiceR cl menu_title
sub_title lines d_lines oldcl offset dofrom i cursor_move height;
menu_item=0;
lines=indirect(EntryR);
if (cl>lines) cl=lines;
menu_title=item_name;
item_name=NULL;
menu_item=-1;
d_lines=indirect(EntryR);
sub_title=item_name;
if (Menu_Mode==TRADITIONAL) height=d_lines+lines+5;
else height=lines;
if (Menu_Mode~=ONCE or TRADITIONAL && height>Menu_Mode) height=Menu_Mode;
if (Menu_Mode==ONCE && height>((0->32)-5)) height=(0->32)-5;
if (height>(0->32)) height=0->32;
skipitem=cl;
menu_item=-5;
while (indirect(EntryR)==SKIP)
{
cl=cl+1;
if (cl>lines) cl=1;
else if (cl<1) cl=lines;
menu_item=-5;
skipitem=cl;
}
oldcl=cl;
dofrom=DM_CheckDofrom(cl, dofrom, d_lines,height);
if (Menu_Mode==Traditional)
{
if (dofrom==0) offset=d_lines+4;
else {
dofrom=dofrom+5;
offset=5-dofrom;
}
}
else offset=1-dofrom;
DM_DrawMenu(menu_choices,menu_title,sub_title,lines,d_lines,
dofrom,height);
DM_PutCursor(offset,cl,oldcl,height);
do {
LocateCursor(0,0);
@read_char 1 0 0 i;
cursor_move=0;
if (i=='n' or 'N' or 130) cursor_move=1;
else if (i=='p' or 'P' or 129) cursor_move=-1;
else if (i=='q' or 'Q' or 27) return -1;
else if (i==10 or 13 or 132)
{
menu_item=cl;
indirect(EntryR);
if (Menu_Mode==traditional)
{
@erase_window -1;
#ifdef V6DEFS_H;
i = StatusWin.GetCharHeight();
@split_window i;
#ifnot;
@split_window 1;
#endif;
i = 0->33; if (i==0) i=80;
#ifndef V6DEFS_H;
@set_window 1;
LocateCursor(1,1);
style reverse; spaces(i);
#ifnot;
StatusWin.Activate();
LocateCursor(1,1);
StatusWin.SetColours(MainWin.GetBGColour(),
MainWin.GetFGColour());
StatusWin.Erase();
#endif;
CenterU(item_name,1);
}
#ifndef V6DEFS_H;
style roman; @set_window 0;
#ifnot;
MainWin.Activate();
#endif;
new_line;
i = indirect(ChoiceR);
if (i==3) return -1;
else if (i==2) return cl;
L__M(##Miscellany,53);
WaitForKey(" ");
return cl;
}
if (cursor_move~=0)
{
do {
cl=cl+cursor_move;
if (cl>lines) cl=1;
else if (cl<1) cl=lines;
menu_item=-5;
skipitem=cl;
} until (indirect(EntryR)~=SKIP);
i=DM_CheckDofrom(cl, dofrom, d_lines,height);
if (i~=dofrom)
{
dofrom=i;
DM_DrawMenu(menu_choices,menu_title,sub_title,lines,d_lines,
dofrom,height);
}
if (Menu_Mode==Traditional)
if (dofrom==0) offset=d_lines+4;
else offset=5-dofrom;
else offset=1-dofrom;
DM_PutCursor(offset,cl,oldcl,height);
oldcl=cl;
}
} until (false);
];
[ DM_CheckDofrom cl dofrom d_lines height offset i;
if (Menu_Mode~=Traditional && dofrom==0) dofrom=1;
if (menu_mode==traditional)
if (dofrom==0) offset=d_lines+4;
else offset=5-dofrom;
else offset=1-dofrom;
if ((offset+cl)>height)
return DM_CheckDofrom(cl,dofrom+1,d_lines,height);
if (Menu_Mode==Traditional) i=5; else i=0;
if ((offset+cl)<=i)
return DM_CheckDofrom(cl,dofrom-1,d_lines,height);
return dofrom;
];
[ DM_DrawMenu menu_choices menu_title sub_title lines d_lines dofrom
height width i j;
if (Menu_Mode==TRADITIONAL) @erase_window -1;
else
#ifdef V6DEFS_H;
StatusWin.Erase();
#ifnot;
@erase_window 1;
#endif;
#Ifdef V6DEFS_H;
i=height*StatusWin.GetCharHeight();
@split_window i;
StatusWin.Activate();
StatusWin.SetColours(MainWin.GetFGColour(),
MainWin.GetBGColour());
StatusWin.Erase();
StatusWin.SetFontStyle(ST_FIXED|ST_REVERSE);
width = StatusWin.GetXSize() / StatusWin.GetCharWidth();
#Ifnot;
@split_window height;
@set_window 1;
style reverse;
width=(0->33); if (width==0) width=80;
#Endif;
if (Menu_Mode==TRADITIONAL) j=3;
else j=height;
for (i=1:i<=j:i++)
{
LocateCursor(i,1);
spaces(width);
}
if (Menu_Mode==TRADITIONAL)
{
CenterU(menu_title,1);
LocateCursor(2,2);
print (string) tagarray-->0;
j=width-tagarray-->5;
LocateCursor(2,j);
print (string) tagarray-->1;
LocateCursor(3,2);
print (string) tagarray-->2;
j=width-tagarray-->6;
LocateCursor(3,j);
if (menu_nesting==1)
print (string) tagarray-->3;
else print (string) tagarray-->4;
if (sub_title ~= NULL)
{
style bold;
CenterU(sub_title,2);
}
style roman;
LocateCursor(5,1);
}
else LocateCursor(1,1);
if (Menu_Mode==TRADITIONAL)
{
j=height-5;
if (dofrom==0) j=j-d_lines;
}
else j=height-1;
if (menu_choices ofclass String) print (string) menu_choices;
else indirect(menu_choices,doFrom,j);
j=width-7;
if ((dofrom==1 && Menu_Mode==Traditional) || dofrom > 1)
{
if (Menu_Mode==Traditional) i=d_lines+4;
else i=1;
LocateCursor(i,j);
print (string) MORE__TX;
}
if (Menu_Mode==Traditional) i=lines+5-dofrom;
else i=lines-dofrom;
if (i>height)
{
i=height;
LocateCursor(i,j);
print (string) MORE__TX;
}
];
[ DM_PutCursor offset cl oldcl height i j;
i=offset+oldcl;
LocateCursor(i,4);
if (Menu_Mode==TRADITIONAL) j=5; else j=1;
if (i>=1 && i<=height)
print " ";
i=offset+cl;
LocateCursor(i,4);
print (char) MENU_MARKER;
];
Endif;