home *** CD-ROM | disk | FTP | other *** search
- {************************************************}
- { }
- { Turbo Pascal for Windows }
- { Demo program }
- { Copyright (c) 1991 by Borland International }
- { }
- {************************************************}
-
- { Simple four function calculator }
-
- program Calc;
-
- {$R CALC.RES}
-
- uses WObjects, WinTypes, WinProcs, Strings;
-
- const
-
- { Application name }
-
- AppName: PChar = 'Calc';
-
- { Number of digits in calculator display }
-
- DisplayDigits = 15;
-
- { Control ID of display static text }
-
- id_Display = 400;
-
- { Color constants }
-
- rgb_Yellow = $0000FFFF;
- rgb_Blue = $00FF0000;
- rgb_Red = $000000FF;
-
- type
-
- { Calculator state }
-
- TCalcState = (cs_First, cs_Valid, cs_Error);
-
- { Calculator dialog window object }
-
- PCalc = ^TCalc;
- TCalc = object(TDlgWindow)
- CalcStatus: TCalcState;
- Number: array[0..DisplayDigits] of Char;
- Negative: Boolean;
- Operator: Char;
- Operand: Real;
- BlueBrush: HBrush;
- constructor Init;
- destructor Done; virtual;
- function GetClassName: PChar; virtual;
- procedure GetWindowClass(var AWndClass: TWndClass); virtual;
- procedure WMControlColor(var Msg: TMessage);
- virtual wm_First + wm_CtlColor;
- procedure WMPaint(var Msg: TMessage);
- virtual wm_First + wm_Paint;
- procedure DefChildProc(var Msg: TMessage); virtual;
- procedure DefCommandProc(var Msg: TMessage); virtual;
- procedure FlashButton(Key: Char);
- procedure CalcKey(Key: Char);
- procedure Clear;
- procedure UpdateDisplay; virtual;
- end;
-
- { Calculator application object }
-
- TCalcApp = object(TApplication)
- procedure InitMainWindow; virtual;
- procedure InitInstance; virtual;
- function ProcessAppMsg(var Message: TMsg) : Boolean; virtual;
- end;
-
- var
-
- { Application instance }
-
- CalcApp: TCalcApp;
-
- { Calculator constructor. Create blue brush for calculator background,
- and do a clear command. }
-
- constructor TCalc.Init;
- begin
- TDlgWindow.Init(nil, AppName);
- BlueBrush := CreateSolidBrush(rgb_Blue);
- Clear;
- end;
-
- { Calculator destructor. Dispose the background brush. }
-
- destructor TCalc.Done;
- begin
- DeleteObject(BlueBrush);
- TDlgWindow.Done;
- end;
-
- { We're changing the window class so we must supply a new class name. }
-
- function TCalc.GetClassName: PChar;
- begin
- GetClassName := AppName;
- end;
-
- { The calculator has its own icon which is installed here. }
-
- procedure TCalc.GetWindowClass(var AWndClass: TWndClass);
- begin
- TDlgWindow.GetWindowClass(AWndClass);
- AWndClass.hIcon := LoadIcon(HInstance, AppName);
- end;
-
- { Colorize the calculator. Allows background to show through corners of
- buttons, uses yellow text on black background in the display, and sets
- the dialog background to blue. }
-
- procedure TCalc.WMControlColor(var Msg: TMessage);
- begin
- case Msg.LParamHi of
- ctlColor_Btn:
- Msg.Result := GetStockObject(null_Brush);
- ctlColor_Static:
- begin
- SetTextColor(Msg.WParam, rgb_Yellow);
- SetBkMode(Msg.WParam, transparent);
- Msg.Result := GetStockObject(black_Brush);
- end;
- ctlcolor_Dlg:
- begin
- SetBkMode(Msg.WParam, Transparent);
- Msg.Result := BlueBrush;
- end;
- else
- DefWndProc(Msg);
- end;
- end;
-
- { Even dialogs can have their background's painted on. This creates
- a red ellipse over the blue background. }
-
- procedure TCalc.WMPaint(var Msg: TMessage);
- var
- OldBrush: HBrush;
- OldPen: HPen;
- R: TRect;
- PS: TPaintStruct;
- begin
- BeginPaint(HWindow, PS);
- OldBrush := SelectObject(PS.hdc, CreateSolidBrush(rgb_Red));
- OldPen := SelectObject(PS.hdc, GetStockObject(null_Pen));
- GetClientRect(HWindow, R);
- R.bottom := R.right;
- OffsetRect(R, -R.right div 4, -R.right div 4);
- Ellipse(PS.hdc, R.left, R.top, R.right, R.bottom);
- SelectObject(PS.hdc, OldPen);
- DeleteObject(SelectObject(PS.hdc, OldBrush));
- EndPaint(HWindow, PS);
- end;
-
- { Flash a button with the value of Key. Looks exactly like a
- click of the button with the mouse. }
-
- procedure TCalc.FlashButton(Key: Char);
- var
- Button: HWnd;
- Delay: Word;
- begin
- if Key = #13 then Key := '=';
- Button := GetDlgItem(HWindow, Integer(UpCase(Key)));
- if Button <> 0 then
- begin
- SendMessage(Button, bm_SetState, 1, 0);
- for Delay := 1 to 30000 do;
- SendMessage(Button, bm_SetState, 0, 0);
- end;
- end;
-
- { Rather then handle each button individually with child ID
- response methods, it is possible to handle them all at
- once with the default child procedure. }
-
- procedure TCalc.DefChildProc(var Msg: TMessage);
- begin
- if (Msg.WParamHi = 0) and (Msg.LParamHi = bn_Clicked) then
- CalcKey(Char(Msg.WParamLo));
- TDlgWindow.DefChildProc(Msg);
- end;
-
- { Rather then handle each accelerator individually with
- command ID response methods, it is possible to handle them
- all at once with the default command procedure. }
-
- procedure TCalc.DefCommandProc(var Msg: TMessage);
- begin
- if Msg.WParamHi = 0 then
- begin
- FlashButton(Char(Msg.WParamLo)); { flash button as if it were pushed }
- CalcKey(Char(Msg.WParamLo));
- end;
- TDlgWindow.DefCommandProc(Msg);
- end;
-
- { Set Display text to the current value. }
-
- procedure TCalc.UpdateDisplay;
- var
- S: array[0..DisplayDigits + 1] of Char;
- begin
- if Negative then StrCopy(S, '-') else S[0] := #0;
- SetWindowText(GetDlgItem(HWindow, id_Display), StrCat(S, Number));
- end;
-
- { Clear the calculator. }
-
- procedure TCalc.Clear;
- begin
- CalcStatus := cs_First;
- StrCopy(Number, '0');
- Negative := False;
- Operator := '=';
- end;
-
- { Process calculator key. }
-
- procedure TCalc.CalcKey(Key: Char);
- var
- R: Real;
-
- procedure Error;
- begin
- CalcStatus := cs_Error;
- StrCopy(Number, 'Error');
- Negative := False;
- end;
-
- procedure SetDisplay(R: Real);
- var
- First, Last: PChar;
- S: array[0..63] of Char;
- begin
- Str(R: 0: 10, S);
- First := S;
- Negative := False;
- if S[0] = '-' then
- begin
- Inc(First);
- Negative := True;
- end;
- if StrLen(First) > DisplayDigits + 1 + 10 then Error else
- begin
- Last := StrEnd(First);
- while Last[Word(-1)] = '0' do Dec(Last);
- if Last[Word(-1)] = '.' then Dec(Last);
- StrLCopy(Number, First, Last - First);
- end;
- end;
-
- procedure GetDisplay(var R: Real);
- var
- E: Integer;
- begin
- Val(Number, R, E);
- if Negative then R := -R;
- end;
-
- procedure CheckFirst;
- begin
- if CalcStatus = cs_First then
- begin
- CalcStatus := cs_Valid;
- StrCopy(Number, '0');
- Negative := False;
- end;
- end;
-
- procedure InsertKey;
- var
- L: Integer;
- begin
- L := StrLen(Number);
- if L < DisplayDigits then
- begin
- Number[L] := Key;
- Number[L + 1] := #0;
- end;
- end;
-
- begin
- Key := UpCase(Key);
- if (CalcStatus = cs_Error) and (Key <> 'C') then Key := ' ';
- case Key of
- '0'..'9':
- begin
- CheckFirst;
- if StrComp(Number, '0') = 0 then Number[0] := #0;
- InsertKey;
- end;
- '.':
- begin
- CheckFirst;
- if StrPos(Number, '.') = nil then InsertKey;
- end;
- #8:
- begin
- CheckFirst;
- if StrLen(Number) = 1 then StrCopy(Number, '0')
- else Number[StrLen(Number) - 1] := #0;
- end;
- '_':
- Negative := not Negative;
- '+', '-', '*', '/', '=', '%', #13:
- begin
- if CalcStatus = cs_Valid then
- begin
- CalcStatus := cs_First;
- GetDisplay(R);
- if Key = '%' then
- case Operator of
- '+', '-': R := Operand * R / 100;
- '*', '/': R := R / 100;
- end;
- case Operator of
- '+': SetDisplay(Operand + R);
- '-': SetDisplay(Operand - R);
- '*': SetDisplay(Operand * R);
- '/': if R = 0 then Error else SetDisplay(Operand / R);
- end;
- end;
- Operator := Key;
- GetDisplay(Operand);
- end;
- 'C':
- Clear;
- end;
- UpdateDisplay;
- end;
-
- { Create calculator as the application's main window. }
-
- procedure TCalcApp.InitMainWindow;
- begin
- MainWindow := New(PCalc, Init);
- end;
-
- { This application loads accelerators so that key input can be used. }
-
- procedure TCalcApp.InitInstance;
- begin
- TApplication.InitInstance;
- HAccTable := LoadAccelerators(HInstance, AppName);
- end;
-
- { This is one of the few places where the order of processing of
- messages is important. The usual order, ProcessDlgMsg,
- ProcessMDIAccels, ProcessAccels, allows an application to define
- accelerators which will not break the keyboard handling in
- child dialogs. In this case, the dialog is the application.
- If we used the default ProcessAppMsg, then the keyboard
- handler, ProcessDlgMsg, would return true and accelerators
- would not be processed. In this case, what we are doing is safe
- because we are not defining any accelerators which conflict
- with the Window's keyboard handling for dialogs. Making this
- change allows us to use keyboard input of the calculator. Also,
- because this is our app, we know that it is not an MDI app,
- therefore we do not need to call ProcessMDIAccels (although it
- would not hurt to do so). }
-
- function TCalcApp.ProcessAppMsg(var Message: TMsg): Boolean;
- begin
- ProcessAppMsg := ProcessAccels(Message) or ProcessDlgMsg(Message);
- end;
-
- begin
- CalcApp.Init(AppName);
- CalcApp.Run;
- CalcApp.Done;
- end.