mysticbbs/mdl/m_menubox.pas

624 lines
15 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Unit m_MenuBox;
{$I M_OPS.PAS}
Interface
Uses
m_Types,
m_Input,
m_Output;
Type
TMenuBox = Class
Console : TOutput;
Image : TConsoleImageRec;
HideImage : ^TConsoleImageRec;
FrameType : Byte;
BoxAttr : Byte;
Box3D : Boolean;
BoxAttr2 : Byte;
BoxAttr3 : Byte;
BoxAttr4 : Byte;
Shadow : Boolean;
ShadowAttr : Byte;
HeadAttr : Byte;
HeadType : Byte;
Header : String;
WasOpened : Boolean;
Constructor Create (Var Screen: TOutput);
Destructor Destroy; Override;
Procedure Open (X1, Y1, X2, Y2: Byte);
Procedure Close;
Procedure Hide;
Procedure Show;
End;
TMenuListStatusProc = Procedure (Num: Word; Str: String);
TMenuListSearchProc = Procedure (Var Owner: Pointer; Str: String);
TMenuListBoxRec = Record
Name : String;
Tagged : Byte; { 0 = false, 1 = true, 2 = never }
End;
TMenuList = Class
InKey : TInput;
List : Array[1..10000] of ^TMenuListBoxRec;
Box : TMenuBox;
HiAttr : Byte;
LoAttr : Byte;
PosBar : Boolean;
Format : Byte;
LoChars : String;
HiChars : String;
ExitCode : Char;
Picked : Integer;
TopPage : Integer;
NoWindow : Boolean;
ListMax : Integer;
AllowTag : Boolean;
TagChar : Char;
TagKey : Char;
TagPos : Byte;
TagAttr : Byte;
Marked : Word;
StatusProc : TMenuListStatusProc;
Width : Integer;
WinSize : Integer;
X1 : Byte;
Y1 : Byte;
NoInput : Boolean;
LastBarPos : Byte;
SearchProc : TMenuListSearchProc;
SearchX : Byte;
SearchY : Byte;
SearchA : Byte;
Constructor Create (Var S: TOutput);
Destructor Destroy; Override;
Procedure Open (BX1, BY1, BX2, BY2: Byte);
Procedure Close;
Procedure Add (Str: String; B: Byte);
Procedure Get (Num: Word; Var Str: String; Var B: Boolean);
Procedure SetStatusProc (P: TMenuListStatusProc);
Procedure SetSearchProc (P: TMenuListSearchProc);
Procedure Clear;
Procedure Delete (RecPos : Word);
Procedure UpdatePercent;
Procedure UpdateBar (X, Y: Byte; RecPos: Word; IsHi: Boolean);
Procedure Update;
End;
Implementation
Uses
m_Strings;
Constructor TMenuBox.Create (Var Screen: TOutput);
Begin
Inherited Create;
Console := Screen;
Shadow := True;
ShadowAttr := 0;
Header := '';
FrameType := 6;
Box3D := True;
BoxAttr := 15 + 7 * 16;
BoxAttr2 := 8 + 7 * 16;
BoxAttr3 := 15 + 7 * 16;
BoxAttr4 := 8 + 7 * 16;
HeadAttr := 15 + 1 * 16;
HeadType := 0;
HideImage := NIL;
WasOpened := False;
FillChar(Image, SizeOf(TConsoleImageRec), 0);
Console.BufFlush;
End;
Destructor TMenuBox.Destroy;
Begin
Inherited Destroy;
End;
Procedure TMenuBox.Open (X1, Y1, X2, Y2: Byte);
Const
BF : Array[1..8] of String[8] =
('<27>Ŀ<EFBFBD><C4BF><EFBFBD><EFBFBD><EFBFBD>',
'<27>ͻ<EFBFBD><CDBB><EFBFBD>ͼ',
'<27>ķ<EFBFBD><C4B7><EFBFBD>Ľ',
'<27>͸<EFBFBD><CDB8><EFBFBD>;',
'<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>',
'<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>',
' ',
'.-.||`-''');
Var
A : Integer;
B : Integer;
Ch : Char;
Begin
If Not WasOpened Then
If Shadow Then
Console.GetScreenImage(X1, Y1, X2 + 2{3}, Y2 + 1, Image)
Else
Console.GetScreenImage(X1, Y1, X2, Y2, Image);
WasOpened := True;
B := X2 - X1 - 1;
If Not Box3D Then Begin
BoxAttr2 := BoxAttr;
BoxAttr3 := BoxAttr;
BoxAttr4 := BoxAttr;
End;
Console.WriteXY (X1, Y1, BoxAttr, BF[FrameType][1] + strRep(BF[FrameType][2], B));
Console.WriteXY (X2, Y1, BoxAttr4, BF[FrameType][3]);
For A := Y1 + 1 To Y2 - 1 Do Begin
Console.WriteXY (X1, A, BoxAttr, BF[FrameType][4] + strRep(' ', B));
Console.WriteXY (X2, A, BoxAttr2, BF[FrameType][5]);
End;
Console.WriteXY (X1, Y2, BoxAttr3, BF[FrameType][6]);
Console.WriteXY (X1+1, Y2, BoxAttr2, strRep(BF[FrameType][7], B) + BF[FrameType][8]);
If Header <> '' Then
Case HeadType of
0 : Console.WriteXY (X1 + 1 + (B - Length(Header)) DIV 2, Y1, HeadAttr, Header);
1 : Console.WriteXY (X1 + 1, Y1, HeadAttr, Header);
2 : Console.WriteXY (X2 - Length(Header), Y1, HeadAttr, Header);
End;
If Shadow Then Begin
For A := Y1 + 1 to Y2 + 1 Do
For B := X2 to X2 + 1 Do Begin
Ch := Console.ReadCharXY(B, A);
Console.WriteXY (B + 1, A, ShadowAttr, Ch);
End;
A := Y2 + 1;
For B := (X1 + 2) To (X2 + 2) Do Begin
Ch := Console.ReadCharXY(B, A);
Console.WriteXY (B, A, ShadowAttr, Ch);
End;
End;
End;
Procedure TMenuBox.Close;
Begin
If WasOpened Then Console.PutScreenImage(Image);
End;
Procedure TMenuBox.Hide;
Begin
If Assigned(HideImage) Then FreeMem(HideImage, SizeOf(TConsoleImageRec));
GetMem (HideImage, SizeOf(TConsoleImageRec));
Console.GetScreenImage (Image.X1, Image.Y1, Image.X2, Image.Y2, HideImage^);
Console.PutScreenImage (Image);
End;
Procedure TMenuBox.Show;
Begin
If Assigned (HideImage) Then Begin
Console.PutScreenImage(HideImage^);
FreeMem (HideImage, SizeOf(TConsoleImageRec));
HideImage := NIL;
End;
End;
Constructor TMenuList.Create (Var S: TOutput);
Begin
Inherited Create;
Box := TMenuBox.Create(S);
InKey := TInput.Create;
ListMax := 0;
HiAttr := 15 + 1 * 16;
LoAttr := 1 + 7 * 16;
PosBar := True;
Format := 0;
LoChars := #13#27;
HiChars := '';
NoWindow := False;
AllowTag := False;
TagChar := '*';
TagKey := #09;
TagPos := 0;
TagAttr := 15 + 7 * 16;
Marked := 0;
Picked := 1;
NoInput := False;
LastBarPos := 0;
StatusProc := NIL;
SearchProc := NIL;
// SearchProc := DefListBoxSearch;
SearchX := 0;
SearchY := 0;
SearchA := 0;
TopPage := 1;
End;
Procedure TMenuList.Clear;
Var
Count : Word;
Begin
For Count := 1 to ListMax Do
Dispose(List[Count]);
ListMax := 0;
Marked := 0;
End;
Procedure TMenuList.Delete (RecPos : Word);
Var
Count : Word;
Begin
If List[RecPos] <> NIL Then Begin
Dispose (List[RecPos]);
For Count := RecPos To ListMax - 1 Do
List[Count] := List[Count + 1];
Dec (ListMax);
End;
End;
Destructor TMenuList.Destroy;
Begin
Box.Free;
InKey.Free;
Clear;
Inherited Destroy;
End;
Procedure TMenuList.UpdateBar (X, Y: Byte; RecPos: Word; IsHi: Boolean);
Var
Str : String;
Attr : Byte;
Begin
If IsHi Then
Attr := HiAttr
Else
Attr := LoAttr;
If RecPos <= ListMax Then Begin
Str := ' ' + List[RecPos]^.Name + ' ';
Case Format of
0 : Str := strPadR(Str, Width, ' ');
1 : Str := strPadL(Str, Width, ' ');
2 : Str := strPadC(Str, Width, ' ');
End;
End Else
Str := strRep(' ', Width);
Box.Console.WriteXY (X, Y, Attr, Str);
If AllowTag Then
If (RecPos <= ListMax) and (List[RecPos]^.Tagged = 1) Then
Box.Console.WriteXY (TagPos, Y, TagAttr, TagChar)
Else
Box.Console.WriteXY (TagPos, Y, TagAttr, ' ');
End;
Procedure TMenuList.UpdatePercent;
Var
NewPos : LongInt;
Begin
If Not PosBar Then Exit;
If (ListMax > 0) and (WinSize > 0) Then Begin
NewPos := (Picked * WinSize) DIV ListMax;
If Picked >= ListMax Then NewPos := Pred(WinSize);
If (NewPos < 0) or (Picked = 1) Then NewPos := 0;
NewPos := Y1 + 1 + NewPos;
If LastBarPos <> NewPos Then Begin
If LastBarPos > 0 Then
Box.Console.WriteXY (X1 + Width + 1, LastBarPos, Box.BoxAttr2, #176);
LastBarPos := NewPos;
Box.Console.WriteXY (X1 + Width + 1, NewPos, Box.BoxAttr2, #178);
End;
End;
End;
Procedure TMenuList.Update;
Var
Loop : LongInt;
CurRec : Integer;
Begin
For Loop := 0 to WinSize - 1 Do Begin
CurRec := TopPage + Loop;
UpdateBar (X1 + 1, Y1 + 1 + Loop, CurRec, CurRec = Picked);
End;
UpdatePercent;
End;
Procedure TMenuList.Open (BX1, BY1, BX2, BY2 : Byte);
Procedure DownArrow;
Begin
If Picked < ListMax Then Begin
If Picked >= TopPage + WinSize - 1 Then Begin
Inc (TopPage);
Inc (Picked);
Update;
End Else Begin
UpdateBar (X1 + 1, Y1 + Picked - TopPage + 1, Picked, False);
Inc (Picked);
UpdateBar (X1 + 1, Y1 + Picked - TopPage + 1, Picked, True);
UpdatePercent;
End;
End;
End;
Var
Ch : Char;
Count : Word;
StartPos : Word;
EndPos : Word;
First : Boolean;
SavedRec : Word;
SavedTop : Word;
SearchStr : String;
LastWasChar : Boolean;
Begin
If Not NoWindow Then
Box.Open (BX1, BY1, BX2, BY2);
If SearchX = 0 Then SearchX := BX1 + 2;
If SearchY = 0 Then SearchY := BY2;
If SearchA = 0 Then SearchA := Box.BoxAttr4;
X1 := BX1;
Y1 := BY1;
If (Picked < TopPage) or (Picked < 1) or (Picked > ListMax) or (TopPage < 1) or (TopPage > ListMax) Then Begin
Picked := 1;
TopPage := 1;
End;
Width := BX2 - X1 - 1;
WinSize := BY2 - Y1 - 1;
TagPos := X1 + 1;
While Picked > TopPage + WinSize - 1 Do
Inc (TopPage);
If PosBar Then
For Count := 1 to WinSize Do
Box.Console.WriteXY (X1 + Width + 1, Y1 + Count, Box.BoxAttr2, #176);
If NoInput Then Exit;
Update;
LastWasChar := False;
SearchStr := '';
Repeat
If Not LastWasChar Then Begin
If Assigned(SearchProc) And (SearchStr <> '') Then
SearchProc (Self, '');
SearchStr := ''
End Else
LastWasChar := False;
If Assigned(StatusProc) Then
If ListMax > 0 Then
StatusProc(Picked, List[Picked]^.Name)
Else
StatusProc(Picked, '');
Ch := InKey.ReadKey;
Case Ch of
#00 : Begin
Ch := InKey.ReadKey;
Case Ch of
#71 : If Picked > 1 Then Begin { home }
Picked := 1;
TopPage := 1;
Update;
End;
#72 : If (Picked > 1) Then Begin
If Picked <= TopPage Then Begin
Dec (Picked);
Dec (TopPage);
Update;
End Else Begin
UpdateBar (X1 + 1, Y1 + Picked - TopPage + 1, Picked, False);
Dec (Picked);
UpdateBar (X1 + 1, Y1 + Picked - TopPage + 1, Picked, True);
UpdatePercent;
End;
End;
#73,
#75 : If (TopPage > 1) or (Picked > 1) Then Begin
If Picked - WinSize > 1 Then Dec (Picked, WinSize) Else Picked := 1;
If TopPage - WinSize < 1 Then TopPage := 1 Else Dec(TopPage, WinSize);
Update;
End;
#79 : If Picked < ListMax Then Begin
If ListMax > WinSize Then TopPage := ListMax - WinSize + 1;
Picked := ListMax;
Update;
End;
#80 : DownArrow;
#77,
#81 : If (Picked <> ListMax) Then Begin
If ListMax > WinSize Then Begin
If Picked + WinSize > ListMax Then
Picked := ListMax
Else
Inc (Picked, WinSize);
Inc (TopPage, WinSize);
If TopPage + WinSize > ListMax Then TopPage := ListMax - WinSize + 1;
End Else Begin
Picked := ListMax;
End;
Update;
End;
Else
If Pos(Ch, HiChars) > 0 Then Begin
ExitCode := Ch;
Exit;
End;
End;
End;
Else
If AllowTag and (Ch = TagKey) and (List[Picked]^.Tagged <> 2) Then Begin
If (List[Picked]^.Tagged = 1) Then Begin
Dec (List[Picked]^.Tagged);
Dec (Marked);
End Else Begin
List[Picked]^.Tagged := 1;
Inc (Marked);
End;
DownArrow;
End Else
If Pos(Ch, LoChars) > 0 Then Begin
ExitCode := Ch;
Exit;
End Else Begin
If Ch <> #01 Then Begin
If Ch = #25 Then Begin
LastWasChar := False;
Continue;
End;
If Ch = #8 Then Begin
If Length(SearchStr) > 0 Then
Dec(SearchStr[0])
Else
Continue;
End Else
If Ord(Ch) < 32 Then
Continue
Else
SearchStr := SearchStr + UpCase(Ch);
End;
SavedTop := TopPage;
SavedRec := Picked;
LastWasChar := True;
First := True;
StartPos := Picked + 1;
EndPos := ListMax;
If Assigned(SearchProc) Then
SearchProc(Self, SearchStr);
If StartPos > ListMax Then StartPos := 1;
Count := StartPos;
While (Count <= EndPos) Do Begin
If Pos(strUpper(SearchStr), strUpper(List[Count]^.Name)) > 0 Then Begin
While Count <> Picked Do Begin
If Picked < Count Then Begin
If Picked < ListMax Then Inc (Picked);
If Picked > TopPage + WinSize - 1 Then Inc (TopPage);
End Else
If Picked > Count Then Begin
If Picked > 1 Then Dec (Picked);
If Picked < TopPage Then Dec (TopPage);
End;
End;
Break;
End;
If (Count = ListMax) and First Then Begin
Count := 0;
StartPos := 1;
EndPos := Picked - 1;
First := False;
End;
Inc (Count);
End;
If TopPage <> SavedTop Then
Update
Else
If Picked <> SavedRec Then Begin
UpdateBar (X1 + 1, Y1 + SavedRec - SavedTop + 1, SavedRec, False);
UpdateBar (X1 + 1, Y1 + Picked - TopPage + 1, Picked, True);
UpdatePercent;
End;
End;
End;
Until False;
End;
Procedure TMenuList.Close;
Begin
If Not NoWindow Then Box.Close;
End;
Procedure TMenuList.Add (Str : String; B : Byte);
Begin
Inc (ListMax);
New (List[ListMax]);
List[ListMax]^.Name := Str;
List[ListMax]^.Tagged := B;
If B = 1 Then Inc(Marked);
End;
Procedure TMenuList.Get (Num : Word; Var Str : String; Var B : Boolean);
Begin
Str := '';
B := False;
If Num <= ListMax Then Begin
Str := List[Num]^.Name;
B := List[Num]^.Tagged = 1;
End;
End;
Procedure TMenuList.SetSearchProc (P: TMenuListSearchProc);
Begin
SearchProc := P;
End;
Procedure TMenuList.SetStatusProc (P: TMenuListStatusProc);
Begin
StatusProc := P;
End;
End.