! DOMENU A replacement for Inform's standard DoMenu ! version 6.2 built for Inform 6 by L. Ross Raszewski ! (rraszews@skipjack.bluecrab.org) ! ! ! 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 (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 : ! to prevent the library from executing a <> 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) <>; 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(); <>; } } ]; ! 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;