{ Copyright (c) 1989 by Borland International, Inc. } unit TCScreen; { Turbo Pascal 5.5 object-oriented example screen routines. This unit is used by TCALC.PAS. See TCALC.DOC for an more information about this example. } {$S-} interface uses Crt, Dos, TCUtil; const ScreenCols = 80; ScreenRows = 50; MinScreenRows = 25; ESCPress = 'Press ESC.'; { Printed in error messages } type Direction = (Up, Down, Left, Right); ScreenColRange = 1..ScreenCols; ScreenRowRange = 1..ScreenRows; VideoTypes = (MDA, CGA, MCGA, EGA, VGA); ScreenChar = record Data : Char; Attrib : Byte; end; ScreenArray = array[ScreenRowRange, ScreenColRange] of ScreenChar; ScreenRow = array[ScreenColRange] of ScreenChar; ScreenPointer = ^ScreenArray; ScreenPos = record Col : ScreenColRange; Row : ScreenRowRange; end; Screen = object CurrRows : ScreenRowRange; CurrCols : ScreenColRange; VideoType : VideoTypes; OldCursor : Word; InsCursor : Word; OldMode : Word; constructor Init; destructor Done; procedure ToggleMaxLinesMode; procedure PrintError(Error : String); procedure PrintMessage(Message : String); procedure ClearMessage; procedure PrintHelpLine(CommandString : String); end; ScreenArea = object UpperLeft, LowerRight : ScreenPos; Attrib : Byte; constructor Init(InitX1 : ScreenColRange; InitY1 : ScreenRowRange; InitX2 : ScreenColRange; InitY2 : ScreenRowRange; InitAttrib : Byte); procedure Scroll(Dir : Direction; Amt : Word); procedure Clear; procedure Erase; end; ColorTableType = (ColorMono, ColorBW, ColorColor); ColorTablePtr = ^ColorTable; ColorTable = object TableType : ColorTableType; BlankColor : Byte; ValueCellColor : Byte; TextCellColor : Byte; FormulaCellColor : Byte; RepeatCellColor : Byte; ColColor : Byte; RowColor : Byte; InfoColor : Byte; HighlightColor : Byte; BlockColor : Byte; InputColor : Byte; InputArrowColor : Byte; ErrorColor : Byte; CellErrorColor : Byte; MemoryColor : Byte; CellDataColor : Byte; PromptColor : Byte; FileNameColor : Byte; ChangedColor : Byte; TitleColor : Byte; ContentsColor : Byte; KeyNameColor : Byte; KeyDescColor : Byte; MenuHiColor : Byte; MenuLoColor : Byte; MessageColor : Byte; constructor Init; procedure FillColorTable; end; const NoCursor = $2000; var Colors : ColorTable; Scr : Screen; ScreenPtr : ScreenPointer; procedure MoveToScreen(var Source, Dest; Len : Word); procedure MoveFromScreen(var Source, Dest; Len : Word); procedure ClrEOLXY(Col : ScreenColRange; Row : ScreenRowRange; Color : Byte); procedure WriteColor(S : String; Color : Byte); procedure WriteXY(S : String; Col : ScreenColRange; Row : ScreenRowRange; Color : Byte); procedure WriteXYClr(S : String; Col : ScreenColRange; Row : ScreenRowRange; Color : Byte); procedure SetCursor(NewCursor : Word); function GetCursor : Word; implementation const TotalColors = 26; WhiteOnRed = White + (Red shl 4); WhiteOnBlue = White + (Blue shl 4); WhiteOnCyan = White + (Cyan shl 4); BlackOnGray = LightGray shl 4; WhiteOnGray = White + (LightGray shl 4); BlinkingLightRed = LightRed + Blink; BlinkingWhite = White + Blink; LightCyanOnBlue = LightCyan + (Blue shl 4); YellowOnBlue = Yellow + (Blue shl 4); type ColorArray = array[1..TotalColors] of Byte; const ColorColors : ColorArray = (White, LightCyan, White, LightMagenta, White, WhiteOnRed, WhiteOnRed, WhiteOnCyan, WhiteOnBlue, WhiteOnCyan, White, LightCyan, WhiteOnRed, BlinkingLightRed, LightRed, LightGreen, Yellow, LightCyan, Yellow, LightMagenta, Yellow, LightCyanOnBlue, YellowOnBlue, LightCyan, White, BlinkingLightRed); BWColors : ColorArray = (White, White, White, White, White, BlackOnGray, BlackOnGray, WhiteOnGray, WhiteOnGray, BlackOnGray, White, White, White, BlinkingWhite, White, White, White, White, White, White, White, BlackOnGray, White, White, LightGray, BlinkingWhite); MonoColors : ColorArray = (White, White, White, White, White, BlackOnGray, BlackOnGray, BlackOnGray, BlackOnGray, BlackOnGray, White, White, White, BlinkingWhite, White, White, White, White, White, White, White, BlackOnGray, White, White, LightGray, BlinkingWhite); const InsCursorSmall = $0007; InsCursorLarge = $000D; var SavedExitProc : Pointer; procedure ClearScreen(X1, Y1, X2, Y2, Attrib : Word); { Clears an area of the screen } var Reg : Registers; begin if (X1 > X2) or (Y1 > Y2) then { Illegal values } Exit; with Reg do begin AX := $0600; { Clear screen through the BIOS } BH := Attrib; CH := Pred(Y1); CL := Pred(X1); DH := Pred(Y2); DL := Pred(X2); Intr($10, Reg); end; { with } end; { ClearScreen } {$L TCMVSMEM} procedure MoveToScreen(var Source, Dest; Len : Word); external; { Moves screen memory from normal RAM to screen memory - see TCMVSMEM.ASM for source } procedure MoveFromScreen(var Source, Dest; Len : Word); external; { Moves screen memory to normal RAM from screen memory - see TCMVSMEM.ASM for source } procedure MoveText(OldX1, OldY1, OldX2, OldY2, NewX1, NewY1 : Word); { Moves an area of text to a new position on the screen } var Counter, Len : Word; begin if (OldX2 < OldX1) or (OldY2 < OldY1) then Exit; Len := Succ(OldX2 - OldX1) shl 1; if NewY1 < OldY1 then begin { Move it row by row, going forwards } for Counter := 0 to OldY2 - OldY1 do MoveFromScreen(ScreenPtr^[OldY1 + Counter, OldX1], ScreenPtr^[NewY1 + Counter, NewX1], Len) end else begin { Move it row by row, going backwards } for Counter := OldY2 - OldY1 downto 0 do MoveFromScreen(ScreenPtr^[OldY1 + Counter, OldX1], ScreenPtr^[NewY1 + Counter, NewX1], Len) end; end; { MoveText } procedure ScrollText(Dir : Direction; X1, Y1, X2, Y2, Amt, Attrib : Word); { Scrolls the screen by an amount in a direction - it does this by moving the text to be scrolled and then clearing the area that wasn't scrolled } begin case Dir of Up : begin MoveText(X1, Y1 + Amt, X2, Y2, X1, Y1); ClearScreen(X1, Succ(Y2 - Amt), X2, Y2, Attrib); end; Down : begin MoveText(X1, Y1, X2, Y2 - Amt, X1, Succ(Y1)); ClearScreen(X1, Y1, X2, Pred(Y1 + Amt), Attrib); end; Left : begin MoveText(X1 + Amt, Y1, X2, Y2, X1, Y1); ClearScreen(Succ(X2 - Amt), Y1, X2, Y2, Attrib); end; Right : begin MoveText(X1, Y1, X2 - Amt, Y2, X1 + Amt, Y1); ClearScreen(X1, Y1, Pred(X1 + Amt), Y2, Attrib); end; end; { case } end; { ScrollText } function EGAInstalled : Boolean; { Tests for the presence of an EGA } var Reg : Registers; begin Reg.AX := $1200; Reg.BX := $0010; Reg.CX := $FFFF; Intr($10, Reg); EGAInstalled := Reg.CX <> $FFFF; end; { EGAInstalled } function PS2 : Boolean; { This function returns True if we are running on a PS/2 type video adapter } var Regs : Registers; begin Regs.AX := $1A00; Intr($10, Regs); PS2 := ((Regs.AL and $FF) = $1A) and ((Regs.BL and $FF) in [$07, $08, $0B, $0C]); end; { PS2 } procedure ClrEOLXY(Col : ScreenColRange; Row : ScreenRowRange; Color : Byte); { Clears to the end-of-line in a color at a specified position } begin GotoXY(Col, Row); TextAttr := Color; ClrEOL; end; { ClrEOLXY } procedure WriteColor(S : String; Color : Byte); { Writes a string in a color } begin TextAttr := Color; Write(S); end; { WriteColor } procedure WriteXY(S : String; Col : ScreenColRange; Row : ScreenRowRange; Color : Byte); { Writes a string in a color at a specified position } begin GotoXY(Col, Row); WriteColor(S, Color); end; { WriteXY } procedure WriteXYClr(S : String; Col : ScreenColRange; Row : ScreenRowRange; Color : Byte); { Clears to the end-of-line in a color at a specified position and then writes a string } begin ClrEOLXY(Col, Row, Color); Write(S); end; { WriteXYClr } procedure SetCursor(NewCursor : Word); { Sets the value of the scan lines of the cursor } var Reg : Registers; begin with Reg do begin AH := 1; BH := 0; CX := NewCursor; Intr($10, Reg); end; { with } end; { SetCursor } function GetCursor : Word; { Returns the value of the scan lines of the cursor } var Reg : Registers; begin with Reg do begin AH := 3; BH := 0; Intr($10, Reg); GetCursor := CX; end; { Reg } end; { GetCursor } constructor Screen.Init; { Finds what type of video adapter is being run on, and initializes various variables based on this information } var Reg : Registers; begin OldMode := LastMode; Reg.AH := $0F; Intr($10, Reg); { Check for the current video mode } if Reg.AL <> 7 then begin if EGAInstalled then begin if PS2 then VideoType := VGA else VideoType := EGA; end else begin if PS2 then VideoType := MCGA else VideoType := CGA; end; ScreenPtr := Ptr($B800, 0); if Reg.AL < 2 then CurrCols := 40 else CurrCols := 80; end else begin VideoType := MDA; ScreenPtr := Ptr($B000, 0); CurrCols := 80; end; CurrRows := Succ(Hi(WindMax)); OldCursor := GetCursor; if (CurrRows = MinScreenRows) and (VideoType <> CGA) then InsCursor := InsCursorLarge else InsCursor := InsCursorSmall; end; { Screen.Init } destructor Screen.Done; { Restores the screen mode and cursor that existed at the start of the program } begin TextMode(OldMode); SetCursor(OldCursor); ExitProc := SavedExitProc; end; { Screen.Done } procedure Screen.ToggleMaxLinesMode; { Toggles the display in and out of 43/50-line mode } begin if CurrRows = MinScreenRows then begin TextMode(Lo(LastMode) + Font8x8); InsCursor := InsCursorSmall; end else begin TextMode(Lo(LastMode)); InsCursor := InsCursorLarge; end; CurrRows := Succ(Hi(WindMax)); end; { Screen.ToggleMaxLinesMode } procedure Screen.PrintError(Error : String); { Prints an error message at the bottom of the screen } var Ch : Word; Buffer : ScreenRow; begin MoveFromScreen(ScreenPtr^[CurrRows, 1], Buffer, SizeOf(ScreenChar) * CurrCols); { Save bottom line } WriteXYClr(CenterStr(Error + '. ' + ESCPress, Pred(CurrCols)), 1, CurrRows, Colors.ErrorColor); Beep; repeat Ch := GetKey; until Ch = ESC; MoveToScreen(Buffer, ScreenPtr^[CurrRows, 1], { Restore bottom line } SizeOf(ScreenChar) * CurrCols); end; { Screen.PrintError } procedure Screen.PrintMessage(Message : String); { Prints a message } begin WriteXYClr(Message + '...', 1, Pred(CurrRows), Colors.MessageColor); end; { Screen.PrintMessage } procedure Screen.ClearMessage; { Clears the last printed message } begin ClrEOLXY(1, Pred(CurrRows), Colors.MessageColor); end; { Screen.ClearMessage } procedure Screen.PrintHelpLine(CommandString : String); { Prints a help line at the bottom of the screen. The command string is made up of a series of keys and descriptions separated by backslashes. Example: 'F1\Help\F2\Save\F3\Load\Alt-X\Exit'} var P : Integer; S : String[ScreenCols]; begin CommandString := CommandString + '\'; ClrEOLXY(1, CurrRows, Colors.KeyDescColor); while CommandString <> '' do begin Write(' '); P := Pos('\', CommandString); WriteColor(Copy(CommandString, 1, Pred(P)), Colors.KeyNameColor); Delete(CommandString, 1, P); P := Pos('\', CommandString); if CommandString[1] = '\' then S := '-' else S := '-' + Copy(CommandString, 1, Pred(P)); WriteColor(S, Colors.KeyDescColor); Delete(CommandString, 1, P); end; end; { Screen.PrintHelpLine } constructor ScreenArea.Init(InitX1 : ScreenColRange; InitY1 : ScreenRowRange; InitX2 : ScreenColRange; InitY2 : ScreenRowRange; InitAttrib : Byte); { Sets up a screen area } begin UpperLeft.Col := InitX1; UpperLeft.Row := InitY1; LowerRight.Col := InitX2; LowerRight.Row := InitY2; Attrib := InitAttrib; end; { ScreenArea.Init } procedure ScreenArea.Scroll(Dir : Direction; Amt : Word); { Scrolls a screen area an certain amount in a direction } begin ScrollText(Dir, UpperLeft.Col, UpperLeft.Row, LowerRight.Col, LowerRight.Row, Amt, Attrib); end; { ScreenArea.Scroll } procedure ScreenArea.Clear; { Clears a screen area } begin ClearScreen(UpperLeft.Col, UpperLeft.Row, LowerRight.Col, LowerRight.Row, Attrib); end; { ScreenArea.Clear } procedure ScreenArea.Erase; { Erases a screen area by writing over it in black } begin ClearScreen(UpperLeft.Col, UpperLeft.Row, LowerRight.Col, LowerRight.Row, Black); end; { ScreenArea.Erase } constructor ColorTable.Init; { Initializes the color table by finding the video mode that is being used } begin case Lo(LastMode) of BW40, BW80 : TableType := ColorBW; CO40, CO80 : TableType := ColorColor; Mono : TableType := ColorMono; end; { case } FillColorTable; end; { ColorTable.Init } procedure ColorTable.FillColorTable; { Moves the correct built-in color table to the program's color table } var P : Pointer; begin case TableType of ColorColor : P := @ColorColors; ColorBW : P := @BWColors; ColorMono : P := @MonoColors; end; { case } Move(P^, BlankColor, TotalColors); end; { ColorTable.FillColorTable } {$F+} procedure ScreenExit; { Clears the screen at exit } begin Scr.Done; end; { ScreenExit } {$F-} begin SavedExitProc := ExitProc; ExitProc := @ScreenExit; TextMode(LastMode); Scr.Init; Colors.Init; end.