diff --git a/mdl/m_menuhelp.pas b/mdl/m_menuhelp.pas
new file mode 100644
index 0000000..5f5acc2
--- /dev/null
+++ b/mdl/m_menuhelp.pas
@@ -0,0 +1,448 @@
+Unit m_MenuHelp;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ m_Input,
+ m_Output;
+
+Const
+ mMaxHelpTest = 200;
+ mMaxHelpKeyLen = 20;
+ mMaxHelpLineLinks = 10;
+
+Type
+ TLinkInfoRec = Record
+ Key : String[mMaxHelpKeyLen];
+ LinkPos : Byte;
+ LinkLen : Byte;
+ End;
+
+ TLineInfoRec = Record
+ Text : String;
+ Links : Byte;
+ Link : Array[1..mMaxHelpLineLinks] of TLinkInfoRec
+ End;
+
+ TMenuHelp = Class
+ HelpFile : Text;
+ CurKey : String[mMaxHelpKeyLen];
+ Text : Array[1..mMaxHelpTest] of TLineInfoRec;
+ Lines : Word;
+ Console : TOutput;
+ Input : TInput;
+ WinX1 : Byte;
+ WinY1 : Byte;
+ WinX2 : Byte;
+ WinY2 : Byte;
+
+ Constructor Create (Var C: TOutput; Var I: TInput);
+ Destructor Destroy; Override;
+
+ Function ReadKeywordData : Boolean;
+ Procedure OpenHelp (Str: String);
+ Function StripLinks (Str: String) : String;
+ End;
+
+Implementation
+
+Uses
+ m_Strings;
+
+Constructor TMenuHelp.Create (Var C: TOutput; Var I: TInput);
+Begin
+ Inherited Create;
+
+ Console := C;
+ Input := I;
+ WinX1 := 1;
+ WinY1 := 11;
+ WinX2 := 79;
+ WinY2 := 23;
+End;
+
+Destructor TMenuHelp.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Function TMenuHelp.StripLinks (Str: String) : String;
+Var
+ A : Byte;
+ B : Byte;
+Begin
+ A := 255;
+
+ While A > 0 Do Begin
+ A := Pos(' 0 Then Begin
+ B := 1;
+ While Str[A + 6 + B] <> '>' Do Inc(B);
+ Delete (Str, A, 7 + B);
+
+ A := Pos('', Str);
+ If A = 0 Then A := Length(Str);
+ Delete (Str, A, 7);
+ End;
+ End;
+
+ Result := Str;
+End;
+
+Function TMenuHelp.ReadKeywordData : Boolean;
+Var
+ Str : String;
+ Key : String;
+ Temp1 : Byte;
+ Temp2 : Byte;
+ Done : Boolean;
+ Buffer : Array[1..4 * 1024] of Char;
+Begin
+ SetTextBuf (HelpFile, Buffer);
+ Reset (HelpFile);
+
+ Done := False;
+
+ While Not Eof(HelpFile) And Not Done Do Begin
+ ReadLn (HelpFile, Str);
+
+ Temp1 := Pos(' ', Str);
+
+ If Temp1 = 0 Then Continue;
+
+ Key := Copy(Str, Temp1 + 10, Length(Str));
+
+ If Key <> CurKey Then Continue;
+
+ Lines := 0;
+
+ While Not Eof(HelpFile) Do Begin
+ ReadLn (HelpFile, Str);
+
+ If Pos('', Str) > 0 Then Begin
+ Done := True;
+ Break;
+ End;
+
+ Inc (Lines);
+
+ Text[Lines].Text := StripLinks(Str);
+ Text[Lines].Links := 0;
+ Str := strStripPipe(Str);
+
+ Repeat
+ Temp1 := Pos(' '>' Do Begin
+ Key := Key + Str[Temp1 + 6 + Temp2];
+ Inc (Temp2);
+ End;
+
+ Delete (Str, Temp1, 7 + Temp2);
+ Temp2 := Pos('', Str);
+ Delete (Str, Temp2, 7);
+
+ Text[Lines].Link[Text[Lines].Links].LinkLen := Temp2 - Temp1;
+ Text[Lines].Link[Text[Lines].Links].Key := Key;
+ Until False;
+ End;
+ End;
+
+ Close (HelpFile);
+
+ Result := Done And (Lines > 0);
+End;
+
+Procedure TMenuHelp.OpenHelp (Str: String);
+Var
+ FN : String;
+ Keyword : String;
+ TopPage : Integer;
+ CurLine : Integer;
+ CurLPos : Byte = 1;
+ WinSize : Integer;
+ LastPos : Byte;
+ LastKey : Array[1..10] of String[mMaxHelpKeyLen];
+
+ Procedure LinkOFF (LineNum: Word; YPos, LPos: Byte);
+ Var
+ S : String;
+ Begin
+ If Text[LineNum].Links = 0 Then Exit;
+
+ With Text[LineNum] Do
+ S := Copy(strStripPipe(Text), Link[LPos].LinkPos, Link[LPos].LinkLen);
+
+ Console.WriteXY (WinX1 + Text[LineNum].Link[LPos].LinkPos - 1, YPos, 9, S);
+ End;
+
+ Procedure DrawPage;
+ Var
+ Count1 : Byte;
+ Count2 : Byte;
+ Begin
+ For Count1 := 1 to WinSize Do Begin
+ If TopPage + Count1 - 1 <= Lines Then Begin
+ Console.WriteXYPipe (WinX1, Count1 + WinY1 - 1, 7, WinX2 - WinX1 + 1, Text[TopPage + Count1 - 1].Text);
+
+ For Count2 := 1 to Text[TopPage + Count1 - 1].Links Do
+ LinkOFF (TopPage + Count1 - 1, Count1 + WinY1 - 1, Count2);
+ End Else
+ Console.WriteXYPipe (WinX1, Count1 + WinY1 - 1, 7, WinX2 - WinX1 + 1, '');
+ End;
+ End;
+
+ Procedure LinkON;
+ Var
+ S : String;
+ Begin
+ With Text[TopPage + CurLine - 1] Do
+ S := Copy(strStripPipe(Text), Link[CurLPos].LinkPos, Link[CurLPos].LinkLen);
+
+ Console.WriteXY (WinX1 + Text[TopPage + CurLine - 1].Link[CurLPos].LinkPos - 1, WinY1 + CurLine - 1, 30, S);
+ Console.CursorXY (WinX1 + Text[TopPage + CurLine - 1].Link[CurLPos].LinkPos - 1, WinY1 + CurLine - 1);
+ End;
+
+ Procedure UpdateCursor;
+ Begin
+ If Text[TopPage + CurLine - 1].Links > 0 Then Begin
+ If CurLPos > Text[TopPage + CurLine - 1].Links Then CurLPos := Text[TopPage + CurLine - 1].Links;
+ If CurLPos < 1 Then CurLPos := 1;
+
+ LinkON;
+ End Else Begin
+ CurLPos := 1;
+
+ Console.CursorXY (WinX1, WinY1 + CurLine - 1);
+ End;
+ End;
+
+ Procedure PageDown;
+ Begin
+ If Lines > WinSize Then Begin
+ If TopPage + WinSize <= Lines - WinSize Then Begin
+ Inc (TopPage, WinSize);
+ End Else Begin
+ TopPage := Lines - WinSize + 1;
+ CurLine := WinSize;
+ End;
+ End Else
+ CurLine := Lines;
+ End;
+
+Var
+ OK : Boolean;
+ Count : Byte;
+ Ch : Char;
+Begin
+ FillChar(LastKey, SizeOf(LastKey), 0);
+
+ FN := strWordGet(1, Str, ';');
+ Keyword := strWordGet(2, Str, ';');
+
+ Assign (HelpFile, FN + '.hlp');
+ {$I-} Reset (HelpFile); {$I+}
+
+ If IoResult <> 0 Then Exit;
+
+ Close (HelpFile);
+
+ TopPage := 1;
+ CurLine := 1;
+ LastPos := 0;
+ WinSize := WinY2 - WinY1 + 1;
+ CurKey := Keyword;
+ OK := ReadKeywordData;
+
+ If Not OK and (CurKey <> 'INDEX') Then Begin
+ CurKey := 'INDEX';
+ OK := ReadKeywordData;
+ End;
+
+ If Not OK Then Exit;
+
+ While OK Do Begin
+ TopPage := 1;
+ CurLine := 1;
+
+ DrawPage;
+
+ For Count := 1 to WinSize Do
+ If Text[Count].Links > 0 Then Begin
+ CurLine := Count;
+ Break;
+ End;
+
+ UpdateCursor;
+
+ Repeat
+ Ch := Input.ReadKey;
+
+ Case Ch of
+ #00 : Case Input.ReadKey of
+ #71 : If (TopPage > 1) or (CurLine > 1) Then Begin
+ TopPage := 1;
+ CurLine := 1;
+
+ DrawPage;
+ UpdateCursor;
+ End;
+ #72 : Begin
+ If (CurLine = 1) and (TopPage > 1) Then Begin
+ Dec (TopPage);
+
+ DrawPage;
+
+ UpdateCursor;
+ End Else If CurLine > 1 Then Begin
+ LinkOFF(TopPage + CurLine - 1, WinY1 + CurLine - 1, CurLPos);
+
+ Dec (CurLine);
+
+ UpdateCursor;
+ End;
+ End;
+ #73 : Begin
+ If TopPage - WinSize > 0 Then Begin
+ Dec (TopPage, WinSize);
+
+ DrawPage;
+ UpdateCursor;
+ End Else If CurLine > 1 Then Begin
+ TopPage := 1;
+ CurLine := 1;
+
+ DrawPage;
+ UpdateCursor;
+ End;
+ End;
+ #75 : If (CurLPos > 1) and (Text[TopPage + CurLine - 1].Links > 0) Then Begin
+ LinkOFF(TopPage + CurLine - 1, WinY1 + CurLine - 1, CurLPos);
+
+ Dec(CurLPos);
+
+ LinkON;
+ End;
+ #77 : If CurLPos < Text[TopPage + CurLine - 1].Links Then Begin
+ LinkOFF(TopPage + CurLine - 1, WinY1 + CurLine - 1, CurLPos);
+
+ Inc(CurLPos);
+
+ LinkON;
+ End;
+ #79 : If TopPage + WinSize <= Lines Then Begin
+ Repeat
+ PageDown;
+ Until TopPage >= Lines - WinSize - 1;
+
+ DrawPage;
+ UpdateCursor;
+ End Else
+ If TopPage + CurLine <= Lines Then Begin
+ LinkOFF (TopPage + CurLine - 1, WinY1 + CurLine - 1, CurLPos);
+
+ CurLine := Lines - TopPage + 1;
+
+ UpdateCursor;
+ End;
+ #80 : Begin
+ If (CurLine = WinSize) and (TopPage + WinSize <= Lines) Then Begin
+ Inc(TopPage);
+
+ DrawPage;
+
+ UpdateCursor;
+ End Else
+ If (CurLine < WinSize) And (TopPage + CurLine <= Lines) Then Begin
+ LinkOFF(TopPage + CurLine - 1, WinY1 + CurLine - 1, CurLPos);
+
+ Inc(CurLine);
+
+ UpdateCursor;
+ End;
+ End;
+ #81 : If TopPage + WinSize <= Lines Then Begin
+ PageDown;
+
+ DrawPage;
+
+ UpdateCursor;
+ End Else
+ If TopPage + CurLine <= Lines Then Begin
+ LinkOFF (TopPage + CurLine - 1, WinY1 + CurLine - 1, CurLPos);
+
+ CurLine := Lines - TopPage + 1;
+
+ UpdateCursor;
+ End;
+ End;
+ #08 : Begin
+ If LastPos = 0 Then
+ CurKey := Keyword
+ Else Begin
+ CurKey := LastKey[LastPos];
+
+ Dec (LastPos);
+ End;
+
+ OK := ReadKeywordData;
+
+ If Not OK Then Begin
+ CurKey := 'INDEX';
+ OK := ReadKeywordData;
+ End;
+
+ Break;
+ End;
+ #13 : If Text[TopPage + CurLine - 1].Links > 0 Then Begin
+ If Text[TopPage + CurLine - 1].Link[CurLPos].Key = '@PREV' Then Begin
+ If LastPos = 0 Then
+ CurKey := Keyword
+ Else Begin
+ CurKey := LastKey[LastPos];
+
+ Dec (LastPos);
+ End;
+ End Else Begin
+ If LastPos < 10 Then
+ Inc (LastPos)
+ Else
+ For Count := 1 to 9 Do LastKey[Count] := LastKey[Count + 1];
+
+ LastKey[LastPos] := CurKey;
+
+ CurKey := Text[TopPage + CurLine - 1].Link[CurLPos].Key;
+ End;
+
+ OK := ReadKeywordData;
+
+ If Not OK Then Begin
+ CurKey := 'INDEX';
+ OK := ReadKeywordData;
+ End;
+
+ Break;
+ End;
+ #27 : Begin
+ OK := False;
+
+ Break;
+ End;
+ End;
+ Until False;
+ End;
+End;
+
+End.