{************************************************} { } { Turbo Vision Debuging Unit } { Copyright (c) 1992 by Borland International } { } {************************************************} unit TVDebug; interface uses Objects, Drivers, Views, App, TextView; const cmTextWinAppendLine = 30000; { Custom options flag so TextInterior will know whether to scroll its text as new lines are added or not. Uses an unused bit of the TView options field. Default is not to scroll on append. } ofScrollonAppend = $0400; type { TApplication } { A debugging version of APP's TApplication that will create a Event window and a Log window on the bottom of the desktop. } TApplication = object(App.TApplication) constructor Init; procedure GetEvent(var E: TEvent); virtual; end; PApplication = ^TApplication; { TTextCollection } { Used internally by TTextInterior to hold the text to display } PTextCollection = ^TTextCollection; TTextCollection = object(TCollection) procedure FreeItem(Item: Pointer); virtual; end; { TTextInterior } { A scrolling view of the text stored in Lines. If the view recieves a cmTextWinAppendLine as an evBroadcast the InfoPtr field is assumed to contain a PString containing a new line to add to Lines. } PTextInterior = ^TTextInterior; TTextInterior = object(TScroller) Lines: TTextCollection; constructor Init( R: TRect; MaxLines: Integer; AHScrollbar, AVScrollbar: PScrollbar); destructor Done; virtual; procedure Draw; virtual; procedure HandleEvent(var E: TEvent); virtual; end; { TTextWindow } { A window designed to contain a TTextInterior } PTextWindow = ^TTextWindow; TTextWindow = object(TWindow) constructor Init(R: TRect; NewTitle: String; Num, MaxLines: Integer); procedure MakeInterior( MaxLines: integer); virtual; end; { TEventWindow } { A text window that will a list of the last MaxLines events sent to it by DisplayEvent. TApplication above calls this method upon receiving an event in GetEvent. If this unit is included after Views in a unit, all Message calls in that unit are also displayed. NOTE: only one of these windows is allowed. If more than one is created the second will return False from Valid causing InsertWindow to refuse to insert the window in the desktop. } PEventWindow = ^TEventWindow; TEventWindow = object(TTextWindow) Filters: Word; constructor Init(var R: TRect; ATitle: String; Num, MaxLines: Integer); destructor Done; virtual; procedure DisplayEvent(var E: TEvent); virtual; procedure FiltersDialog; function GetPalette: PPalette; virtual; procedure HandleEvent(var Event: TEvent); virtual; procedure MakeInterior(Maxlines: Integer); virtual; function Valid(Command: Word): Boolean; virtual; end; { TLogWindow } { Creating a TLogWindow will redirect all Write and Writeln's to the window. Only one of these windows should be created, if more than one is create Valid will return False and InsertWindow will refuse to insert the window into the desktop. } PLogWindow = ^TLogWindow; TLogWindow = object(TWindow) Interior: PTerminal; constructor Init(var Bounds: TRect; BufSize: Word); destructor Done; virtual; function Valid(Command: Word): Boolean; virtual; end; { An alternate Message from View's that will log the message to the event window before sending it. } function Message(Receiver: PView; What, Command: Word; InfoPtr: Pointer): Pointer; implementation uses Dos, Menus, Dialogs, KeyNamer, CmdNamer; var EventWindow: PEventWindow; { TApplication } constructor TApplication.Init; var R: TRect; begin inherited Init; BuiltInCommandNames; Desktop^.GetExtent(R); R.Assign(R.A.X, R.B.Y-10, R.B.X div 2, R.B.Y); InsertWindow(New(PEventWindow, Init(R, 'Event Window', wnNoNumber, 100))); Desktop^.GetExtent(R); R.Assign(R.B.X div 2, R.B.Y-10, R.B.X, R.B.Y); InsertWindow(New(PLogWindow, Init(R, 1024))); end; procedure TApplication.GetEvent(var E: TEvent); begin inherited GetEvent(E); if EventWindow <> nil then EventWindow^.DisplayEvent(E); end; const CEWMenu = #9#10#11#12#13#14; { TEWMenubox } type PEWMenubox = ^TEWMenubox; TEWMenubox = object(TMenubox) function GetPalette: PPalette; virtual; end; function TEWMenubox.GetPalette: PPalette; const P: String[length(CEWMenu)] = CEWMenu; begin GetPalette:= @P; end; { TEWMenubar } type PEWMenubar = ^TEWMenubar; TEWMenubar = object(TMenubar) function GetPalette: PPalette; virtual; function NewSubView(var Bounds: TRect; AMenu: PMenu; AParentMenu: PMenuView): PMenuView; virtual; end; function TEWMenubar.GetPalette: PPalette; const P: string[length(CEWMenu)] = CEWMenu; begin GetPalette:= @P; end; function TEWMenubar.NewSubView(var Bounds: TRect; AMenu: PMenu; AParentMenu: PMenuView): PMenuView; begin NewSubView := New(PEWMenuBox, Init(Bounds, AMenu, AParentMenu)); end; { TTextCollection } procedure TTextCollection.FreeItem(Item: Pointer); begin DisposeStr(Item); end; { TTextInterior } constructor TTextInterior.Init( R: TRect; MaxLines: Integer; AHScrollbar, AVScrollbar: PScrollbar); begin inherited Init(R, AHScrollbar, AVScrollbar); if MaxLines = 0 then Lines.Init(Size.X, 1) { let it grow unchecked: 16K items max} else Lines.Init(Maxlines, 0); { fix size and rollover when full } SetLimit(128,Size.X); GrowMode:= gfGrowHiX + gfGrowHiY; end; destructor TTextInterior.Done; begin Lines.Done; inherited Done; end; procedure TTextInterior.Draw; var color: byte; Y, I: Integer; B: TDrawBuffer; begin { draw only what's visible } Color:= GetColor(1); for y:= 0 to Size.Y-1 do begin MoveChar(B,' ',Color,Size.X); I:= Delta.Y+Y; if (I < Lines.Count) and (Lines.At(I) <> nil) then MoveStr(B, Copy(PString(Lines.At(I))^,Delta.X+1, Size.X), Color); WriteLine(0,Y,Size.X,1,B); end; end; procedure TTextInterior.HandleEvent(var E: TEvent); begin inherited HandleEvent(E); case E.What of evBroadcast: case E.Command of cmTextWinAppendLine: begin if Lines.Count < Lines.Limit then { let it grow } begin Lines.Insert(E.Infoptr); if Lines.Count > Size.Y then begin SetLimit(128,Lines.Count); if (Owner <> nil) and ((Owner^.Options and ofScrollonAppend) <> 0) then VScrollbar^.SetValue(Lines.Count); end; end else begin Lines.AtFree(0); { zap the first item } Lines.Insert(E.InfoPtr); { before adding new one } end; DrawView; end { show the changes } else Exit; end; else Exit; end; ClearEvent(E); end; { TTextWindow } constructor TTextWindow.Init( R: TRect; NewTitle: String; Num, MaxLines: Integer); begin inherited Init(R,NewTitle, Num); MakeInterior(MaxLines); end; procedure TTextWindow.MakeInterior( MaxLines: Integer); var R: TRect; begin GetExtent(R); R.Grow(-1, -1); Insert(New(PTextInterior, Init(R, MaxLines, StandardScrollBar(sbHorizontal), StandardScrollBar(sbVertical)))); end; { TEventWindow } const cmEventFilters = 503; constructor TEventWindow.Init(var R: TRect; ATitle: String; Num, Maxlines: Integer); begin inherited Init(R, ATitle, Num, MaxLines); { custom option flag for TextWindow's interior} Options:= Options or (ofScrollOnAppend + ofFirstClick); Filters := evMouse or evKeyBoard or evMessage; EventWindow := @Self; end; destructor TEventWindow.Done; begin inherited Done; EventWindow := nil; end; procedure TEventWindow.DisplayEvent(var E: TEvent); var st,xs,ys: String; Event: Word; begin st:=''; if State and sfSelected = 0 then begin Event := E.What and Filters; case Event of evNothing: Exit; evMouseDown, evMouseUp, evMouseMove, evMouseAuto: begin st := 'Mouse '; case E.What of evMouseDown: st := st + 'Down, '; evMouseUp: st := st + 'Up, '; evMouseMove: st := st + 'Move, '; evMouseAuto: st := st + 'Auto, '; end; case E.Buttons of mbLeftButton: st := st + 'Left Button, '; mbRightButton: st := st + 'Right Button, '; $04: st := st + 'Center Button, '; end; if (E.Buttons <> 0) and E.Double then st := st +'Double Click '; Str(E.Where.X:0, xs); Str(E.Where.Y:0, ys); st := st + 'X:' + xs + ' Y:' + ys; end; evKeyDown: begin st := KeyName(E.KeyCode); if st = '' then st := KeyName(Word(E.CharCode)); st := 'Keyboard ' + st; end; evCommand, evBroadcast: begin if E.What = evCommand then st := 'Command ' else st := 'Broadcast '; St := Concat(St, CommandName(E.Command)); end; else Str(E.What:0, xs); st := 'Unknown Event.What: ' + xs; end; {case} Views.Message(@Self, evBroadcast, cmTextWinAppendLine, NewStr(st)); end; { if } end; procedure TEventWindow.FiltersDialog; var D: PDialog; R: TRect; DataRec: Word; begin R.Assign(10,6,40,20); D := New(PDialog, Init(R, 'Message Filters')); with D^ do begin R.Assign(7,2,22,10); Insert(New(PCheckBoxes, Init(R, NewSItem('Mouse ~D~own', NewSItem('Mouse ~U~p', NewSItem('Mouse ~M~ove', NewSItem('Mouse ~A~uto', NewSItem('~K~eyboard', NewSItem('~C~ommand', NewSItem('~B~roadcast', NewSItem('~O~ther', nil))))))))))); R.Assign(5,11,13,13); Insert(New(PButton, Init(R, 'Ok', cmOk, bfDefault))); R.Assign(14,11,24,13); Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal))); SelectNext(False); end; { transfer data from filters to a more linear datarec } DataRec := 0; DataRec := Filters and (evMouse or evKeyDown); DataRec := DataRec or ((Filters - DataRec) shr 3); if Application^.ExecuteDialog(D, @DataRec) <> cmCancel then begin Filters := 0; Filters := DataRec and (evMouse or evKeyDown); Filters := Filters or ((DataRec - Filters) shl 3); end; end; function TEventWindow.GetPalette: PPalette; const P: String[length(CBlueWindow)+ length(CMenuView)] = CBlueWindow + CMenuView; begin GetPalette := @P; end; procedure TEventWindow.HandleEvent(var Event: TEvent); begin inherited HandleEvent(Event); if (Event.What = evCommand) and (Event.Command = cmEventFilters) then begin FiltersDialog; ClearEvent(Event); end; end; procedure TEventWindow.MakeInterior(Maxlines: Integer); var R: TRect; M: PMenubar; begin GetExtent(R); R.Grow(-1,-1); R.B.Y:= R.A.Y+1; Insert(New(PEWMenubar, Init(R, NewMenu( NewSubMenu('~O~ptions', hcNoContext, NewMenu( NewItem('~F~ilters', '', 0, cmEventFilters, hcNoContext, nil)), nil))))); GetExtent(R); R.Grow(-1, -1); Inc(R.A.Y); Insert(New(PTextInterior, Init(R, MaxLines, StandardScrollBar(sbHorizontal+sbHandleKeyboard), StandardScrollBar(sbVertical+sbHandleKeyboard)))); end; function TEventWindow.Valid(Command: Word): Boolean; begin if inherited Valid(Command) then Valid := EventWindow = @Self else Valid := False; end; { TLogWindow } function AssignedTo(var T: Text; View: PTextDevice): Boolean; begin AssignedTo := Pointer((@TextRec(T).UserData)^) = View; end; constructor TLogWindow.Init(var Bounds: TRect; BufSize: Word); var R: TRect; vSB, hSB: PScrollBar; begin inherited Init(Bounds, 'Messages Log', wnNoNumber); vSB := StandardScrollBar(sbVertical + sbHandleKeyboard); Insert(vSB); hsb := StandardScrollBar(sbHorizontal + sbHandleKeyboard); Insert(hSB); GetExtent(R); R.Grow(-1, -1); Interior := New(PTerminal, Init(R, hSB, vSB, BufSize)); Insert(Interior); AssignDevice(Output, Interior); Rewrite(Output); end; destructor TLogWindow.Done; begin if AssignedTo(Output, Interior) then begin Assign(Output, ''); Rewrite(Output); end; inherited Done; end; function TLogWindow.Valid(Command: Word): Boolean; begin Valid := AssignedTo(Output, Interior); end; { Message } function Message(Receiver: PView; What, Command: Word; InfoPtr: Pointer): Pointer; var E: TEvent; begin E.What := What; E.Command := Command; E.Infoptr := Infoptr; { no point in displaying our own message to display an event...} if (EventWindow <> nil) and (Command <> cmTextWinAppendLine) then EventWindow^.DisplayEvent(E); { pass the intercepted data on to the Message function it was intended for } Message:= Views.Message(Receiver, What, Command, InfoPtr); end; end.