unit WatchWin;

{$O+,F+,S-,X+}

interface

uses Objects, Drivers, Views, Dialogs;

const

  CWatchViewer = #6#6#7#6#6;

type

  TWatchString = string[80];

  PWatchViewer = ^TWatchViewer;
  TWatchViewer = object(TListViewer)
    Exprs: PStringCollection;
    constructor Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar);
    constructor Load(var S: TStream);
    procedure Add(S: PString);
    procedure Remove;
    procedure RemoveAll;
    function  GetPalette: PPalette; virtual;
    function  GetText(Item: Integer; MaxLen: Integer): string; virtual;
    procedure Get(var S: TWatchString);
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure Store(var S: TStream);
  end;

const

  RWatchViewer: TStreamRec = (
    ObjType: 6001;
    VmtLink: Ofs(TypeOf(TWatchViewer)^);
    Load:    @TWatchViewer.Load;
    Store:   @TWatchViewer.Store
  );

function AddWatchDialog: PDialog;
function EditWatchDialog: PDialog;
function WatchWindow: PWindow;

implementation

uses TVars, TWindows, Compiler, Utils, Controls, Context, StrNames;

constructor TWatchViewer.Init(var Bounds: TRect;
  AHScrollBar, AVScrollBar: PScrollBar);
begin
  TListViewer.Init(Bounds, 1, AHScrollBar, AVScrollBar);
  GrowMode := gfGrowHiX + gfGrowHiY;
  EventMask := EventMask or evDebugger;
  SetRange(1);
  HScrollBar^.Options := HScrollBar^.Options or ofPreProcess;
  HScrollBar^.SetRange(1, 255);
  Exprs := New(PStringCollection, Init(30, 0));
end;

constructor TWatchViewer.Load(var S: TStream);
begin
  TListViewer.Load(S);
  Exprs := PStringCollection(S.Get);
end;

procedure TWatchViewer.Add(S: PString);
var
  P: PString;
  I: Integer;

function Equals(P: PString): Boolean; far;
begin
  Inc(I);
  Equals := P^ = S^;
end;

begin
  if S <> nil then
  begin
    I := -1;
    P := Exprs^.FirstThat(@Equals);
    if P <> nil then
    begin
      Exprs^.AtDelete(I);
      DisposeStr(S);
      S := P;
      if Focused > Exprs^.Count then
        Dec(Focused);
    end;
    if Exprs^.Count < Exprs^.Limit then
      Exprs^.AtInsert(Focused, S)
    else
      MessageBox(sTooManyWatches, nil, mfError + mfOkButton);
    SetRange(Exprs^.Count + 1);
    FocusItem(Focused + 1);
    DrawView;
  end;
end;

procedure TWatchViewer.Remove;
begin
  if (Exprs^.Count > 0) and (Focused < Exprs^.Count) then
  begin
    DisposeStr(Exprs^.At(Focused));
    Exprs^.AtDelete(Focused);
    SetRange(Exprs^.Count + 1);
    DrawView;
  end;
end;

procedure TWatchViewer.RemoveAll;
begin
  if Exprs^.Count > 0 then
  begin
    FocusItem(0);
    while Exprs^.Count <> 0 do
      Remove;
  end;
end;

function TWatchViewer.GetPalette: PPalette;
const
  P: string[Length(CWatchViewer)] = CWatchViewer;
begin
  GetPalette := @P;
end;

function TWatchViewer.GetText(Item: Integer; MaxLen: Integer): string;
var
  CanModify: Boolean;
  C: Char;
  P: PString;
  Result: Longint;
  S: string;
begin
  if Item >= Exprs^.Count then
    GetText := ''
  else
  begin
    P := Exprs^.At(Item);
    Result := Evaluate(P^, S, CanModify);
    if Result <> 0 then
      S := Strings^.Get(LongRec(Result).Lo + sErrorBase);
    if Item = Focused then
      C := #7
    else
      C := ' ';
    GetText := C + P^ + ': ' + S;
  end;
end;

procedure TWatchViewer.Get(var S: TWatchString);
begin
  if (Exprs^.Count > 0) and (Focused < Exprs^.Count) then
    S := PString(Exprs^.At(Focused))^
  else
    S := '';
end;

procedure TWatchViewer.HandleEvent(var Event: TEvent);

procedure PutEv(I: Word);
begin
  Event.What := evCommand;
  Event.Command := I;
  PutEvent(Event);
end;

begin
  if (Event.What = evMouseDown) and Event.Double then
  begin
    PutEv(cmEditWatch);
    ClearEvent(Event);
  end;
  TListViewer.HandleEvent(Event);
  if Event.What and evMessage <> 0 then
    case Event.Command of
      cmRefreshInfo:
        DrawView;
      cmFindWatchWindow:
        ClearEvent(Event);
    end;
end;

procedure TWatchViewer.Store(var S: TStream);
begin
  TListViewer.Store(S);
  S.Put(Exprs);
end;

function AddWatchDialog: PDialog;
var
  R: TRect;
  Dialog: PDialog;
  Control: PView;
begin
  R.Assign(11, 3, 68, 12);
  Dialog := New(PDialog, Init(R, 'Add Watch'));
  with Dialog^ do
  begin
    Options := Options or ofCentered;
    R.Assign(3, 3, 50, 4);
    Control := SetHelp(New(PEditLine, Init(R, 80)), hcAddWatchExpression);
    Insert(Control);
    Insert(StandardLabel('~W~atch expression', Control, lfTop));
    Insert(StandardHistory(PEditLine(Control), hlAddWatchExpression));
    Insert(OkButton(22, 5));
    Insert(CnlButton(33, 5));
    Insert(HelpButton(44, 5, hcAddWatchDialog));
    SelectNext(False);
  end;
  AddWatchDialog := Dialog;
end;

function EditWatchDialog: PDialog;
var
  R: TRect;
  Dialog: PDialog;
  Control: PView;
begin
  R.Assign(11, 3, 68, 12);
  Dialog := New(PDialog, Init(R, 'Edit Watch'));
  with Dialog^ do
  begin
    Options := Options or ofCentered;
    R.Assign(3, 3, 50, 4);
    Control := SetHelp(New(PInputLine, Init(R, 80)), hcEditWatchExpression);
    Insert(Control);
    Insert(StandardLabel('~W~atch expression', Control, lfTop));
    Insert(StandardHistory(PInputLine(Control), hlEditWatchExpression));
    Insert(OkButton(22, 5));
    Insert(CnlButton(33, 5));
    Insert(HelpButton(44, 5, hcEditWatchDialog));
    SelectNext(False);
  end;
  EditWatchDialog := Dialog;
end;

function WatchWindow: PWindow;
var
  R: TRect;
  Window: PWindow;
begin
  R.Assign(0, 16, 80, 23);
  Window := New(PTurboWindow, Init(R, 'Watches', wnNoNumber, wpWatchWindow));
  with Window^ do
  begin
    HelpCtx := hcWatchWindow;
    Flags := Flags or (wfPutOnBottom + wfSaveable);
    GetExtent(R);
    R.Grow(-1, -1);
    Insert(New(PWatchViewer, Init(R, StandardScrollBar(sbHorizontal),
      StandardScrollBar(sbVertical))));
  end;
  WatchWindow := Window;
end;

end.
