unit fCalc;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, PdxForms;

type
  TF_Calculator = class(TParadoxForm)
    mDisplay: TMemo;
    Panel1: TPanel;
    Key1: TButton;
    Key2: TButton;
    Key3: TButton;
    Key7: TButton;
    Key8: TButton;
    Key9: TButton;
    Key4: TButton;
    Key5: TButton;
    Key6: TButton;
    Key0: TButton;
    KeyDot: TButton;
    bPlus: TButton;
    bMinus: TButton;
    bMult: TButton;
    bDiv: TButton;
    bEquals: TButton;
    bClear: TButton;
    bClearEntry: TButton;
    procedure bClearClick(Sender: TObject);
    procedure bClearEntryClick(Sender: TObject);
    procedure NumberKeyClick(Sender: TObject);
    procedure KeyDotClick(Sender: TObject);
    procedure OperatorClick(Sender: TObject);
    procedure EqualsClick(Sender: TObject);
  private
    { Private declarations }
    cSavedOp :Char;
    LastNumber :Extended;
    Procedure ShowText(S:String);
    Function GetCurrentLine:String;
    Function GetCurrentNumber:Extended;
    Procedure SetCurrentLine(newValue:String);
    Procedure SetCurrentNumber(newValue:Extended);
  public
    { Public declarations }
  published
    Property RunningTotal:Extended read LastNumber;
    Property CurrentLine:String read getCurrentLine write setCurrentLine;
    Property CurrentNumber:Extended read getCurrentNumber write setCurrentNumber;
    Property Display: TMemo read mDisplay;
  end;

var
  F_Calculator: TF_Calculator;

implementation

{$R *.DFM}

procedure TF_Calculator.bClearClick(Sender: TObject);
begin
  mDisplay.Clear;
end;

procedure TF_Calculator.bClearEntryClick(Sender: TObject);
begin
  if mDisplay.Lines.Count > 0 then
    mDisplay.Lines.Strings[mDisplay.Lines.Count-1] := '';
end;

procedure TF_Calculator.ShowText(S:String);
begin
  SendMessage( mDisplay.Handle, EM_REPLACESEL, Longint(TRUE), Longint(PChar(S)) );
end;

function TF_Calculator.GetCurrentLine:String;
Begin
  if mDisplay.Lines.Count = 0 then
    Result := ''
  else
    Result := mDisplay.Lines.Strings[mDisplay.Lines.Count-1];
End;

Function TF_Calculator.GetCurrentNumber:Extended;
Var
  Line:String;
  S,E :PChar;
Begin
  Line := GetCurrentLine;
  UniqueString(Line);
  S := PChar(Line);
  While S^ in [#9,' '] do Inc(S);
  E := S;
  While E^ in ['0'..'9','.'] do Inc(E);
  E^ := #0;
  If S=E Then
    Result := 0
  Else
    Result := StrToFloat(S);
End;

Procedure TF_Calculator.SetCurrentLine(newValue:String);
Begin
  if mDisplay.Lines.Count > 0 then
    mDisplay.Lines.Strings[mDisplay.Lines.Count-1] := newValue
  Else
    mDisplay.Text := newValue;
End;

Procedure TF_Calculator.SetCurrentNumber(newValue:Extended);
Begin
  SetCurrentLine( FloatToStr( newValue ) );
End;

procedure TF_Calculator.NumberKeyClick(Sender: TObject);
begin
  with Sender as TButton do
    ShowText(Caption);
end;

procedure TF_Calculator.KeyDotClick(Sender: TObject);
begin
  if mDisplay.Lines.Count > 0 then
    if Pos( '.', mDisplay.Lines.Strings[mDisplay.Lines.Count-1]) <> 0 Then
      Exit;
  ShowText('.');
end;

procedure TF_Calculator.OperatorClick(Sender: TObject);
begin
  With Sender as TButton do
    Begin
      ShowText(' ' + Caption);
      EqualsClick(Sender);
      cSavedOp := Caption[1];
    End;
end;

procedure TF_Calculator.EqualsClick(Sender: TObject);
Var ThisNumber :Extended;
    ThisLine :String;
    L :Integer;
begin
  ThisLine := GetCurrentLine;
  L := Length(ThisLine);
  While (L > 0) and (ThisLine[L] in [' ','+','-','*','/'])
    do Dec(L);
  If L = 0 Then Exit;

  ThisNumber := StrToFloat( Copy( ThisLine, 1, L ) );
  Case cSavedOp of
  #0:  LastNumber := ThisNumber;
  '+': LastNumber := LastNumber + ThisNumber;
  '-': LastNumber := LastNumber - ThisNumber;
  '*': LastNumber := LastNumber * ThisNumber;
  '/': LastNumber := LastNumber / ThisNumber;
  else
    Raise Exception.CreateFmt('Unknown operation "%s"',[cSavedOp]);
  end;
  if Sender = bEquals Then
    Begin
      ShowText(' ='#13#10 + FloatToStr(LastNumber));
    End
  Else { Operator }
    Begin
      ShowText(' (' + FloatToStr(LastNumber) + ')'#13#10);
    End;
  cSavedOp := #0;
end;

end.
