home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c082_122 / 6.ddi / CALC.ZIP / CALC.CPP next >
Encoding:
C/C++ Source or Header  |  1992-06-10  |  10.0 KB  |  394 lines

  1. // ObjectWindows - (C) Copyright 1992 by Borland International
  2.  
  3. /* Simple four function calculator */
  4. #include <owl.h>
  5. #include <dialog.h>
  6. #include <string.h>
  7. #include <ctype.h>
  8. #include <math.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11.  
  12. #define min(a,b) (((a) < (b)) ? (a) : (b))
  13.  
  14. #define APPNAME "Calc"
  15.  
  16. /* Number of digits in calculator display */
  17.   const int DisplayDigits = 15;
  18.  
  19. /* Control ID of display static text */
  20.   const int ID_DISPLAY = 400;
  21.  
  22. /* Color constants */
  23.   const long RGB_YELLOW = 0x0000FFFFL;
  24.   const long RGB_BLUE   = 0x00FF0000L;
  25.   const long RGB_RED    = 0x000000FFL;
  26.  
  27.  
  28. /* Calculator state */
  29.  
  30.   enum TCalcState {CS_FIRST, CS_VALID, CS_ERROR};
  31.  
  32. /* Calculator dialog window object */
  33. class TCalc : public TDialog {
  34. public:
  35.     TCalcState CalcStatus;
  36.     char Number[DisplayDigits + 1];
  37.     BOOL Negative;
  38.     char Operator;
  39.     double Operand;
  40.     HBRUSH BlueBrush;
  41.     TCalc();
  42.     virtual ~TCalc();
  43.     virtual LPSTR GetClassName();
  44.     virtual void GetWindowClass(WNDCLASS&);
  45.     virtual void WMControlColor(TMessage& Msg)
  46.                                 = [WM_FIRST + WM_CTLCOLOR];
  47.     virtual void WMPaint(TMessage& Msg)
  48.                          = [WM_FIRST + WM_PAINT];
  49.     virtual void DefChildProc(TMessage& Msg);
  50.     virtual void DefCommandProc(TMessage& Msg);
  51.     void FlashButton(char Key);
  52.     void Error();
  53.     void SetDisplay(double R);
  54.     void GetDisplay(double& R);
  55.     void CheckFirst();
  56.     void InsertKey(char Key);
  57.     void CalcKey(char Key);
  58.     void Clear();
  59.     virtual void UpdateDisplay();
  60. };
  61.  
  62. /* Calculator application object */
  63.  
  64. class TCalcApp : public TApplication {
  65. public:
  66.     TCalcApp(LPSTR AName, HINSTANCE hInstance,
  67.           HINSTANCE hPrevInstance, LPSTR lpCmd,
  68.           int nCmdShow)
  69.             : TApplication(AName, hInstance,
  70.                    hPrevInstance, lpCmd, nCmdShow) {};
  71.     virtual void InitMainWindow();
  72.     virtual void InitInstance();
  73.     virtual BOOL ProcessAppMsg(LPMSG AMessage);
  74. };
  75.  
  76.  
  77. /* Calculator constructor.  Create blue brush for calculator background,
  78.   and do a clear command. */
  79.  
  80. TCalc::TCalc() : TDialog(NULL, APPNAME)
  81. {
  82.   BlueBrush = CreateSolidBrush(RGB_BLUE);
  83.   Clear();
  84. }
  85.  
  86. /* Calculator destructor.  Delete the background brush. */
  87.  
  88. TCalc::~TCalc()
  89. {
  90.   DeleteObject(BlueBrush);
  91. }
  92.  
  93. /* We're changing the window class so we must supply a new class name.*/
  94.  
  95. LPSTR TCalc::GetClassName()
  96. {
  97.   return APPNAME;
  98. }
  99.  
  100. /* The calculator has its own icon which is installed here. */
  101. void TCalc::GetWindowClass(WNDCLASS& AWndClass)
  102. {
  103.   TDialog::GetWindowClass(AWndClass);
  104.   AWndClass.hIcon = LoadIcon(GetApplication()->hInstance, APPNAME);
  105. }
  106.  
  107. /* Colorize the calculator. Allows background to show through corners of
  108.   buttons, uses yellow text on black background in the display, and sets
  109.   the dialog background to blue. */
  110. void TCalc::WMControlColor(TMessage& Msg)
  111. {
  112.   switch (Msg.LP.Hi) {
  113.     case CTLCOLOR_BTN:
  114.       Msg.Result = (LRESULT)GetStockObject(NULL_BRUSH);
  115.       break;
  116.     case CTLCOLOR_STATIC:
  117.     SetTextColor((HDC)Msg.WParam, RGB_YELLOW);
  118.     SetBkMode((HDC)Msg.WParam, TRANSPARENT);
  119.     Msg.Result = (LRESULT)GetStockObject(BLACK_BRUSH);
  120.       break;
  121.     case CTLCOLOR_DLG:
  122.     SetBkMode((HDC)Msg.WParam, TRANSPARENT);
  123.     Msg.Result = (LRESULT)BlueBrush;
  124.       break;
  125.     default:
  126.       DefWndProc(Msg);
  127.   }
  128. }
  129.  
  130. /* Even dialogs can have their backgrounds painted on.  This creates
  131.   a red ellipse over the blue background. */
  132.  
  133. void TCalc::WMPaint(TMessage&)
  134. {
  135.   HBRUSH OldBrush;
  136.   HPEN OldPen;
  137.   RECT R;
  138.   PAINTSTRUCT PS;
  139.  
  140.   BeginPaint(HWindow, &PS);
  141.   OldBrush = (HBRUSH)SelectObject(PS.hdc, CreateSolidBrush(RGB_RED));
  142.   OldPen = (HPEN)SelectObject(PS.hdc, GetStockObject(NULL_PEN));
  143.   GetClientRect(HWindow, &R);
  144.   R.bottom = R.right;
  145.   OffsetRect(&R, -R.right/4, -R.right/4);
  146.   Ellipse(PS.hdc, R.left, R.top, R.right, R.bottom);
  147.   SelectObject(PS.hdc, OldPen);
  148.   DeleteObject(SelectObject(PS.hdc, OldBrush));
  149.   EndPaint(HWindow, &PS);
  150. }
  151.  
  152. /* Flash a button with the value of Key.  Looks exactly like a
  153.   click of the button with the mouse. */
  154.  
  155. void TCalc::FlashButton(char Key)
  156. {
  157.   HWND Button;
  158.   WORD Delay;
  159.  
  160.   if ( Key == 0x0D) Key = '=';  // Treat Enter like '='
  161.   Button = GetDlgItem(HWindow, toupper(Key));
  162.   if ( Button )
  163.   {
  164.     SendMessage(Button, BM_SETSTATE, 1, 0);
  165.     for (Delay = 1; Delay <= 30000; ++Delay)
  166.       ;
  167.     SendMessage(Button, BM_SETSTATE, 0, 0);
  168.   }
  169. }
  170.  
  171. /* Rather than handle each button individually with child ID
  172.   response methods, it is possible to handle them all at
  173.   once with the default child procedure. */
  174.  
  175. void TCalc::DefChildProc(TMessage& Msg)
  176. {
  177.   if ( (Msg.WP.Hi == 0) && (Msg.LP.Hi == BN_CLICKED) )
  178.     CalcKey(Msg.WP.Lo);
  179.   TDialog::DefChildProc(Msg);
  180. }
  181.  
  182. /* Rather than handle each accelerator individually with
  183.   command ID response methods, it is possible to handle them
  184.   all at once with the default command procedure. */
  185. void TCalc::DefCommandProc(TMessage& Msg)
  186. {
  187.   if ( Msg.WP.Hi == 0 )
  188.   {
  189.     FlashButton(Msg.WP.Lo); /* flash button as if it were pushed */
  190.     CalcKey(Msg.WP.Lo);
  191.   }
  192.   TDialog::DefCommandProc(Msg);
  193. }
  194.  
  195. /* Set Display text to the current value. */
  196. void TCalc::UpdateDisplay()
  197. {
  198.   char S[DisplayDigits + 2];
  199.  
  200.   if ( Negative )
  201.     strcpy(S, "-");
  202.   else
  203.     S[0] = '\0';
  204.   SetWindowText(GetDlgItem(HWindow, ID_DISPLAY), strcat(S, Number));
  205. }
  206.  
  207. /* Clear the calculator. */
  208. void TCalc::Clear()
  209. {
  210.   CalcStatus = CS_FIRST;
  211.   _fstrcpy(Number, "0");
  212.   Negative = FALSE;
  213.   Operator = '=';
  214. }
  215.  
  216. void TCalc::Error()
  217. {
  218.   CalcStatus = CS_ERROR;
  219.   _fstrcpy(Number, "Error");
  220.   Negative = FALSE;
  221. }
  222.  
  223. void TCalc::SetDisplay(double R)
  224. {
  225.   LPSTR First, Last;
  226.   int CharsToCopy;
  227.   char S[64];
  228.  
  229. // limit results of calculations to 7 digits to the right of the decimal point
  230.   R = (floor(R * 10000000L + .5))/10000000L ;
  231.  
  232.   sprintf(S, "%0.10f", R);
  233.   First = S;
  234.   Negative = FALSE;
  235.   if ( S[0] == '-' )
  236.   {
  237.     ++First;
  238.     Negative = TRUE;
  239.   }
  240.   if ( _fstrlen(First) > DisplayDigits + 1 + 10 )
  241.     Error();
  242.   else
  243.   {
  244.     Last = _fstrchr(First, 0);
  245.     while ( Last[-1] == '0' )
  246.       --Last;
  247.     if ( Last[-1] == '.' )
  248.       --Last;
  249.     CharsToCopy = min(DisplayDigits + 1, (int)(Last - First));
  250.     _fstrncpy(Number, First, CharsToCopy);
  251.     Number[CharsToCopy] = '\0';
  252.   }
  253. }
  254.  
  255. void TCalc::GetDisplay(double& R)
  256. {
  257.   R = atof(Number);
  258.   if ( Negative )
  259.     R = -R;
  260. }
  261.  
  262. void TCalc::CheckFirst()
  263. {
  264.   if ( CalcStatus == CS_FIRST )
  265.   {
  266.     CalcStatus = CS_VALID;
  267.     _fstrcpy(Number, "0");
  268.     Negative = FALSE;
  269.   }
  270. }
  271.  
  272. void TCalc::InsertKey(char Key)
  273. {
  274.   int L;
  275.  
  276.   L = _fstrlen(Number);
  277.   if ( L < DisplayDigits )
  278.   {
  279.     Number[L] = Key;
  280.     Number[L + 1] = '\0';
  281.   }
  282. }
  283.  
  284.  
  285. /* Process calculator key. */
  286. void TCalc::CalcKey(char Key)
  287. {
  288.   double R;
  289.  
  290.   Key = toupper(Key);
  291.   if ( (CalcStatus == CS_ERROR) && (Key != 'C') )
  292.     Key = ' ';
  293.   if ( Key >= '0' && Key <= '9' )
  294.   {
  295.     CheckFirst();
  296.     if ( _fstrcmp(Number, "0") == 0 )
  297.       Number[0] = '\0';
  298.     InsertKey(Key);
  299.   }
  300.   else
  301.     if ( Key == '+' || Key == '-' || Key == '*' ||
  302.          Key == '/' || Key == '=' || Key == '%' || Key == 0x0D )
  303.     {
  304.       if ( CalcStatus == CS_VALID )
  305.       {
  306.         CalcStatus = CS_FIRST;
  307.         GetDisplay(R);
  308.         if ( Key == '%' )
  309.           if ( Operator == '+' || Operator == '-' )
  310.             R = Operand * R / 100;
  311.           else
  312.             if ( Operator == '*' || Operator == '/' )
  313.               R = R / 100;
  314.         switch ( Operator ) {
  315.           case '+': SetDisplay(Operand + R);
  316.                     break;
  317.            case '-': SetDisplay(Operand - R);
  318.                     break;
  319.         case '*': SetDisplay(Operand * R);
  320.                     break;
  321.         case '/': if ( R == 0 )
  322.                       Error();
  323.                     else
  324.                       SetDisplay(Operand / R);
  325.                     break;
  326.         }
  327.       }
  328.       Operator = Key;
  329.       GetDisplay(Operand);
  330.     }
  331.     else
  332.       switch ( Key ) {
  333.         case '.': CheckFirst();
  334.               if ( _fstrchr(Number, '.') == NULL )
  335.                   InsertKey(Key);
  336.                   break;
  337.         case 0x8: CheckFirst();
  338.                   if ( _fstrlen(Number) == 1 )
  339.                     _fstrcpy(Number, "0");
  340.               else
  341.                     Number[_fstrlen(Number) - 1] = '\0';
  342.                   break;
  343.         case '_': Negative = !Negative;
  344.                   break;
  345.         case 'C': Clear();
  346.                   break;
  347.       }
  348.   UpdateDisplay();
  349. }
  350.  
  351. /* Create calculator as the application's main window. */
  352.  
  353. void TCalcApp::InitMainWindow()
  354. {
  355.   MainWindow = new TCalc();
  356. }
  357.  
  358. /* This application loads accelerators so that key input can be used. */
  359.  
  360. void TCalcApp::InitInstance()
  361. {
  362.   TApplication::InitInstance();
  363.   HAccTable = LoadAccelerators(hInstance, APPNAME);
  364. }
  365.  
  366. /* This is one of the few places where the order of processing of
  367.   messages is important.  The usual order, ProcessDlgMsg,
  368.   ProcessMDIAccels, ProcessAccels, allows an application to define
  369.   accelerators which will not break the keyboard handling in
  370.   child dialogs.  In this case, the dialog is the application.
  371.   If we used the default ProcessAppMsg, then the keyboard
  372.   handler, ProcessDlgMsg, would return TRUE and accelerators
  373.   would not be processed.  In this case, what we are doing is safe
  374.   because we are not defining any accelerators which conflict
  375.   with the Window's keyboard handling for dialogs.  Making this
  376.   change allows us to use keyboard input for the calculator.  Also,
  377.   because this is our app, we know that it is not an MDI app,
  378.   therefore we do not need to call ProcessMDIAccels (although it
  379.   would not hurt to do so). */
  380.  
  381. BOOL TCalcApp::ProcessAppMsg(LPMSG Message)
  382. {
  383.   return ProcessAccels(Message) || ProcessDlgMsg(Message);
  384. }
  385.  
  386. int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  387.            LPSTR lpCmd, int nCmdShow)
  388. {
  389.   TCalcApp CalcApp(APPNAME, hInstance, hPrevInstance,
  390.         lpCmd, nCmdShow);
  391.   CalcApp.Run();
  392.   return CalcApp.Status;
  393. }
  394.