Subversion Repositories oidplus

Compare Revisions

Regard whitespace Rev 732 → Rev 733

/trunk_dos/VTSCUI.PAS
0,0 → 1,542
unit VTSCUI;
 
(************************************************)
(* VTSCUI.PAS *)
(* Author: Daniel Marschall *)
(* Revision: 2022-02-12 *)
(* License: Apache 2.0 *)
(* This file contains: *)
(* - ViaThinkSoft CUI (Console User Interface) *)
(************************************************)
 
interface
 
uses
StrList;
 
const
ScreenWidth = 80;
ScreenHeight = 25;
SINGLE_LINE_BOX_PADDING = 3;
SINGLE_LINE_BOX_PADDING_INNER = 10;
 
procedure DrawThinBorder(x, y, width, height: integer);
procedure DrawDoubleBorder(x, y, width, height: integer);
procedure DrawTextBar(str: string; line: integer);
procedure DrawTitleBar(str: string);
procedure DrawStatusBar(str: string);
function DrawSelectionList(X, Y, ListWidth, ListHeight: integer;
items: PStringList; allowESC: boolean;
Title: string; borderStrength: integer): integer;
procedure ClearSection(x, y, width, height: integer);
function QueryVal(var s: string; initX, initY, width, height: integer;
Title: string; borderStrength: integer): boolean;
procedure ShowMessage(msg: string; title: string; dobeep: boolean);
 
implementation
 
uses
Crt, Drivers, VtsFuncs;
 
type
TCharDefs = array[0..7] of char;
 
const
ThinLineChars: TCharDefs =
( #$DA, #$C4, #$BF,
#$B3, #$B3,
#$C0, #$C4, #$D9
);
DoubleLineChars: TCharDefs =
( #$C9, #$CD, #$BB,
#$BA, #$BA,
#$C8, #$CD, #$BC
);
 
function FillRight(str: string; len: integer; c: char): string;
var
s: string;
i: integer;
begin
s := str;
for i := Length(str) to len-1 do
begin
s := s + c;
end;
FillRight := s;
end;
 
procedure DrawBorder(x, y, width, height, thickness: integer);
var
ix,iy: integer;
chars: TCharDefs;
begin
if thickness = 1 then
chars := ThinLineChars;
if thickness = 2 then
chars := DoubleLineChars;
 
(* Top line *)
if y >= 1 then
begin
(* Top left corner *)
if x >= 1 then
begin
GotoXY(x,y);
Write(chars[0]);
end
else
begin
GotoXY(1,y);
end;
 
(* Top edge *)
for ix := 1 to width-2 do
Write(chars[1]);
 
(* Top right corner *)
if x+width-1 <= ScreenWidth then
Write(chars[2]);
end;
 
(* Left edge *)
for iy := 1 to height-2 do
begin
if (x >= 1) and (x <= ScreenWidth) and
(y+iy >= 1) and (y+iy <= ScreenHeight) then
begin
GotoXY(x,y+iy);
Write(chars[3]);
end;
end;
 
(* Right edge *)
for iy := 1 to height-2 do
begin
if (x+width-1 >= 1) and (x+width-1 <= ScreenWidth) and
(y+iy >= 1) and (y+iy <= ScreenHeight) then
begin
GotoXY(x+width-1,y+iy);
Write(chars[4]);
end;
end;
 
(* Bottom line *)
if y+height-1 <= ScreenHeight then
begin
(* Bottom left corner *)
if x >= 1 then
begin
GotoXY(x,y+height-1);
Write(chars[5]);
end
else
begin
GotoXY(1,y+height-1);
end;
 
(* Bottom edge *)
for ix := 1 to width-2 do
Write(chars[6]);
 
(* Bottom right corner *)
if x+width-1 <= ScreenWidth then
Write(chars[7]);
end;
end;
 
procedure DrawThinBorder(x, y, width, height: integer);
begin
DrawBorder(x, y, width, height, 1);
end;
 
procedure DrawDoubleBorder(x, y, width, height: integer);
begin
DrawBorder(x, y, width, height, 2);
end;
 
procedure DrawTextBar(str: string; line: integer);
var
i, left, right: integer;
len: integer;
begin
GotoXY(1,line);
 
TextBackground(White);
TextColor(Black);
len := Length(str);
 
left := round((ScreenWidth-len)/2);
right := ScreenWidth - left - len;
 
for i := 1 to left do
begin
Write(' ');
end;
 
Write(str);
 
(* TODO: If we do "for i := 1 to right", then the console will scroll *)
(* since the char in the right bottom corner is written! *)
for i := 1 to right-1 do
begin
Write(' ');
end;
 
TextBackground(Black);
TextColor(White);
end;
 
procedure DrawTitleBar(str: string);
begin
DrawTextBar(str, 1);
end;
 
procedure DrawStatusBar(str: string);
begin
DrawTextBar(str, ScreenHeight);
end;
 
function DrawSelectionList(X, Y, ListWidth, ListHeight: integer;
items: PStringList; allowESC: boolean;
Title: string; borderStrength: integer): integer;
var
i: integer;
itemIndex: integer;
sc: char;
iStartScope, iEndScope: integer;
label
doAgain;
begin
if borderStrength = 1 then
begin
DrawThinBorder(X-1, Y-1, ListWidth+2, ListHeight+2);
end;
 
if borderStrength = 2 then
begin
DrawDoubleBorder(X-1, Y-1, ListWidth+2, ListHeight+2);
end;
 
if Title <> '' then
begin
if borderStrength > 0 then
GoToXY(X+1, Y-1)
else
GoToXY(X, Y-1);
Write(Title);
end;
 
(*CursorOff;*)
 
itemIndex := 0;
iStartScope := itemIndex;
iEndScope := itemIndex + ListHeight;
 
doAgain:
 
if itemIndex < 0 then
itemIndex := 0;
if itemIndex > ListCount(items)-1 then
itemIndex := ListCount(items)-1;
 
if itemIndex < iStartScope then
begin
Dec(iEndScope);
Dec(iStartScope);
end;
 
if itemIndex > iEndScope-1 then
begin
Inc(iEndScope);
Inc(iStartScope);
end;
 
if borderStrength > 0 then
begin
(* Show scroll arrows *)
GotoXY(X+ListWidth, Y);
if iStartScope > 0 then
begin
TextBackground(White);
TextColor(Black);
WriteLn(#$18(*ArrowUp*));
TextBackground(Black);
TextColor(White);
end
else if borderStrength = 1 then
WriteLn(ThinLineChars[4])
else if borderStrength = 2 then
WriteLn(DoubleLineChars[4]);
 
GotoXY(X+ListWidth, Y+ListHeight-1);
if ListCount(items) > iEndScope then
begin
TextBackground(White);
TextColor(Black);
WriteLn(#$19(*ArrowDown*));
TextBackground(Black);
TextColor(White);
end
else if borderStrength = 1 then
WriteLn(ThinLineChars[4])
else if borderStrength = 2 then
WriteLn(DoubleLineChars[4]);
end;
 
for i := iStartScope to iEndScope-1 do
begin
if itemIndex = i then
begin
TextColor(Black);
TextBackground(White);
end
else
begin
TextColor(White);
TextBackground(Black);
end;
GotoXY(x,y+i-iStartScope);
if i > ListCount(items)-1 then
Write(FillRight('', ListWidth, ' '))
else
Write(FillRight(ListGetElement(items, i), ListWidth, ' '));
TextColor(White);
TextBackground(Black);
end;
 
repeat
GotoXY(ScreenWidth, ScreenHeight);
 
sc := ReadKey;
if sc = #$00(*ExtendedKeyCode*) then
begin
sc := ReadKey;
if sc = #$48(*UpKey*) then
begin
dec(itemIndex);
goto doAgain;
end
else if sc = #$50(*DownKey*) then
begin
inc(itemIndex);
goto doAgain;
end
else if sc = #$47(*POS1*) then
begin
itemIndex := 0;
goto doAgain;
end
else if sc = #$4F(*END*) then
begin
itemIndex := ListCount(items);
goto doAgain;
end;
end;
 
if sc = #13(*Return*) then
begin
DrawSelectionList := itemIndex;
break;
end;
 
if allowESC and (sc = #27(*ESC*)) then
begin
DrawSelectionList := -1;
break;
end;
until false;
 
(*CursorOn;*)
end;
 
procedure ClearSection(x, y, width, height: integer);
var
ix, iy: integer;
begin
for iy := y to y+height-1 do
begin
for ix := x to x+width-1 do
begin
GoToXY(ix,iy);
Write(' ');
end;
end;
end;
 
function QueryVal(var s: string; initX, initY, width, height: integer;
Title: string; borderStrength: integer): boolean;
var
x, y: integer;
i, j: integer;
sc: char;
stmp: string;
begin
if borderStrength = 1 then
DrawThinBorder(initX-1,initY-1,width+2,height+2);
if borderStrength = 2 then
DrawDoubleBorder(initX-1,initY-1,width+2,height+2);
if title <> '' then
begin
if borderStrength > 0 then
GoToXY(initX+1, initY-1)
else
GoToXY(initX, initY-1);
Write(title);
end;
ClearSection(initX,initY,width,height);
 
x := initX;
y := initY;
 
(* Write existing string value and set cursor *)
stmp := s;
s := '';
for i := 1 to Length(stmp) do
begin
if stmp[i] = #10 then
begin
s := s + stmp[i];
continue;
end;
 
GoToXY(x,y);
s := s + stmp[i];
Write(stmp[i]);
Inc(x);
 
if (x=initX+width-1) and (y=initY+height-1) then
begin
(* Attention: Data following after this will be lost! *)
break;
end;
 
if stmp[i] = #13 then
begin
if y=initY+height-1 then
begin
(* Attention: Data following after this will be lost! *)
s := Copy(s, 1, Length(s)-1);
Dec(x);
break;
end;
x := initX;
Inc(y);
continue;
end;
 
if x=initX+width then
begin
Inc(y);
x := initX;
end;
end;
 
repeat
GotoXY(x, y);
sc := ReadKey;
 
if sc = #0 then
begin
(* Extended key code *)
sc := ReadKey;
(* TODO: Implement keys like DEL, END, POS1, and ArrowKeys *)
Beep;
continue;
end
else if sc = #8(*Backspace*) then
begin
if x <= initX then
begin
if y = initY then
begin
Beep;
continue;
end;
Dec(y);
 
(* Find out length of previous line *)
j := Length(s)-2(*CRLF*);
while (j >= 0) do
begin
if (s[j]=#13) or (s[j]=#10) then break;
Dec(j);
end;
j := Length(s)-2(*CRLF*)-j;
x := initX + j;
s := Copy(s, 1, Length(s)-1); (* Remove #10 now. #13 will be removed below *)
end
else
begin
Dec(x);
end;
GotoXY(x, y);
Write(' ');
GotoXY(x, y);
s := Copy(s, 1, Length(s)-1);
continue;
end
else if sc = #13(*Return*) then
begin
if GetShiftState and kbRightShift <> 0 then
begin
if y=initY+height-1 then
begin
Beep;
continue;
end;
s := s + #13 + #10;
x := initX;
Inc(y);
end
else
begin
QueryVal := true;
exit;
end;
end
else if sc = #27(*ESC*) then
begin
QueryVal := false;
exit;
end
else
begin
if (x=initX+width-1) and (y=initY+height-1) then
begin
Beep;
continue;
end;
s := s + sc;
Write(sc);
Inc(x);
if x >= initX+width then
begin
Inc(y);
x := initX;
end;
end;
until false;
end;
 
procedure ShowMessage(msg: string; title: string; dobeep: boolean);
var
x, y, w, h: integer;
begin
x := SINGLE_LINE_BOX_PADDING_INNER;
y := ScreenHeight div 2 - 1;
w := ScreenWidth - (SINGLE_LINE_BOX_PADDING_INNER-1)*2;
h := 1;
DrawDoubleBorder(x-1, y, w+2, h+2);
ClearSection(x, y+1, w-2, h);
if title <> '' then
begin
GoToXY(x+1, y);
Write(title);
end;
GoToXY(x, y+1);
Write(msg);
if DoBeep then Beep;
end;
 
end.