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

interface

uses Objects, Drivers, Views, Dialogs;

type
  PDialList = ^TDialList;
  TDialList = object(TListViewer)
    function GetText(Item: Integer; MaxLen: Integer): String; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure Redial;
    procedure Abort;
    procedure InsertStr;
    procedure LoadList;
    procedure Paste;
    procedure Delete;
    procedure NewItemPos;
    procedure Update;
  end;

  PDialDialog = ^TDialDialog;
  TDialDialog = object(TDialog)
    DialList: PDialList;
    constructor Init;
    constructor Load(var S: TStream);
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure Store(var S: TStream);
  end;

function DialerReady(Open: Boolean): Boolean;
function GetDialStr: String;
function GetDialStatus: FNameStr;
function EnterDialStr: String;
function AddDialStr(Index: Integer; var S: String): Boolean;
procedure RSRDialDialog(RSR: PResourceFile);
procedure DialDialog;

implementation

uses Dos, Memory, App, MsgBox, StdDlg, HistList, History, Terminal,
  EditMsg, Swaps, ClipPrim, HeapUMB, LFN, DosTools, TPDate, TPString,
  Consts, Commands, Defaults;

function DialerReady(Open: Boolean): Boolean;
begin
  if Open then Message(Application, evCommand, cmTerminal, nil);
  DialerReady := (TheTerminal <> nil) and
    (PTerminal(TheTerminal)^.DialList <> nil);
end;

function GetDialStr: String;
var
  I: Integer;
  P: PString;
  S: String;
begin
  if not DialerReady(False) or
    (PTerminal(TheTerminal)^.DialList^.Count = 0) or
    (PTerminal(TheTerminal)^.State and sfDialing = 0) then GetDialStr := ''
  else with PTerminal(TheTerminal)^ do
  begin
    if DialPtr = nil then S := '' else S := DialPtr^;
    I := 1;
    while I <= Length(S) do
      if (S[I] = ';') and (I > 1) and (S[I - 1] <> '\') then
        S[0] := Char(I - 1)
      else Inc(I);
    TrimSt(S);
    GetDialStr := S;
  end;
end;

function GetDialSeconds: FNameStr;
var
  DelaySec: Word;
  Code: Integer;
  D, T: Time;
  S: String;
begin
  S := '';
  Val(RedialDelay, DelaySec, Code);
  if (DelaySec <> 0) and DialerReady(False) then
  begin
    D := PTerminal(TheTerminal)^.DialTime;
    if D = -1 then D := DelaySec
    else
    begin
      T := CurrentTime;
      if T < D then Inc(T, SecondsInDay);
      Inc(D, DelaySec);
      if T >= D then D := 1 else Dec(D, T);
    end;
    FormatStr(S, ' (%d)', D);
  end;
  GetDialSeconds := S;
end;

function GetDialStatus: FNameStr;
var
  S: FNameStr;
begin
  S := GetDialStr;
  if S = '' then GetDialStatus := ''
  else
  begin
    if S[0] > #24 then S[0] := #24;
    GetDialStatus := 'Calling ' + S + GetDialSeconds +
      '. Space - redial, Esc - abort.';
  end;
end;

function EnterDialStr: String;
var
  D: PDialog;
  Control: PInputLine;
  R: TRect;
  S: String;
begin
  R.Assign(0, 0, 51, 7);
  D := New(PDialog, Init(R, 'Dial'));
  with D^ do
  begin
    Options := Options or ofCentered;
    HelpCtx := hcDial;
    R.Assign(16, 2, Size.X - 6, 3);
    Control := New(PInputLine, Init(R, 255));
    Insert(Control);
    StandardHistory(Control, PhoneHistoryId);
    R.Assign(2, 2, 16, 3);
    Insert(New(PLabel, Init(R, 'P~h~one number', Control)));
    R.Assign(15, Size.Y - 3, 25, Size.Y - 1);
    StandardOkButton(R, D);
    Inc(R.A.X, 23); Inc(R.B.X, 23);
    StandardCancelButton(R, D);
    SelectNext(False);
  end;
  S := HistoryStr(PhoneHistoryID, 0);
  if Application^.ExecuteDialog(D, @S, True) = cmCancel then
    EnterDialStr := ''
  else EnterDialStr := S;
end;

function AddDialStr(Index: Integer; var S: String): Boolean;
begin
  if PTerminal(TheTerminal)^.DialList^.Count = 255 then
  begin
    MessageBox(^C'Too many phone numbers.', nil, mfError + mfOkButton);
    AddDialStr := False;
    Exit;
  end;
  TrimSt(S);
  if S <> '' then
  begin
{    PTerminal(TheTerminal)^.DialList^.AtInsert(Index, NewStr(S));}
    PTerminal(TheTerminal)^.DialList^.AtInsert(Index, NewStrUMB(S));
    Inc(Index);
  end;
  Str(Index, S);
  AddDialStr := not LowMemory;
end;

procedure UpdateCommands;
begin
  with Application^ do
    if not DialerReady(False) or
      (PTerminal(TheTerminal)^.DialList^.Count = 0) then
        DisableCommands(ModifyCommands + DeleteCommands +
          RunItemCommands + NewItemPosCommands)
    else
    begin
      EnableCommands(ModifyCommands + DeleteCommands);
      if PTerminal(TheTerminal)^.State and sfDialing <> 0 then
        EnableCommands(RunItemCommands)
      else DisableCommands(RunItemCommands);
      if PTerminal(TheTerminal)^.DialList^.Count > 1 then
        EnableCommands(NewItemPosCommands)
      else DisableCommands(NewItemPosCommands);
    end;
end;

function TDialList.GetText(Item: Integer; MaxLen: Integer): String;
var
  C: Char;
  P: PString;
begin
  if not DialerReady(False) or
    (Item > PTerminal(TheTerminal)^.DialList^.Count - 1) then GetText := ''
  else with PTerminal(TheTerminal)^ do
  begin
    P := DialList^.At(Item);
    if (State and sfDialing <> 0) and (P = DialPtr) then
      if Options and ofCalling <> 0 then C := 'û'
      else C := ''
    else C := ' ';
    GetText := C + ' ' + P^;
  end;
end;

procedure TDialList.HandleEvent(var Event: TEvent);
begin
  if ((Event.What = evMouseDown) and Event.Double) or
     ((Event.What = evKeyDown) and (Event.CharCode = ' ')) then
  begin
    Pending.What := evCommand;
    Pending.Command := cmModifyItem;
    Event.What := evNothing;
  end
  else TListViewer.HandleEvent(Event);
  if (Event.What = evBroadcast) and (Event.Command = cmUpdateViews) then
  begin
    Update;
    Event.What := evNothing;
  end;

  if Event.What = evKeyDown then
  begin
    case Event.KeyCode of
      kbCtrlUp: if DialerReady(False) and not MoveListItem(
	@Self, PTerminal(TheTerminal)^.DialList, True) then Exit;
      kbCtrlDn: if DialerReady(False) and not MoveListItem(
	@Self, PTerminal(TheTerminal)^.DialList, False) then Exit;
      else Exit;
    end;
    Event.What := evNothing;
  end;
end;

procedure TDialList.Redial;
var
  I: Integer;
begin
  if DialerReady(True) then with PTerminal(TheTerminal)^ do
    if Focused < DialList^.Count then
    begin
      if State and sfDialing <> 0 then Abort;
      I := Focused - 1;
      if I < 0 then I := DialList^.Count - 1;
      DialPtr := DialList^.At(I);
      State := State or sfDialing;
    end;
  if GetShiftState and kbCtrlShift <> 0 then EndModal(cmCancel);
end;

procedure TDialList.Abort;
begin
  if DialerReady(False) then
  begin
    PTerminal(TheTerminal)^.AbortDialing(True);
    Update;
  end;
end;

procedure TDialList.InsertStr;
var
  S: String;
begin
  if DialerReady(True) then
  begin
    S := EnterDialStr;
    AddDialStr(Focused, S);
    Update;
  end;
end;

procedure TDialList.LoadList;
var
  I, Code: Integer;
  F: lText;
  S: String;
begin
  S := '';
  if Application^.ExecuteDialog(New(PFileDialog, Init('*.DIA',
    'Phone dialer list file', '~N~ame', fdOkButton + fdHelpButton,
    HistoryID)), @S, False) = cmCancel then Exit;
  LogEvent := lcDiskRead;
  DrawStatusLine('Loading phone numbers from ' +
    TrimmedName(S, 40) + '.');
  lAssignText(F, S);
  Reset(F.T);
  if IOResult <> 0 then
  begin
    CouldNotOpenMsgBox(S);
    Exit;
  end;
  if IsDevice(TextRec(F.T).Handle) then
  begin
    Close(F.T);
    InOutRes := 0;
    FileIsDeviceMsgBox(S);
    Exit;
  end;
  I := Focused;
  if DialerReady(True) then while not Eof(F.T) do
  begin
    Readln(F.T, S);
    if not AddDialStr(I, S) then Break;
    Val(S, I, Code);
  end;
  Close(F.T);
  InOutRes := 0;
  Update;
end;

procedure TDialList.Paste;
var
  I, II, Code: Integer;
  S: String;
begin
  if (DiskClipStream = nil) or not ClipAreaSelected then Exit;
  if not DialerReady(True) then Exit;
  DrawStatusLine('Pasting the selected phone numbers.');
  Message(Desktop, evBroadcast, cmUpdateViews, nil);
  II := Focused;
  for I := ClipArea^.KB to ClipArea^.KK do
  begin
    GetClipLine(I, S);
    if not AddDialStr(II, S) then Break;
    Val(S, II, Code);
  end;
  Update;
end;

procedure TDialList.Delete;
begin
  if DialerReady(False) then with PTerminal(TheTerminal)^ do
  begin
    if GetShiftState and kbCtrlShift <> 0 then DialList^.FreeAll
    else if Focused < DialList^.Count then DialList^.AtFree(Focused);
    Update;
  end;
end;

procedure TDialList.NewItemPos;
var
  SaveFocused, LastFocused: Integer;
  SaveCtx: Word;
  P: Pointer;
  Event: TEvent;
begin
  if DialerReady(False) then
  begin
    if Focused >= PTerminal(TheTerminal)^.DialList^.Count then Exit;
    P := PTerminal(TheTerminal)^.DialList^.At(Focused);
    SaveFocused := Focused;
    SaveCtx := Owner^.HelpCtx;
    Owner^.HelpCtx := hcNewItemPos;
    repeat
      GetEvent(Event);
      HandleEvent(Event);
      LastFocused := PTerminal(TheTerminal)^.DialList^.IndexOf(P);
      if (Focused < PTerminal(TheTerminal)^.DialList^.Count) and
         (LastFocused <> -1) and (LastFocused <> Focused) then
      begin
        PTerminal(TheTerminal)^.DialList^.AtDelete(LastFocused);
	PTerminal(TheTerminal)^.DialList^.AtInsert(Focused, P);
        FocusItem(Focused);
        DrawView;
      end;
      if ((Event.What = evKeyDown) and (Event.KeyCode = kbEnter)) or
         ((Event.What = evCommand) and (Event.Command = cmOk)) then Break;
      if (Event.What = evKeyDown) and (Event.KeyCode = kbEsc) then
      begin
	PTerminal(TheTerminal)^.DialList^.AtDelete(LastFocused);
	PTerminal(TheTerminal)^.DialList^.AtInsert(SaveFocused, P);
	FocusItem(SaveFocused);
	DrawView;
	Break;
      end;
    until False;
    Owner^.HelpCtx := SaveCtx;
  end;
  Select;
end;

procedure TDialList.Update;
begin
  if DialerReady(False) then SetRange(
    PTerminal(TheTerminal)^.DialList^.Count + 1)
  else SetRange(1);
  FocusItem(Focused);
  DrawView;
  UpdateCommands;
end;

constructor TDialDialog.Init;
var
  VBar, HBar: PScrollBar;
  R: TRect;
begin
  R.Assign(0, 0, 51, 21);
  TDialog.Init(R, 'Dialer');
  Options := Options or ofCentered;
  HelpCtx := hcDialer;

  R.Assign(3, 18, 36, 19);
  HBar := New(PScrollBar, Init(R));
  HBar^.Options := HBar^.Options or ofPostProcess;
  HBar^.SetRange(0, 255);
  Insert(HBar);
  R.Assign(36, 2, 37, 18);
  VBar := New(PScrollBar, Init(R));
  VBar^.Options := VBar^.Options or ofPostProcess;
  Insert(VBar);

  R.Assign(3, 2, 36, 18);
  DialList := New(PDialList, Init(R, 1, HBar, VBar));
  Insert(DialList);
  R.Assign(4, 1, 37, 2);
  Insert(New(PLabel, Init(R, 'P~h~one number', DialList)));

  R.Assign(38, 2, 48, 4);
  Insert(New(PButton, Init(R, '~R~edial', cmModifyItem, bfDefault)));
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  Insert(New(PButton, Init(R, '~A~bort', cmRunItem, bfNormal)));
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  Insert(New(PButton, Init(R, '~I~nsert', cmDial, bfNormal)));
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  Insert(New(PButton, Init(R, '~L~oad', cmFileOpen, bfNormal)));
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  Insert(New(PButton, Init(R, '~P~aste', cmGetLine, bfNormal)));
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  Insert(New(PButton, Init(R, '~D~elete', cmDeleteItem, bfNormal)));
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  Insert(New(PButton, Init(R, 'Ad~j~ust', cmNewItemPos, bfNormal)));
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  StandardCancelButton(R, @Self);
  Inc(R.A.Y, 2); Inc(R.B.Y, 2);
  StandardHelpButton(R, @Self);

  DialList^.Select;
end;

constructor TDialDialog.Load(var S: TStream);
begin
  Inherited Load(S);
  GetSubViewPtr(S, DialList);
  DialList^.Update;
end;

procedure TDialDialog.HandleEvent(var Event: TEvent);
begin
  Inherited HandleEvent(Event);
  if Event.What = evKeyDown then
    case Event.KeyCode of
      kbCtrlEnter:
        begin
          Event.What := evCommand;
          Event.Command := cmModifyItem;
        end;
      kbDel, kbCtrlDel:
        begin
          Event.What := evCommand;
          Event.Command := cmDeleteItem;
        end;
      kbShiftIns:
        begin
          Event.What := evCommand;
          Event.Command := cmGetLine;
        end;
      kbIns:
        begin
          Event.What := evCommand;
          Event.Command := cmDial;
        end;
    end;
  if Event.What = evCommand then
  begin
    case Event.Command of
      cmModifyItem:
        if CommandEnabled(cmModifyItem) then DialList^.Redial;
      cmRunItem: DialList^.Abort;
      cmDeleteItem: DialList^.Delete;
      cmDial: DialList^.InsertStr;
      cmFileOpen: DialList^.LoadList;
      cmGetLine: DialList^.Paste;
      cmNewItemPos: DialList^.NewItemPos;
      else Exit;
    end;
    Event.What := evNothing;
  end;
end;

procedure TDialDialog.Store(var S: TStream);
begin
  TDialog.Store(S);
  PutSubViewPtr(S, DialList);
end;

procedure RSRDialDialog(RSR: PResourceFile);
var
  D: PDialDialog;
begin
  D := New(PDialDialog, Init);
  RSR^.Put(D, 'DIALERDIALOG');
  Dispose(D, Done);
end;

procedure DialDialog;
var
  D: PDialDialog;
begin
  D := PDialDialog(Resource^.Get('DIALERDIALOG'));
  if D <> nil then Application^.ExecuteDialog(D, nil, True)
  else ResourceNotFoundMsgBox('Dialer dialog');
end;

end.