unit menu; { Note: Specify target pascal version - or USE_STRING if unsure } {$DEFINE USE_STRING} {*DEFINE USE_PCHAR} interface {$IFDEF USE_PCHAR} uses llist,crt,strings; {$ELSE} uses llist,crt; {$ENDIF} { Change these for a different color scheme } const selected=15+(2 shl 4); normal=10; corner=10; border=2; {$IFDEF USE_STRING} { Maximum width of a menu item - this can save memory } maxwidth=80; {$ENDIF} { TMenuItem object - based on TItem by Emil Mikulic } type PMenuItem=^TMenuItem; TMenuItem=object(TItem) {$IFDEF USE_PCHAR} caption:PChar; len:word; {$ELSE} caption:string[maxwidth]; {$ENDIF} value:integer; constructor init(x:string; v:integer; nxt:PMenuItem); procedure custom; virtual; procedure foreach; virtual; function get:integer; virtual; destructor done; end; { TMenu object - Menu handler by Emil Mikulic } type PMenu=^TMenu; TMenu=object menuitems:PMenuItem; current,number:integer; x,y,w:integer; constructor init(xx,yy,ww:integer); procedure additem(s:string; i:integer); procedure draw; function getchoice:integer; function getcurrent:integer; destructor done; end; implementation constructor TMenuItem.init(x:string; v:integer; nxt:PMenuItem); begin {$IFDEF USE_PCHAR} { Get the length } len:=length(x); { Allocate memory } getmem(caption,len+1); { Set the string } StrPCopy(caption,x); {$ELSE} caption:=x; {$ENDIF} { Set the integer } value:=v; { Initialise the TItem } inherited init(nxt); end; procedure TMenuItem.custom; begin writeln(caption); end; procedure TMenuItem.foreach; begin custom; if next<>nil then next^.foreach; end; function TMenuItem.get:integer; begin get:=value; end; destructor TMenuItem.done; begin {$IFDEF USE_PCHAR} { Free up the string } freemem(caption,len+1); {$ENDIF} { Just pass it on to TItem } inherited done; end; constructor TMenu.init(xx,yy,ww:integer); begin number:=0; current:=1; menuitems:=nil; x:=xx; y:=yy; w:=ww; end; procedure TMenu.additem(s:string; i:integer); begin if menuitems=nil then menuitems:=new(PMenuItem,init(s,i,nil)) else menuitems^.add( new(PMenuItem,init(s,i,nil)) ); number:=number+1; end; procedure TMenu.draw; var i,j:integer; tmp:PItem; begin textattr:=corner; gotoxy(x,y); write(#218); textattr:=border; for i:=1 to w do write(#196); textattr:=corner; write(#191); textattr:=border; for i:=1 to number do begin gotoxy(x,y+i); write(#179); gotoxy(x+w+1,y+i); write(#179); end; textattr:=corner; gotoxy(x,y+number+1); write(#192); textattr:=border; for i:=1 to w do write(#196); textattr:=corner; write(#217); i:=1; tmp:=menuitems; while (tmp<>nil) do begin gotoxy(x+1,y+i); if i=current then textattr:=selected else textattr:=normal; for j:=1 to w do write(' '); gotoxy(x+1,y+i); tmp^.custom; tmp:=tmp^.next; i:=i+1; end; textattr:=7; end; function TMenu.getcurrent:integer; var i:integer; tmp:PItem; begin if current=1 then getcurrent:=menuitems^.get else begin tmp:=menuitems; for i:=2 to current do tmp:=tmp^.next; getcurrent:=tmp^.get; end; end; CONST KEnter = $000D; KEsc = $001B; KLeft = $4B00; KRight = $4D00; KDown = $5000; KUp = $4800; function TMenu.getchoice:integer; var ok:Boolean; inc:char; inw:word; begin ok:=false; repeat draw; inc:=readkey; if (inc=#0) and keypressed then begin inc:=readkey; inw:=word(inc) shl 8; end else inw:=ord(inc); case inw OF KLeft, KUp : if current>1 then current:=current-1; KRight, KDown: if current