{ I saw someone could use a menu unit like the one used with RA/FD/ALLFIX.. Well... I have one... : úoO BEGIN Menus.Pas Ooú (* This unit is (c) 1995 by Archangel/DMA You can use this unit, or parts of it in your own programs as long as you mention my name somewhere (I didn't code it all for fun you know =]) It's a pretty straightforward unit, if you don't get it, try to look at the code some more and probably you'll understand. Or you can read the comments. About comments, there are not much comments since I'm not good at commenting my own sources... *) {$G+,F+,X+,V-,R-,O+} {$M 8192,0,128000} { Set up some local stack space } Unit RAMenu; Interface Type SaveRecord = Record { Record used for 'pushscreen' } Case UseDisk: Boolean of { Use disk? } TRUE : (FName: string[12]); { Yes? What file } FALSE: (MemPtr: Pointer); { No? Where? } End; SaveStackType = array[1..50] of SaveRecord; { Save stack } SubMenuRecord = record { Record used to store submenus } ItemName : string[40]; { Name of item (displayed) } ItemProc : Procedure; { Pointer to procedure to exec } ItemHelp : string[79]; { The helpline } end; SubMenuType = array[1..20] of SubMenuRecord; { One submenu } MainMenuRecord = record { Record used to store main menu items } ItemName : string[20]; ItemHelp : string[79]; SubMenu : SubMenuType; { Submenu of this mainmenu } end; MainMenuType = array[1..10] of MainMenuRecord; { The total menu structure } VRecord = record { Video memory type } VChar : char; VAttr : byte; End; VMemType = array[1..25,1..80] of VRecord; { Video memory } TabType = array[1..10] of Byte; { A table type } TabType2 = array[1..20] of Byte; { A table type } Var Menu : ^MainMenuType; XTab : TabType; SubRemP : TabType2; ColorMem : VMemType absolute $B800:0000; MonoMem : VMemType absolute $B000:0000; Mono : Boolean; VideoSegment: Word; OldExit : Pointer; CommentColor: byte; { Color of helplines } Const StackMaxMem : Byte=10; (* STACKMAXMEM This is something to consider, this constant holds the amount of screens saved in memory before the unit starts saving on disks. Since I've only built in total screen saving, 10 screens will take up 40000 bytes. If you need that memory either set it to a lower value or set it to '1'. *) Function GetAttr(X,Y: Byte): Byte; { Get attribute from a position } Procedure SetAttr(X,Y,Attr: byte); { Set attribute on a position } Procedure VWriteCh(Ch: char;x,y: byte); { Write a character to the video memory with the attribute stored in 'textattr'} Procedure VWriteStr(S: string;fg,bg,x,y: byte); { Write a string to the video memory with the colors 'fg' and 'bg' and start writing on 'x', 'y' } Procedure Color(fg,bg: byte); { Set the current colors to 'fg' and 'bg' } Procedure DrawBox(x1,y1,x2,y2,bfg,bbg,wfg,wbg: byte;Title: string;TFG,TBG: byte);{ Draw a box, variables are: x1,y1,x2,y2: Upper left and lower right corners bfg,bbg : Color of the box wfg,wbg : Color of the inside of the box Title : A title to give to the box, special chars are: '!' - Put title on upper left side '@' - Centre title on upper side '#' - Put title on upper right side Tfg,Tbg : Color of the title Example: DrawBox(1,1,80,25,11,0,7,0,'!Upper left',15,0); } Procedure ErrorMessage(Message: string); { Displays an errormessage } Procedure Message(Message: string); { Displays a normal message } Function AskBox(S,T: String;bfg,bbg,tfg,tbg: Byte): Boolean; { Ask a Yes/No question, returns TRUE on yes } Procedure CursorOff; { Turns cursor off } Procedure CursorOn; { Turns cursor on } Function TopMenu: Byte; { Start the mainmenu } Procedure SubMenu(Var Which: Byte); { Execute a submenu } Procedure SetXTab; { Used to get a table for the menu positions } Procedure PopScreen; { Save a screen to savestack} Procedure PushScreen; { Restore a screen from savestack } Procedure HelpLine(S: string); { Prints a helpline on line 25 } Procedure ClearHelp; { Clear line 25} Implementation Uses Crt,Dos; Var SaveCurHi : Byte; { High scan line of cursor } SaveCurLo : Byte; { Low scanline of cursor } RemTop : Byte; { Temp save for top menu position } SaveStack : ^SaveStackType; { Screen save stack } SaveStackPtr: Byte; { Screen save stack pointer } Procedure WaitKey; { Waits for a key } Begin ReadKey; End; Function FStr(A: Longint): string; { Turns a longint to a string } Var Temp : string; Begin Str(A,Temp); FStr :=Temp; End; Function FVal(S: string): Longint; { Turns a string into a longint } Var Temp: Longint; Code: Integer; Begin Val(S,Temp,Code); FVal :=Temp; End; Function ForceBack(s: string): string; { Adds a '\' to a string if it's not there }Begin If S[length(s)]<>'\' then S :=s+'\'; ForceBack :=s; End; Function LZ(w : Word) : String; Var S : String; begin Str(w:0,s); if Length(s)=1 then s := '0' + s; LZ := s; end; Procedure ClrScr; { Clears the screen, keeping the current unit colors } Var Bak : byte; Begin Bak :=TextAttr; TextColor(7); TextBackGround(0); Crt.ClrScr; TextAttr :=Bak; End; Function Expand(S: string;Len: Byte): String; { Expand a string } Begin Expand :=S; If Length(S)>Len then Exit; While Length(s)StackMaxMem) then With SaveStack^[SaveStackPtr] do Begin UseDisk :=TRUE; FName :=BasePath+'SAV'+FStr(SaveStackPtr)+'.TMP'; Assign(SaveStackFile,FName); {$i-} Rewrite(SaveStackFile); {$i+} If IOResult<>0 then Begin ErrorMessage('Cannot open temporary file for writing'); Halt(10); End; Case Mono of TRUE : Write(SaveStackFile,MonoMem); FALSE: Write(SaveStackFile,ColorMem); End; Close(SaveStackFile); End Else With SaveStack^[SaveStackPtr] do Begin GetMem(MemPtr,4000); Case Mono of TRUE : Move(MonoMem,MemPtr^,4000); FALSE: Move(ColorMem,MemPtr^,4000); End; End; Inc(SaveStackPtr); End; Procedure PopScreen; Var SaveStackFile : File of VMemType; Temp : VMemType; Begin If SaveStackPtr=1 then Exit; Dec(SaveStackPtr); With SaveStack^[SaveStackPtr] do Begin If UseDisk then Begin Assign(SaveStackFile,FName); {$i-} Reset(SaveStackFile); {$i+} If IOResult<>0 then Begin ErrorMessage('Cannot open temporary file for reading'); Halt(10); End; Read(SaveStackFile,Temp); Close(SaveStackFile); Case Mono of TRUE : Move(Temp,MonoMem,4000); FALSE: Move(Temp,ColorMem,4000); End; End else Begin Case Mono of TRUE : Move(MemPtr^,MonoMem,4000); FALSE: Move(MemPtr^,ColorMem,4000); End; FreeMem(MemPtr,4000); MemPtr :=NIL; End; End; End; Procedure CursorOff; assembler; ASM MOV AX,0300h MOV BH,0 INT 10h MOV [SaveCurHi],CH MOV [SaveCurLo],CL MOV AX,0100h MOV CX,2000h INT 10h END; Procedure CursorOn; assembler; ASM MOV AX,0100h MOV CH,[SaveCurHi] MOV CL,[SaveCurLo] INT 10h END; Procedure Color(fg,bg: byte); Begin TextColor(Fg); TextBackground(bg); End; Function GetAttr(X,Y: Byte): Byte; Begin Case Mono of TRUE : GetAttr :=MonoMem[Y,X].VAttr; FALSE: GetAttr :=ColorMem[Y,X].VAttr; End; End; Procedure SetAttr(X,Y,Attr: byte); Begin Case Mono of TRUE : MonoMem[Y,X].VAttr :=Attr; FALSE: ColorMem[Y,X].VAttr :=Attr; End; End; Function MakeAttr(Fg,Bg: Byte): Byte; { Creates an attribute out of a foreground/background color }Begin MakeAttr :=Fg+16*Bg; End; Procedure SetAttrRange(X1,X2,Y,Attr: Byte); { Sets the attribute over a range }Var Tel : Byte; Begin For Tel :=1 to x2-x1 do SetAttr(x1-1+Tel,Y,Attr); End; Procedure VWrite(Ch: char;x,y: byte); Begin Case Mono of TRUE : With MonoMem[Y,X] do Begin VChar :=Ch; VAttr :=TextAttr; End; FALSE: With ColorMem[Y,X] do Begin VChar :=Ch; VAttr :=TextAttr; End; End; End; Procedure VWriteCh(Ch: char;x,y: byte); Begin VWrite(ch,x,y); End; Procedure VWriteStr(S: string;fg,bg,x,y: byte); Var Tel : byte; Bak : byte; Begin Bak :=TextAttr; Color(Fg,Bg); For Tel :=1 to length(s) do VWrite(S[Tel],x-1+tel,y); TextAttr :=Bak; End; { Returns the appropriate shade color for the 'drawbox' routine } Function ReturnShade(X,Y: byte): Byte; Var TA : Byte; FG : Byte; BG : Byte; Begin TA :=GetAttr(x,y); BG :=TA SHR 4; FG :=TA-BG; If Fg>8 then Begin Dec(Fg,8); Bg :=0; End else Begin Fg :=8; Bg :=0; End; ReturnShade :=Fg+(16*Bg); End; Procedure DrawBox(x1,y1,x2,y2,bfg,bbg,wfg,wbg: byte;Title: string;TFG,TBG: byte);Var Tel,Tel2: byte; A,B: Word; Begin A :=WindMax; B :=WindMin; Color(wfg,wbg); Window(x1,y1,x2,y2); Crt.ClrScr; WindMax :=A; WindMin :=B; Color(bfg,bbg); For Tel :=1 to x2-x1 do Begin VWriteCh('Í',x1-1+tel,y1); VWriteCh('Í',x1-1+tel,y2); End; For Tel :=1 to y2-y1 do Begin Color(bfg,bbg); VWriteCh('³',x1,y1-1+tel); Color(bfg,bbg); VWriteCh('³',x2,y1-1+tel); End; VWriteCh('¾',x2,y2); VWriteCh('¸',x2,y1); VWriteCh('Ô',x1,y2); VWriteCh('Õ',x1,y1); For Tel :=1 to x2-x1 do SetAttr(x1+Tel,y2+1,ReturnShade(x1+Tel,y2+1)); For Tel :=1 to (y2-y1)+1 do SetAttr(x2+1,y1+Tel,ReturnShade(x2+1,y1+Tel)); If Title<>'' then Begin If Title[1]='!' then VWriteStr(' '+Copy(Title,2,Length(Title)-1)+'',Tfg,Tbg,x1+2,y1); If Title[1]='@' then VWriteStr(''+Copy(Title,2,Length(Title)-1)+' ',Tfg,Tbg,x2-Length(Title)-2,y1); If Title[1]='#' then VWriteStr(' '+Copy(Title,2,Length(Title)-1)+'',Tfg,Tbg,x1+((x2-x1) div 2)-(Length(Title) div 2),y1); End; End; Procedure HelpLine(S: string); Begin VWriteStr(Expand(S,79),CommentColor,0,2,25); End; Procedure ClearHelp; Begin VWriteStr(Expand(' ',79),7,0,2,25); End; Procedure Message(Message: string); Const Prompt = 'Press any key'; Var A : Byte; Begin PushScreen; ClearHelp; Message :=Message+' - '+Prompt; A :=40-(Length(Message) div 2); DrawBox(a,11,a+Length(Message)+1,15,12,4,14,4,'',15,4); VWriteStr(Message,14,4,a+1,13); WaitKey; PopScreen; End; Procedure ErrorMessage(Message: string); Const Prompt = 'Press any key'; Var A : Byte; Begin PushScreen; ClearHelp; Message :=Message+' - '+Prompt; A :=40-(Length(Message) div 2); DrawBox(a,11,a+Length(Message)+1,15,12,4,14,4,'!ERROR',15,4); VWriteStr(Message,14,4,a+1,13); WaitKey; PopScreen; End; Function AskBox(S,T: String;bfg,bbg,tfg,tbg: Byte): Boolean; Var A : Byte; Ch : Char; Begin PushScreen; A :=40-(Length(S) div 2); DrawBox(a,12,a+Length(S)+1,14,Bfg,Bbg,Tfg,Tbg,T,Tfg,TBg); VWriteStr(S,Tfg,Tbg,a+1,13); Repeat Ch :=UpCase(ReadKey); Until Ch in ['Y','N']; AskBox :=(Ch='Y'); PopScreen; End; { Used internally for the submenu's } Function GetLastX(SubRec: SubMenuType): Byte; Var Temp : Byte; Tel : Byte; Begin Temp :=0; For Tel :=1 to 20 do with SubRec[Tel] do If ItemName<>'' then If Length(ItemName)>Temp then Temp :=Length(ItemName); GetLastX :=Temp; End; { Used internally for the submenu's } Function GetLastY(SubRec: SubMenuType): Byte; Var Temp : Byte; Tel : Byte; Begin Temp :=0; For Tel :=1 to 20 do With SubRec[Tel] do If ItemName<>'' then Inc(Temp); GetLastY :=Temp; End; { Used internally for the main menu } Function TopItems: Byte; Var Tel : Byte; Temp: Byte; Begin Temp :=0; For Tel :=1 to 10 do If Menu^[Tel].ItemName<>'' then Inc(Temp); TopItems :=Temp; End; { Draws the main menu } Procedure DrawTop; Var Tel : Byte; Begin For Tel :=1 to TopItems do VWriteStr(Menu^[Tel].ItemName,7,0,XTab[Tel],1); End; { Draws a sub menu } Procedure DrawMenu(Which: Byte;Var LastX,LastY: Byte); Var Tel : Byte; Begin LastX :=GetLastX(Menu^[Which].SubMenu); LastY :=GetLastY(Menu^[Which].SubMenu); DrawBox(XTab[Which],2,XTab[Which]+LastX+1,2+LastY+1,11,0,7,0,'',0,0); For Tel :=1 to LastY do VWriteStr(Menu^[Which].SubMenu[Tel].ItemName,7,0,XTab[Which]+1,2+Tel); DrawTop; VWriteStr(Menu^[Which].ItemName,1,3,XTab[Which],1); End; Procedure SubMenu(Var Which: Byte); Var MPos : Byte; OPos : Byte; LastY : Byte; LastX : Byte; TI : Byte; OW : Byte; Begin DrawTop; PushScreen; TI :=TopItems; MPos :=SubRemP[Which]; OPos :=MPos; OW :=0; DrawMenu(Which,LastX,LastY); While True Do Begin VWriteStr(Expand(Menu^[Which].SubMenu[MPos].ItemName,LastX),1,7,XTab[Which]+1,2+MPos); HelpLine(Menu^[Which].SubMenu[MPos].ItemHelp); If MPos<>OPos then VWriteStr(Expand(menu^[Which].SubMenu[OPos].ItemName,LastX),7,0,XTab[Which]+1,2 +OPos); OPos :=MPos; OW :=Which; Case ReadKey of #0: Case ReadKey of #80: If MPos1 then Dec(Which) else Which :=TI; DrawMenu(Which,LastX,LastY); RemTop :=Which; MPos :=SubRemP[Which]; OPos :=MPos; End; #72: If MPos>1 then Dec(MPos) else MPos :=LastY; End; #13: Begin SubRemP[Which] :=MPos; PushScreen; Menu^[Which].SubMenu[MPos].ItemProc; PopScreen; End; #27: Begin SubRemP[Which] :=MPos; PopScreen; Exit; End; End; End; End; Function TopMenu: Byte; Var MPos,OPos: Byte; TI : Byte; Begin DrawTop; PushScreen; MPos :=RemTop; OPos :=MPos; TI :=TopItems; While True do Begin VWriteStr(Menu^[MPos].ItemName,1,3,XTab[MPos],1); HelpLine(Menu^[MPos].ItemHelp); If OPos<>MPos then VWriteStr(Menu^[OPos].ItemName,7,0,XTab[OPos],1); OPos :=MPos; Case ReadKey of #0: Case ReadKey of #77: If MPos1 then Dec(MPos) else MPos :=TI; #80: Begin TopMenu :=MPos; RemTop :=MPos; Exit; End; End; #13: Begin TopMenu :=MPos; RemTop :=MPos; Exit; End; End; End; End; Procedure ExitProcedure; Var Tel : byte; T : file; Begin Color(7,0); Crt.ClrScr; CursorOn; End; Var Tel : byte; Begin Case LastMode of 7: VideoSegment :=$B000; 3: VideoSegment :=$B800; else VideoSegment :=$B800; End; OldExit :=ExitProc; ExitProc :=@ExitProcedure; CursorOff; SaveStackPtr :=1; XTab[1] :=2; FillChar(SubRemP,SizeOf(SubRemP),1); RemTop :=1; New(Menu); New(SaveStack); FillChar(SaveStack^,SizeOf(SaveStack^),0); FillChar(Menu^,SizeOf(Menu^),0); CommentColor :=7; End.