PROGRAM TVLife; { Turbo Vision Life v1.0 } { by Ben Ziegler } { February 16, 1992 } { } { TVLife is a simple program that illustrates a few of Turbo Visions's } { features: 1) how to use the Idle event to execute background tasks, } { and 2) how to incorporate menus inside of Twindows. It is merely } { meant to be a demonstration program for Turbo Pascal v6.0 } { } { Send any questions or comments to: } { } { Ben Ziegler Internet Email Address: } { 4010 Terrace Dr bpz4r@virginia.edu } { Annandale, VA 22003 (email valid until May 1992) } {$R-,S-} { This will speed up program execution } USES Objects, Drivers, Views, Menus, App; CONST cmLife = 101; { Opens a Life window } cmIdle = 102; { issued when TV is Idle } cmStart = 103; { Starts a Life window running } cmStop = 104; { Stops a Life window } cmClearBoard = 105; { Clears the Life Board } cmRandom = 106; { Randomly fills the Life Board } cmHighRes = 107; { Set Screen to VGA 43/50 Lines } cmLowRes = 108; { Set Screen to 25 Lines } Xm = 80; { Max X Size of Life Window } Ym = 48; { Max Y Size of Life Window } TYPE Board = array[1..Xm, 1..Ym] of byte; TMyApp = object(TApplication) constructor Init; procedure HandleEvent(var Event: TEvent); virtual; procedure InitMenuBar; virtual; procedure InitStatusLine; virtual; procedure idle; virtual; procedure DoLife; procedure HighRes; procedure LowRes; end; PMyMenuBar = ^TMyMenuBar; TMyMenuBar = object(TMenuBar) function GetPalette: PPalette; virtual; end; PLifeInterior = ^TLifeInterior; TLifeInterior = object(Tview) OldB : ^Board; mx,my : integer; running : boolean; constructor Init(var Bounds: TRect); procedure HandleEvent(var Event:TEvent); virtual; procedure Iterate(var o : Board); procedure InitBoard(var b : Board); procedure ClearBoard(var b : Board); procedure Update; virtual; procedure Draw; virtual; end; PLifeView = ^TLifeView; TLifeView = object(TWindow) MyInterior : PLifeInterior; MB : PMyMenuBar; constructor Init(Bounds:Trect; s : string; num:integer); procedure handleevent(var event : Tevent); virtual; procedure SizeLimits(var Min, Max: TPoint); virtual; end; { ************* } { TLifeInterior } { ************* } CONSTRUCTOR TLifeInterior.Init(var Bounds: TRect); BEGIN TView.Init(Bounds); GrowMode := gfGrowHiX + gfGrowHiY; Options := Options OR ofFramed; EventMask := $FFFF; { Listen for all types of events } mx := 0; my := 0; NEW(OldB); InitBoard(OldB^); end; PROCEDURE TLifeInterior.InitBoard(var b : Board); VAR x,y,i : integer; BEGIN FOR x := 1 TO Xm DO FOR y := 1 TO Ym DO b[x,y] := 0; Randomize; FOR i := 1 TO 999 DO BEGIN x := Random(Xm-2)+2; y := Random(Ym-2)+2; b[x,y] := 1; END; END; PROCEDURE TLifeInterior.ClearBoard(var b : Board); VAR x,y : integer; BEGIN FOR x := 1 TO Xm DO FOR y := 1 TO Ym DO b[x,y] := 0; END; PROCEDURE TLifeInterior.Draw; VAR x,y : integer; R : TRect; ex,ey : integer; B : array[0..2047] of word; { Buffer used to speed up Draw } BEGIN GetExtent(R); ex := R.B.X+1; ey := R.B.Y+1; FOR y := 2 TO ey DO BEGIN FOR x := 2 TO ex DO BEGIN IF OldB^[x,y]=0 THEN BEGIN MoveChar(B[x-2], #32, GetColor(2), 1); END ELSE BEGIN MoveChar(B[x-2], #9, GetColor(2), 1); END; END; WriteLine(0, y-2, Size.X, 1, B); END; END; PROCEDURE TLifeInterior.Iterate(var o : Board); VAR x,y,num : integer; n : Board; BEGIN n := o; FOR x := 2 TO Xm-1 DO FOR y := 2 TO Ym-1 DO BEGIN { Find number of neighbors } num := o[x-1,y-1] + o[x,y-1] + o[x+1,y-1] + o[x-1,y] + o[x+1,y] + o[x-1,y+1] + o[x,y+1] + o[x+1,y+1]; IF o[x,y]=1 THEN IF ((num=2) OR (num=3)) THEN n[x,y] := 1 ELSE n[x,y] := 0; IF o[x,y]=0 THEN IF num=3 THEN n[x,y] := 1 { Birth = 3! } ELSE n[x,y] := 0; END; o := n; END; PROCEDURE TLifeInterior.Update; BEGIN Iterate(OldB^); Draw; END; PROCEDURE TLifeInterior.HandleEvent(var event : Tevent); VAR p,o : Tpoint; BEGIN tview.handleevent(event); IF event.what = evCommand THEN CASE event.command OF cmStart : running := TRUE; cmStop : running := FALSE; end; IF event.what = evBroadCast THEN IF event.command = cmIdle THEN BEGIN IF running THEN Update; END; IF event.what = evCommand THEN IF event.command = cmClearBoard THEN BEGIN ClearBoard(OldB^); Draw; ClearEvent(event); END; IF event.what = evCommand THEN IF event.command = cmRandom THEN BEGIN InitBoard(OldB^); Draw; ClearEvent(event); END; IF (event.what AND (evMouseDown OR evMouseAuto)) <> 0 THEN BEGIN o := event.where; MakeLocal(o, p); p.x := p.x+2; p.y := p.y+2; IF (mx<>p.x) OR (my<>p.y) THEN BEGIN OldB^[p.x, p.y] := 1-OldB^[p.x, p.y]; Draw; mx := p.x; my := p.y; END; END; END; { ********* } { TLifeView } { ********* } CONSTRUCTOR TLifeView.Init(Bounds:Trect; s : string; num:integer); VAR R : TRect; BEGIN Twindow.init(Bounds, s, num); GetExtent(R); R.Grow(-1,-1); R.B.Y := R.A.Y + 1; MB := New(PMyMenuBar, Init(R, NewMenu( NewSubMenu('~A~ction', hcNoContext, NewMenu( NewItem('~S~tart', 'Alt-S', kbAltS, cmStart, hcNoContext, NewItem('Sto~p~', 'Alt-P', kbAltP, cmStop, hcNoContext, NewItem('~C~lear Board', 'Alt-C', kbAltC, cmClearBoard, hcNoContext, NewItem('~R~andomize', 'Alt-R', kbAltR, cmRandom, hcNoContext, NewLine( NewItem('Close ~W~indow', 'Alt-F3', kbAltF3, cmClose, hcNoContext, nil))))))), nil) ))); Insert(MB); GetClipRect(Bounds); Bounds.Grow(-1,-2); Bounds.B.y := Bounds.b.y + 1; MyInterior := New(PLifeInterior, Init(Bounds)); Insert(MyInterior); Options := Options OR (ofFirstClick OR ofTileable); dragmode := $F0; { Can't move window off screen } END; PROCEDURE TLifeView.HandleEvent(var event : Tevent); VAR HelloThere : pointer; BEGIN { NOTE: HelloThere must come before twindow.he or CRASH! } HelloThere := Message(MyInterior, event.what, event.command, nil); Twindow.HandleEvent(event); END; PROCEDURE TLifeView.SizeLimits(var Min, Max: TPoint); CONST MyMin : TPoint = (X: 28; Y: 11); VAR R : TRect; BEGIN Desktop^.GetExtent(R); Min := MyMin; Max := R.B; END; { ********** } { TMyMenuBar } { ********** } FUNCTION TMyMenuBar.GetPalette: PPalette; CONST CMyStuff = #4#3#6#5#6#7; PMyStuff : string[Length(CMyStuff)] = CMyStuff; BEGIN GetPalette := @PMyStuff; END; { ****** } { TMyApp } { ****** } PROCEDURE Tile; VAR R: TRect; BEGIN Desktop^.GetExtent(R); Desktop^.Tile(R); END; PROCEDURE Cascade; VAR R: TRect; BEGIN Desktop^.GetExtent(R); Desktop^.Cascade(R); END; PROCEDURE TMyApp.HandleEvent(var Event: TEvent); BEGIN TApplication.HandleEvent(Event); IF Event.What = evCommand THEN BEGIN CASE Event.Command OF cmLife : DoLife; cmTile : Tile; cmCascade : Cascade; cmHighRes : HighRes; cmLowRes : LowRes; ELSE Exit; END; ClearEvent(Event); END; END; PROCEDURE TMyApp.InitMenuBar; VAR R: TRect; BEGIN GetExtent(R); R.B.Y := R.A.Y + 1; MenuBar := New(PMenuBar, Init(R, NewMenu( NewSubMenu('~F~ile', hcNoContext, NewMenu( NewItem('~L~ife Window', 'F9', kbF9, cmLife, hcNoContext, NewLine( NewItem('E~x~it', 'Alt-X', kbAltX, cmQuit, hcNoContext, nil)))), NewSubMenu('~W~indow', hcNoContext, NewMenu( NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext, NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext, NewItem('~T~ile', '', 0, cmTile, 0, NewItem('~C~ascade', '', 0, cmCascade, 0, NewItem('~H~igh Res', 'Alt-H', kbAltH, cmHighRes, 0, NewItem('~L~ow Res', 'Alt-L', kbAltL, cmLowRes, 0, nil))))))), nil) )))); END; PROCEDURE TMyApp.InitStatusLine; VAR R: TRect; BEGIN GetExtent(R); R.A.Y := R.B.Y - 1; StatusLine := New(PStatusLine, Init(R, NewStatusDef(0, $FFFF, NewStatusKey('', kbF10, cmMenu, NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit, NewStatusKey('~F9~ Life Window', kbF9, cmLife, NewStatusKey('~Alt-F3~ Close', kbAltF3, cmClose, nil)))), nil) )); END; PROCEDURE TMyApp.DoLife; VAR R : TRect; R2 : TRect; Life : PLifeView; BEGIN GetExtent(R2); R.Assign(0, 0, 28, 11); R.Move(Random(R2.B.X-29), Random(R2.B.Y-12)); Life := New(PLifeView, Init(R, 'Life', 0)); Desktop^.Insert(Life); END; PROCEDURE TMyApp.Idle; VAR HelloThere : pointer; FUNCTION IsTileable(P: PView): Boolean; far; BEGIN IsTileable := P^.Options and ofTileable <> 0; END; BEGIN TApplication.Idle; IF Desktop^.FirstThat(@IsTileable) <> NIL THEN EnableCommands([cmTile, cmCascade]) ELSE DisableCommands([cmTile, cmCascade]); HelloThere := Message(DeskTop, evBroadcast, cmIdle, nil); end; PROCEDURE TMyApp.HighRes; BEGIN SetScreenMode(ScreenMode OR smFont8x8); DisableCommands([cmHighRes]); EnableCommands([cmLowRes]); END; PROCEDURE TMyApp.LowRes; BEGIN SetScreenMode(ScreenMode AND NOT smFont8x8); DisableCommands([cmLowRes]); EnableCommands([cmHighRes]); END; CONSTRUCTOR TMyApp.Init; BEGIN Tapplication.init; DisableCommands([cmLowRes]); END; VAR MyApp: TMyApp; BEGIN MyApp.Init; MyApp.Run; MyApp.Done; END.