home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / tpwinst / owldemos.pak / CALC.PAS < prev    next >
Pascal/Delphi Source File  |  1991-05-21  |  10KB  |  381 lines

  1. {************************************************}
  2. {                                                }
  3. {   Turbo Pascal for Windows                     }
  4. {   Demo program                                 }
  5. {   Copyright (c) 1991 by Borland International  }
  6. {                                                }
  7. {************************************************}
  8.  
  9. { Simple four function calculator }
  10.  
  11. program Calc;
  12.  
  13. {$R CALC.RES}
  14.  
  15. uses WObjects, WinTypes, WinProcs, Strings;
  16.  
  17. const
  18.  
  19. { Application name }
  20.  
  21.   AppName: PChar = 'Calc';
  22.  
  23. { Number of digits in calculator display }
  24.  
  25.   DisplayDigits = 15;
  26.  
  27. { Control ID of display static text }
  28.  
  29.   id_Display = 400;
  30.  
  31. { Color constants }
  32.  
  33.   rgb_Yellow = $0000FFFF;
  34.   rgb_Blue   = $00FF0000;
  35.   rgb_Red    = $000000FF;
  36.  
  37. type
  38.  
  39. { Calculator state }
  40.  
  41.   TCalcState = (cs_First, cs_Valid, cs_Error);
  42.  
  43. { Calculator dialog window object }
  44.  
  45.   PCalc = ^TCalc;
  46.   TCalc = object(TDlgWindow)
  47.     CalcStatus: TCalcState;
  48.     Number: array[0..DisplayDigits] of Char;
  49.     Negative: Boolean;
  50.     Operator: Char;
  51.     Operand: Real;
  52.     BlueBrush: HBrush;
  53.     constructor Init;
  54.     destructor Done; virtual;
  55.     function GetClassName: PChar; virtual;
  56.     procedure GetWindowClass(var AWndClass: TWndClass); virtual;
  57.     procedure WMControlColor(var Msg: TMessage);
  58.       virtual wm_First + wm_CtlColor;
  59.     procedure WMPaint(var Msg: TMessage);
  60.       virtual wm_First + wm_Paint;
  61.     procedure DefChildProc(var Msg: TMessage); virtual;
  62.     procedure DefCommandProc(var Msg: TMessage); virtual;
  63.     procedure FlashButton(Key: Char);
  64.     procedure CalcKey(Key: Char);
  65.     procedure Clear;
  66.     procedure UpdateDisplay; virtual;
  67.   end;
  68.  
  69. { Calculator application object }
  70.  
  71.   TCalcApp = object(TApplication)
  72.     procedure InitMainWindow; virtual;
  73.     procedure InitInstance; virtual;
  74.     function ProcessAppMsg(var Message: TMsg) : Boolean; virtual;
  75.   end;
  76.  
  77. var
  78.  
  79. { Application instance }
  80.  
  81.   CalcApp: TCalcApp;
  82.  
  83. { Calculator constructor.  Create blue brush for calculator background,
  84.   and do a clear command. }
  85.  
  86. constructor TCalc.Init;
  87. begin
  88.   TDlgWindow.Init(nil, AppName);
  89.   BlueBrush := CreateSolidBrush(rgb_Blue);
  90.   Clear;
  91. end;
  92.  
  93. { Calculator destructor.  Dispose the background brush. }
  94.  
  95. destructor TCalc.Done;
  96. begin
  97.   DeleteObject(BlueBrush);
  98.   TDlgWindow.Done;
  99. end;
  100.  
  101. { We're changing the window class so we must supply a new class name. }
  102.  
  103. function TCalc.GetClassName: PChar;
  104. begin
  105.   GetClassName := AppName;
  106. end;
  107.  
  108. { The calculator has its own icon which is installed here. }
  109.  
  110. procedure TCalc.GetWindowClass(var AWndClass: TWndClass);
  111. begin
  112.   TDlgWindow.GetWindowClass(AWndClass);
  113.   AWndClass.hIcon := LoadIcon(HInstance, AppName);
  114. end;
  115.  
  116. { Colorize the calculator.  Allows background to show through corners of
  117.   buttons, uses yellow text on black background in the display, and sets
  118.   the dialog background to blue. }
  119.  
  120. procedure TCalc.WMControlColor(var Msg: TMessage);
  121. begin
  122.   case Msg.LParamHi of
  123.     ctlColor_Btn:
  124.       Msg.Result := GetStockObject(null_Brush);
  125.     ctlColor_Static:
  126.       begin
  127.         SetTextColor(Msg.WParam, rgb_Yellow);
  128.         SetBkMode(Msg.WParam, transparent);
  129.         Msg.Result := GetStockObject(black_Brush);
  130.       end;
  131.     ctlcolor_Dlg:
  132.       begin
  133.         SetBkMode(Msg.WParam, Transparent);
  134.         Msg.Result := BlueBrush;
  135.       end;
  136.   else
  137.     DefWndProc(Msg);
  138.   end;
  139. end;
  140.  
  141. { Even dialogs can have their background's painted on.  This creates
  142.   a red ellipse over the blue background. }
  143.  
  144. procedure TCalc.WMPaint(var Msg: TMessage);
  145. var
  146.   OldBrush: HBrush;
  147.   OldPen: HPen;
  148.   R: TRect;
  149.   PS: TPaintStruct;
  150. begin
  151.   BeginPaint(HWindow, PS);
  152.   OldBrush := SelectObject(PS.hdc, CreateSolidBrush(rgb_Red));
  153.   OldPen := SelectObject(PS.hdc, GetStockObject(null_Pen));
  154.   GetClientRect(HWindow, R);
  155.   R.bottom := R.right;
  156.   OffsetRect(R, -R.right div 4, -R.right div 4);
  157.   Ellipse(PS.hdc, R.left, R.top, R.right, R.bottom);
  158.   SelectObject(PS.hdc, OldPen);
  159.   DeleteObject(SelectObject(PS.hdc, OldBrush));
  160.   EndPaint(HWindow, PS);
  161. end;
  162.  
  163. { Flash a button with the value of Key.  Looks exactly like a
  164.   click of the button with the mouse. }
  165.  
  166. procedure TCalc.FlashButton(Key: Char);
  167. var
  168.   Button: HWnd;
  169.   Delay: Word;
  170. begin
  171.   if Key = #13 then Key := '=';
  172.   Button := GetDlgItem(HWindow, Integer(UpCase(Key)));
  173.   if Button <> 0 then
  174.   begin
  175.     SendMessage(Button, bm_SetState, 1, 0);
  176.     for Delay := 1 to 30000 do;
  177.     SendMessage(Button, bm_SetState, 0, 0);
  178.   end;
  179. end;
  180.  
  181. { Rather then handle each button individually with child ID
  182.   response methods, it is possible to handle them all at
  183.   once with the default child procedure. }
  184.  
  185. procedure TCalc.DefChildProc(var Msg: TMessage);
  186. begin
  187.   if (Msg.WParamHi = 0) and (Msg.LParamHi = bn_Clicked) then
  188.     CalcKey(Char(Msg.WParamLo));
  189.   TDlgWindow.DefChildProc(Msg);
  190. end;
  191.  
  192. { Rather then handle each accelerator individually with
  193.   command ID response methods, it is possible to handle them
  194.   all at once with the default command procedure. }
  195.  
  196. procedure TCalc.DefCommandProc(var Msg: TMessage);
  197. begin
  198.   if Msg.WParamHi = 0 then
  199.   begin
  200.     FlashButton(Char(Msg.WParamLo)); { flash button as if it were pushed }
  201.     CalcKey(Char(Msg.WParamLo));
  202.   end;
  203.   TDlgWindow.DefCommandProc(Msg);
  204. end;
  205.  
  206. { Set Display text to the current value. }
  207.  
  208. procedure TCalc.UpdateDisplay;
  209. var
  210.   S: array[0..DisplayDigits + 1] of Char;
  211. begin
  212.   if Negative then StrCopy(S, '-') else S[0] := #0;
  213.   SetWindowText(GetDlgItem(HWindow, id_Display), StrCat(S, Number));
  214. end;
  215.  
  216. { Clear the calculator. }
  217.  
  218. procedure TCalc.Clear;
  219. begin
  220.   CalcStatus := cs_First;
  221.   StrCopy(Number, '0');
  222.   Negative := False;
  223.   Operator := '=';
  224. end;
  225.  
  226. { Process calculator key. }
  227.  
  228. procedure TCalc.CalcKey(Key: Char);
  229. var
  230.   R: Real;
  231.  
  232.   procedure Error;
  233.   begin
  234.     CalcStatus := cs_Error;
  235.     StrCopy(Number, 'Error');
  236.     Negative := False;
  237.   end;
  238.  
  239.   procedure SetDisplay(R: Real);
  240.   var
  241.     First, Last: PChar;
  242.     S: array[0..63] of Char;
  243.   begin
  244.     Str(R: 0: 10, S);
  245.     First := S;
  246.     Negative := False;
  247.     if S[0] = '-' then
  248.     begin
  249.       Inc(First);
  250.       Negative := True;
  251.     end;
  252.     if StrLen(First) > DisplayDigits + 1 + 10 then Error else
  253.     begin
  254.       Last := StrEnd(First);
  255.       while Last[Word(-1)] = '0' do Dec(Last);
  256.       if Last[Word(-1)] = '.' then Dec(Last);
  257.       StrLCopy(Number, First, Last - First);
  258.     end;
  259.   end;
  260.  
  261.   procedure GetDisplay(var R: Real);
  262.   var
  263.     E: Integer;
  264.   begin
  265.     Val(Number, R, E);
  266.     if Negative then R := -R;
  267.   end;
  268.  
  269.   procedure CheckFirst;
  270.   begin
  271.     if CalcStatus = cs_First then
  272.     begin
  273.       CalcStatus := cs_Valid;
  274.       StrCopy(Number, '0');
  275.       Negative := False;
  276.     end;
  277.   end;
  278.  
  279.   procedure InsertKey;
  280.   var
  281.     L: Integer;
  282.   begin
  283.     L := StrLen(Number);
  284.     if L < DisplayDigits then
  285.     begin
  286.       Number[L] := Key;
  287.       Number[L + 1] := #0;
  288.     end;
  289.   end;
  290.  
  291. begin
  292.   Key := UpCase(Key);
  293.   if (CalcStatus = cs_Error) and (Key <> 'C') then Key := ' ';
  294.   case Key of
  295.     '0'..'9':
  296.       begin
  297.         CheckFirst;
  298.         if StrComp(Number, '0') = 0 then Number[0] := #0;
  299.         InsertKey;
  300.       end;
  301.     '.':
  302.       begin
  303.         CheckFirst;
  304.         if StrPos(Number, '.') = nil then InsertKey;
  305.       end;
  306.     #8:
  307.       begin
  308.         CheckFirst;
  309.         if StrLen(Number) = 1 then StrCopy(Number, '0')
  310.         else Number[StrLen(Number) - 1] := #0;
  311.       end;
  312.     '_':
  313.       Negative := not Negative;
  314.     '+', '-', '*', '/', '=', '%', #13:
  315.       begin
  316.         if CalcStatus = cs_Valid then
  317.         begin
  318.           CalcStatus := cs_First;
  319.           GetDisplay(R);
  320.           if Key = '%' then
  321.             case Operator of
  322.               '+', '-': R := Operand * R / 100;
  323.               '*', '/': R := R / 100;
  324.             end;
  325.           case Operator of
  326.             '+': SetDisplay(Operand + R);
  327.             '-': SetDisplay(Operand - R);
  328.             '*': SetDisplay(Operand * R);
  329.             '/': if R = 0 then Error else SetDisplay(Operand / R);
  330.           end;
  331.         end;
  332.         Operator := Key;
  333.         GetDisplay(Operand);
  334.       end;
  335.     'C':
  336.       Clear;
  337.   end;
  338.   UpdateDisplay;
  339. end;
  340.  
  341. { Create calculator as the application's main window. }
  342.  
  343. procedure TCalcApp.InitMainWindow;
  344. begin
  345.   MainWindow := New(PCalc, Init);
  346. end;
  347.  
  348. { This application loads accelerators so that key input can be used. }
  349.  
  350. procedure TCalcApp.InitInstance;
  351. begin
  352.   TApplication.InitInstance;
  353.   HAccTable := LoadAccelerators(HInstance, AppName);
  354. end;
  355.  
  356. { This is one of the few places where the order of processing of
  357.   messages is important.  The usual order, ProcessDlgMsg,
  358.   ProcessMDIAccels, ProcessAccels, allows an application to define
  359.   accelerators which will not break the keyboard handling in
  360.   child dialogs.  In this case, the dialog is the application.
  361.   If we used the default ProcessAppMsg, then the keyboard
  362.   handler, ProcessDlgMsg, would return true and accelerators
  363.   would not be processed.  In this case, what we are doing is safe
  364.   because we are not defining any accelerators which conflict
  365.   with the Window's keyboard handling for dialogs.  Making this
  366.   change allows us to use keyboard input of the calculator.  Also,
  367.   because this is our app, we know that it is not an MDI app,
  368.   therefore we do not need to call ProcessMDIAccels (although it
  369.   would not hurt to do so). }
  370.  
  371. function TCalcApp.ProcessAppMsg(var Message: TMsg): Boolean;
  372. begin
  373.   ProcessAppMsg := ProcessAccels(Message) or ProcessDlgMsg(Message);
  374. end;
  375.  
  376. begin
  377.   CalcApp.Init(AppName);
  378.   CalcApp.Run;
  379.   CalcApp.Done;
  380. end.
  381.