home *** CD-ROM | disk | FTP | other *** search
/ ftp.alaska-software.com / 2014.06.ftp.alaska-software.com.tar / ftp.alaska-software.com / acsn / BusiCalc_v1-40.Zip / BusiCalc.prg < prev    next >
Text File  |  2002-10-06  |  133KB  |  3,480 lines

  1. ********************************************************************************
  2. *
  3. *  Module:     BusiCalc.prg
  4. *  Category:   Calculator
  5. *  Used By:    Stand Alone and Multiple Apps.
  6. *  Purpose:    To provide a calculator facility for general business
  7. *              applications that is keyboard and mouse aware and capable
  8. *              of Euro, Pound and Dollar exchange convertions.
  9. *
  10. *  Author:     Greg Doran, Dublin Ireland  e-mail: GDO@eircom.net
  11. *  Version:    1.40
  12. *  Date:       6.Oct.2002  -  Released to Public Domain
  13. *
  14. *  Language:   Xbase++  1.8 (Should be ok with 1.7,1.6 and ?)
  15. *
  16. *  Compile:    Standard.
  17. *  Link:       Standard.
  18. *  (PBuild BusiCalc)
  19. *
  20. *  Requires:   Project  : BusiCalc.xpj   v1.40
  21. *              Class    : BusCalcCls.prg v1.40
  22. *              Main     : BusiCalc.prg   v1.40 (This File)
  23. *              Include  : BusiCalc.ch    v1.40
  24. *              Rates(*) : BusiCalc.xch
  25. *              Resource : BusiCalc.arc   v1.40
  26. *              files    : Resource.zip  (All Icons,Bitmaps,Pointers + Blank_PB-BM.zip)
  27. *
  28. *  Creates:    (*) If not exist: BusiCalc.xch
  29. *
  30. *
  31. *  RunTime:    ASRDBC10.DLL
  32. *  (not        SOM.DLL
  33. *  included    XPPNAT.DLL
  34. *  in Zip)     XPPRT1.DLL
  35. *              XPPUI1.DLL
  36. *
  37. *****************************************************************************
  38. *
  39. *  Revision History:
  40. *  =================
  41. *  Rev 1.10 12th.Jul.2001
  42. *           Initial release.
  43. *
  44. *  Rev 1.20 15th.Nov.2001
  45. *           Changed: To a Text File replacing the exchange rate
  46. *                    Dbf thus obviating the need for a DBE and DBF in
  47. *                    'Stand Alone' situations.
  48. *
  49. *  Rev 1.30 30th.Sep.2002
  50. *           Added: Additional commenting for release to the Public Domain.
  51. *
  52. *  Rev 1.40 6th.Oct.2002
  53. *           Added: TrapCursor() to prevent loosing 'grip' when moving
  54. *                  calculator and the cursor is near the edge of Titlebar.
  55. *           Changed: Minor text changes.
  56. *
  57. *****************************************************************************
  58. *
  59. *****************************************************************************
  60. *
  61. *  Copyright (c) Greg J Doran. 2001-2002   GDO@eircom.net
  62. *
  63. *  This code is released under the GNU General Public Licence
  64. *  and is governed by the conditions and protocols setout therein.
  65. *
  66. *  The code is provided "as is", without warranty of any kind,
  67. *  either expressed, or implied, including, but not limited to,
  68. *  the implied warranties of merchantability and fitness for a
  69. *  particular purpose. The entire risk as to the quality of this
  70. *  software, its use or misuse, modified or otherwise, lies with
  71. *  you, the recipient.
  72. *
  73. *  For details regarding the GNU General Public Licence visit
  74. *  Phil Ide's Xbase++ FAQ.s at:
  75. *
  76. *  http://www.idep.org.uk/xbase/xbfaq/xbfaq.htm
  77. *
  78. *****************************************************************************
  79. *
  80. #include "Gra.ch"
  81. #include "Xbp.ch"
  82. #include "Appevent.ch"
  83. #include "Error.ch"
  84. #include "dll.ch"
  85. #include "busicalc.ch"
  86. #include "Fileio.ch"
  87. *
  88. *****************************************************************************
  89. *                       FUNCTIONS appsys() DbeSys()
  90. *****************************************************************************
  91. FUNCTION AppSys()
  92. RETURN .T.
  93. *
  94. FUNCTION DbeSys()
  95. RETURN .T.
  96. *                    End of FUNCTIONS appsys() DbeSys()
  97. *****************************************************************************
  98. *
  99. *****************************************************************************
  100. *
  101. *               DLLFUNCTIONS(...)    DO NOT WRAP LINES
  102.  
  103. DLLFUNCTION FindWindowA(lpClassName,lpWindowName) USING STDCALL FROM USER32.DLL
  104. DLLFUNCTION BringWindowToTop(hwnd) USING STDCALL FROM USER32.DLL
  105. DLLFUNCTION SetForegroundWindow(hwnd) USING STDCALL FROM USER32.DLL
  106. DLLFUNCTION ShowWindow(hwnd,SW_SHOWMAXIMIZED) USING STDCALL FROM USER32.DLL
  107. DLLFUNCTION UpdateWindow(hwnd) USING STDCALL FROM USER32.DLL
  108. DLLFUNCTION SetCursorPos( nX, nY ) USING STDCALL FROM USER32.DLL
  109. DLLFUNCTION MoveWindow(nhwnd,nx,ny,nWidth,nHeight,nbRepaint) USING STDCALL FROM USER32.DLL
  110. *
  111. * Also, Used directly...
  112. *
  113. *  DllCall("User32.DLL", DLL_STDCALL,"GetWindowRect", hwnd, a4LTRB_lpRect)
  114. *  DllCall("User32.DLL", DLL_STDCALL, "ClipCursor", a4LTRB_lpRect)
  115. *
  116. *                     End of DLLFUNCTIONS(...)
  117. *****************************************************************************
  118. *
  119. *****************************************************************************
  120. *                            FUNCTION main()
  121. *****************************************************************************
  122. *
  123. FUNCTION Main()
  124.  
  125.    LOCAL aLBDownAtPos   := {nil,nil}
  126.    LOCAL aPos           := {nil,nil}
  127.    LOCAL aPP            := {}
  128.    LOCAL aSize          := {nil,nil}
  129.    LOCAL cTitle         := "BusiCalc v1.40"
  130.    LOCAL hwnd           := 0
  131.    LOCAL lLBDown        := .F.
  132.    LOCAL mp1            := 0
  133.    LOCAL mp2            := 0
  134.    LOCAL nEvent         := 0
  135.    LOCAL oDlg
  136.    LOCAL oXbp
  137.    LOCAL aRates         := {}
  138.    LOCAL nStatus        := 0
  139.  
  140.    ************************************
  141.    ****** Is this a duplicate process?
  142.    ************************************
  143.    hwnd := FindWindowA( 0, cTitle )
  144.  
  145.    IF !(hwnd == 0)
  146.       ShowItsOpen(hwnd)  // 'Tracers' to Top Titlebar
  147.       CANCEL             // Duplicate, just cancel this one
  148.    ENDIF
  149.  
  150.    *****************************************************************************
  151.    ******************          CREATE DIALOG         ***************************
  152.    *****************************************************************************
  153.    *
  154.    *** Get size of the desktop to display the application window centered
  155.    *
  156.    aSize         := {318, 348}   // Dialog MaxSize
  157.    aPos[1]       := (SetAppWindow():currentSize()[1]-aSize[1])/2
  158.    aPos[2]       := (SetAppWindow():currentSize()[2]-aSize[2])/2
  159.  
  160.    ***** Presentation Parameters
  161.    *
  162.    AAdd ( aPP, { XBP_PP_BGCLR, GRA_CLR_DARKBLUE } )
  163.    AAdd ( aPP, { XBP_PP_FGCLR, GRA_CLR_PALEGRAY } )
  164.    AAdd ( aPP, { XBP_PP_COMPOUNDNAME, "8.Arial" } )
  165.  
  166.    *****  Dialog from BusiCalc Class
  167.    *
  168.    oDlg:= BusiCalc():New( AppDesktop(),, aPos,aSize,aPP, .F.)
  169.  
  170.    oDlg:border             := XBPDLG_NO_BORDER
  171.    oDlg:titleBar           := .F.
  172.    oDlg:sysmenu            := .F.
  173.    oDlg:alwaysontop        := .T.
  174.    oDlg:title              := cTitle
  175.    oDlg:maxsize            := aSize
  176.    oDlg:drawingArea:bitmap := BUSICALC
  177.    oDlg:setDisplayFocus    := {|| setAppFocus(oDlg:oKeyTrap)}
  178.    oDlg:oKeyTrap:keyBoard  := {|nKey| Calckeys(nKey,oDlg,@aRates)}
  179.  
  180.    ***** Move Window
  181.    oDlg:drawingArea:motion := {| MP1, uNIL, obj |            ;
  182.                                  iif(lLBDown                ,;
  183.                                  MoveWin(MP1,aLBDownAtPos,oDlg), nil) }
  184.  
  185.    oDlg:drawingArea:lbDown := {| MP1, uNIL, obj |            ;
  186.                                  lLBDown:= .T.              ,;
  187.                                  aLBDownAtPos:= MP1         ,;
  188.                                  obj:setPointer(            ,;
  189.                                  100                        ,; // Hand
  190.                                  XBPWINDOW_POINTERTYPE_POINTER ),;
  191.                                  TrapCursor(oDlg:drawingArea,.T.) }
  192.  
  193.    oDlg:drawingArea:lbUp   := {| MP1, uNIL, obj |            ;
  194.                                  lLBDown:= .F.              ,;
  195.                                  setAppFocus(oDlg)          ,;
  196.                                  setAppFocus(oDlg:oKeyTrap) ,;
  197.                                  obj:setPointer( , ;
  198.                                  XBPSTATIC_SYSICON_ARROW, ;
  199.                                  XBPWINDOW_POINTERTYPE_SYSPOINTER ),;
  200.                                  TrapCursor(oDlg:drawingArea,.F.) }
  201.    oDlg:Create()
  202.    oDlg:show()
  203.    oDlg:aCalcSize := aSize
  204.    SetAppWindow(oDlg)
  205.    setAppFocus(oDlg)
  206.    setAppFocus(oDlg:oKeyTrap)
  207.    *
  208.    *****************************************************************************
  209.    ******************       END CREATE DIALOG         **************************
  210.    *****************************************************************************
  211.    *
  212.  
  213.    ***** Programwide Static
  214.    *
  215.    bSaveErrorBlock := ErrorBlock( {|oError| MyErrorHandler(oError,oDlg) })
  216.  
  217.    ***** Get the Help Text and Bitmaps into an array.
  218.    *
  219.    MakeHelp(oDlg)
  220.  
  221.    ***** Get the Exchange rates and the last conversion mode used. (cXRate)
  222.    *
  223.    aRates:={StrZero(1.0,10),StrZero(1.0,10),StrZero(1.0,10),"DE"+Space(8)}
  224.  
  225.    nStatus:= GetData("BusiCalc.xch",@aRates, 10)
  226.  
  227.    IF nStatus%4 == 0       // Ignore closing error (4) and pray when saving....
  228.       oDlg:nPE             := Val(aRates[1])
  229.       oDlg:nDE             := Val(aRates[2])
  230.       oDlg:nDP             := Val(aRates[3])    // == (1/oDlg:nPE)/(1/oDlg:nDE)
  231.       oDlg:cXRate          := Alltrim(aRates[4])// DE, PE or DP
  232.    Else
  233.       ***** Oppps!   First time instal or file missing....
  234.       ***** Instal Default exchange values and advise of need to check....
  235.       *
  236.       MyErrorMsg(-247,oDlg,nStatus)
  237.       *****      Rates at  30.Sep.2002
  238.       oDlg:nPE             := 0.627131          // p
  239.       oDlg:nDE             := 0.978700          // b
  240.       oDlg:nDP             := 1.560598          // u
  241.       ***** Default: Conversion mode as Dollars to Euro
  242.       ***** Alt: PE, Pounds to Euro and DP, Dollars to Pounds)
  243.       oDlg:cXRate          := "DE"
  244.  
  245.       ***** Clear the decks lest we have a corrupt Array
  246.       ARemove(aRates,1,Len(aRates))
  247.       ***** All Records must be length 10
  248.       aAdd(aRates,Str(0.627131,10,6))
  249.       aAdd(aRates,Str(0.978700,10,6))
  250.       aAdd(aRates,Str(1.560598,10,6))
  251.       aAdd(aRates,oDlg:cXRate+Space(8))
  252.  
  253.       ***** Save the rates
  254.       *
  255.       nStatus:= PutData("BusiCalc.xch", @aRates, 10)
  256.  
  257.       IF !(nStatus == 0)
  258.          MyErrorMsg(-246,oDlg,nStatus)
  259.       ENDIF
  260.  
  261.    ENDIF
  262.  
  263.  
  264.    ***** Reset Variables
  265.    *
  266.    ClearAll(oDlg)
  267.  
  268.    ***** Initialise Variables not in ClearAll()
  269.    *
  270.    SET DECIMALS TO 14
  271.    oDlg:nDecs              := 4  //  Default no. of decimals display
  272.    oDlg:oDecSet2:setCaption( Alltrim(Str(oDlg:nDecs))+" Places") // Display
  273.    oDlg:nMemVal            := 0
  274.    oDlg:oMemStore:setCaption("Memory Store"+Space(7)+"0.")
  275.    oDlg:nStoredOpNbr       := 0
  276.    oDlg:nAccumStore        := 0
  277.    oDlg:nStoredValue       := 0
  278.    oDlg:nImmediateValue    := 0
  279.    oDlg:lKillKeys          := .F.
  280.    oDlg:nOpCount           := 0
  281.  
  282.    ***** Setup the currency Conversion Mode
  283.    postAppEvent(xbeP_Keyboard,;
  284.       IIF(oDlg:cXRate == "PE",Asc('P'),;
  285.       IIF(oDlg:cXRate == "DP",Asc('B'),;
  286.                               Asc('U'))),,;
  287.                               oDlg:oKeyTrap)
  288.  
  289.    nEvent := 0
  290.    DO WHILE nEvent <> xbeP_Close
  291.       nEvent := AppEvent(@mp1,@mp2,@oXbp)
  292.       oXbp:handleEvent(nEvent,mp1,mp2)
  293.    ENDDO
  294.  
  295.    ErrorBlock(bSaveErrorBlock)
  296.    oDlg:Destroy()
  297.  
  298. RETURN nil
  299. *
  300. ********************************************************************************
  301. *********************           END  MAIN             **************************
  302. ********************************************************************************
  303.  
  304.  
  305. ********************************************************************************
  306. *                     STATIC FUNCTION CalcKeys()
  307. *                           (Key Handler)
  308. ********************************************************************************
  309. *
  310. STATIC FUNCTION CalcKeys(nKey,oDlg,aRates)
  311. LOCAL i        := 0
  312. LOCAL j        := 0
  313. LOCAL nStatus  := 0
  314.  
  315. Do While .T.
  316.  
  317.    ***************************************************************
  318.    *   Keystroke checks prior to Key handling.
  319.    ***************************************************************
  320.    *
  321.    ***** Only process functional keystrikes
  322.    IF !(UPPER(Chr(nKey))$" !#%()*+-./0123456789=ABCDEFGIKLNPQRSTUWX^" .OR.;
  323.       StrZero(nKey,3)$"003/008/013/022/027/028/255" .OR. nKey == 524320)
  324.       nKey := 0
  325.       EXIT          // ^C/ BS/ CR/ ^V/Esc/ F1/Special     Ctrl+Space
  326.    ENDIF
  327.  
  328.    ***** Ignore second click on Enter Key if % operation has
  329.    ***** just been executed.
  330.    *
  331.    IF oDlg:oOperText:Caption == BM_PERCENT .AND.;
  332.       oDlg:oSymbol:Caption   == BM_PERCENT .AND.;
  333.       (nKey == 13 .OR. nKey == 61)   // (Enter or '=' keys)
  334.       nKey := 0
  335.       EXIT
  336.    ENDIF
  337.  
  338.    ***** If the Right bracket is open...
  339.    ***** and the keypress is 'Enter' or '=', discard the keystroke.
  340.    ***** Only a Right Bracket close can evaluate the "(content)"
  341.    *
  342.    IF !(oDlg:lRBracketClosed)  .AND. (nKey == 13 .OR. nKey == 61)
  343.       nKey := 0
  344.       EXIT
  345.    ENDIF
  346.  
  347.    ***** Establish operator status for bracketed operation commencement.
  348.    ***** Cannot commence bracketed operation without one of these
  349.    ***** operators preceding it, check the key _preceding_ the current
  350.    ***** key being evaluated.
  351.    *
  352.    IF Chr(oDlg:nLastKey)$"+*%-/^#"
  353.       oDlg:lOperator:= .T.
  354.    ELSE
  355.       oDlg:lOperator:= .F.
  356.    ENDIF
  357.    oDlg:nLastKey:= nKey
  358.  
  359.    ****************************************************************
  360.    * Disable BackSpace for anything other than these keystrokes
  361.    ****************************************************************
  362.    *
  363.    IF !(oDlg:lKillKeys)
  364.       IF oDlg:lKeysEnabled
  365.          IF (Chr(nKey)$".0123456789()"+chr(13) )
  366.             oDlg:oPBbspace:setCaption( PB_BSPACE_ENA )
  367.             oDlg:lBS_Enabled:= .T.
  368.          Else
  369.             If !(UPPER(Chr(nKey))$" ABEPQTUWX" .OR.;
  370.                StrZero(nKey,3)$"003/008/022/027/028/")   // (^C/BS/^V/Esc/F1)
  371.                oDlg:oPBbspace:setCaption( PB_BSPACE_DIS )
  372.                oDlg:lBS_Enabled:= .F.
  373.             Endif
  374.          Endif
  375.       ENDIF
  376.    ENDIF
  377.    ***************************************************************
  378.    *   END OF Keystroke checks prior to Key handling.
  379.    ***************************************************************
  380.    *
  381.    ****************************************************************
  382.    * ***************   Now Handle the Keystroke   *****************
  383.    ****************************************************************
  384.    *
  385.    Do While .T.
  386.  
  387.  
  388.       ****************************************************************
  389.       *     MINIMISED ON TOP TITLEBAR   -   EXPAND THE WINDOW
  390.       ****************************************************************
  391.       *
  392.       IF nKey == 524320   // Ctrl+SpaceBar
  393.          ***** Ctrl+SpaceBar only works when BusiCalc still has focus
  394.          ***** Otherwise a mouse-click is required
  395.          *
  396.          ***** Hide the 'open up' button  ( Top of screen: [ BusiCalc ][x] )
  397.          ***** Resize as normal window at last recorded position
  398.          ***** Direct all keystrokes to the Keytrap
  399.          ***** And allow other Key interpretations
  400.  
  401.          oDlg:oOpenUp:Hide()
  402.          oDlg:oPBExit:Hide()
  403.          oDlg:configure( , , {oDlg:aPos[1],oDlg:aPos[2]} , oDlg:aCalcSize)
  404.          setAppFocus(oDlg:oKeyTrap)
  405.          oDlg:lKillKeys:= .F.
  406.  
  407.       ELSEIF oDlg:lKillKeys
  408.          ***** BusiCalc is 'minimised' and may have focus, only nKey=524320
  409.          ***** is valid, ignore any other keystrokes received for processing.
  410.          ***** The Close button [X] is a 'system' button and will be actioned
  411.          ***** by the event handler.
  412.          EXIT
  413.          *************************************************************
  414.          *                            Help File
  415.          *************************************************************
  416.          *
  417.       ELSEIF nKey==xbeK_F1       // HELP
  418.          ***** Opening a Modal with own Key Handler
  419.          Info( "Help", oDlg:aCalcSize, oDlg)
  420.  
  421.          *************************************************************
  422.          *                            About File
  423.          *************************************************************
  424.          *
  425.       ELSEIF nKey == 81 .OR. nKey == 113    //... 'Q' Query About
  426.          ***** Opening a Modal with own Key Handler
  427.          Info( "About", oDlg:aCalcSize, oDlg)
  428.  
  429.  
  430.          ****************************************************************
  431.          *     COLLAPSE THE WINDOW    -   MINIMISE ON TOP TITLEBAR
  432.          ****************************************************************
  433.          *
  434.       ELSEIF nKey == 32    // SpaceBar
  435.  
  436.          ***** Save the current dialog position
  437.          ***** Show the 'open up' button     ( [ BusiCalc ][x]  )
  438.          ***** Resize as 'collapsed' window at Top Titlebar
  439.          ***** Prevent all Key interpretations except Ctrl+Space,
  440.          ***** BusiCalc still has focus
  441.  
  442.          oDlg:aPos:= oDlg:currentPos()
  443.          oDlg:oOpenUp:Show()
  444.          oDlg:oPBExit:Show()
  445.          oDlg:configure( , ,{AppDesktop():CurrentSize()[1]-200,;
  446.          AppDesktop():CurrentSize()[2]-18} , {121,16})
  447.          oDlg:lKillKeys:= .T.
  448.          ***** Will now only respond to Ctrl+SpaceBar (as long as BusiCalc
  449.          ***** has focus) or a Mouse Click on the 'Open-up' button
  450.  
  451.  
  452.          ****************************************************************
  453.          *                       NUMERIC KEYSTROKE
  454.          ****************************************************************
  455.          *
  456.       ELSEIF Chr(nKey)$".0123456789" .AND. oDlg:lKeysEnabled
  457.  
  458.          ***** Let other program sections know that an input has been entered
  459.          *
  460.          oDlg:lNewInput          := .T.
  461.  
  462.          ******  Is this the first numeric keystroke input?
  463.          *
  464.          If oDlg:lNewCalculation
  465.             oDlg:cDisplay        := "0."              // Set start condition
  466.             oDlg:lHaveDecimal    := .F.               // We have not got a decimal
  467.             oDlg:lNewCalculation := .F.               // Reset
  468.             oDlg:oPBbspace:setCaption(PB_BSPACE_ENA)  // Enable the backspace key
  469.             oDlg:lBS_Enabled     := .T.
  470.          Endif
  471.  
  472.          oDlg:oDisplay:setCaption( oDlg:cDisplay )    // Display it
  473.          oDlg:nDisplay:= Val(oDlg:cDisplay)           // Get the value of the input
  474.  
  475.          ***** What, if anything, do we display....
  476.          *
  477.          Do While .T.
  478.  
  479.             ****** If the display is '0.' and the key is '0' or '.' ignore
  480.             ****** it, but record it as a decimal point if the key is '.'
  481.             ****** provided that we do not already have one.
  482.             *
  483.             IF oDlg:cDisplay   == "0." .AND.;
  484.                (Chr(nKey) == "." .OR. (Chr(nKey) == '0' .AND. !(oDlg:lHaveDecimal)))
  485.  
  486.                IF Chr(nKey) == "."
  487.                   oDlg:lHaveDecimal := .T.          // We got a decimal point
  488.                Endif
  489.  
  490.                EXIT
  491.             ELSE
  492.                ****** If we have a decimal point and key is '.', ignore it
  493.                IF oDlg:lHaveDecimal .AND. Chr(nKey) == "."
  494.                   EXIT                              // Duplicate '.'
  495.                ENDIF
  496.  
  497.                ****** If we have a decimal point just concatenate the input
  498.                *
  499.                If oDlg:lHaveDecimal
  500.                   oDlg:cDisplay += Chr(nKey)
  501.                Else
  502.                   ****** If we have not got a decimal point and the display
  503.                   ****** is zero, then show only the input in the display
  504.                   *
  505.                   If oDlg:cDisplay == "0."
  506.                      oDlg:cDisplay := Chr(nKey)
  507.                   Else
  508.                      ****** Otherwise just concatenate it
  509.                      *
  510.                      oDlg:cDisplay += Chr(nKey)
  511.                      IF Chr(nKey) == "."
  512.                         oDlg:lHaveDecimal := .T.          // We got a decimal point
  513.                      Endif
  514.                   Endif
  515.                Endif
  516.                ****** Display and convert
  517.                *
  518.                oDlg:oDisplay:setCaption( oDlg:cDisplay )  // Show it
  519.                oDlg:nDisplay:= Val(oDlg:cDisplay)         // Get the value of the input
  520.  
  521.             ENDIF
  522.  
  523.             EXIT
  524.          ENDDO .T.
  525.  
  526.          ****** Special case to feed back to nKey ='D'
  527.          ****** on pressing the Enter Key (Set Decimals)
  528.          *
  529.          IF oDlg:lGettingDecsInput
  530.             oDlg:nValueStore := oDlg:nDisplay
  531.          Endif
  532.  
  533.  
  534.          ****************************************************************
  535.          *         INLINE OPERATOR KEYSTROKE  -   DOUBLE ENTRY
  536.          ****************************************************************
  537.          *
  538.       ELSEIF Chr(nKey)$"+*%-/^#"   // '^' is nth. Power, '#' is nth Root
  539.  
  540.          ***** Enable the number keys
  541.          oDlg:lKeysEnabled:= .T.
  542.          Dis_EnableKeys(oDlg)
  543.  
  544.          oDlg:lLastOpWasImmediate := .F.  // Sqr(), 1/x, +/- etc.
  545.  
  546.          ***** Could be an operator change only.
  547.          ***** oDlg:lNewInput is set in the Numerics "ElseIf" section
  548.          *
  549.          If !(oDlg:lNewInput)
  550.             ***** What is the operator?
  551.             oDlg:nOperation := At(Chr(nKey),"+*%-/^#")
  552.             ***** Get the operator bitmap and display it
  553.             Do Case
  554.                Case oDlg:nOperation == 1
  555.                   oDlg:nBMP := BM_PLUS
  556.                Case oDlg:nOperation == 2
  557.                   oDlg:nBMP := BM_MULTIPLY
  558.                Case oDlg:nOperation == 3
  559.                   oDlg:nBMP := BM_PERCENT
  560.                Case oDlg:nOperation == 4
  561.                   oDlg:nBMP := BM_MINUS
  562.                Case oDlg:nOperation == 5
  563.                   oDlg:nBMP := BM_DIVIDE
  564.                Case oDlg:nOperation == 6
  565.                   oDlg:nBMP := BM_NTH_POWER
  566.                Case oDlg:nOperation == 7
  567.                   oDlg:nBMP := BM_NTH_ROOT
  568.             EndCase
  569.             ***** Show Operator before Main Display
  570.             oDlg:oSymbol:setCaption( oDlg:nBMP )
  571.             ***** This was an operator change... Goodbye
  572.             EXIT
  573.          Endif
  574.  
  575.          ***** Reset the NewInput flag
  576.          oDlg:lNewInput   := .F.
  577.  
  578.          Do While .T.
  579.             ***** Keep a record of the number of operators used
  580.             oDlg:nOpCount++
  581.  
  582.             ***** NOTE:  For the purpose of explaning the Operation of this
  583.             *****        code section the 'Add' Operation is assumed.
  584.             *****        All other operators are treated in a similar manner.
  585.  
  586.             ***** On the input of the first number the Operation number
  587.             ***** will be at level '0' so we just store the number input and
  588.             ***** set the Operation number to 1 (add), indicating an addition is
  589.             ***** in progress and then indicate that this is a new calculation.
  590.  
  591.             ***** Following the display of the number entered, we exit the DO
  592.             ***** to get the next input.
  593.  
  594.             If oDlg:nOperation == 0
  595.                ***** Put the displayed value in the accumulator.
  596.                oDlg:nAccumulator := oDlg:nDisplay
  597.  
  598.                ***** Extract the Operation Number
  599.                oDlg:nOperation   := At(Chr(nKey),"+*%-/^#")  // In the example, add = 1
  600.                ***** We are now processing a new calculation
  601.                oDlg:lNewCalculation:= .T.
  602.  
  603.                ***** We have indicated that this is a new calculation
  604.                ***** We have stored what was in the display (a first input),
  605.                ***** and have an operator to execute. (1) Add
  606.  
  607.                ***** Get the bitmap for the operator
  608.                Do Case
  609.                   Case oDlg:nOperation == 1
  610.                      oDlg:nBMP := BM_PLUS
  611.                   Case oDlg:nOperation == 2
  612.                      oDlg:nBMP := BM_MULTIPLY
  613.                   Case oDlg:nOperation == 3
  614.                      oDlg:nBMP := BM_PERCENT
  615.                   Case oDlg:nOperation == 4
  616.                      oDlg:nBMP := BM_MINUS
  617.                   Case oDlg:nOperation == 5
  618.                      oDlg:nBMP := BM_DIVIDE
  619.                   Case oDlg:nOperation == 6
  620.                      oDlg:nBMP := BM_NTH_POWER
  621.                   Case oDlg:nOperation == 7
  622.                      oDlg:nBMP := BM_NTH_ROOT
  623.                EndCase
  624.                ***** Show Operator before Main Display
  625.                oDlg:oSymbol:setCaption( oDlg:nBMP )
  626.  
  627.                ***** Update the Audit trail (three statics above the main display)
  628.                oDlg:oValInput:setCaption( AllTrim(Str(Round(oDlg:nIntermediate, oDlg:nDecs))))
  629.                oDlg:oValInter:setCaption( AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))))
  630.                oDlg:oValAccum:setCaption( AllTrim(Str(Round(oDlg:nAccumulator, oDlg:nDecs))))
  631.  
  632.                ***** We exit here to get the 'next' number input
  633.                ***** so that we can apply the operator to the two values.
  634.                ****** Go get second input...
  635.                * Slan Leat.... Goodbye....
  636.                EXIT
  637.             Endif
  638.  
  639.             ***** Blank out the Operator before Main Display
  640.             oDlg:oSymbol:setCaption( BM_NIL_BLANK  )
  641.  
  642.  
  643.             ***** If the Operation number is not 1 (Add) (remember this is an example!)
  644.             ***** we must be executing a string of calculations and this is the next
  645.             ***** Operator (unless of course it is Add again) Therefore, we must execute
  646.             ***** the previous operator instruction now ( 1 = Add ), and then set the
  647.             ***** Operation number to the new operator instruction and Exit.
  648.  
  649.             ****************************************************************************
  650.             *    ACTION THE OPERATOR KEYS   ---  USED IN MORE THAN ONE PLACE
  651.             ****************************************************************************
  652.             *
  653.             ***** This performs the calculation and handles errors..
  654.             KeysProcess(oDlg)
  655.             *
  656.             ****************************************************************************
  657.  
  658.             ***** Now reset for a new Operation
  659.             *
  660.             oDlg:lNewCalculation:= .T.
  661.  
  662.             ***** But check if it was an attempted 'Divide by Zero'
  663.             ***** The Xbase++ erorrhandler was disabled during these operations and
  664.             ***** replaced by a 'local handler' in KeysProcess(), otherwise recovery
  665.             ***** would have been made difficult by the Xbase++ Handler returning
  666.             ***** a '0' on an error condition.  This way we can let the user correct
  667.             ***** the error or abort the calculation.
  668.             *
  669.             If (( oDlg:nOperation == 3 .OR. oDlg:nOperation == 5) .AND. oDlg:nDisplay == 0)
  670.                IF oDlg:nOperation == 3
  671.                   oDlg:oSymbol:setCaption( BM_PERCENT  )   // Show symbol before Main Display
  672.                ELSE
  673.                   oDlg:oSymbol:setCaption( BM_DIVIDE  )
  674.                ENDIF
  675.                oDlg:nDisplay := 0                          // The display prior to X/0
  676.                oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  677.                oDlg:oDisplay:setCaption( oDlg:cDisplay )   // Display it
  678.                EXIT                                        // Exit DO now to correct error.
  679.             Endif
  680.  
  681.             ****** All ok, get the new Operation number
  682.             *
  683.             oDlg:nOperation := At(Chr(nKey),"+*%-/^#")
  684.             Do Case
  685.                Case oDlg:nOperation == 1
  686.                   oDlg:nBMP := BM_PLUS
  687.                Case oDlg:nOperation == 2
  688.                   oDlg:nBMP := BM_MULTIPLY
  689.                Case oDlg:nOperation == 3
  690.                   oDlg:nBMP := BM_PERCENT
  691.                Case oDlg:nOperation == 4
  692.                   oDlg:nBMP := BM_MINUS
  693.                Case oDlg:nOperation == 5
  694.                   oDlg:nBMP := BM_DIVIDE
  695.                Case oDlg:nOperation == 6
  696.                   oDlg:nBMP := BM_NTH_POWER
  697.                Case oDlg:nOperation == 7
  698.                   oDlg:nBMP := BM_NTH_ROOT
  699.             EndCase
  700.             ****** Show Operator before Main Display
  701.             oDlg:oSymbol:setCaption( oDlg:nBMP )
  702.  
  703.             EXIT
  704.  
  705.          ENDDO
  706.  
  707.          ****************************************************************
  708.          *                      EQUALS (ENTER) KEYSTROKE
  709.          ****************************************************************
  710.          *
  711.          ***** A valid '=' request is determined by checking what symbol is
  712.          ***** displayed in front of the Main Display.  The way the BMP symbols
  713.          ***** are numbered it must always be a number equal to or higher than
  714.          ***** the BM_PLUS BMP ID.  +-*/ etc.
  715.          *
  716.          ***** If the key is the Enter or Equals key and we have a valid request,
  717.          ***** or, we are setting the number of decimals, or, a Left bracket is
  718.          ***** 'closed' and a Right bracket 'open', we can evaluate the expression.
  719.          *
  720.          ***** (The latter occurs when a bracketed operation is being processed.
  721.          *
  722.          ***** Following a keypress on the right bracket, the 'ElseIf' transfers
  723.          ***** control here for expression evaluation and Right Bracket closure.)
  724.  
  725.       ELSEIF( Chr(nKey) == '=' .OR. nKey == 13 ).AND.;
  726.          (oDlg:oSymbol:caption >= BM_PLUS    .OR.;
  727.          oDlg:lGettingDecsInput              .OR.(;
  728.          !(oDlg:lLBracketOpen)               .AND.;
  729.          !(oDlg:lRBracketClosed)))
  730.  
  731.          ***** This symbol can only be the currency  -  Either a Symbol or Blank
  732.          oDlg:oSymbol:setCaption( oDlg:nSymbol)
  733.  
  734.          ***** nOpCount=0  Means that no operations were actioned
  735.          ***** inside the brackets e.g. 5* (9) so we need to force one.
  736.          *
  737.          IF oDlg:nOpCount== 0       .AND.;
  738.             !(oDlg:lLBracketOpen)   .AND.;
  739.             !(oDlg:lRBracketClosed)
  740.             ***** Force a multiply by 1 to satisfy the program algorithm
  741.             oDlg:nAccumulator := 1
  742.             oDlg:nOperation   := 2   //(Multiply)
  743.          ENDIF
  744.  
  745.          Do While .T.
  746.  
  747.             ***** Reset the NewInput flag to prevent an operator keystroke
  748.             ***** adding the display to the accumulator.  Percent needs additional
  749.             ***** handling and is placed at the start of the CalcKey() function.
  750.             *
  751.             oDlg:lNewInput   := .F.
  752.  
  753.             Do While .T.
  754.  
  755.  
  756.                ***** We must execute the previous operator instruction now.
  757.                *
  758.                ** NOTE:
  759.                *
  760.                ***** When a bracketed operation is closed, the oDlg:nStoredValue is passed
  761.                ***** into oDlg:nAccumulator.  This was the result of any operations carried
  762.                ***** out prior to the bracketed operation.
  763.                *
  764.                ***** The result from the bracketed operation is currently in oDlg:nDisplay.
  765.  
  766.                ****************************************************************************
  767.                *    ACTION THE OPERATOR KEYS   ---  USED IN MORE THAN ONE PLACE
  768.                ****************************************************************************
  769.                *
  770.                ***** This performs the calculation and handles errors..
  771.                KeysProcess(oDlg)
  772.                *
  773.                ****************************************************************************
  774.  
  775.  
  776.                ***** Now reset for a new Operation
  777.                oDlg:lNewCalculation:= .T.
  778.                oDlg:lNewInput:= .F.
  779.                oDlg:lKeysEnabled:= .T.
  780.                Dis_EnableKeys(oDlg)    // Enable the number keys
  781.  
  782.                ****** Check if there was an attempted 'Divide by Zero'
  783.                *
  784.                If (( oDlg:nOperation == 3 .OR. oDlg:nOperation == 5) .AND.;
  785.                   oDlg:nDisplay == 0)
  786.  
  787.                   IF oDlg:nOperation == 3
  788.                      oDlg:oSymbol:setCaption( BM_PERCENT  )   // Show symbol before Main Display
  789.                   ELSE
  790.                      oDlg:oSymbol:setCaption( BM_DIVIDE  )
  791.                   ENDIF
  792.  
  793.                   oDlg:nDisplay := 0                          // The display prior to X/0
  794.                   oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  795.                   oDlg:oDisplay:setCaption( oDlg:cDisplay )   // Display it
  796.                   EXIT                                        // Exit DO now to correct error.
  797.                Endif
  798.  
  799.                ****** OtherWise, all ok, reset to get the new Operation number
  800.                *
  801.                oDlg:nOperation:= 0
  802.  
  803.                EXIT
  804.  
  805.             ENDDO
  806.  
  807.             ******Special Case.... See Set Decimals, ElseIf Chr(nKey) == 'D'
  808.             *
  809.             IF oDlg:lGettingDecsInput
  810.  
  811.                ***** Reset flag
  812.                oDlg:lGettingDecsInput := .F.
  813.  
  814.                ***** Re-establish the standard message.
  815.                oDlg:oSetDecMsg:setPresParam({ ;
  816.                { XBP_PP_BGCLR, GRA_CLR_DARKBLUE },  ;
  817.                { XBP_PP_FGCLR, GRA_CLR_DARKGRAY },  ;
  818.                { XBP_PP_COMPOUNDNAME, "8.Arial"}  } )
  819.  
  820.                oDlg:oSetDecMsg:setcaption("Press SpaceBar to minimize")
  821.  
  822.                ***** Get the new number of Decimals held in oDlg:nValueStore.
  823.                ***** Was transferred from oDlg:nDisplay.
  824.                *
  825.                oDlg:nDecs := Abs(Int(oDlg:nValueStore))
  826.  
  827.                ***** Limit it to 14 places  (This is NOT a Set Decimals)
  828.                oDlg:nDecs := IIF( oDlg:nDecs > 14, 14, oDlg:nDecs)
  829.  
  830.                ****** ReStore the Vars
  831.                *
  832.                PopVars(oDlg)
  833.  
  834.             Endif
  835.  
  836.             ***** Just finished a bracketed session ?
  837.             ***** If so then we may need to repeat the operation to
  838.             ***** evaluate the chained calculation.
  839.             *
  840.             IF !(oDlg:lLBracketOpen) .AND. !(oDlg:lRBracketClosed)
  841.  
  842.                ***** That is, the ')' was pressed and the final operation inside
  843.                ***** the brackets has just been executed. We now need to reinstate
  844.                ***** the operation initiated prior to opening the brackets.
  845.  
  846.                IF oDlg:lLastOpWasImmediate                    // Sqr(), Sqroot() etc.
  847.                   IF oDlg:nOpCount == 1                       // Only one op in brackets eg. Sqr()
  848.                      oDlg:nDisplay := oDlg:nImmediateValue    // The result of the bracketed calc
  849.                   ENDIF                                       // was held in oDlg:nImmediateValue
  850.                ENDIF
  851.                ***** reset
  852.                oDlg:lLastOpWasImmediate := .F.
  853.                oDlg:nOpCount:= 0
  854.  
  855.                ***** Retrieve the pending Operation, e.g. 2 (multiply). Len() because we
  856.                ***** could have more than one bracketed session active, we want the last.
  857.                oDlg:nOperation := oDlg:aPreBracket[Len(oDlg:aPreBracket)][1]
  858.  
  859.                ***** Get the associated bitmap
  860.                Do Case
  861.                   Case oDlg:nOperation == 1
  862.                      oDlg:nBMP := BM_PLUS
  863.                   Case oDlg:nOperation == 2
  864.                      oDlg:nBMP := BM_MULTIPLY
  865.                   Case oDlg:nOperation == 3
  866.                      oDlg:nBMP := BM_PERCENT
  867.                   Case oDlg:nOperation == 4
  868.                      oDlg:nBMP := BM_MINUS
  869.                   Case oDlg:nOperation == 5
  870.                      oDlg:nBMP := BM_DIVIDE
  871.                   Case oDlg:nOperation == 6
  872.                      oDlg:nBMP := BM_NTH_POWER
  873.                   Case oDlg:nOperation == 7
  874.                      oDlg:nBMP := BM_NTH_ROOT
  875.                EndCase
  876.                oDlg:oSymbol:setCaption( oDlg:nBMP )      // before display.
  877.                oDlg:oOperText:setCaption( BM_NIL_BLANK ) // Between Input and Inter
  878.  
  879.                ***** Retrieve the stored value
  880.                oDlg:nIntermediate   := oDlg:aPreBracket[Len(oDlg:aPreBracket)][2]
  881.  
  882.                ***** Retrieve the stored value
  883.                oDlg:nAccumulator    := oDlg:nIntermediate
  884.  
  885.                **** Update the displays
  886.                oDlg:oValInput:SetCaption( "" )
  887.                oDlg:oValInter:SetCaption( AllTrim(Str(Round(oDlg:nIntermediate, oDlg:nDecs))))
  888.                oDlg:oValAccum:SetCaption( AllTrim(Str(Round(oDlg:nAccumulator, oDlg:nDecs))))
  889.                oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  890.                oDlg:oDisplay:SetCaption( oDlg:cDisplay )
  891.  
  892.                **** Reset: Reset the NewInput flag, Operation process in progress
  893.                oDlg:lNewInput         := .T.
  894.                oDlg:lNewCalculation   := .F.
  895.  
  896.                ***** Remove the last operation and value
  897.                ARemove(oDlg:aPreBracket)
  898.  
  899.                ***** If we have more bracketed operations pending we need to re-establish
  900.                ***** the 'lockout' conditions and execute the current operation pending.
  901.                *
  902.                IF Len(oDlg:aPreBracket)>=1
  903.                   oDlg:lRBracketClosed   := .F.
  904.                   oDlg:lLBracketOpen     := .T.
  905.                   oDlg:oPBLbracket:setCaption( PB_L_BRACKET_DIS )  // Blue '('
  906.                   oDlg:oPBRbracket:setCaption( PB_R_BRACKET_ENA )  // Red ')'
  907.                   oDlg:oBrackets:setCaption(" ( ... "+Alltrim(Str(Len(oDlg:aPreBracket))))
  908.                   ****** Execute the pending operation.
  909.                   LOOP
  910.                Else
  911.                   ***** Do not execute the pending operation, let the User press the
  912.                   ***** Enter key or continue with the calculation.
  913.                   ***** Close the Right Bracket.
  914.                   oDlg:lRBracketClosed   := .T.
  915.                   ***** Hide the 'number of open brackets indicator display'
  916.                   oDlg:oBrackets:hide()
  917.                ENDIF
  918.             Endif
  919.             EXIT
  920.          ENDDO
  921.  
  922.  
  923.          ****************************************************************
  924.          *                LEFT BRACKET KEYSTROKE
  925.          ****************************************************************
  926.          *
  927.  
  928.          *****  If the Left bracket is pressed and an operator preceded it.
  929.  
  930.       ELSEIF   nKey == 40 .AND. oDlg:lOperator
  931.  
  932.          ***** Store current Operation number and the calculation value so far.
  933.          aAdd(oDlg:aPreBracket,{oDlg:nOperation,oDlg:nAccumulator})
  934.  
  935.          ***** Clear All to start a new background session
  936.          ***** False prevents ARemove(aPreBracket,1,ALL)
  937.          *
  938.          ClearAll(oDlg,.F.)
  939.  
  940.          ****** This establishes it as a background session
  941.          oDlg:lLBracketOpen     := .T.
  942.          oDlg:lRBracketClosed   := .F.
  943.          oDlg:oPBLbracket:setCaption( PB_L_BRACKET_DIS )  // Blue '('
  944.          oDlg:oPBRbracket:setCaption( PB_R_BRACKET_ENA )  // Red ')'
  945.          oDlg:nOpCount:=0
  946.  
  947.          ***** 'number of open brackets indicator display'
  948.          oDlg:oBrackets:setCaption(" ( ... "+Alltrim(Str(Len(oDlg:aPreBracket))))
  949.          oDlg:oBrackets:show()
  950.  
  951.  
  952.          ****************************************************************
  953.          *                RIGHT BRACKET KEYSTROKE
  954.          ****************************************************************
  955.          *
  956.          *****  If the Right bracket is pressed and oDlg:lLBracketOpen is True
  957.          *****  and oDlg:lRBracketClosed is False
  958.          *
  959.       ELSEIF nKey == 41 .AND. ;        // 41 = ')'
  960.          oDlg:lLBracketOpen .AND. !(oDlg:lRBracketClosed)
  961.  
  962.          ***** Is it the final Bracketed operation?
  963.          IF Len(oDlg:aPreBracket) == 1
  964.             oDlg:oPBLbracket:setCaption( PB_L_BRACKET )
  965.             oDlg:oPBRbracket:setCaption( PB_R_BRACKET )
  966.             ***** The Equals, '=', ElseIf will close this bracket
  967.             ***** when the content has been evaluated.
  968.             oDlg:lRBracketClosed := .F.
  969.          ENDIF
  970.  
  971.          oDlg:lLBracketOpen   := .F.
  972.          oDlg:lKeysEnabled    := .F.
  973.          Dis_EnableKeys(oDlg)
  974.  
  975.          ***** Enable the 'Enter' key
  976.          oDlg:oPBequals:setCaption( PB_EQUALS_ENA )
  977.  
  978.          ***** Execute the Enter Key (or '=')
  979.          ***** DO NOT use Postappevent! need to stay in 'Local' loop
  980.          nKey := Asc("=")
  981.          LOOP
  982.  
  983.  
  984.  
  985.          ****************************************************************
  986.          *                  IMMEDIATE OPERATOR KEYSTROKE
  987.          ****************************************************************
  988.          *
  989.       ELSEIF Upper(Chr(nKey)) $ "NILFSR!K"
  990.  
  991.          // N = Negate
  992.          // I = Invert    (reciprocal)
  993.          // L = LocalTo   (Pound to Euro) (Dollar to Euro or Pound)
  994.          // F = ForeignTo (Euro to Pound) (Euro or Pound to Dollar)
  995.          // S = Square
  996.          // R = SqRoot
  997.          // ! = Factoral  Limited to n== 21.  (Max. display capability)
  998.          // K = Pi to 14 decimals
  999.  
  1000.  
  1001.          ***** Indicate that the result  of this operation was immediate.
  1002.          ***** The result will be stored in oDlg:nImmediateValue
  1003.          oDlg:lLastOpWasImmediate:= .T.
  1004.          oDlg:nOpCount++
  1005.  
  1006.          Do While .T.
  1007.  
  1008.             oDlg:nAction := At( Upper(Chr(nKey)), "NILFSR!K")
  1009.             ****** Store original value for display
  1010.             oDlg:nValueStore :=oDlg:nDisplay
  1011.  
  1012.             Do Case
  1013.  
  1014.                Case 1 == oDlg:nAction
  1015.                   oDlg:nDisplay:= oDlg:nDisplay * -1      // Negate (switch sign)
  1016.                   oDlg:oOperText:setCaption( BM_NEGATE  )
  1017.  
  1018.                Case 2 == oDlg:nAction
  1019.                   ***** May not be necessary, but....
  1020.                   BEGIN SEQUENCE
  1021.                   oDlg:nDisplay  :=  1 / oDlg:nDisplay    // Reciprocal  (1/x)
  1022.                   oDlg:oOperText:setCaption( BM_RECIP  )
  1023.                   ENDSEQUENCE
  1024.  
  1025.                Case 3 == oDlg:nAction
  1026.                   ***** To satisfy purests..... 10^38 Pounds to Euro <g>
  1027.                   BEGIN SEQUENCE
  1028.                   ****** Pound to Euro, Dollar to Euro, Dollar to Pound
  1029.                   Do Case
  1030.  
  1031.                      ******* If the currency is Pound/Euro and
  1032.                      ******* the symbol is already Euro its illegal
  1033.                      ******* If the currency is Pound/Euro and
  1034.                      ******* the symbol is Dollar its illegal etc.
  1035.                      *
  1036.                      Case oDlg:cXRate == "PE" .AND.;
  1037.                         !(oDlg:nSymbol == BM_EUR  .OR.;
  1038.                         oDlg:nSymbol == BM_DLR )  // Euro/Dollar
  1039.  
  1040.                         oDlg:nDisplay:= oDlg:nDisplay / oDlg:nConvertRate
  1041.                         oDlg:oOperText:setCaption( BM_PND_TO_EUR  )
  1042.                         oDlg:nSymbol:= BM_EUR
  1043.                         oDlg:oSymbol:setCaption( oDlg:nSymbol )
  1044.  
  1045.                      Case oDlg:cXRate == "DE" .AND.;
  1046.                         !(oDlg:nSymbol == BM_EUR  .OR.;
  1047.                         oDlg:nSymbol == BM_PND )   // Euro/Pound
  1048.  
  1049.                         oDlg:nDisplay:= oDlg:nDisplay / oDlg:nConvertRate
  1050.                         oDlg:oOperText:setCaption( BM_DLR_TO_EUR  )
  1051.                         oDlg:nSymbol:= BM_EUR
  1052.                         oDlg:oSymbol:setCaption( oDlg:nSymbol )
  1053.  
  1054.                      Case oDlg:cXRate == "DP" .AND.;
  1055.                         !(oDlg:nSymbol == BM_PND  .OR.;
  1056.                         oDlg:nSymbol == BM_EUR )  // Pound/Euro
  1057.  
  1058.                         oDlg:nDisplay:= oDlg:nDisplay / oDlg:nConvertRate
  1059.                         oDlg:oOperText:setCaption( BM_DLR_TO_PND  )
  1060.                         oDlg:nSymbol:= BM_PND
  1061.                         oDlg:oSymbol:setCaption( oDlg:nSymbol )
  1062.  
  1063.                      Otherwise
  1064.                         // Do Nowt
  1065.                   Endcase
  1066.                   ENDSEQUENCE
  1067.  
  1068.                Case 4 == oDlg:nAction
  1069.                   BEGIN SEQUENCE
  1070.                   ****** Euro to Pound, Euro to Dollar, Pound to Dollar
  1071.                   Do Case
  1072.  
  1073.                      ***** If the currency is Pound/Euro and
  1074.                      ***** the symbol is already Pound its illegal
  1075.                      ***** If the currency is Pound/Euro and
  1076.                      ***** the symbol is Dollar its illegal etc.
  1077.                      *
  1078.                      Case oDlg:cXRate == "PE" .AND. ;
  1079.                         !(oDlg:nSymbol == BM_PND  .OR.;
  1080.                         oDlg:nSymbol == BM_DLR )  // Pound/Dollar
  1081.  
  1082.                         oDlg:nDisplay:= oDlg:nDisplay * oDlg:nConvertRate
  1083.                         oDlg:oOperText:setCaption( BM_EUR_TO_PND  )
  1084.                         oDlg:nSymbol:= BM_PND            //
  1085.                         oDlg:oSymbol:setCaption( oDlg:nSymbol )
  1086.  
  1087.                      Case oDlg:cXRate == "DE" .AND.;
  1088.                         !(oDlg:nSymbol == BM_DLR  .OR.;
  1089.                         oDlg:nSymbol == BM_PND )  // Dollar/Pound
  1090.  
  1091.                         oDlg:nDisplay:= oDlg:nDisplay * oDlg:nConvertRate
  1092.                         oDlg:oOperText:setCaption( BM_EUR_TO_DLR  )
  1093.                         oDlg:nSymbol:= BM_DLR            //
  1094.                         oDlg:oSymbol:setCaption( oDlg:nSymbol )
  1095.  
  1096.                      Case oDlg:cXRate == "DP" .AND.;
  1097.                         !(oDlg:nSymbol == BM_DLR  .OR.;
  1098.                         oDlg:nSymbol == BM_EUR )   // Dollar/Euro
  1099.  
  1100.                         oDlg:nDisplay:= oDlg:nDisplay * oDlg:nConvertRate
  1101.                         oDlg:oOperText:setCaption( BM_PND_TO_DLR  )
  1102.                         oDlg:nSymbol:= BM_DLR            //
  1103.                         oDlg:oSymbol:setCaption( oDlg:nSymbol )
  1104.  
  1105.                      Otherwise
  1106.                         // Do Nowt
  1107.                   Endcase
  1108.                   ENDSEQUENCE
  1109.  
  1110.                Case 5 == oDlg:nAction  // Square
  1111.                   BEGIN SEQUENCE
  1112.                   oDlg:nDisplay:= oDlg:nDisplay * oDlg:nDisplay
  1113.                   oDlg:oOperText:setCaption( BM_SQUARED  )
  1114.                   ENDSEQUENCE
  1115.  
  1116.                Case 6 == oDlg:nAction   // Square Root
  1117.  
  1118.                   If oDlg:nDisplay < 0
  1119.                      oDlg:cDisplay := AllTrim(Str(oDlg:nDisplay))
  1120.                      MyErrorMsg(-254,oDlg)
  1121.                      EXIT
  1122.                   Else
  1123.                      oDlg:nDisplay:= Sqrt(oDlg:nDisplay)
  1124.                      oDlg:oOperText:setCaption( BM_SQR_ROOT  )
  1125.                   Endif
  1126.  
  1127.                Case 7 == oDlg:nAction  // Factorial
  1128.                   oDlg:nValueStore:= ;
  1129.                   IIF(At(".",oDlg:cDisplay)>0,;
  1130.                   Int(Val(Left(oDlg:cDisplay,At(".",oDlg:cDisplay)-1))),;
  1131.                   Val(oDlg:cDisplay))
  1132.                   If oDlg:nValueStore <= 0 .OR. oDlg:nValueStore >21
  1133.                      oDlg:cDisplay := AllTrim(Str(oDlg:nDisplay))
  1134.                      MyErrorMsg(-253,oDlg)
  1135.                      EXIT
  1136.                   Else
  1137.  
  1138.                      j:= 1
  1139.                      For i := 1 to oDlg:nValueStore
  1140.                      j *= i
  1141.                      Next i
  1142.                      oDlg:nDisplay   := j
  1143.                      oDlg:oOperText:setCaption( BM_N_FACTORIAL  )
  1144.  
  1145.                   Endif
  1146.  
  1147.                   ****** Temporary change to Zero Decimal Places
  1148.                   i    := oDlg:nDecs
  1149.                   oDlg:nDecs:= 0
  1150.  
  1151.  
  1152.                Case 8 == oDlg:nAction // PI
  1153.                   ****** Pi to 14 places (From 58 Billion places)
  1154.                   oDlg:nDisplay:= 3.14159265358979
  1155.                   ***** Clear the inter display symbol not relevant
  1156.                   oDlg:oOperText:setCaption( BM_NIL_BLANK )
  1157.                OtherWise
  1158.                   // do Nowt
  1159.             EndCase
  1160.  
  1161.  
  1162.             If  oDlg:lKeysEnabled
  1163.                ***** If numbers are enabled then use input value
  1164.                oDlg:oValAccum:SetCaption( AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))))
  1165.                oDlg:oValInput:SetCaption( AllTrim(Str(Round(oDlg:nValueStore, oDlg:nDecs))))
  1166.                oDlg:oValInter:SetCaption( "" )
  1167.                oDlg:oDisplay:SetCaption( AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))) )
  1168.                oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  1169.             Else
  1170.                ***** Otherwise use Accumulator value
  1171.                oDlg:nAccumulator:=oDlg:nDisplay
  1172.                oDlg:nIntermediate:=oDlg:nAccumulator
  1173.                oDlg:oValInter:SetCaption( "" )
  1174.                oDlg:oValInput:SetCaption( AllTrim(Str(Round(oDlg:nValueStore, oDlg:nDecs))))
  1175.                oDlg:oValAccum:SetCaption( AllTrim(Str(Round(oDlg:nAccumulator, oDlg:nDecs))))
  1176.                oDlg:cDisplay := AllTrim(Str(Round(oDlg:nAccumulator, oDlg:nDecs)))
  1177.                oDlg:oDisplay:SetCaption( oDlg:cDisplay )
  1178.             Endif
  1179.  
  1180.             ***** Re-establish decimal Places following n!
  1181.             If oDlg:nAction == 7
  1182.                oDlg:nDecs := i
  1183.             Endif
  1184.  
  1185.             ***** Disable the number keys
  1186.             oDlg:lKeysEnabled:= .F.
  1187.             Dis_EnableKeys(oDlg)
  1188.  
  1189.             oDlg:lNewInput := .T.
  1190.             ***** Now get the immediate value and store it.
  1191.             ***** This may be used later. e.g. in a bracketed operation.
  1192.             oDlg:nImmediateValue := oDlg:nDisplay
  1193.             EXIT
  1194.          ENDDO
  1195.  
  1196.  
  1197.          ****************************************************************
  1198.          *                         CLEAR ALL
  1199.          ****************************************************************
  1200.          *
  1201.       ELSEIF Upper(Chr(nKey)) == 'C'
  1202.          ClearAll(oDlg)
  1203.  
  1204.  
  1205.          ****************************************************************
  1206.          *                       CLEAR ENTRY
  1207.          ****************************************************************
  1208.          *
  1209.       ELSEIF UPPER(Chr(nKey)) == "E"  .OR. nKey == 27  //Esc
  1210.  
  1211.          oDlg:cDisplay := "0."
  1212.          oDlg:oDisplay:setCaption( oDlg:cDisplay )   // Clear the Display
  1213.          oDlg:nDisplay := Val( oDlg:cDisplay)
  1214.  
  1215.          ***** Enable the number keys
  1216.          oDlg:lKeysEnabled := .T.
  1217.          Dis_EnableKeys(oDlg)
  1218.  
  1219.  
  1220.  
  1221.          ****************************************************************
  1222.          *                       BACKSPACE KEYSTROKE
  1223.          ****************************************************************
  1224.          *
  1225.       ELSEIF  nKey == 8
  1226.  
  1227.  
  1228.          ***** Allow backspacing if flag is enabled
  1229.          IF oDlg:lBS_Enabled
  1230.  
  1231.             IF Len(oDlg:cDisplay)  > 1 .AND. ;
  1232.                !(Len(oDlg:cDisplay) == 2 .AND. oDlg:cDisplay =='0.')
  1233.                oDlg:cDisplay := SubStr(oDlg:cDisplay,1,Len(oDlg:cDisplay)-1)
  1234.             Else
  1235.                oDlg:cDisplay := "0."
  1236.             Endif
  1237.             oDlg:oDisplay:setCaption( oDlg:cDisplay )
  1238.             oDlg:nDisplay := Val(oDlg:cDisplay)
  1239.          ENDIF
  1240.  
  1241.  
  1242.          ****************************************************************
  1243.          *                       ADD TO MEMORY KEYSTROKE
  1244.          ****************************************************************
  1245.          *
  1246.       ELSEIF UPPER(Chr(nKey)) ==  'A'   //....Add to Memory
  1247.          ***** Need to check for Round( oDlg:nDisplay ) = oDlg:cDisplay
  1248.          ***** in case this was the result of a calculation and not just
  1249.          ***** an input. If calculation then oDlg:nDisplay should be used.
  1250.          *
  1251.          IF AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))) == oDlg:cDisplay
  1252.             ***** It resulted from a calculation
  1253.             oDlg:nMemVal  := oDlg:nMemVal +oDlg:nDisplay
  1254.          ELSE
  1255.             ***** It is an input
  1256.             oDlg:nMemVal  := oDlg:nMemVal + Val(oDlg:cDisplay)
  1257.          ENDIF
  1258.          oDlg:oMemStore:setCaption( AllTrim(Str(Round(oDlg:nMemVal, oDlg:nDecs))))
  1259.  
  1260.  
  1261.          ****************************************************************
  1262.          *                     TAKE FROM MEMORY KEYSTROKE
  1263.          ****************************************************************
  1264.          *
  1265.       ELSEIF UPPER(Chr(nKey)) ==  'T'   //....Take from Memory
  1266.          IF AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))) == oDlg:cDisplay
  1267.             ***** It resulted from a calculation
  1268.             oDlg:nMemVal  := oDlg:nMemVal - oDlg:nDisplay
  1269.          ELSE
  1270.             ***** It is an input
  1271.             oDlg:nMemVal  := oDlg:nMemVal - Val(oDlg:cDisplay)
  1272.          ENDIF
  1273.          oDlg:oMemStore:setCaption( AllTrim(Str(Round(oDlg:nMemVal, oDlg:nDecs))))
  1274.  
  1275.  
  1276.          ****************************************************************
  1277.          *                       WIPE MEMORY KEYSTROKE
  1278.          ****************************************************************
  1279.          *
  1280.       ELSEIF UPPER(Chr(nKey)) ==  'W'   //....Wipe Memory
  1281.  
  1282.          oDlg:nMemVal:=0
  1283.          oDlg:oMemStore:setCaption( "Memory Store"+Space(7)+"0.")
  1284.  
  1285.  
  1286.          ****************************************************************
  1287.          *                       GET MEMORY KEYSTROKE
  1288.          ****************************************************************
  1289.          *
  1290.       ELSEIF UPPER(Chr(nKey)) ==  'G'   //....Get Memory
  1291.  
  1292.          ***** Input Operation in progress
  1293.          oDlg:lNewCalculation  := .F.
  1294.          oDlg:lNewInput       := .T.
  1295.  
  1296.          ***** Just get it into the Main Display var.
  1297.          oDlg:nDisplay  := oDlg:nMemVal
  1298.  
  1299.          oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  1300.          oDlg:oDisplay:setCaption( oDlg:cDisplay )
  1301.  
  1302.          oDlg:oValInput:SetCaption( "" )
  1303.          oDlg:oValInter:setCaption( AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))))
  1304.          oDlg:oValAccum:setCaption( "" )
  1305.  
  1306.          oDlg:lKeysEnabled:= .F.
  1307.          Dis_EnableKeys(oDlg)
  1308.  
  1309.  
  1310.          ****************************************************************
  1311.          *             SET NUMBER OF DECIMALS KEYSTROKE
  1312.          ****************************************************************
  1313.          *
  1314.       ELSEIF Upper(Chr(nKey)) == "D"
  1315.  
  1316.          ****** Don't allow change during bracketed operation
  1317.          If !(oDlg:lRBracketClosed)
  1318.             MyErrorMsg(-250,oDlg)
  1319.             EXIT
  1320.          Endif
  1321.  
  1322.          ******* Sets the maximum number of decimals displayed.
  1323.          ******* The Display is limited but the calculations
  1324.          ******* are not affected.  Any inprocess calculations
  1325.          ******* are performed to as many decimal places as
  1326.          ******* the system allows, the result is then rounded
  1327.          ******* for display purposes.  Hence, with nDec == 0
  1328.          ******* 2.4 + 2.2 will display 5, Duh!
  1329.  
  1330.          ******* Save Where we are at.
  1331.          *
  1332.          oDlg:lGettingDecsInput := .T.
  1333.          PushVars(oDlg,.T.)
  1334.  
  1335.          ****** Clear the decks
  1336.          *
  1337.          ClearAll(oDlg)
  1338.          oDlg:oSetDecMsg:setPresParam({ ;
  1339.          { XBP_PP_BGCLR, GRA_CLR_BLACK },  ;
  1340.          { XBP_PP_FGCLR, GRA_CLR_GREEN },  ;
  1341.          { XBP_PP_COMPOUNDNAME, "8.Arial"}  } )
  1342.  
  1343.          oDlg:oSetDecMsg:setCaption(;
  1344.          "Input a number from 0-14 in the display and Press 'Enter'")
  1345.  
  1346.          ****** Go get the input now.
  1347.          *
  1348.          ****** The task is completed in the ElseIf code Chr(nKey) == '='
  1349.          ****** when the equals / enter key is pressed.
  1350.          ****** oDlg:lGettingDecsInput being True completes the action.
  1351.  
  1352.          oDlg:lGettingDecsInput := .T.
  1353.          ****** First time this was set .T. was for 'save' above
  1354.          ****** and was subsequently cleared by Clear All.
  1355.  
  1356.  
  1357.          ****************************************************************
  1358.          *                SET EXCHANGE RATE KEYSTROKE
  1359.          ****************************************************************
  1360.          *
  1361.       ELSEIF Upper(Chr(nKey)) == "X"
  1362.  
  1363.          ****** Don't allow change during bracketed operation
  1364.          If !(oDlg:lRBracketClosed)
  1365.             MyErrorMsg(-248,oDlg)
  1366.             EXIT
  1367.          Endif
  1368.  
  1369.          ***** Get the rates Update.
  1370.          *
  1371.          aRates:= Info( "Rates", oDlg:aCalcSize, oDlg)
  1372.  
  1373.          ***** Get the new rates into the Vars
  1374.          oDlg:nPE             := Val(aRates[1])
  1375.          oDlg:nDE             := Val(aRates[2])
  1376.          oDlg:nDP             := Val(aRates[3])
  1377.          oDlg:cXRate          := Alltrim(aRates[4])
  1378.  
  1379.          ***** Save the rates
  1380.          *
  1381.          nStatus:= PutData("BusiCalc.xch", @aRates, 10)
  1382.  
  1383.          IF !(nStatus == 0)
  1384.             MyErrorMsg(-246,oDlg,nStatus)
  1385.          ENDIF
  1386.  
  1387.          ***** Setup the currency Conversion Mode
  1388.          postAppEvent(xbeP_Keyboard,;
  1389.          IIF(oDlg:cXRate == "PE",Asc('P'),;
  1390.          IIF(oDlg:cXRate == "DP",Asc('B'),;
  1391.          Asc('U'))),,;
  1392.          oDlg:oKeyTrap)
  1393.  
  1394.          ***** Perform a crosscheck
  1395.          //ValidateRates(oDlg)
  1396.  
  1397.  
  1398.          ****************************************************************
  1399.          *               SELECT POUNDS TO EUROS KEYSTROKE
  1400.          ****************************************************************
  1401.          *
  1402.       ELSEIF Upper(Chr(nKey)) == "P"
  1403.          IF oDlg:oSymbol:Caption == BM_DLR
  1404.             EXIT
  1405.          ENDIF
  1406.          oDlg:oPBLocalTo:setCaption( PB_PND_TO_EUR  )
  1407.          oDlg:oPBForeignTo:setCaption( PB_EUR_TO_PND  )
  1408.          oDlg:nConvertRate := oDlg:nPE
  1409.          oDlg:cXRate := "PE"
  1410.          oDlg:oXchgRate:setCaption( Alltrim(Str(Abs(oDlg:nConvertRate))))
  1411.  
  1412.          ***** Update the Array
  1413.          aRates[4]:=  oDlg:cXRate+Space(8)
  1414.  
  1415.          ***** Syntax: PutData( cFile, @aArray, nRec_Len, nRecNo)
  1416.          ***** Update record 4 in the file.
  1417.          nStatus:= PutData("BusiCalc.xch", @aRates, 10, 4)
  1418.  
  1419.          IF !(nStatus == 0)
  1420.             MyErrorMsg(-246,oDlg,nStatus)
  1421.          ENDIF
  1422.  
  1423.          ***** Perform a crosscheck
  1424.          ValidateRates(oDlg)
  1425.  
  1426.          ****************************************************************
  1427.          *               SELECT DOLLARS TO POUNDS KEYSTROKE
  1428.          ****************************************************************
  1429.          *
  1430.       ELSEIF Upper(Chr(nKey)) == "B"
  1431.          IF oDlg:oSymbol:Caption == BM_EUR
  1432.             EXIT
  1433.          ENDIF
  1434.          oDlg:oPBLocalTo:setCaption( PB_PND_TO_DLR  )
  1435.          oDlg:oPBForeignTo:setCaption( PB_DLR_TO_PND  )
  1436.          oDlg:nConvertRate := oDlg:nDP
  1437.          oDlg:cXRate := "DP"
  1438.          oDlg:oXchgRate:setCaption( Alltrim(Str(Abs(oDlg:nConvertRate))))
  1439.  
  1440.          ***** Update the Array
  1441.          aRates[4]:=  oDlg:cXRate+Space(8)
  1442.  
  1443.          ***** Syntax: PutData( cFile, @aArray, nRec_Len, nRecNo)
  1444.          ***** Update record 4 in the file.
  1445.          nStatus:= PutData("BusiCalc.xch", @aRates, 10, 4)
  1446.  
  1447.          IF !(nStatus == 0)
  1448.             MyErrorMsg(-246,oDlg,nStatus)
  1449.          ENDIF
  1450.  
  1451.          ***** Perform a crosscheck
  1452.          ValidateRates(oDlg)
  1453.  
  1454.          ****************************************************************
  1455.          *               SELECT DOLLARS TO EUROS KEYSTROKE
  1456.          ****************************************************************
  1457.          *
  1458.       ELSEIF Upper(Chr(nKey)) == "U"
  1459.          IF oDlg:oSymbol:Caption == BM_PND
  1460.             EXIT
  1461.          ENDIF
  1462.          oDlg:oPBLocalTo:setCaption( PB_DLR_TO_EUR  )
  1463.          oDlg:oPBForeignTo:setCaption( PB_EUR_TO_DLR  )
  1464.          oDlg:nConvertRate := oDlg:nDE
  1465.          oDlg:cXRate := "DE"
  1466.          oDlg:oXchgRate:setCaption( Alltrim(Str(Abs(oDlg:nConvertRate))))
  1467.  
  1468.          ***** Update the Array
  1469.          aRates[4]:=  oDlg:cXRate+Space(8)
  1470.  
  1471.          ***** Syntax: PutData( cFile, @aArray, nRec_Len, nRecNo)
  1472.          ***** Update record 4 in the file.
  1473.          nStatus:= PutData("BusiCalc.xch", @aRates, 10, 4)
  1474.  
  1475.          IF !(nStatus == 0)
  1476.             MyErrorMsg(-246,oDlg,nStatus)
  1477.          ENDIF
  1478.  
  1479.          ***** Perform a crosscheck
  1480.          ValidateRates(oDlg)
  1481.  
  1482.          ****************************************************************
  1483.          *               IMPORT FROM CLIPBOARD KEYSTROKE
  1484.          ****************************************************************
  1485.          *
  1486.       ELSEIF nKey == xbeK_CTRL_V     // Ctrl-V  Insert/Paste
  1487.  
  1488.          ***** Data, and any errors handled in PollClipBoard()
  1489.          oDlg:cClipText:= PollClipBoard( oDlg )
  1490.  
  1491.          If !(Empty( oDlg:cClipText ))
  1492.  
  1493.             ***** Push the value
  1494.             oDlg:nDisplay  := Val(oDlg:cClipText)
  1495.  
  1496.             ***** Set Input Operation in progress
  1497.             oDlg:lNewCalculation := .F.
  1498.             ***** Set the NewInput flag
  1499.             oDlg:lNewInput       := .T.
  1500.  
  1501.             ***** Show the clipboard data in the display
  1502.             oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  1503.             oDlg:oDisplay:setCaption( oDlg:cDisplay )
  1504.  
  1505.             oDlg:oValInter:setCaption( AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))))
  1506.             oDlg:oValInput:setCaption( AllTrim(Str(Round(oDlg:nIntermediate, oDlg:nDecs))))
  1507.             oDlg:oValAccum:setCaption( AllTrim(Str(Round(oDlg:nAccumulator, oDlg:nDecs))))
  1508.             oDlg:lKeysEnabled:= .F.
  1509.             Dis_EnableKeys(oDlg)                      // Disable the number keys
  1510.  
  1511.          Endif
  1512.  
  1513.          ****************************************************************
  1514.          *               EXPORT TO CLIPBOARD KEYSTROKE
  1515.          ****************************************************************
  1516.          *
  1517.       ELSEIF nKey == xbeK_CTRL_C     // Ctrl-C  Copy to Clipboard
  1518.  
  1519.          oDlg:oClipBoard:open()
  1520.          oDlg:oClipBoard:clear()
  1521.          oDlg:oClipBoard:setBuffer( oDlg:cDisplay )
  1522.          oDlg:oClipBoard:close()
  1523.  
  1524.          *****************************************************************
  1525.          ********  END OF IFs in KEYSTROKE HANDLER SECTION ***************
  1526.          *****************************************************************
  1527.       ENDIF   //nKeys
  1528.  
  1529.       EXIT
  1530.  
  1531.    ENDDO
  1532.    EXIT
  1533. ENDDO
  1534.  
  1535. ***** Always Set the focus back to the keytrap
  1536. setAppFocus(oDlg:oKeyTrap)
  1537.  
  1538. Return nil
  1539. *
  1540. *                  END of STATIC FUNCTION CalcKeys()
  1541. *****************************************************************************
  1542. *
  1543. *****************************************************************************
  1544. *                          FUNCTION KeysProcess()
  1545. *****************************************************************************
  1546. *
  1547. FUNCTION KeysProcess(oDlg)
  1548.  
  1549.    Do While .T.
  1550.       ****** Intermediate is a temporary store used for manipulation
  1551.       oDlg:nIntermediate:= oDlg:nAccumulator
  1552.  
  1553.       ****** Execute the instruction
  1554.       Do Case
  1555.  
  1556.          Case 1 == oDlg:nOperation
  1557.             BEGIN SEQUENCE
  1558.             oDlg:nAccumulator   := oDlg:nIntermediate + oDlg:nDisplay
  1559.             oDlg:oOperText:setCaption( BM_PLUS  )  // + between audit boxes
  1560.             ENDSEQUENCE
  1561.  
  1562.  
  1563.          Case 2 == oDlg:nOperation
  1564.             BEGIN SEQUENCE
  1565.             oDlg:nAccumulator   := oDlg:nIntermediate * oDlg:nDisplay
  1566.             oDlg:oOperText:setCaption( BM_MULTIPLY  )
  1567.             ENDSEQUENCE
  1568.  
  1569.  
  1570.          Case 3 == oDlg:nOperation
  1571.             If !oDlg:nDisplay== 0   // Zero Divide trap.
  1572.                oDlg:nAccumulator   := oDlg:nIntermediate * 100 / oDlg:nDisplay
  1573.                oDlg:oOperText:setCaption( BM_PERCENT  ) // % between audit boxes
  1574.                oDlg:oSymbol:setCaption( BM_PERCENT  )   // % before display.
  1575.  
  1576.                ***** If it was currency, it is no longer a currency
  1577.                oDlg:nSymbol:= BM_NIL_BLANK
  1578.             Else
  1579.                MyErrorMsg(XPP_ERR_NUMERR,oDlg)
  1580.                EXIT
  1581.             Endif
  1582.  
  1583.          Case 4 == oDlg:nOperation
  1584.             oDlg:nAccumulator   := oDlg:nIntermediate - oDlg:nDisplay
  1585.             oDlg:oOperText:setCaption( BM_MINUS  )
  1586.  
  1587.          Case 5 == oDlg:nOperation
  1588.             If !oDlg:nDisplay== 0   // Zero Divide trap.
  1589.                oDlg:nAccumulator   := oDlg:nIntermediate / oDlg:nDisplay
  1590.                oDlg:oOperText:setCaption( BM_DIVIDE  )
  1591.                ***** If it was, it is no longer a currency
  1592.                oDlg:nSymbol:= BM_NIL_BLANK
  1593.             ELSE
  1594.                MyErrorMsg(XPP_ERR_ZERODIV,oDlg)
  1595.                EXIT
  1596.             ENDIF
  1597.  
  1598.          Case 6 == oDlg:nOperation
  1599.             BEGIN SEQUENCE
  1600.             oDlg:nAccumulator   := oDlg:nIntermediate ** oDlg:nDisplay
  1601.             oDlg:oOperText:setCaption( BM_NTH_POWER  )
  1602.             ENDSEQUENCE
  1603.  
  1604.          Case 7 == oDlg:nOperation  // nth root
  1605.             If oDlg:nIntermediate < 0
  1606.                oDlg:cDisplay := AllTrim(Str(oDlg:nDisplay))
  1607.                MyErrorMsg(-255,oDlg)
  1608.                EXIT
  1609.             Else
  1610.                oDlg:nAccumulator   := oDlg:nIntermediate**(1/oDlg:nDisplay)
  1611.                oDlg:oOperText:setCaption( BM_NTH_ROOT  )
  1612.             Endif
  1613.  
  1614.          OtherWise
  1615.             // do Nowt
  1616.       EndCase
  1617.  
  1618.       oDlg:oValInter:setCaption( AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs))))
  1619.  
  1620.       ****** Retain full decimal integrity so that any calculations
  1621.       ****** using an unmodified display value have maximum accuracy
  1622.       ****** eg. 1/x or x^2.
  1623.  
  1624.       oDlg:nDisplay := oDlg:nAccumulator
  1625.       oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  1626.       oDlg:oDisplay:setCaption( oDlg:cDisplay )
  1627.  
  1628.       oDlg:oValInput:setCaption( AllTrim(Str(Round(oDlg:nIntermediate, oDlg:nDecs))))
  1629.       oDlg:oValAccum:setCaption( AllTrim(Str(Round(oDlg:nAccumulator, oDlg:nDecs))))
  1630.  
  1631.       EXIT
  1632.  
  1633.    ENDDO
  1634.  
  1635. RETURN nil
  1636. *                        END FUNCTION KeysProcess()
  1637. *****************************************************************************
  1638. *
  1639. *****************************************************************************
  1640. *
  1641. *                    FUNCTION ValidateRates()
  1642. *
  1643. *     Uses the exchange rates to cross check for disparity
  1644. *
  1645. *     PE = Pound to Euro, DE = Dollar to Euro, DP = Dollar to Pound
  1646. *
  1647. *     Using two rates calculate the third and derive the percent error.
  1648. *
  1649. *****************************************************************************
  1650. *
  1651. FUNCTION ValidateRates(oDlg)
  1652.    LOCAL nDPcalculated := (1/oDlg:nPE)/(1/oDlg:nDE)
  1653.    LOCAL nError        := Abs(nDPcalculated - oDlg:nDP) * 100/oDlg:nDP
  1654.  
  1655.  
  1656.    nError := Round(nError,6)
  1657.  
  1658.    If nError > 0.01  // 0.01% error
  1659.       MyErrorMsg(-249,oDlg,nError)
  1660.    Endif
  1661.  
  1662. Return nil
  1663. *
  1664. *                   END FUNCTION ValidateRates()
  1665. *****************************************************************************
  1666. *
  1667. *****************************************************************************
  1668. *                         FUNCTION ClearAll()
  1669. *****************************************************************************
  1670. *
  1671. FUNCTION ClearAll(oDlg,lSetPreBracket)
  1672.  
  1673.    IF Valtype(lSetPreBracket)=="U"
  1674.       lSetPreBracket:= .T.
  1675.    ENDIF
  1676.  
  1677.    ***** Discard the data from the calculations prior to opening brackets
  1678.    IF lSetPreBracket
  1679.       ARemove(oDlg:aPreBracket,1,Len(oDlg:aPreBracket))
  1680.    ENDIF
  1681.  
  1682.    ***** Hide the 'number of brackets open' display
  1683.    oDlg:oBrackets:hide()
  1684.  
  1685.    ***** Set the Display to zero
  1686.    oDlg:cDisplay                                   := "0."
  1687.  
  1688.    ***** Enable BackSpace Key
  1689.    oDlg:lBS_Enabled                                := .T.
  1690.  
  1691.    ***** Set Getting number of decimals to false
  1692.    oDlg:lGettingDecsInput                          := .F.
  1693.  
  1694.    ***** Reset to not got a decimal point
  1695.    oDlg:lHaveDecimal                               := .F.
  1696.  
  1697.    ***** Set the number key flag
  1698.    oDlg:lKeysEnabled                               := .T.
  1699.  
  1700.    ***** Status of inline Operations
  1701.    oDlg:lLastOpWasImmediate                        := .F.
  1702.  
  1703.    ***** Set New Calculation
  1704.    oDlg:lNewCalculation                            := .F.
  1705.  
  1706.    ***** Set New input
  1707.    oDlg:lNewInput                                  := .F.
  1708.  
  1709.    ***** The main store
  1710.    oDlg:nAccumulator                               := 0
  1711.  
  1712.    ***** The 'real' value used in calculations
  1713.    oDlg:nDisplay                                   := 0
  1714.  
  1715.    ***** A WIP Value
  1716.    oDlg:nIntermediate                              := 0
  1717.  
  1718.    ***** Operation number e.g. for  +,-,x,/
  1719.    oDlg:nOperation                                 := 0
  1720.  
  1721.    ***** Set the Symbol to 'Blank'
  1722.    oDlg:nSymbol                                    := BM_NIL_BLANK
  1723.  
  1724.    ***** Temporary value store
  1725.    oDlg:nValueStore                                := 0
  1726.  
  1727.    ***** Left Bracket InActive
  1728.    oDlg:lLBracketOpen                              := .F.
  1729.  
  1730.    ***** Right Bracket InActive
  1731.    oDlg:lRBracketClosed                            := .T.
  1732.  
  1733.    ***** Show decimals
  1734.    oDlg:oDecSet2:setCaption(Alltrim(Str(oDlg:nDecs))+" Places")
  1735.  
  1736.    ***** Clear the Main Display
  1737.    oDlg:oDisplay:setCaption("0.")
  1738.  
  1739.    ***** Kill the symbol input / intermediate
  1740.    oDlg:oOperText:setCaption( BM_NIL_BLANK  )
  1741.  
  1742.    ***** Show the enabled BS key
  1743.    oDlg:oPBbspace:setCaption( PB_BSPACE_ENA  )
  1744.  
  1745.    ***** Kill the symbol before the Display
  1746.    oDlg:oSymbol:setCaption( BM_NIL_BLANK  )
  1747.  
  1748.    ***** Kill the accum value text
  1749.    oDlg:oValAccum:setCaption("Result")
  1750.  
  1751.    ***** Kill the inter value text
  1752.    oDlg:oValInput:setCaption("Value X")
  1753.  
  1754.    ***** Kill the input value text
  1755.    oDlg:oValInter:setCaption("Value n")
  1756.  
  1757.    ***** Show Exchange Rate
  1758.    oDlg:oXchgRate:setCaption(AllTrim(Str(oDlg:nConvertRate)))
  1759.  
  1760.    ***** Enable the number keys
  1761.    Dis_EnableKeys(oDlg)
  1762.  
  1763.    ***** Standard L Bracket Button
  1764.    oDlg:oPBLbracket:setCaption( PB_L_BRACKET  )
  1765.  
  1766.    ***** Standard R Bracket Button
  1767.    oDlg:oPBRbracket:setCaption( PB_R_BRACKET  )
  1768.  
  1769.    ***** Enable the Enter Key
  1770.    oDlg:oPBequals:setCaption( PB_EQUALS_ENA  )
  1771.  
  1772. RETURN nil
  1773. *
  1774. *                   END FUNCTION ClearAll()
  1775. *****************************************************************************
  1776. *
  1777. *****************************************************************************
  1778. *
  1779. *                         FUNCTION PushVars()
  1780. *
  1781. *   Saves the variables to allow re-establishment of conditions prior
  1782. *   to an action such as a change in the number of decimals displayed.
  1783. *
  1784. *  Note: This is a 'sawn-off' Array, it is used for more than one
  1785. *        calculator.  Only [1][n] applies to BusiCalc so it has been
  1786. *        modified to do the task.
  1787. *****************************************************************************
  1788. *
  1789. FUNCTION PushVars(oDlg,lAdd)
  1790.  
  1791.    IF ValType(lAdd)=="U"
  1792.       lAdd:= .F.
  1793.    ENDIF
  1794.  
  1795.    IF lAdd
  1796.       ARemove(oDlg:aDataStore,1,1)
  1797.       aAdd(oDlg:aDataStore,{;
  1798.       oDlg:aPos                  ,;
  1799.       oDlg:aSize                 ,;
  1800.       oDlg:cClipText             ,;
  1801.       oDlg:cDisplay              ,;
  1802.       oDlg:cXRate                ,;
  1803.       oDlg:lBS_Enabled           ,;
  1804.       oDlg:lHaveDecimal          ,;
  1805.       oDlg:lGettingDecsInput     ,;
  1806.       oDlg:lKeysEnabled          ,;
  1807.       oDlg:lKillKeys             ,;
  1808.       oDlg:lLastOpWasImmediate   ,;
  1809.       oDlg:lLBracketOpen         ,;
  1810.       oDlg:lNewCalculation       ,;
  1811.       oDlg:lNewInput             ,;
  1812.       oDlg:lRBracketClosed       ,;
  1813.       oDlg:nAccumStore           ,;
  1814.       oDlg:nAccumulator          ,;
  1815.       oDlg:nAction               ,;
  1816.       oDlg:nBMP                  ,;
  1817.       oDlg:nDecs                 ,;
  1818.       oDlg:nDisplay              ,;
  1819.       oDlg:nIntermediate         ,;
  1820.       oDlg:nMemVal               ,;
  1821.       oDlg:nOpCount              ,;
  1822.       oDlg:nOperation            ,;
  1823.       oDlg:nStoredOpNbr          ,;
  1824.       oDlg:nStoredValue          ,;
  1825.       oDlg:nSymbol               ,;
  1826.       oDlg:nValueStore           ,;
  1827.       oDlg:oOperText:Caption     ,;
  1828.       oDlg:oSymbol:Caption       ,;
  1829.       oDlg:oValAccum:Caption     ,;
  1830.       oDlg:oValInput:Caption     ,;
  1831.       oDlg:oValInter:Caption      },1)
  1832.  
  1833.    ELSE
  1834.       oDlg:aDataStore[1]:={;
  1835.       oDlg:aPos                  ,;
  1836.       oDlg:aSize                 ,;
  1837.       oDlg:cClipText             ,;
  1838.       oDlg:cDisplay              ,;
  1839.       oDlg:cXRate                ,;
  1840.       oDlg:lBS_Enabled           ,;
  1841.       oDlg:lHaveDecimal          ,;
  1842.       oDlg:lGettingDecsInput     ,;
  1843.       oDlg:lKeysEnabled          ,;
  1844.       oDlg:lKillKeys             ,;
  1845.       oDlg:lLastOpWasImmediate   ,;
  1846.       oDlg:lLBracketOpen         ,;
  1847.       oDlg:lNewCalculation       ,;
  1848.       oDlg:lNewInput             ,;
  1849.       oDlg:lRBracketClosed       ,;
  1850.       oDlg:nAccumStore           ,;
  1851.       oDlg:nAccumulator          ,;
  1852.       oDlg:nAction               ,;
  1853.       oDlg:nBMP                  ,;
  1854.       oDlg:nDecs                 ,;
  1855.       oDlg:nDisplay              ,;
  1856.       oDlg:nIntermediate         ,;
  1857.       oDlg:nMemVal               ,;
  1858.       oDlg:nOpCount              ,;
  1859.       oDlg:nOperation            ,;
  1860.       oDlg:nStoredOpNbr          ,;
  1861.       oDlg:nStoredValue          ,;
  1862.       oDlg:nSymbol               ,;
  1863.       oDlg:nValueStore           ,;
  1864.       oDlg:oOperText:Caption     ,;
  1865.       oDlg:oSymbol:Caption       ,;
  1866.       oDlg:oValAccum:Caption     ,;
  1867.       oDlg:oValInput:Caption     ,;
  1868.       oDlg:oValInter:Caption      }
  1869.    ENDIF
  1870.  
  1871. Return nil
  1872. *
  1873. *                   END FUNCTION PushVars()
  1874. *****************************************************************************
  1875. *
  1876. *****************************************************************************
  1877. *
  1878. *                         FUNCTION PopVars()
  1879. *
  1880. *  Re-establishes the conditions pertaining prior to decimals set.
  1881. *
  1882. *  Note: This is a 'sawn-off' Array, it is used for more than one
  1883. *        calculator, only [1][n] applies to BusiCalc.
  1884. *****************************************************************************
  1885. *
  1886. FUNCTION PopVars(oDlg)
  1887.  
  1888.    oDlg:aPos               :=  oDlg:aDataStore[1][ 1]
  1889.    oDlg:aSize              :=  oDlg:aDataStore[1][ 2]
  1890.    oDlg:cClipText          :=  oDlg:aDataStore[1][ 3]
  1891.    oDlg:cDisplay           :=  oDlg:aDataStore[1][ 4]
  1892.    oDlg:cXRate             :=  oDlg:aDataStore[1][ 5]
  1893.    oDlg:lBS_Enabled        :=  oDlg:aDataStore[1][ 6]
  1894.    oDlg:lHaveDecimal       :=  oDlg:aDataStore[1][ 7]
  1895.    oDlg:lGettingDecsInput  :=  oDlg:aDataStore[1][ 8]
  1896.    oDlg:lKeysEnabled       :=  oDlg:aDataStore[1][ 9]
  1897.    oDlg:lKillKeys          :=  oDlg:aDataStore[1][10]
  1898.    oDlg:lLastOpWasImmediate:=  oDlg:aDataStore[1][11]
  1899.    oDlg:lLBracketOpen      :=  oDlg:aDataStore[1][12]
  1900.    oDlg:lNewCalculation    :=  oDlg:aDataStore[1][13]
  1901.    oDlg:lNewInput          :=  oDlg:aDataStore[1][14]
  1902.    oDlg:lRBracketClosed    :=  oDlg:aDataStore[1][15]
  1903.    oDlg:nAccumStore        :=  oDlg:aDataStore[1][16]
  1904.    oDlg:nAccumulator       :=  oDlg:aDataStore[1][17]
  1905.    oDlg:nAction            :=  oDlg:aDataStore[1][18]
  1906.    oDlg:nBMP               :=  oDlg:aDataStore[1][19]
  1907.    IF !oDlg:lGettingDecsInput
  1908.       oDlg:nDecs           :=  oDlg:aDataStore[1][20]
  1909.    ENDIF
  1910.    oDlg:lGettingDecsInput  :=  .F.
  1911.    oDlg:nDisplay           :=  oDlg:aDataStore[1][21]
  1912.    oDlg:nIntermediate      :=  oDlg:aDataStore[1][22]
  1913.    oDlg:nMemVal            :=  oDlg:aDataStore[1][23]
  1914.    oDlg:nOpCount           :=  oDlg:aDataStore[1][24]
  1915.    oDlg:nOperation         :=  oDlg:aDataStore[1][25]
  1916.    oDlg:nStoredOpNbr       :=  oDlg:aDataStore[1][26]
  1917.    oDlg:nStoredValue       :=  oDlg:aDataStore[1][27]
  1918.    oDlg:nSymbol            :=  oDlg:aDataStore[1][28]
  1919.    oDlg:nValueStore        :=  oDlg:aDataStore[1][29]
  1920.    oDlg:oOperText:setCaption(  oDlg:aDataStore[1][30])
  1921.    oDlg:oSymbol:setCaption(    oDlg:aDataStore[1][31])
  1922.    oDlg:oValAccum:setCaption(  oDlg:aDataStore[1][32])
  1923.    oDlg:oValInput:setCaption(  oDlg:aDataStore[1][33])
  1924.    oDlg:oValInter:setCaption(  oDlg:aDataStore[1][34])
  1925.  
  1926.    If oDlg:nDisplay == 0
  1927.       oDlg:cDisplay:= "0."
  1928.    Else
  1929.       oDlg:cDisplay := AllTrim(Str(Round(oDlg:nDisplay, oDlg:nDecs)))
  1930.       oDlg:lKeysEnabled   :=  .F.
  1931.       Dis_EnableKeys(oDlg)
  1932.    Endif
  1933.  
  1934.    oDlg:oDisplay:setCaption( oDlg:cDisplay )
  1935.  
  1936.    oDlg:oMemStore:setCaption( AllTrim(Str(Round(oDlg:nMemVal, oDlg:nDecs))))
  1937.    oDlg:oDecSet2:setCaption( Alltrim(Str(oDlg:nDecs))+" Places") // Display
  1938.  
  1939.    oDlg:oPBbspace:setCaption( PB_BSPACE_DIS )
  1940.    oDlg:lBS_Enabled:= .F.
  1941.  
  1942. RETURN nil
  1943. *
  1944. *                   END FUNCTION PopVars()
  1945. *****************************************************************************
  1946.  
  1947. *****************************************************************************
  1948. *                         FUNCTION PollClipBoard( )
  1949. *****************************************************************************
  1950. *
  1951. FUNCTION PollClipBoard(oDlg)
  1952.  
  1953.    LOCAL aFormats, nTextLen, j
  1954.    LOCAL cValidChars := "-.0123456789+"
  1955.    LOCAL cNbrText := ""
  1956.    LOCAL lGotDecimal := .F.
  1957.  
  1958.   ****** Open clipboard and determine the data formats
  1959.   ****** contained in it.
  1960.  
  1961.   oDlg:oClipBoard:open()
  1962.  
  1963.   Do While .T.
  1964.  
  1965.       aFormats := oDlg:oClipBoard:queryFormats()
  1966.  
  1967.       ***** If text is available in the clipboard, import the text
  1968.       ***** into a variable assuming that it is valid numeric text
  1969.  
  1970.       IF AScan( aFormats, XBPCLPBRD_TEXT ) > 0
  1971.  
  1972.          ******  Excel conveniently <g> adds a CRLF to the value!!! hence StrTran()
  1973.          *
  1974.          oDlg:cClipText := ;
  1975.          Alltrim(StrTran(oDlg:oClipBoard:getBuffer( XBPCLPBRD_TEXT ),CRLF," "))
  1976.  
  1977.          If Empty(oDlg:cClipText)
  1978.             oDlg:oClipBoard:close()
  1979.             MyErrorMsg(-251,oDlg)
  1980.             EXIT
  1981.          Endif
  1982.  
  1983.          nTextLen:=Len(oDlg:cClipText)
  1984.  
  1985.          FOR j := 1 TO nTextLen
  1986.          IF Asc(SubStr(oDlg:cClipText,j,1)) < 33   // Space or Control Char
  1987.             cNbrText := ""                // Kill it
  1988.             j := nTextLen                 // Slßn leat! (Goodbye)
  1989.          Else
  1990.             IF SubStr(oDlg:cClipText,j,1) $ cValidChars
  1991.                IF !(lGotDecimal .AND. SubStr(oDlg:cClipText,j,1) == ".")
  1992.                   IF !(SubStr(oDlg:cClipText,j,1) $ "+-" .AND. j>1)
  1993.                      cNbrText += SubStr(oDlg:cClipText,j,1)
  1994.                      IF SubStr(oDlg:cClipText,j,1) == '.'
  1995.                         lGotDecimal := .T.
  1996.                      Endif
  1997.                   ELSE
  1998.                      cNbrText := ""                // Kill it
  1999.                      j := nTextLen                 // Slßn leat!
  2000.                   ENDIF
  2001.                ELSE
  2002.                   cNbrText := ""                // Kill it
  2003.                   j := nTextLen                 // Slßn leat!
  2004.                ENDIF
  2005.             ELSE
  2006.                cNbrText := ""                // Kill it
  2007.                j := nTextLen                 // Slßn leat!
  2008.             ENDIF
  2009.          ENDIF
  2010.          Next j
  2011.  
  2012.          If Empty( cNbrText )
  2013.             MyErrorMsg(-252,oDlg)
  2014.             EXIT
  2015.          Endif
  2016.  
  2017.       ENDIF
  2018.       EXIT
  2019.    ENDDO
  2020.    oDlg:oClipBoard:close()
  2021.  
  2022. RETURN (cNbrText)
  2023. *
  2024. *                   END FUNCTION PollClipBoard( )
  2025. *****************************************************************************
  2026. *
  2027. *****************************************************************************
  2028. *
  2029. *                    FUNCTION Dis_EnableKeys()
  2030. *
  2031. *  Pseudo Enables / Disables number keys
  2032. *
  2033. *  Not a real disable/enable just flags to ignore a keypress
  2034. *****************************************************************************
  2035. *
  2036. FUNCTION Dis_EnableKeys(oDlg)
  2037.  
  2038.    If !(oDlg:lKeysEnabled)
  2039.       oDlg:oPB_0:setCaption( PB__0_DIS  )
  2040.       oDlg:oPB_1:setCaption( PB__1_DIS  )
  2041.       oDlg:oPB_2:setCaption( PB__2_DIS  )
  2042.       oDlg:oPB_3:setCaption( PB__3_DIS  )
  2043.       oDlg:oPB_4:setCaption( PB__4_DIS  )
  2044.       oDlg:oPB_5:setCaption( PB__5_DIS  )
  2045.       oDlg:oPB_6:setCaption( PB__6_DIS  )
  2046.       oDlg:oPB_7:setCaption( PB__7_DIS  )
  2047.       oDlg:oPB_8:setCaption( PB__8_DIS  )
  2048.       oDlg:oPB_9:setCaption( PB__9_DIS  )
  2049.       oDlg:oPBPoint:setCaption( PB_POINT_DIS  )
  2050.    ELSE
  2051.       oDlg:oPB_0:setCaption( PB__0_ENA  )
  2052.       oDlg:oPB_1:setCaption( PB__1_ENA  )
  2053.       oDlg:oPB_2:setCaption( PB__2_ENA  )
  2054.       oDlg:oPB_3:setCaption( PB__3_ENA  )
  2055.       oDlg:oPB_4:setCaption( PB__4_ENA  )
  2056.       oDlg:oPB_5:setCaption( PB__5_ENA  )
  2057.       oDlg:oPB_6:setCaption( PB__6_ENA  )
  2058.       oDlg:oPB_7:setCaption( PB__7_ENA  )
  2059.       oDlg:oPB_8:setCaption( PB__8_ENA  )
  2060.       oDlg:oPB_9:setCaption( PB__9_ENA  )
  2061.       oDlg:oPBPoint:setCaption( PB_POINT_ENA  )
  2062.    ENDIF
  2063. RETURN nil
  2064. *
  2065. *                    END FUNCTION Dis_EnableKeys()
  2066. *****************************************************************************
  2067. *
  2068. *****************************************************************************
  2069. *                             FUNCTION Info()
  2070. *
  2071. *  WARNING: This Modal window is used by multiple functions, Help, About,
  2072. *           messages etc. I did it like that for convenience rather than
  2073. *           duplicate code by having multiple Modals.
  2074. *
  2075. *           DO NOT use Info() recursively.  That is, if you have called
  2076. *           Info() for anything, DO NOT call it again for a second task
  2077. *           until it has returned from the first call.
  2078. *
  2079. *           Why? You will have two instances of the Modal window open
  2080. *           and only one will close on return....
  2081. *
  2082. *           You could modify the code to make them all close but I just
  2083. *           followed my own advice and didn't bother! <g>
  2084. *
  2085. *****************************************************************************
  2086. *
  2087. FUNCTION Info( cSubTitle, aSize, oDlg)
  2088.    STATIC oInfoKeys
  2089.    LOCAL nEvent, mp1, mp2, oXbp, lExit, oExit,oDa,aPos
  2090.    LOCAL oMod, oFocus, oTitleStat, oNext, oPrev, aRates,oPE,oDE,oDP,oSave
  2091.  
  2092.    oFocus:= setAppFocus()
  2093.  
  2094.    aPos:= oDlg:currentPos()
  2095.    aRates:= {}
  2096.    ***** Create Modal dialog window with different parent and owner windows
  2097.    *
  2098.    oMod := XbpDialog():new( AppDesktop(), SetAppWindow(), aPos, aSize )
  2099.    oMod:drawingArea:ColorBG:= GRA_CLR_DARKBLUE
  2100.    oMod:moveWithOwner      := .T.
  2101.    oMod:titleBar           := .F.
  2102.    oMod:sysmenu            := .F.
  2103.    oMod:alwaysontop        := .T.
  2104.    oMod:border             := XBPDLG_NO_BORDER
  2105.    oMod:minButton          := .F.
  2106.    oMod:maxButton          := .F.
  2107.    oMod:maxsize            := aSize
  2108.    oMod:drawingArea:bitmap := BUSICALC_HA    //
  2109.    oMod:Close    := {|| lExit := .T. }
  2110.    oMod:create()
  2111.    oDa:= oMod:drawingArea
  2112.    oDa:setFontCompoundName( "8.Arial" )
  2113.    oDa:setColorBG( XBPSYSCLR_TRANSPARENT)
  2114.    oMod:setModalState( XBP_DISP_APPMODAL)
  2115.  
  2116.    oTitleStat:= ;
  2117.    XbpStatic():new( oDa, ,{aSize[1]-65, aSize[2]-35}, { 50, 16}, { ;
  2118.    { XBP_PP_BGCLR, GRA_CLR_DARKBLUE },  ;
  2119.    { XBP_PP_FGCLR, GRA_CLR_YELLOW },     ;
  2120.    { XBP_PP_COMPOUNDNAME, "10.Arial Bold" } } )
  2121.    oTitleStat:caption      := cSubTitle
  2122.    oTitleStat:clipSiblings := .T.
  2123.    oTitleStat:options      := XBPSTATIC_TEXT_VCENTER+XBPSTATIC_TEXT_CENTER
  2124.    oTitleStat:Create()
  2125.  
  2126.    oInfoKeys:= XbpStatic():new( oDa, ,{20,20}, {2,2}, { ;
  2127.    { XBP_PP_BGCLR, GRA_CLR_DARKBLUE }, ;
  2128.    { XBP_PP_FGCLR, GRA_CLR_PALEGRAY }, ;
  2129.    { XBP_PP_COMPOUNDNAME, "8.Arial" } } )
  2130.    oInfoKeys:caption      := ""
  2131.    oInfoKeys:clipSiblings:= .T.
  2132.    oInfoKeys:Create()
  2133.    oInfoKeys:keyBoard := ;
  2134.    {|nKey| MsgKeys(nKey,oDa,@lExit,oInfoKeys,oDlg,oPrev,oNext,@aRates,@oPE,@oDE,@oDP)}
  2135.  
  2136.    ***** Set focus to the Modal KeyHandler InfoKeys
  2137.    oMod:setDisplayFocus    := {|| setAppFocus(oInfoKeys)}
  2138.  
  2139.    oExit          := XbpPushbutton():new( oDa, , {140,14}, {40,18} )
  2140.    oExit:caption  := PB_CLOSE_1E  // 1= ~C  E= enabled
  2141.    oExit:activate := {|| postAppEvent(xbeP_Keyboard,Asc("C"),,oInfoKeys) }
  2142.    oExit:TabStop  := .T.
  2143.    oExit:create()
  2144.  
  2145.    IF "ABOUT"$Upper(cSubTitle)
  2146.       postAppEvent(xbeP_Keyboard,252,,oInfoKeys)  // 252 don't want user key!
  2147.  
  2148.    ELSEIF "ERROR"$Upper(cSubTitle)
  2149.       postAppEvent(xbeP_Keyboard,253,,oInfoKeys) // 253 No user key!
  2150.  
  2151.    ELSEIF "HELP"$Upper(cSubTitle)
  2152.       oNext          := XbpPushbutton():new( oDa, , {180,14}, {40,18} )
  2153.       oNext:caption  := PB_NEXT_0E
  2154.       oNext:activate := {|| postAppEvent(xbeP_Keyboard,xbeK_UP,,oInfoKeys) }
  2155.       oNext:TabStop  := .T.
  2156.       oNext:create()
  2157.  
  2158.       oPrev          := XbpPushbutton():new( oDa, , {100,14}, {40,18} )
  2159.       oPrev:caption  := PB_PREV_0E
  2160.       oPrev:activate := {|| postAppEvent(xbeP_Keyboard,xbeK_DOWN  ,,oInfoKeys) }
  2161.       oPrev:TabStop  := .T.
  2162.       oPrev:create()
  2163.       postAppEvent(xbeP_Keyboard,254,,oInfoKeys) // 254 No user key!
  2164.  
  2165.       oPrev:hide()
  2166.       oNext:hide()
  2167.    ELSE
  2168.       ***** Rates
  2169.       ***** All Records are length 10
  2170.       aAdd(aRates,Str(oDlg:nPE,10,6))
  2171.       aAdd(aRates,Str(oDlg:nDE,10,6))
  2172.       aAdd(aRates,Str(oDlg:nDP,10,6))
  2173.       aAdd(aRates,oDlg:cXRate+Space(8))
  2174.  
  2175.       oExit:hide()
  2176.  
  2177.       oSave          := XbpPushbutton():new( oDa, , {140,14}, {40,18} )
  2178.       oSave:caption  := PB_SAVE_1E
  2179.       oSave:activate := {|| postAppEvent(xbeP_Keyboard,Asc("S"),,oInfoKeys) }
  2180.       oSave:TabStop  := .T. // Need to discard the 'S' unless it is Rate Change.
  2181.       oSave:create()
  2182.       postAppEvent(xbeP_Keyboard,251,,oInfoKeys) // 251 No user key! Get Rates
  2183.  
  2184.    ENDIF
  2185.  
  2186.    SetAppWindow(oMod)
  2187.    oMod:show()
  2188.  
  2189.    lExit := .F.
  2190.    DO WHILE ! lExit
  2191.       nEvent := AppEvent( @mp1, @mp2, @oXbp )
  2192.       oXbp:handleEvent( nEvent, mp1, mp2 )
  2193.    ENDDO
  2194.  
  2195.    oMod:setModalState( XBP_DISP_MODELESS )
  2196.    oMod:destroy()
  2197.    SetAppFocus( oFocus )
  2198.  
  2199. RETURN aRates
  2200. *                       END FUNCTION Info()
  2201. *****************************************************************************
  2202. *
  2203. *****************************************************************************
  2204. *                       FUNCTION MsgKeys()
  2205. *****************************************************************************
  2206. *
  2207. ***** Declare them once, use many times...
  2208. STATIC FUNCTION MsgKeys(nKey,oDa,lExit,oInfoKeys,oDlg,oPrev,oNext,aRates,oPE,oDE,oDP)
  2209.    STATIC aBmps:={}
  2210.    STATIC aData:={}
  2211.    STATIC oBmp01,oBmp02,oBmp03,oBmp04,oBmp05,oBmp06,oBmp07,oBmp08
  2212.    STATIC oBmp09,oBmp10,oBmp11,oBmp12,oBmp13,oBmp14,oBmp15,oBmp16
  2213.    STATIC i
  2214.  
  2215.    Do While .T.
  2216.       IF nKey == 67  .OR. nKey == 99   .OR. nKey == xbeK_ESC  //C... ~Close
  2217.          lExit  := .T.
  2218.          EXIT
  2219.          ***** We don't want the user to be able to interfere with these three
  2220.          ***** keys used for routing purposes so they are 'safe' key values <g>.
  2221.          *
  2222.       ELSEIF nKey == 251      //.. Get Rates
  2223.          RatesUpdate(oDa,oDlg,@aRates,@oPE,@oDE,@oDP)
  2224.  
  2225.       ELSEIF nKey == 252      //.. About
  2226.          ShowAbout(oDa,oDlg)
  2227.  
  2228.       ELSEIF nKey == 253      //.. Error
  2229.          ShowError(oDa,oDlg)
  2230.  
  2231.       ELSEIF nKey == 254      //.. Help
  2232.  
  2233.          ***** Always page 1
  2234.          oDlg:nPage := 1
  2235.  
  2236.          ***** Create three statics for Help - Top line, Text Body, Left Margin
  2237.          ***** oDlg:oTopLine, oDlg:oGenText, oDlg:oBmpBar
  2238.          InfoStats(oDa,"H",oDlg)
  2239.  
  2240.          ***** oDlg:aHelp format: {{"top"},{"text;;"},{{nY,nBmp1},{nY,nBmp2},{...}}}
  2241.          ***** nY is the line number, nBmp is a Bitmap ID (Operator or Line No.)
  2242.  
  2243.          ***** Extract the Top Line
  2244.          oDlg:oTopLine:setcaption(oDlg:aHelp[oDlg:nPage][1][1])
  2245.  
  2246.          ***** Extract the Text Body
  2247.          oDlg:oGenText:setcaption(StrTran(oDlg:aHelp[oDlg:nPage][2][1],";",CRLF))
  2248.  
  2249.          ***** Create 16 statics oBmp01 thro oBmp16
  2250.          ***** and place them on the BmpBar (Left margin)
  2251.          ***** These will hold the line numbers or operator Bitmaps
  2252.          ***** They will always be referred to indirectly.
  2253.          ***** e.g. aBmp[9] = oBmp09, so they never see the light of day
  2254.          ***** and a compiler warning will say they are not used, not so.
  2255.  
  2256.          ***** Clear the decks...
  2257.          ARemove(aBmps,1,Len(aBmps))
  2258.  
  2259.          FOR i:= 1 to 16
  2260.          aAdd(aBmps,{"oBmp"+StrZero(i,2)})
  2261.          aBmps[i]         := XbpStatic():new(oDlg:oBmpBar,,{5,238-(i*15)},{22,16})
  2262.          aBmps[i]:type    := XBPSTATIC_TYPE_BITMAP
  2263.          aBmps[i]:caption := WHITE_BLANK
  2264.          aBmps[i]:create()
  2265.          NEXT i
  2266.  
  2267.          **** Instead of using a function to 'paint' the bitmaps we use
  2268.          **** an ElseIf in the keyhandler...
  2269.          nKey:= 255
  2270.          postAppEvent(xbeP_Keyboard,255,,oInfoKeys)     // Dummy - paint Bitmaps
  2271.          EXIT
  2272.  
  2273.       ELSEIF nKey == 80 .OR. nKey == 112  .OR. ;
  2274.          nKey == xbeK_DOWN  .OR. nKey == xbeK_LEFT  //P... Previous
  2275.  
  2276.          If oDlg:nPage == 1
  2277.             Exit
  2278.          Endif
  2279.  
  2280.          **** Clear the margin Bmps
  2281.          FOR i:= 1 to 16
  2282.             aBmps[i]:setCaption( WHITE_BLANK )
  2283.          NEXT i
  2284.  
  2285.          oDlg:nPage--
  2286.  
  2287.          ***** Extract the Top Line
  2288.          oDlg:oTopLine:setcaption(oDlg:aHelp[oDlg:nPage][1][1])
  2289.  
  2290.          ***** Extract the Text Body
  2291.          oDlg:oGenText:setcaption(StrTran(oDlg:aHelp[oDlg:nPage][2][1],";",CRLF))
  2292.  
  2293.          nKey:= 255
  2294.          postAppEvent(xbeP_Keyboard,255,,oInfoKeys) // Dummy - paint Bitmaps
  2295.          EXIT
  2296.  
  2297.       ELSEIF nKey == 78 .OR. nKey == 110   .OR. ;
  2298.          nKey ==  xbeK_UP .OR. nKey ==  xbeK_RIGHT  //N... Next
  2299.  
  2300.          If oDlg:nPage == Len(oDlg:aHelp)
  2301.             Exit
  2302.          Endif
  2303.  
  2304.          **** Clear the margin Bmps
  2305.          FOR i:= 1 to 16
  2306.             aBmps[i]:setCaption( WHITE_BLANK )
  2307.          NEXT i
  2308.  
  2309.          oDlg:nPage++
  2310.  
  2311.          ***** Extract the Top Line
  2312.          oDlg:oTopLine:setcaption(oDlg:aHelp[oDlg:nPage][1][1])
  2313.  
  2314.          ***** Extract the Text Body
  2315.          oDlg:oGenText:setcaption(StrTran(oDlg:aHelp[oDlg:nPage][2][1],";",CRLF))
  2316.  
  2317.          nKey:= 255
  2318.          postAppEvent(xbeP_Keyboard,255,,oInfoKeys) // Dummy - paint Bitmaps
  2319.          EXIT
  2320.  
  2321.       ELSEIF nKey == 255   // Dummy - paint Bitmaps
  2322.          ***** Get the bitmaps, if any, to be displayed
  2323.          aData:= oDlg:aHelp[oDlg:nPage][3]
  2324.          IF !(Empty(aData))
  2325.             FOR i:= 1 to Len(aData)
  2326.             *****  {Line number   ,         Bitmap}
  2327.             aBmps[aData[i][1]]:setCaption(aData[i][2])
  2328.             NEXT i
  2329.          ENDIF
  2330.          IIF(oDlg:nPage == 1, oPrev:hide(), oPrev:show())
  2331.          IIF(oDlg:nPage == Len(oDlg:aHelp), oNext:hide(), oNext:show())
  2332.  
  2333.       ELSEIF UPPER(Chr(nKey)) == "S"      //.. Save rates
  2334.          ***** TRAP INVALID 'S' KEYSTRIKE (in Help,About etc)
  2335.          IF !(oDlg:cTopText== "Exchange Rates Update")
  2336.             EXIT
  2337.          ENDIF
  2338.          ***** Str(Val()) used as quick reformat
  2339.          aRates[1] := Str( Val( oPE:getData() ) ,10,6)
  2340.          aRates[2] := Str( Val( oDE:getData() ) ,10,6)
  2341.          aRates[3] := Str( Val( oDP:getData() ) ,10,6)
  2342.          aRates[4] := oDlg:cXRate+Space(8)
  2343.          lExit  := .T. // Close
  2344.       ENDIF
  2345.  
  2346.       EXIT
  2347.    ENDDO
  2348.    ***** Set focus to keyhandler
  2349.    setAppFocus(oInfoKeys)
  2350.  
  2351. RETURN nil
  2352. *                       END FUNCTION MsgKeys()
  2353. *****************************************************************************
  2354. *
  2355. *****************************************************************************
  2356. *                       FUNCTION ShowAbout()
  2357. *****************************************************************************
  2358. *
  2359. FUNCTION ShowAbout(oDa,oDlg)
  2360.  
  2361.  
  2362.    oDlg:cTopText:=;
  2363.    "Greg J Doran;"+;
  2364.    "MIS Software;"+;
  2365.    "Raheny, Dublin 5, Ireland.;"+;
  2366.    "gdo@eircom.net"
  2367.  
  2368.    oDlg:cGenText:=;
  2369.    "Copyright (c) Gregory J Doran 2001-2002;"+;
  2370.    "BusiCalc is one of a series of calculators "+;
  2371.    "written entirely in Alaska Xbase++ (c);"+;
  2372.    ";"+;
  2373.    "The code used in the design of BusiCalc is released under "+;
  2374.    "the GNU General Public Licence and is governed by the "+;
  2375.    "conditions and protocols setout therein.  See Phil Ide's Xbase++ FAQs ;"+;
  2376.    "http://www.idep.org.uk/xbase/xbfaq/xbfaq.htm"
  2377.  
  2378.  
  2379.  
  2380.    InfoStats(oDa,"A",oDlg)
  2381.    oDlg:oTopLine:setcaption(StrTran(oDlg:cTopText,";",CRLF))
  2382.    oDlg:oGenText:setcaption(StrTran(oDlg:cGenText,";",CRLF))
  2383. RETURN nil
  2384. *                       END FUNCTION ShowAbout()
  2385. *****************************************************************************
  2386. *
  2387. *****************************************************************************
  2388. *                       FUNCTION ShowError()
  2389. *****************************************************************************
  2390.  
  2391. FUNCTION ShowError(oDa,oDlg )
  2392.  
  2393.    InfoStats(oDa,"M",oDlg)
  2394.    oDlg:oTopLine:setcaption(StrTran(oDlg:cTopText,";",CRLF))
  2395.    oDlg:oGenText:setcaption(StrTran(oDlg:cGenText,";",CRLF))
  2396. RETURN nil
  2397. *                       END FUNCTION ShowError()
  2398. *****************************************************************************
  2399. *
  2400. *****************************************************************************
  2401. *                       FUNCTION RatesUpdate()
  2402. *****************************************************************************
  2403.  
  2404. FUNCTION RatesUpdate(oDa,oDlg, aRates,oPE,oDE,oDP )
  2405.    LOCAL oXbp
  2406.    oDlg:cTopText:=;
  2407.    "Exchange Rates Update"
  2408.  
  2409.    oDlg:cGenText:=;
  2410.    "Modify the Exchange Rates and press &Save when completed.;"+;
  2411.    ";"+;
  2412.    "6 Decimal places of accuracy recommended."
  2413.  
  2414.    ***** Borrow Message Stats.
  2415.    InfoStats(oDa,"M",oDlg)
  2416.    oDlg:oTopLine:setcaption(StrTran(oDlg:cTopText,";",CRLF))
  2417.    oDlg:oGenText:setcaption(StrTran(oDlg:cGenText,";",CRLF))
  2418.  
  2419.    oXbp             := XbpStatic():new( oDlg:oGenText, , {10,84}, {120,20} )
  2420.    oXbp:caption     := "Pounds = 1 Euro:"
  2421.    oXbp:options     := XBPSTATIC_TEXT_LEFT
  2422.    oXbp:create()
  2423.  
  2424.    oXbp             := XbpStatic():new( oDlg:oGenText, , {10,52}, {120,20} )
  2425.    oXbp:caption     := "Dollars = 1 Euro:"
  2426.    oXbp:options     := XBPSTATIC_TEXT_LEFT
  2427.    oXbp:create()
  2428.  
  2429.    oXbp             := XbpStatic():new( oDlg:oGenText, , {10,20}, {120,20} )
  2430.    oXbp:caption     := "Dollars = 1 Pound:"
  2431.    oXbp:options     := XBPSTATIC_TEXT_LEFT
  2432.    oXbp:create()
  2433.  
  2434.    oPE              := XbpSLE():new( oDlg:oGenText, , {145,86}, {60,20} )
  2435.    oPE:tabStop      := .T.
  2436.    oPE:bufferLength := 10
  2437.    oPE:create()
  2438.    oPE:Setdata(aRates[1])
  2439.  
  2440.    oDE              := XbpSLE():new( oDlg:oGenText, , {145,54}, {60,20} )
  2441.    oDE:tabStop      := .T.
  2442.    oDE:bufferLength := 10
  2443.    oDE:create()
  2444.    oDE:Setdata(aRates[2])
  2445.  
  2446.    oDP              := XbpSLE():new( oDlg:oGenText, , {145,22}, {60,20} )
  2447.    oDP:tabStop      := .T.
  2448.    oDP:bufferLength := 10
  2449.    oDP:create()
  2450.    oDP:Setdata(aRates[3])
  2451.  
  2452. RETURN nil
  2453. *                       END FUNCTION RatesUpdate()
  2454. *****************************************************************************
  2455. *
  2456. *****************************************************************************
  2457. *                       FUNCTION InfoStats()
  2458. *****************************************************************************
  2459.  
  2460. FUNCTION InfoStats(oDa,cMode,oDlg)
  2461.    LOCAL aColor:= {GRA_CLR_BLACK,;
  2462.    XBPSYSCLR_DIALOGBACKGROUND}
  2463.  
  2464.    IF cMode== "H"
  2465.       oDlg:oBmpBar   := XbpStatic():new( oDa,, {40,40},{30,265},;
  2466.       {{ XBP_PP_FGCLR, GRA_CLR_DARKRED} ,;
  2467.       { XBP_PP_BGCLR, GRA_CLR_WHITE} })
  2468.       oDlg:oBmpBar:type    := XBPSTATIC_TYPE_TEXT
  2469.       oDlg:oBmpBar:caption := ""
  2470.  
  2471.       oDlg:oTopLine      := XbpStatic():new( oDa,, {40,281},{250,24},;
  2472.       {{ XBP_PP_FGCLR, GRA_CLR_BLUE} ,;
  2473.       { XBP_PP_BGCLR, GRA_CLR_WHITE} ,;
  2474.       { XBP_PP_COMPOUNDNAME, "12.Times New Roman Bold"}} )
  2475.       oDlg:oTopLine:options :=  XBPSTATIC_TEXT_CENTER+XBPSTATIC_TEXT_VCENTER
  2476.       oDlg:oTopLine:caption := "TITLE"
  2477.       oDlg:oTopLine:type       := XBPSTATIC_TYPE_TEXT
  2478.  
  2479.       oDlg:oGenText      := XbpStatic():new( oDa,, {70,37},{220,243},;
  2480.       {{ XBP_PP_FGCLR, GRA_CLR_BLACK} ,;
  2481.       { XBP_PP_BGCLR, GRA_CLR_WHITE} ,;
  2482.       { XBP_PP_COMPOUNDNAME, "10.Times New Roman"}} )
  2483.       oDlg:oGenText:options := XBPSTATIC_TEXT_VCENTER+;
  2484.       XBPSTATIC_TEXT_WORDBREAK+;
  2485.       XBPSTATIC_TEXT_LEFT
  2486.       oDlg:oGenText:caption := "Help Text"
  2487.       oDlg:oGenText:type    := XBPSTATIC_TYPE_TEXT
  2488.  
  2489.    ELSEIF cMode== "A" // About
  2490.       oDlg:oBmpBar   := XbpStatic():new( oDa,, {133,252},{50,50},;
  2491.       {{ XBP_PP_FGCLR, aColor[1]} ,;
  2492.       { XBP_PP_BGCLR, aColor[2]} })
  2493.       oDlg:oBmpBar:type    := XBPSTATIC_TYPE_BITMAP
  2494.       oDlg:oBmpBar:caption := GJDLOGO
  2495.  
  2496.       oDlg:oTopLine      := XbpStatic():new( oDa,, {40,183},{235,65},;
  2497.       {{ XBP_PP_FGCLR, GRA_CLR_BLUE} ,;
  2498.       { XBP_PP_BGCLR, GRA_CLR_WHITE} ,;
  2499.       { XBP_PP_COMPOUNDNAME, "10.Arial Bold"}} )
  2500.       oDlg:oTopLine:options := XBPSTATIC_TEXT_VCENTER+;
  2501.       XBPSTATIC_TEXT_WORDBREAK+;
  2502.       XBPSTATIC_TEXT_CENTER
  2503.       oDlg:oTopLine:caption := "TITLE"
  2504.       oDlg:oTopLine:type    := XBPSTATIC_TYPE_TEXT
  2505.  
  2506.       oDlg:oGenText      := XbpStatic():new( oDa,, {55,40},{235,137},;
  2507.       {{ XBP_PP_FGCLR, GRA_CLR_BLACK} ,;
  2508.       { XBP_PP_BGCLR, GRA_CLR_WHITE} ,;
  2509.       { XBP_PP_COMPOUNDNAME, "10.Times New Roman"}} )
  2510.       oDlg:oGenText:options := XBPSTATIC_TEXT_VCENTER+;
  2511.       XBPSTATIC_TEXT_WORDBREAK+;
  2512.       XBPSTATIC_TEXT_LEFT
  2513.       oDlg:oGenText:caption     := "About Blurp"
  2514.       oDlg:oGenText:type    := XBPSTATIC_TYPE_TEXT
  2515.  
  2516.    ELSE  // Message
  2517.  
  2518.       oDlg:oBmpBar   := XbpStatic():new( oDa,, {142,261},{32,32},;
  2519.       {{ XBP_PP_FGCLR, GRA_CLR_BLACK} ,;
  2520.       { XBP_PP_BGCLR, GRA_CLR_WHITE} })
  2521.       oDlg:oBmpBar:type    := XBPSTATIC_TYPE_ICON
  2522.       oDlg:oBmpBar:caption := ;
  2523.       IIF(!(oDlg:cTopText=="Exchange Rates Update"),EXCLAIM,EURO)
  2524.  
  2525.       oDlg:oTopLine      := XbpStatic():new( oDa,, {40,183},{235,65},;
  2526.       {{ XBP_PP_FGCLR, GRA_CLR_RED} ,;
  2527.       { XBP_PP_BGCLR, GRA_CLR_WHITE} ,;
  2528.       { XBP_PP_COMPOUNDNAME, "10.Times New Roman Bold"}} )
  2529.       oDlg:oTopLine:options := XBPSTATIC_TEXT_VCENTER+;
  2530.       XBPSTATIC_TEXT_WORDBREAK+;
  2531.       XBPSTATIC_TEXT_CENTER
  2532.       oDlg:oTopLine:caption := "TITLE"
  2533.       oDlg:oTopLine:type    := XBPSTATIC_TYPE_TEXT
  2534.  
  2535.       oDlg:oGenText      := XbpStatic():new( oDa,, {55,40},{235,170},;
  2536.       {{ XBP_PP_FGCLR, GRA_CLR_BLACK} ,;
  2537.       { XBP_PP_BGCLR, GRA_CLR_WHITE} ,;
  2538.       { XBP_PP_COMPOUNDNAME, "9.Times New Roman"}} )
  2539.       oDlg:oGenText:options := XBPSTATIC_TEXT_VCENTER+;
  2540.       XBPSTATIC_TEXT_WORDBREAK+;
  2541.       XBPSTATIC_TEXT_LEFT
  2542.       oDlg:oGenText:caption     := "Message"
  2543.       oDlg:oGenText:type    := XBPSTATIC_TYPE_TEXT
  2544.    ENDIF
  2545.  
  2546.    oDlg:oBmpBar:create()
  2547.    oDlg:oTopLine:create()
  2548.    oDlg:oGenText:create()
  2549.  
  2550. RETURN nil
  2551. *                       END FUNCTION InfoStats()
  2552. *****************************************************************************
  2553. *
  2554. *****************************************************************************
  2555. *                          FUNCTION MoveWin()
  2556. *****************************************************************************
  2557. *
  2558. FUNCTION MoveWin(aPMot,aLBDownAtPos, oDlg)
  2559.  
  2560.    ***** Get Mouse Movement differentials
  2561.    *
  2562.    LOCAL nPx   := (aPMot[1]-aLBDownAtPos[1]) * 2      //  magnify x2
  2563.    LOCAL nPy   := (aPMot[2]-aLBDownAtPos[2]) * (-2)   //  magnify x2 and
  2564.                                                       //  Negate, this is WinAPI
  2565.                                                       //  not upside-down pixels
  2566.  
  2567.    ***** AppDeskTop boundaries for window move, Bot,Rt
  2568.    *
  2569.    LOCAL nB := AppDeskTop():currentSize()[2]-oDlg:aSize[2]
  2570.    LOCAL nR := AppDeskTop():currentSize()[1]-oDlg:aSize[1]
  2571.  
  2572.    ***** Only process if there is a difference
  2573.    *
  2574.    If Abs(nPx) > 0 .OR. Abs(nPy) > 0
  2575.  
  2576.       ***** Add cursor movement differentials
  2577.       *
  2578.       oDlg:aPos[1]+= nPx
  2579.       oDlg:aPos[2]+= nPy
  2580.  
  2581.       ***** Check Boundaries  -  Nothing to go off-screen
  2582.       *
  2583.       oDlg:aPos[1]:=INT(IIF(oDlg:aPos[1]< 0, 0, oDlg:aPos[1])) // Left check
  2584.       oDlg:aPos[1]:=INT(IIF(oDlg:aPos[1]>nR,nR, oDlg:aPos[1])) // Right check
  2585.       oDlg:aPos[2]:=INT(IIF(oDlg:aPos[2]< 0, 0, oDlg:aPos[2])) // Top Check
  2586.       oDlg:aPos[2]:=INT(IIF(oDlg:aPos[2]>nB,nB, oDlg:aPos[2])) // Bottom Check
  2587.  
  2588.       setAppFocus(oDlg)
  2589.  
  2590.       ***** Free the cursor
  2591.       *
  2592.       TrapCursor(oDlg:drawingArea,.F.)
  2593.  
  2594.       ****** WinApi Move function
  2595.       *
  2596.       MoveWindow(oDlg:getHWND(),oDlg:aPos[1],oDlg:aPos[2],;
  2597.       oDlg:aSize[1],oDlg:aSize[2],1)
  2598.  
  2599.       ****** Restore the cursor to the original location
  2600.       *
  2601.       SetCursorPos( oDlg:aPos[1]+aLBDownAtPos[1],;
  2602.       oDlg:aSize[2]+oDlg:aPos[2]-aLBDownAtPos[2])
  2603.  
  2604.       ***** Entrap the cursor
  2605.       *
  2606.       TrapCursor(oDlg:drawingArea,.T.)
  2607.  
  2608.    ENDIF
  2609. Return nil
  2610. *                       END FUNCTION MoveWin
  2611. *****************************************************************************
  2612. *****************************************************************************
  2613. *
  2614. *                       FUNCTION TrapCursor()
  2615. *
  2616. *****************************************************************************
  2617. *
  2618. FUNCTION TrapCursor(o,lTrap)
  2619. LOCAL cBuffer     := Space(16) // cBuffer == a4LTRB_lpRect:= {nL,nT,nR,nB}
  2620.  
  2621. ***** Entrap the cursor in the titlebar region?
  2622. IF lTrap
  2623.    ***** get the absolute position of oDlg:drawing area (relative to DeskTop)
  2624.    *
  2625.    DllCall("User32.DLL", DLL_STDCALL,"GetWindowRect", o:GetHwnd(), @cBuffer)
  2626.  
  2627.    ***** Remove the area covered by oDlg:Mask  (adjusting bottom value)
  2628.    *
  2629.    cBuffer:= substr(cBuffer,  1, 12)+U2Bin(Bin2U(substr(cBuffer, 13, 4))-304)
  2630.  
  2631.    ***** Entrap the cursor in this rectangle.
  2632.    *
  2633.    DllCall("User32.DLL", DLL_STDCALL, "ClipCursor", cBuffer)
  2634. ELSE
  2635.    ***** Set the cursor free to roam.
  2636.    *
  2637.    DllCall("User32.DLL", DLL_STDCALL, "ClipCursor", 0)
  2638. ENDIF
  2639.  
  2640. RETURN nil
  2641. *                       FUNCTION TrapCursor()
  2642. *****************************************************************************
  2643. *
  2644. *****************************************************************************
  2645. *                       FUNCTION ShowItsOpen()
  2646. *****************************************************************************
  2647. *
  2648. FUNCTION ShowItsOpen(hwnd)
  2649.  
  2650.    ***** THE WINDOW IS 'MINIMISED'!!!
  2651.    ***** CREATE A GHOST WINDOW
  2652.    ***** MOVE IT AS AN INDICATOR THEN DESTROY IT
  2653.    LOCAL oDlg,  i
  2654.    LOCAL aPP      := {}
  2655.    LOCAL aPos     := {nil,nil}
  2656.    LOCAL aSize    := {318, 348}   // Dialog
  2657.    aPos[1]        := (SetAppWindow():currentSize()[1] -  aSize[1]) / 2
  2658.    aPos[2]        := (SetAppWindow():currentSize()[2] -  aSize[2]) / 2
  2659.    AAdd ( aPP, { XBP_PP_BGCLR, GRA_CLR_DARKBLUE } )
  2660.    AAdd ( aPP, { XBP_PP_FGCLR, GRA_CLR_PALEGRAY } )
  2661.    AAdd ( aPP, { XBP_PP_COMPOUNDNAME, "8.Arial" } )
  2662.    oDlg:= BusiCalc():New( AppDesktop(),, aPos,aSize,aPP, .F.)
  2663.    oDlg:border                        := XBPDLG_NO_BORDER
  2664.    oDlg:titleBar                      := .F.
  2665.    oDlg:sysmenu                       := .F.
  2666.    oDlg:alwaysontop                   := .T.
  2667.    oDlg:title                         := "BusiCalc Ghost"
  2668.    oDlg:maxsize                       := aSize
  2669.    oDlg:drawingArea:bitmap            := BUSICALC
  2670.    oDlg:Create()
  2671.    SetAppWindow(oDlg)
  2672.    SetAppFocus( oDlg)
  2673.    oDlg:show()
  2674.  
  2675.    ***** Now move it towards the minimised App ( [ BusiCalc ][X] ) at
  2676.    ***** the top of the screen and reduce its size as we go.
  2677.    For i:= 1 to 5
  2678.    aPos[1]:= aPos[1]*(1/i)
  2679.    aPos[2]:= aPos[2]*(1/i)
  2680.    aSize[1]:= aSize[1]*0.5
  2681.    aSize[2]:= aSize[2]*0.5
  2682.    oDlg:configure( , ,{AppDesktop():CurrentSize()[1]-(200+aPos[1]),;
  2683.    AppDesktop():CurrentSize()[2]-(18+aPos[2])} , {aSize[1],aSize[2]})
  2684.    Sleep(5) // Flicker....
  2685.    NEXT
  2686.    oDlg:destroy()
  2687.    Sleep(100*70)
  2688.    ***** This is not needed, unless a 'real' minimise is used.
  2689.    ***** If so, the above code would not be used, only this.
  2690.    SetForegroundWindow( hwnd )       // Not needed unless there are
  2691.    BringWindowToTop( hwnd )          // two windows in competition
  2692.    ShowWindow(hwnd,SW_SHOW_NORMAL)   // for the 'top' spot
  2693.    UpdateWindow( hwnd )              // Or a 'real' minimise is used
  2694.    Sleep(100*20)
  2695.    PostAppevent(xbeM_Motion)
  2696. RETURN nil
  2697. *                       END FUNCTION ShowItsOpen()
  2698. *****************************************************************************
  2699. *
  2700. *****************************************************************************
  2701. *                        FUNCTION MyErrorHandler()
  2702. *****************************************************************************
  2703. *
  2704. FUNCTION MyErrorHandler(oError,oDlg)
  2705.  
  2706.    ***** Do not use Xbase++ handler for Divide errors, handle locally.
  2707.    ***** Zero is returned and recovery is difficult
  2708.    ***** Most errors are determined locally in code and error messages displayed.
  2709.    *
  2710.    IF oError:gencode == XPP_ERR_NUMOVERFLOW
  2711.       ***** Fatal Error, issue message and go straight down the stank!.
  2712.       MyErrorMsg(XPP_ERR_NUMOVERFLOW,oDlg)
  2713.       oDlg:Destroy()
  2714.       ERRORLEVEL(1)
  2715.       CANCEL
  2716.    ELSE
  2717.       MyErrorMsg(oError:gencode,oDlg,oError:oscode)
  2718.    ENDIF
  2719. RETURN nil
  2720. *                    END FUNCTION MyErrorHandler()
  2721. *****************************************************************************
  2722. *
  2723. *****************************************************************************
  2724. *                   FUNCTION MyErrorMsg()
  2725. *****************************************************************************
  2726. *
  2727. FUNCTION MyErrorMsg(nError,oDlg,nAlt)
  2728.  
  2729.    IF Valtype(nAlt)=="U"
  2730.       nAlt:= -999  // Just in case there is none.
  2731.    ENDIF
  2732.  
  2733.    IF nError == XPP_ERR_ZERODIV
  2734.       oDlg:cTopText :=  "ERROR: Divide by Zero"
  2735.       oDlg:cGenText :=;
  2736.       "You have attempted to divide by Zero!;"  +;
  2737.       ";"+;
  2738.       "The divide operation has been halted...;"   +;
  2739.       ";"+;
  2740.       "This is a recoverable error, you may continue the calculation.;"  +;
  2741.       ";"+;
  2742.       "Input a non-zero divisor and continue, or press Clear All [CA] to abort."
  2743.  
  2744.    ELSEIF nError == XPP_ERR_NUMOVERFLOW
  2745.       oDlg:cTopText :=  "ERROR: Numeric Overflow"
  2746.       oDlg:cGenText :=;
  2747.       "The value resulting from your last calculation is in excess of "+;
  2748.       "the largest value capable of being processed by this calculator "+;
  2749.       "and has caused a numeric overflow.;"+;
  2750.       ";"+;
  2751.       "This is a Fatal Error, recovery is not possible...;"+;
  2752.       ";"+;
  2753.       "The Calculator will now Close."
  2754.  
  2755.    ELSEIF nError == XPP_ERR_NUMERR   //Sic "Number Error"
  2756.       oDlg:cTopText :=  "ERROR: Percent - Divide by Zero"
  2757.       oDlg:cGenText :=;
  2758.       "You have attempted to divide by Zero in a Percent operation.;"  +;
  2759.       ";"+;
  2760.       "The divide operation has been halted...;"   +;
  2761.       ";"+;
  2762.       "This is a recoverable error, you may continue the calculation.;"  +;
  2763.       ";"+;
  2764.       "Input a non-zero divisor and continue, or press Clear All [CA] to abort."
  2765.  
  2766.    ELSEIF nError == (-255)
  2767.       oDlg:cTopText :=  "ERROR: Number Base - nth. Root"
  2768.       oDlg:cGenText :=;
  2769.       "You have entered a negative number base.;"+;
  2770.       ";"+;
  2771.       "BusiCalc cannot resolve a Root for a number with a negative base.;"+;
  2772.       ";"+;
  2773.       "    "+Alltrim(Str(oDlg:nIntermediate))+" ^ (1/"+oDlg:cDisplay+")"
  2774.  
  2775.    ELSEIF nError == (-254)
  2776.       oDlg:cTopText :=  "ERROR: Sqrt(Negative Number)"
  2777.       oDlg:cGenText :=;
  2778.       "You have entered a negative number base.;"+;
  2779.       ";"+;
  2780.       "This request would result in complex number roots which "+;
  2781.       "BusiCalc cannot resolve.;"+;
  2782.       ";"+;
  2783.       "TechiCalc will perform this operation.;"+;
  2784.       ";"+;
  2785.       "    Sqrt( "+oDlg:cDisplay+" )."
  2786.  
  2787.    ELSEIF nError == (-253)
  2788.       oDlg:cTopText :=  "ERROR: Number resolution."
  2789.       oDlg:cGenText :=;
  2790.       "You have entered a number which is either invalid or "+;
  2791.       "outside the range defined for BusiCalc operations.   "+;
  2792.       "Factorial: n= "+oDlg:cDisplay+";"+;
  2793.       ";"+;
  2794.       "The number must be positive and within the range 1 - 21 "+;
  2795.       "inclusive.;"+;
  2796.       ";"+;
  2797.       "Decimal numbers will be reduced to Integer status."
  2798.  
  2799.    ELSEIF nError == (-252)
  2800.       oDlg:cClipText:=StrTran(oDlg:cClipText,';',' ')
  2801.       ***** (Otherwise we get CRLFs in display)
  2802.       oDlg:cTopText :=  "ERROR: ClipBoard Junk."
  2803.       oDlg:cGenText :=;
  2804.       "There is something on the clipboard but, it is not a valid number:-;"+;
  2805.       ";"+;
  2806.       IIF(Len(oDlg:cClipText)>100,Left(oDlg:cClipText,100),oDlg:cClipText)+";"+;
  2807.       ";"+;
  2808.       "(Any carriage returns have been stripped out of the above text"+;
  2809.       IIF(Len(oDlg:cClipText)>100," and it was truncated at 100 characters.)",".)")
  2810.  
  2811.    ELSEIF nError == (-251)
  2812.       oDlg:cTopText :=  "ClipBoard Status."
  2813.       oDlg:cGenText :=;
  2814.       "The clipboard is empty....;"+;
  2815.       ";"+;
  2816.       "There is nothing to Import...."
  2817.  
  2818.    ELSEIF nError == (-250)
  2819.       oDlg:cTopText :=  "Decimal Places Change."
  2820.       oDlg:cGenText :=;
  2821.       "Please complete the current operation within brackets first. ;"+;
  2822.       ";"+;
  2823.       "Making a change later will not affect the accuracy of your calculations."
  2824.  
  2825.    ELSEIF nError == (-249)
  2826.       oDlg:cTopText :=  "Exchange Rate Disparity."
  2827.       oDlg:cGenText :=;
  2828.       "Warning!;"+;
  2829.       "A validation check on the exchange "+;
  2830.       "rates indicates an inter-rate ;"+;
  2831.       ";"+;
  2832.       "conversion error of: "+Alltrim(Str(nAlt,12,6))+"%;"+;
  2833.       ";"+;
  2834.       "You may proceed in this knowledge "+;
  2835.       "or enter the correct exchange rates."
  2836.  
  2837.    ELSEIF nError == (-248)
  2838.       oDlg:cTopText :=  "Exchange Rate Update."
  2839.       oDlg:cGenText :=;
  2840.       "Please complete the current calculation first. ;"+;
  2841.       ";"+;
  2842.       "Making a currency change at this juncture cannot be accomodated."
  2843.  
  2844.    ELSEIF nError == (-247)
  2845.  
  2846.       oDlg:cTopText :=  "Exchange Rate Substitution"
  2847.       oDlg:cGenText :=;
  2848.       "The Exchange Rates file 'BusiCalc.xch' was "+;
  2849.       IIF(nAlt==1,"not found and has been created.",;
  2850.       IIF(nAlt==2,"not accessible. You cannot save Rates to this file.",;
  2851.       "unreadable or is 'Read Only'.  You cannot save Rates to this file."))+;
  2852.       ";"+;
  2853.       ";"+;
  2854.       "Temporary Exchange Rates have been installed as of 30.Sep.2002.;"+;
  2855.       ";"+;
  2856.       "It is essential that you check these rates and make adjustments "+;
  2857.       "as necessary.  "+;
  2858.       IIF(nAlt==2 .OR. nAlt==3,;
  2859.       "Check out why there is a problem with the Rates File.;","")+;
  2860.       "Exchange Rates:    http://www.xe.net/ucc/"
  2861.  
  2862.    ELSEIF nError == (-246)
  2863.       oDlg:cTopText :=  "Exchange Rate File Problem"
  2864.       oDlg:cGenText :=;
  2865.       "There is a problem with the Exchange Rates file 'BusiCalc.xch'.;"+;
  2866.       ";"+;
  2867.       "When trying to save the Conversion Mode, or current Exchange Rates "+;
  2868.       "to the file, the file could not be "+;
  2869.       IIF(nAlt==1,"opened!.",;
  2870.       IIF(nAlt==2,"created!.",;
  2871.       IIF(nAlt==3,"found!.",;
  2872.       IIF(nAlt==4,"written to!.",;
  2873.       IIF(nAlt==5,"closed!.",;
  2874.       "written to or closed!.")))))+;
  2875.       ";;"+;
  2876.       "Your data has not been saved.  If you just changed the conversion "+;
  2877.       "mode, try: press 'x' (or click 'Set Rate') and press 's' (or click 'Save')."
  2878.    ELSE
  2879.       oDlg:cTopText :=  "ERROR: Code Gen "+Alltrim(Str(nError))+;
  2880.       IIF(nAlt==-999,""," OS "+Alltrim(Str(nAlt)))
  2881.       oDlg:cGenText :=;
  2882.       "An error has occurred which is not documented in BusiCalc.;"  +;
  2883.       ";"+;
  2884.       "The status of the calculator is indeterminate...;"   +;
  2885.       ";"+;
  2886.       "It is recommended that you close the calculator and start again or "+;
  2887.       "at least press Clear All [CA]."
  2888.  
  2889.    ENDIF
  2890.    Info( "Error", oDlg:aCalcSize, oDlg)
  2891. RETURN nil
  2892. *                     END FUNCTION MyErrorMsg()
  2893. *****************************************************************************
  2894. *
  2895. *****************************************************************************
  2896. *                      FUNCTION MakeHelp()
  2897. *****************************************************************************
  2898. *
  2899. FUNCTION MakeHelp(oDlg)
  2900.  
  2901.    aAdd(oDlg:aHelp,{{"Contents"},{;
  2902.    "General information.;"+;
  2903.    "Notes and Display Limits;"+;
  2904.    "Number Keys Disable.;"+;
  2905.    "Mouse and Keyboard Action;"+;
  2906.    "The Keyboard Keys;"+;
  2907.    "The Keyboard Keys (continued);"+;
  2908.    "Reciprocal, Negate, Factorial, Square;"+;
  2909.    "and Square Root;"+;
  2910.    "nth. Power, nth. Root, Percent;"+;
  2911.    "Bracketed Operations;"+;
  2912.    "Currency Operations;"+;
  2913.    "Switching Currency;"+;
  2914.    "Set Decimal Places;"+;
  2915.    "Set Exchange Rates;"+;
  2916.    "Import and Export Values;"+;
  2917.    "Move - Minimise - Busicalc.        Adenda."},{;
  2918.    { 1,NO_2},{ 2,NO_3},{ 3,NO_4},{ 5,NO_5},;
  2919.    { 6,NO_6},{ 7,NO_7},{ 9,NO_8},{10,NO_9},;
  2920.    {11,NO_10},{12,NO_11},{13,NO_12},{14,NO_13},;
  2921.    {15,NO_14},{16,NO_15}}})
  2922.  
  2923.    aAdd(oDlg:aHelp,{{"General Information"},;
  2924.    {"It is important to note that this help file will not teach "+;
  2925.    "you how to use a calculator.   It is assumed that you have a "+;
  2926.    "basic understanding of the principles involved.;"+;
  2927.    ";"+;
  2928.    "The help file will detail the aspects of BusiCalc which are "+;
  2929.    "less frequently encountered and how those particular functions "+;
  2930.    "can be used.;"+;
  2931.    ";"+;
  2932.    "BusiCalc was designed to facilitate general business calculations.  "+;
  2933.    "It has some features not normally associated with general business "+;
  2934.    "and in some cases limitations to their use apply, for example, n! "+;
  2935.    "is limited to 21! ";
  2936.    },{{ 16,NO_2}}})
  2937.  
  2938.    aAdd(oDlg:aHelp,{{"Notes and Display Limits"},;
  2939.    {"All calculations are performed to 14 places of decimal "+;
  2940.    "regardless of what the display is set to.  This means you "+;
  2941.    "can change the number of decimals displayed and observe a more "+;
  2942.    "(or less) accurate result at anytime during a calculation.  "+;
  2943.    "The result displayed is rounded up to the current number of "+;
  2944.    "decimal places shown in the Set Decimals window.;"+;
  2945.    ";"+;
  2946.    "BusiCalc can display results up to slightly less than 10^20 "+;
  2947.    "(10 to the power of 20) and as long as the final result is less "+;
  2948.    "than this figure, and the intervening results are not greater than "+;
  2949.    "10^38, the calculator will display the correct result.";
  2950.    },{{ 16,NO_3}}})
  2951.  
  2952.    aAdd(oDlg:aHelp,{{"Keys, Mouse and Keyboard"},;
  2953.    {"To reduce the potential for error during calculations the Number "+;
  2954.    "Keys are sometimes disabled, indicating that an Operator Key is "+;
  2955.    "expected.  In all other respects it is a standard calculator.;"+;
  2956.    ";"+;
  2957.    "With one or two exceptions, all of the main features can be actioned "+;
  2958.    "using Keyboard Keys, or Mouse Clicks.  Where possible the keys have "+;
  2959.    "been selected to describe the Key function.  You can use upper or "+;
  2960.    "lower case.;"+;
  2961.    ";"+;
  2962.    "For example, the function 'Add to Memory' is actioned by pressing "+;
  2963.    "the letter 'A'. ...Add or clicking the appropriate key [ M+ ].";
  2964.    },{{ 16,NO_4}}})
  2965.  
  2966.    // Do not Line up text !!
  2967.    aAdd(oDlg:aHelp,{{"The Keyboard Keys"},;
  2968.    {;
  2969.    "Key   Action;"+;
  2970.    "A ..... Add to Memory;"+;
  2971.    "B ..... British Pounds to Dollars;"+;
  2972.    "C ..... Clear Accumulator;"+;
  2973.    "D ..... Decimals set;"+;
  2974.    "E .....  Entry Clear;"+;
  2975.    "F .....  Foreign to Local Currency;"+;
  2976.    "G ..... Get Memory Value;"+;
  2977.    "I .....   Invert Number (Reciprocal);"+;
  2978.    "K ..... Pi constant;"+;
  2979.    "L .....  Local to Foreign Currency;"+;
  2980.    "N ..... Negate Number (Toggle);"+;
  2981.    "P .....  Pounds British to Euro;"+;
  2982.    "Q .....  Information about this program;"+;
  2983.    "R .....  Root of value X"},{{ 16,NO_5}}})
  2984.  
  2985.    // Do not Line up text !!
  2986.    aAdd(oDlg:aHelp,{{"The Keyboard Keys (cont.)"},;
  2987.    {;
  2988.    "S .....  Square of value X;"+;
  2989.    "T ..... Take from Memory (Mem Subtract);"+;
  2990.    "U ..... US Dollars to Euro Currency;"+;
  2991.    "W .... Wipe Memory (Mem Clear);"+;
  2992.    "X ..... Exchange Rates Set;"+;
  2993.    ";"+;
  2994.    "Numerics  ! # % ( ) * + - . / 0123456789 = ^;"+;
  2995.    ";"+;
  2996.    "F1 ...  Help, This file.;"+;
  2997.    "^V ...  Paste from Clipboard (Ctrl+V);"+;
  2998.    "^C ...  Copy to Clipboard (Ctrl+C);"+;
  2999.    "Esc...  Entry Clear;"+;
  3000.    ";"+;
  3001.    "Space     Minimise:   Spacebar;"+;
  3002.    "^Space   Maximise:   Ctrl-SpaceBar;"+;
  3003.    "(Operative only when BusiCalc has Focus)"},{{ 16,NO_6}}})
  3004.  
  3005.    aAdd(oDlg:aHelp,{{"Operator Keys  -  Single Entry"},;
  3006.    {;
  3007.    "Some keys perform an immediate operation. ;"+;
  3008.    ";"+;
  3009.    "Reciprocal  [1/x];"+;
  3010.    "Input 2 Press [1/x],  Result is 0.5 (1/2);"+;
  3011.    "Press [1/x] again and Result = Input, 2 ;"+;
  3012.    "Negate  [+/-];"+;
  3013.    "Input 3 Press [+/-],  Result is -3     (minus 3);"+;
  3014.    "Press [+/-] again and Result = Input, 3;"+;
  3015.    "Factorial  [n!];"+;
  3016.    "Input 5 Press [n!]  Result: 120   (5x4x3x2x1);"+;
  3017.    ";"+;
  3018.    "Square  [XSqd];"+;
  3019.    "Input 4 Press [XSqd]  Result: 16     (4x4=16);"+;
  3020.    "...the reverse is:;"+;
  3021.    "Square Root   [SqRt];"+;
  3022.    "Input 16 Press [SqRt]  Result: 4      ( ?x?=16)"},;
  3023.    {{3,BM_RECIP},{6,BM_NEGATE},{9,BM_N_FACTORIAL},;
  3024.    {12,BM_SQUARED},{15,BM_SQR_ROOT},{ 16,NO_7}}})
  3025.  
  3026.  
  3027.    aAdd(oDlg:aHelp,{{"Operator Keys  -  Double Entry"},;
  3028.    {;
  3029.    "In each case you need to Input the first value;"+;
  3030.    "'X', press the Key, Input the second value 'n',;"+;
  3031.    "then press Enter, or a new Operator. The;"+;
  3032.    "'X' value may be the current display value.;"+;
  3033.    ";"+;
  3034.    "What is 4 multipled by 4  five times ?;"+;
  3035.    "X to the power of n   [Xn] ;"+;
  3036.    "Input 4 Press [Xn] Input 5,    Result: 1024;;"+;
  3037.    "What multiplied by itself four times = 625 ?;"+;
  3038.    "nth Root of X    [nRtX] ;"+;
  3039.    "Input 625 Press [nRtX] Input 4, Result: 5;;"+;
  3040.    "How many 1/100ths (percent) is 40/80 ?;"+;
  3041.    "Percent  [%];"+;
  3042.    "Input 40 Press [%] Input 80,  Result: 50%  (50/100)"},;
  3043.    {{7,BM_NTH_POWER},{11,BM_NTH_ROOT},{15,BM_PERCENT},{ 16,NO_8}}})
  3044.  
  3045.    aAdd(oDlg:aHelp,{{"Bracketed Operations"},;
  3046.    {;
  3047.    "You need to be careful using bracketed operations.  "+;
  3048.    "The operation does not, except in certain circumstances, "+;
  3049.    "work like a computer spreadsheet evaluation where the "+;
  3050.    "innermost bracketed contents are evaluated first and the "+;
  3051.    "overall expression evaluated from right to left.;"+;
  3052.    ";"+;
  3053.    "BusiCalc evaluates your input as you enter it and regards "+;
  3054.    "a bracketed operation as a 'new' calculation until you close "+;
  3055.    "the brackets.  Then it evaluates the 'new' value from the "+;
  3056.    "brackets and your input preceding the brackets."},{{16,NO_9}}})
  3057.  
  3058.  
  3059.    aAdd(oDlg:aHelp,{{"Currency Operations"},;
  3060.    {;
  3061.    "BusiCalc is capable of converting between;"+;
  3062.    "Pounds and Euro;"+;
  3063.    "Euro and Pounds;;"+;
  3064.    "US Dollars and Pounds;"+;
  3065.    "Pounds and US Dollars;;"+;
  3066.    "US Dollars and Euro;"+;
  3067.    "Euro and US Dollars.;;"+;
  3068.    "Which currency is default depends on your last choice "+;
  3069.    "and the two larger buttons labelled 'L' (Local) and 'F' "+;
  3070.    "(Foreign), for the want of a better description, allow "+;
  3071.    "you to toggle the displayed value in those currencies."},;
  3072.    {{2,BM_PND_TO_EUR},{3,BM_EUR_TO_PND},{5,BM_DLR_TO_PND},;
  3073.    {6,BM_PND_TO_DLR},{8,BM_DLR_TO_EUR},{9,BM_EUR_TO_DLR},{16,NO_10}}})
  3074.  
  3075.  
  3076.  
  3077.    aAdd(oDlg:aHelp,{{"Switching Currency"},;
  3078.    {;
  3079.    "If no currency symbol is showing to the left of the "+;
  3080.    "main display, it is a simple matter of clicking one of "+;
  3081.    "the three small currency buttons 'p','b','u' at the "+;
  3082.    "bottom of BusiCalc to set the currency mode.;"+;
  3083.    ";"+;
  3084.    "If however, a currency symbol is showing against the main "+;
  3085.    "display, you may only instal conversion buttons that have a "+;
  3086.    "corresponding symbol.  This is done to prevent errors.;"+;
  3087.    ";"+;
  3088.    "Displaying     Cannot select;"+;
  3089.    "'Euro'              Dollars to Pounds;"+;
  3090.    "'Dollar'            Pounds to Euro;"+;
  3091.    "'Pound'            Dollars to Euro"},;
  3092.    {{2,PB_PND_EUR},{3,PB_DLR_PND},{4,PB_DLR_EUR},{ 16,NO_11}}})
  3093.  
  3094.  
  3095.    aAdd(oDlg:aHelp,{{"Set Decimal Places"},;
  3096.    {;
  3097.    "As mentioned earlier, all calculations are "+;
  3098.    "performed to 14 decimal places of precision regardless "+;
  3099.    "of what number of decimals the display is set to display.;"+;
  3100.    ";"+;
  3101.    "To set the number of decimals displayed click 'Set "+;
  3102.    "Dec' or press 'd'.   Input the number of decimals "+;
  3103.    "required, and press Enter.;"+;
  3104.    ";"+;
  3105.    "This may be done at anytime during a calculation "+;
  3106.    "with one exception, you may not do it during a "+;
  3107.    "bracketed (...) operation.;"+;
  3108.    ";"+;
  3109.    "Weird results can be displayed if you do not set enough "+;
  3110.    "places.  For example, with 0 places, 3/2 = 2"},;
  3111.    {{7,PB_SET_DEC},{ 16,NO_12}}})
  3112.  
  3113.  
  3114.    aAdd(oDlg:aHelp,{{"Set Exchange Rate(s)"},;
  3115.    {;
  3116.    "To set the rates click 'Set Rate' or press 'x';"+;
  3117.    "Two of the rates are relative to the Euro.  That is, "+;
  3118.    "you Input the Dollar exchange rate to 1 Euro and the "+;
  3119.    "Pound rate to 1 Euro.;"+;
  3120.    ";"+;
  3121.    "For the third rate, you Input the Dollar rate to 1 Pound.;"+;
  3122.    ";"+;
  3123.    "Any time you change the rates a validate routine will  "+;
  3124.    "run a test that cross-checks the three rates against "+;
  3125.    "one another and outputs a message as to the accuracy "+;
  3126.    "of the stored values.;"+;
  3127.    ";"+;
  3128.    "This site has an exchange rate service. ;"+;
  3129.    "http://www.xe.net/ucc/"},;
  3130.    {{1,PB_SET_RATE},{ 16,NO_13}}})
  3131.  
  3132.  
  3133.    aAdd(oDlg:aHelp,{{"Import and Export Values"},;
  3134.    {;
  3135.    "This feature allows you to Copy a value from "+;
  3136.    "another application to the ClipBoard and Import it "+;
  3137.    "into the display.  Click Import or press Ctrl+C;"+;
  3138.    ";"+;
  3139.    "You can also Export the value in the display to the "+;
  3140.    "ClipBoard for Pasting to another application.  Click "+;
  3141.    "Export or press Ctrl+V;"+;
  3142.    ";"+;
  3143.    "This feature can also provide you with another way "+;
  3144.    "of saving the result of a calculation for later use by "+;
  3145.    "Exporting it and Importing when needed."},;
  3146.    {{4,PB_COPY},{8,PB_PASTE},{ 16,NO_14}}})
  3147.  
  3148.    aAdd(oDlg:aHelp,{{"Move - Minimise - Busicalc"},;
  3149.    {;
  3150.    "To move BusiCalc: place the pointer in the Title area, click "+;
  3151.    "with the left button and hold it down while you move the "+;
  3152.    "calculator to a new location.  This can only be done on the "+;
  3153.    "Calculator window, not on Help, About or Messages.;"+;
  3154.    ";"+;
  3155.    "To minimise BusiCalc: press the SpaceBar or click the minimise "+;
  3156.    "button [ - ] on the title bar of BusiCalc.;"+;
  3157.    ";"+;
  3158.    "To maximise BusiCalc: click the 'BusiCalc' button at the top "+;
  3159.    "right side of the current screen.   You may also press "+;
  3160.    "Ctrl+SpaceBar if you have not clicked anything else since you "+;
  3161.    "minimised.;"},{{ 16,NO_15}}})
  3162.  
  3163.    aAdd(oDlg:aHelp,{{"Acknowledgements"},;
  3164.    {;
  3165.    "This Calculator was written using Alaska Software GmbH Xbase++ "+;
  3166.    "(TM), a Win32 bit GUI / OOP relational database programming language "+;
  3167.    "using a CA-Clipper (TM) style syntax.  Alaska Software GmbH can be "+;
  3168.    "reached at: ;http://www.alaska-software.com/;"+;
  3169.    ";"+;
  3170.    "My thanks to Clayton Jones for the insight into GUI / OOP "+;
  3171.    "programming techniques using the Xbase++ language, learned in "+;
  3172.    "record time by studying his Top-Down (TM) Library code and "+;
  3173.    "Tutorials.  Clayton Jones can be reached at: ;"+;
  3174.    "http://www.cjcom.net/bizcore.html"},{{ 16,NO_16}}})
  3175.  
  3176. RETURN nil
  3177. *                       END FUNCTION MakeHelp()
  3178. *****************************************************************************
  3179. *
  3180. *****************************************************************************
  3181. *
  3182. *  Note concerning PutData(), GetData() and PullDataRecord()
  3183. *
  3184. *  These routines may seem an overkill for handling 4 miserly bits of data
  3185. *  transferring from Array - File - Array.   They are however 'cut-down'
  3186. *  versions of routines used in a more elaborate variable record size
  3187. *  Random Access dB system and as such they were ready to hand.
  3188. *
  3189. *  I suppose I could have reduced them further but I have to cut the grass!
  3190. *
  3191. *****************************************************************************
  3192. *
  3193. *****************************************************************************
  3194. *                      FUNCTION GetData()
  3195. *****************************************************************************
  3196. *
  3197. *  FUNCTION GetData(cFileName, aData, nRecord_Len, nRecNo) --> nErrorCode
  3198. *
  3199. *  Notes:
  3200. *  ------
  3201. *  aData is passed in by reference.
  3202. *  nRecNo is only required when a specific record is to be returned.
  3203. *
  3204. *  If used, nRecNo must be pre-validated to ensure it is an element
  3205. *  of aData or an error will occur.
  3206. *
  3207. *  Specifying a Record Number will only retrieve that record from the
  3208. *  file and modify the array element accordingly.  If nRecNo is ommitted,
  3209. *  the aData array is cleared to zero elements and loaded with the file
  3210. *  data records.
  3211. *
  3212. *  Note: This is a modified and reduced features implementation of
  3213. *        a more sophisticated routine used for Random Access Db work.
  3214. *        Data error trapping is not required and has been removed.
  3215. *
  3216. *
  3217. *  Returns ErrorCode levels.
  3218. *
  3219. *  Error    Description
  3220. *
  3221. *    4      ERROR CLOSING
  3222. *    3      ERROR READING
  3223. *    2      ERROR OPENING
  3224. *    1      ERROR NOTEXIST
  3225. *    0      SUCCESSFUL
  3226. *
  3227. *  Requires:  Companion function - PullDataRecord()
  3228. *
  3229. *****************************************************************************
  3230. //#include "Fileio.ch"
  3231. *
  3232. FUNCTION GetData(cFileName, aData, nRecord_Len, nRecNo)
  3233.    LOCAL nHandle     := 0
  3234.    LOCAL nBytes      := 0
  3235.    LOCAL nErrorCode  := 0
  3236.    LOCAL lEndofFile  := .F.
  3237.    LOCAL cDataRecord := ""
  3238.  
  3239.    nRecNo            := IIF(ValType(nRecNo)="U","void",nRecNo)
  3240.  
  3241.    IF !(FILE( cFileName ))
  3242.       ********************                   DOES NOT EXIST
  3243.       nErrorCode                             := 1
  3244.    ELSE
  3245.       nHandle:= ;
  3246.       FOPEN( cFileName, FO_READ )
  3247.  
  3248.       IF !(FERROR() == 0)
  3249.          *****************                   ERROR OPENING
  3250.          nErrorCode                          := 2
  3251.       ELSE
  3252.          nBytes:= ;
  3253.          FSEEK(nHandle, 0, FS_END)
  3254.          IF !(FERROR() == 0)
  3255.             **************                   ERROR READING
  3256.             nErrorCode                       := 3
  3257.          ELSE
  3258.  
  3259.             ***** Reset to start
  3260.             FSeek( nHandle, 0 , FS_SET )
  3261.             lEndofFile:= .F.
  3262.             IF !(ValType(nRecNo)=="N")
  3263.                ARemove(aData,1,Len(aData))
  3264.             ENDIF
  3265.             Do While !(lEndofFile)
  3266.                cDataRecord :=    ;
  3267.                PullDataRecord(   ;
  3268.                nHandle          ,;
  3269.                nRecord_Len      ,;
  3270.                @lEndofFile      ,;
  3271.                nBytes,nRecNo)
  3272.  
  3273.                IF cDataRecord == ;
  3274.                   "~|ERROR|~"
  3275.                   **************             ERROR READING
  3276.                   nErrorCode                 := 3
  3277.                   EXIT
  3278.                ENDIF
  3279.  
  3280.                IF !(ValType(nRecNo)=="N")
  3281.                   aAdd(aData,cDataRecord)
  3282.                ELSE
  3283.                   aData[nRecNo]:=  cDataRecord
  3284.                ENDIF
  3285.  
  3286.             ENDDO  // !EOF
  3287.          ENDIF
  3288.       ENDIF
  3289.       IF !(FClose( nHandle ))
  3290.          *****************                   ERROR CLOSING
  3291.          nErrorCode                       := 4
  3292.       ENDIF
  3293.    ENDIF
  3294.  
  3295. RETURN (nErrorCode)
  3296. *                       END FUNCTION GetData()
  3297. *****************************************************************************
  3298. *
  3299. *****************************************************************************
  3300. *
  3301. *  FUNCTION PutData( cFilename, aData, nRecord_Len, nRecNo ) --> nErrorCode
  3302. *
  3303. *  Writes fixed length Record Data from an Array to a file.
  3304. *
  3305. *  If a Record Number is omitted the routine will Create a new file
  3306. *  overwriting any file of the same name with all aData elements
  3307. *  submitted, otherwise, it replaces the relevant record in an
  3308. *  existing file.  Note *Existing File*
  3309. *
  3310. *  Note: This is a modified and reduced features implementation of
  3311. *        a more sophisticated routine used for Random Access Db work.
  3312. *        Data error trapping is not required and has been removed.
  3313. *
  3314. *  Returns ErrorCode levels.
  3315. *
  3316. *  Error    Description
  3317. *
  3318. *    6      ERROR WRITE+CLOSE
  3319. *    5      ERROR CLOSE
  3320. *    4      ERROR WRITE
  3321. *    3      ERROR BAD-ARGS
  3322. *    2      ERROR CREATE
  3323. *    1      ERROR OPEN
  3324. *    0      SUCCESSFUL
  3325. *
  3326. *****************************************************************************
  3327. *
  3328. //#include "Fileio.ch"
  3329. FUNCTION PutData( cFilename, aData, nRecord_Len, nRecNo )
  3330.    LOCAL nHandle     := 0
  3331.    LOCAL i           := 0
  3332.    LOCAL nErrorCode  := 0
  3333.    LOCAL cBuffer     := ""
  3334.    LOCAL nOffset     := 0
  3335.    nRecNo            := IIF(ValType(nRecNo)="U","void",nRecNo)
  3336.  
  3337.    Do Case
  3338.       Case  ValType(nRecNo) == "N" .AND. FExists( cFilename )
  3339.          nHandle := FOpen( cFilename, FO_READWRITE+FO_EXCLUSIVE )
  3340.          IF !(FError() == 0)
  3341.             ************               ERROR OPEN
  3342.             nErrorCode                 := 1
  3343.          ENDIF
  3344.  
  3345.       Case  ValType(nRecNo) == "C"
  3346.          nHandle := FCreate( cFilename, FC_NORMAL )
  3347.          IF !(FError() == 0)
  3348.             ************               ERROR CREATE
  3349.             nErrorCode                 := 2
  3350.          ENDIF
  3351.       OtherWise
  3352.          ***************               ERROR BAD-ARGS
  3353.          nErrorCode                    := 3
  3354.    ENDCASE
  3355.  
  3356.    IF nErrorCode == 0
  3357.       IF !Empty(aData)
  3358.          IF ValType(nRecNo) == "N"
  3359.             cBuffer  := aData[nRecNo]
  3360.             nOffSet  := (nRecNo -1) * nRecord_Len
  3361.             FSeek( nHandle, nOffSet, FS_SET)
  3362.             FWrite(nHandle, cBuffer, nRecord_Len)
  3363.             IF !(FError() == 0)
  3364.                ************            ERROR WRITE
  3365.                nErrorCode              := 4
  3366.             ENDIF
  3367.          ELSE
  3368.             For i:= 1 TO Len(aData)
  3369.             cBuffer  := aData[i]
  3370.             FWrite( nHandle, cBuffer, nRecord_Len)
  3371.             IF !(FError() == 0)
  3372.                ************            ERROR WRITE
  3373.                nErrorCode              := 4
  3374.                EXIT // From FOR
  3375.             ENDIF
  3376.             Next i
  3377.          ENDIF
  3378.       ENDIF
  3379.  
  3380.       ***** nErrorCode%2 = 0 is...
  3381.       ***** No Error, or File opened + Write Error
  3382.       IF nErrorCode%2 == 0
  3383.          IF !(FClose( nHandle ))
  3384.             IF nErrorCode == 0
  3385.                ************            ERROR CLOSE
  3386.                nErrorCode              := 5
  3387.             ELSE
  3388.                ************            ERROR ERROR WRITE + CLOSE
  3389.                nErrorCode              := 6
  3390.             ENDIF
  3391.          ENDIF
  3392.       ENDIF
  3393.    ENDIF
  3394.  
  3395. RETURN ( nErrorCode )
  3396. *                       END FUNCTION PutData()
  3397. *****************************************************************************
  3398. *
  3399. *****************************************************************************
  3400. *                FUNCTION PullDataRecord() --> cDatarecord
  3401. *
  3402. *  Pull a Data Record of set size or delimited by CRLF from a file.
  3403. *  lEndofFile will change to .T. when EOF is reached.
  3404. *  nRecord_Len holds the data record length and defaults to 80 chars.
  3405. *
  3406. *  CRLF cannot be part of the data stream, it is a delimiter.
  3407. *
  3408. *
  3409. *  Note: This is a modified and reduced features implementation of
  3410. *        a more sophisticated routine used for Random Access Db work.
  3411. *
  3412. *****************************************************************************
  3413. *
  3414. *
  3415. FUNCTION PullDataRecord( nFileHandle,nRecord_Len,lEndofFile,nSizeOfFile, xRecNo)
  3416.  
  3417.    LOCAL cCRLF                := CHR(13) + CHR(10)
  3418.    LOCAL cDataRecord          := ''
  3419.    LOCAL cTextBuffer          := ''
  3420.    LOCAL nCharsRead           := 0
  3421.    LOCAL nCharsToRead         := 0
  3422.    LOCAL nEndOfLinePosition   := 0
  3423.    LOCAL nCurrentPosition     := FSeek(nFileHandle, 0, FS_RELATIVE)
  3424.  
  3425.  
  3426.    IF Valtype(xRecNo) == "N"
  3427.       nCurrentPosition:= (xRecNo -1) * nRecord_Len
  3428.       IF !(nCurrentPosition > nSizeOfFile - nRecord_Len)
  3429.          FSeek( nFileHandle, nCurrentPosition, FS_SET )
  3430.          nCharsToRead:= Min( nRecord_Len, nSizeOfFile - nCurrentPosition )
  3431.          cTextBuffer := Space( nCharsToRead )
  3432.          nCharsRead  := FRead( nFileHandle, @cTextBuffer, nCharsToRead )
  3433.          cDataRecord := ''
  3434.       ELSE
  3435.          cDataRecord:= "~|ERROR|~"
  3436.       ENDIF
  3437.    ELSE
  3438.       nCharsToRead   := Min( nRecord_Len, nSizeOfFile - nCurrentPosition )
  3439.       cTextBuffer    := Space( nCharsToRead )
  3440.       nCharsRead     := FRead( nFileHandle, @cTextBuffer, nCharsToRead )
  3441.       cDataRecord    := ''
  3442.    ENDIF
  3443.  
  3444.  
  3445.    ******* Check for read errors
  3446.    *
  3447.    IF !(FERROR() == 0) .OR. (cDataRecord == "~|ERROR|~")
  3448.       cDataRecord:= "~|ERROR|~"
  3449.    ELSE
  3450.       ******** Update the Textbuffer and the current file position
  3451.       *
  3452.       nEndOfLinePosition := At( cCRLF, cTextBuffer )
  3453.  
  3454.       IF nEndOfLinePosition  ==  0
  3455.          cDataRecord := Left( cTextBuffer, nCharsRead )
  3456.          nCurrentPosition += nCharsRead
  3457.       ELSE
  3458.          cDataRecord := Left( cTextBuffer, ( nEndOfLinePosition - 1 ))
  3459.          nCurrentPosition += ( nEndOfLinePosition + Len( cCRLF ) ) - 1
  3460.          FSeek( nFileHandle, nCurrentPosition, FS_SET )
  3461.       ENDIF
  3462.  
  3463.       ******** Hit the wall?
  3464.       *
  3465.       IF nCurrentPosition >= nSizeOfFile .OR. Valtype(xRecNo)=="N"
  3466.          lEndofFile := .T.
  3467.       ENDIF
  3468.  
  3469.    ENDIF
  3470.  
  3471. RETURN( cDataRecord )
  3472. *
  3473. *                          END OF PullDataRecord()
  3474. *****************************************************************************
  3475. *
  3476. *****************************************************************************
  3477. ******************************      END    **********************************
  3478. *****************************************************************************
  3479.  
  3480.