{$I-,X+,O+,F+}
unit BarWin;

interface

uses Objects, Drivers, Views, Dialogs, App;

type
  PBarView = ^TBarView;
  TBarView = object(TView)
    Volume, Count: Longint;
    Width: Byte;
    Left: Boolean;
    constructor Init(var Bounds: TRect; AVolume: Longint; ALeft: Boolean);
    function GetPalette: PPalette; virtual;
    procedure Draw; virtual;
    procedure Update;
  end;

  PBarWindow = ^TBarWindow;
  TBarWindow = object(TWindow)
    Bar: PBarView;
    NotReady: PStaticText;
    constructor Init(const ATitle, AMessage: String; AVolume: Longint;
      ALeft: Boolean);
    function GetPalette: PPalette; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure SetState(AState: Word; Enable: Boolean); virtual;
  end;

const
  CBarColor      = #$3E#$3F#$3A#$3E#$70#$30#$70#$03;
  CBarBlackWhite = #$07#$0F#$07#$0F#$78#$07#$70#$70;
  CBarMonochrome = #$07#$0F#$07#$0F#$78#$07#$70#$70;
  CBarView       = #8;
  CBarWindow     = #80#81#82#83#84#85#86#87;

procedure BarWindow(const ATitle, AMessage: String; AVolume: Longint;
  ALeft: Boolean);

implementation

uses Commands;

constructor TBarView.Init(var Bounds: TRect; AVolume: Longint;
  ALeft: Boolean);
begin
  TView.Init(Bounds);
  Volume := AVolume;
  Left := ALeft;
end;

function TBarView.GetPalette: PPalette;
const
  P: String[Length(CBarView)] = CBarView;
begin
  GetPalette := @P;
end;

procedure TBarView.Draw;
var
  C: Byte;
  B: TDrawBuffer;
begin
  C := GetColor(1);
  MoveChar(B, '²', C, Size.X);
  if Left then MoveChar(B[Size.X - Width], ' ', C, Width)
  else MoveChar(B, ' ', C, Width);
  WriteLine(0, 0, Size.X, 1, B);
end;

procedure TBarView.Update;
var
  NewWidth: Byte;
begin
  Inc(Count);
  if Volume = 0 then NewWidth := Size.X
  else NewWidth := Trunc(Size.X / (Volume / Count));
  if NewWidth > Size.X then NewWidth := Size.X;
  if NewWidth <> Width then
  begin
    Width := NewWidth;
    DrawView;
  end;
end;

constructor TBarWindow.Init(const ATitle, AMessage: String;
  AVolume: Longint; ALeft: Boolean);
var
  R: TRect;
begin
  R.Assign(0, 0, 36, 9);
  TWindow.Init(R, ATitle, wnNoNumber);
  Options := (Options or ofCentered) and not (ofSelectable + ofTopSelect);
  GrowMode := gfExplode;
  Flags := wfMove;
  Frame^.State := Frame^.State or sfActive;

  R.Assign(2, 2, 35, 3);
  Insert(New(PStaticText, Init(R, ^C'Press Esc to abort.')));

  R.Assign(2, 4, 34, 5);
  Bar := New(PBarView, Init(R, AVolume, ALeft));
  Insert(Bar);

  R.Assign(2, 6, 35, 7);
  NotReady := New(PStaticText, Init(R, ^C + AMessage));
  Insert(NotReady);
  NotReady^.Hide;
end;

function TBarWindow.GetPalette: PPalette;
const
  P: String[Length(CBarWindow)] = CBarWindow;
begin
  GetPalette := @P;
end;

procedure TBarWindow.HandleEvent(var Event: TEvent);
begin
  if Event.What = evBroadcast then
  begin
    case Event.Command of
      cmUpdateBar: Bar^.Update;
      cmNotReadyShow: if NotReady <> nil then NotReady^.Show;
      cmNotReadyHide: if NotReady <> nil then NotReady^.Hide;
      cmBarVolume: Bar^.Volume := Event.InfoLong;
      cmResetBar: Bar^.Count := 0;
      cmCloseBar:
	begin
	  ClearEvent(Event);
	  State := State or sfFocused;
	  Close;
	  Exit;
	end;
      else Exit;
    end;
    ClearEvent(Event);
  end
  else TWindow.HandleEvent(Event);
end;

procedure TBarWindow.SetState(AState: Word; Enable: Boolean);
begin
  TGroup.SetState(AState, Enable);
end;

procedure BarWindow(const ATitle, AMessage: String; AVolume: Longint;
  ALeft: Boolean);
begin
  Application^.Insert(Application^.ValidView(
    New(PBarWindow, Init(ATitle, AMessage, AVolume, ALeft))));
end;

end.