FloatEdit.cpp
上传用户:chengtong
上传日期:2007-01-02
资源大小:18k
文件大小:17k
源码类别:

编辑框

开发平台:

Visual C++

  1. // 
  2. // CFloatEdit implementation 
  3. // Daniel Gabroveanu, January 2, 1999
  4. // updated - March 14, 1999
  5. //
  6. #include "stdafx.h"
  7. #include "FloatTest.h"
  8. #include "FloatEdit.h"
  9. #include <locale.h>
  10. #ifdef _DEBUG
  11. #define new DEBUG_NEW
  12. #undef THIS_FILE
  13. static char THIS_FILE[] = __FILE__;
  14. #endif
  15. #define MAX_LENGTH            100
  16. #define INT_KEY_DELETE        46
  17. #define INT_KEY_INSERT        45
  18. #define MINUS_SIGN            '-'
  19. #define PLUS_SIGN             '+'
  20. #define DEFAULT_COMMA         ','
  21. #define DEFAULT_DECIMAL_POINT '.'
  22. #define RETRIEVE_BUFFER_SPACE 100
  23. /////////////////////////////////////////////////////////////////////////////
  24. // CFloatEdit
  25. CFloatEdit::CFloatEdit()
  26. {
  27.   m_bCommas = TRUE; // by default use commas
  28.   m_cComma = DEFAULT_COMMA; // comma and decimal characters
  29.   m_cDecimalPoint = DEFAULT_DECIMAL_POINT; 
  30.   m_nDecimals = 2; // the number of decimal places
  31.   m_dblCurrentValue = 0.0;
  32.   // take the local system settings
  33.   LocalSettings();
  34. }
  35. CFloatEdit::~CFloatEdit()
  36. {
  37. }
  38. BEGIN_MESSAGE_MAP(CFloatEdit, CEdit)
  39. //{{AFX_MSG_MAP(CFloatEdit)
  40. ON_WM_CHAR()
  41. ON_WM_RBUTTONDOWN()
  42. //}}AFX_MSG_MAP
  43. END_MESSAGE_MAP()
  44. /////////////////////////////////////////////////////////////////////////////
  45. // CFloatEdit message handlers
  46. double CFloatEdit::GetValue(void) // get the stored value
  47. {
  48.   // the m_dblCurrentValue is currently updated
  49.   return m_dblCurrentValue;
  50. }
  51. void CFloatEdit::SetValue(double dblValue) // set the new stored value
  52. {
  53.   // set the current value to the member variable
  54.   m_dblCurrentValue = dblValue;
  55.   // determine the appropriate string
  56.   CString strCurrent;
  57.   toString(m_dblCurrentValue, strCurrent);
  58.   // set the current string
  59.   setStringAndSelection(strCurrent, 0);
  60.   // set the selection to the first character
  61.   SetSel(0, 0);
  62. }
  63. int CFloatEdit::GetDecimals( void ) // get the decimal places
  64. {
  65.   return m_nDecimals;
  66. }
  67. void CFloatEdit::SetDecimals( int nDecimals ) // set the decimal places
  68. {
  69.   m_nDecimals = nDecimals;
  70.   SetValue(m_dblCurrentValue);
  71. }
  72. void CFloatEdit::EnableCommas(BOOL bNewHasCommas) // enable / disable commas
  73. {
  74.   m_bCommas = bNewHasCommas;
  75.   SetValue(m_dblCurrentValue);  // refresh contents
  76. }
  77. void CFloatEdit::SetComma(char cNewComma) // setting the comma character
  78. {
  79.   m_cComma = cNewComma;
  80.   SetValue(m_dblCurrentValue); // refresh contents
  81. }
  82. char CFloatEdit::GetComma(void)  // getting the comma characters
  83. {
  84.   return m_cComma;
  85. }
  86. void CFloatEdit::SetDecimalPoint(char cNewDecimal) // setting the decimal point character
  87. {
  88.   m_cDecimalPoint = cNewDecimal;
  89.   SetValue(m_dblCurrentValue); // refresh contents
  90. }
  91. char CFloatEdit::GetDecimalPoint(void)  // getting the decimal point character
  92. {
  93.   return m_cDecimalPoint;
  94. }
  95. // private function implementation
  96. void CFloatEdit::toString(double dblValue, CString & strCurrent)
  97. {
  98.   CString strNumber;
  99.   CString strInteger, strDecimal; 
  100.   CString strWork;
  101.   int nDecimalPosition, nSign;
  102.   strNumber = CString(_ecvt(dblValue, MAX_LENGTH, &nDecimalPosition, &nSign));
  103.   strInteger = strNumber.Left(nDecimalPosition);
  104.   strDecimal = midString(strNumber, nDecimalPosition, m_nDecimals);
  105.   if(m_nDecimals != 0)
  106.     strWork = strInteger + CString(m_cDecimalPoint) + strDecimal;
  107.   else
  108.     strWork = strInteger;
  109.   // put commas if necessary
  110.   putCommas(strWork, strCurrent);
  111.   // attach the sign to the current string
  112.   if(nSign == 1)
  113.     strCurrent = CString(MINUS_SIGN) + strCurrent;
  114. }
  115. void CFloatEdit::putCommas(CString strSource, CString & strDestination)
  116. {
  117.   CString strWork = strSource;
  118.   
  119.   // remove existing commas
  120.   removeCommas(strWork);
  121.   strDestination = strWork;
  122.   if(m_bCommas)
  123.   {
  124.     // split the current string into decimal and integer part
  125.     CString strInteger, strDecimal;
  126.     getIntegerAndDecimal(strWork, strInteger, strDecimal);
  127.     // put commas into the integer part
  128.     int nNumber = 0;
  129.     CString strCommaInteger = CString("");
  130.     for(int nCount = 0; nCount < strInteger.GetLength(); nCount ++)
  131.     {
  132.     
  133.       strCommaInteger = 
  134.         midString(strInteger, strInteger.GetLength() - nCount - 1, 1) 
  135.         + strCommaInteger;
  136.       nNumber ++;
  137.       nNumber %= 3;
  138.       if(nNumber == 0)
  139.       {
  140.         strCommaInteger = CString(m_cComma) + strCommaInteger;
  141.       }
  142.     }
  143.     // reassemble the integer and the decimal part through the decimal point
  144.     if(m_nDecimals == 0)
  145.       strDestination = strCommaInteger;
  146.     else
  147.       strDestination = strCommaInteger + 
  148.                     CString(m_cDecimalPoint) + strDecimal;
  149.     // remove the comma if the first character
  150.     removeFirstComma(strDestination);
  151.   } // if m_bCommas - will be processes as is
  152. }
  153. void CFloatEdit::removeCommas( CString & strSource)
  154. {
  155.   int nCount;
  156.   CString strWork = strSource;
  157.   CString strResult = CString("");
  158.   for(nCount = 0; nCount < strSource.GetLength(); nCount ++)
  159.   {
  160.     if(strWork.GetAt(nCount) != m_cComma)
  161.     {
  162.       strResult += CString(strWork.GetAt(nCount));
  163.     }
  164.   } // for
  165.   strSource = strResult;
  166. }
  167. void CFloatEdit::deleteCharLeft(CString &strSource, int nSelection, int nNumber)
  168. {
  169.   int nCurrentSelection  = nSelection - nNumber;
  170.   deleteCharRight(strSource, nCurrentSelection, nNumber);
  171. }
  172. void CFloatEdit::deleteCharRight(CString &strSource, int nSelection, int nNumber)
  173. {
  174.   // splitting the string into three parts, and assembling the first and the last
  175.   CString str1, str3;
  176.   str1 = strSource.Left(nSelection);
  177.   str3 = midString(strSource, nSelection + nNumber, strSource.GetLength());
  178.   strSource = str1 + str3;
  179. }
  180. BOOL CFloatEdit::bIsLeftDecimal(CString strSource, int nSelection)
  181. {
  182.   BOOL bResult = TRUE;
  183.   int nPos = strSource.Find(m_cDecimalPoint);
  184.   if(nPos != -1)
  185.   {
  186.     if(nSelection > nPos)
  187.       bResult = FALSE;
  188.   } // if
  189.   return bResult;
  190. }
  191. BOOL CFloatEdit::bIsLeftCharacterComma(CString strSource, int nSelection)
  192. {
  193.   BOOL bResult = FALSE;
  194.   if(nSelection > 0)
  195.   {
  196.     if(strSource.GetAt(nSelection - 1) == m_cComma)
  197.     {
  198.       bResult = TRUE;
  199.     }
  200.   }
  201.   return bResult;
  202. }
  203. BOOL CFloatEdit::bIsRightCharacterComma(CString strSource, int nSelection)
  204. {
  205.   BOOL bResult = FALSE;
  206.   if(nSelection < strSource.GetLength())
  207.   {
  208.     if(strSource.GetAt(nSelection) == m_cComma)
  209.     {
  210.       bResult = TRUE;
  211.     }
  212.   }
  213.   return bResult;
  214. }
  215. BOOL CFloatEdit::bIsLeftCharacterDecimal(CString strSource, int nSelection)
  216. {
  217.   BOOL bResult = FALSE;
  218.   if(nSelection > 0)
  219.   {
  220.     if(strSource.GetAt(nSelection - 1) == m_cDecimalPoint)
  221.     {
  222.       bResult = TRUE;
  223.     }
  224.   }
  225.   return bResult;
  226. }
  227. BOOL CFloatEdit::bIsRightCharacterDecimal(CString strSource, int nSelection)
  228. {
  229.   BOOL bResult = FALSE;
  230.   if(nSelection < strSource.GetLength())
  231.   {
  232.     if(strSource.GetAt(nSelection) == m_cDecimalPoint)
  233.     {
  234.       bResult = TRUE;
  235.     }
  236.   }
  237.   return bResult;
  238. }
  239. void CFloatEdit::getStringAndSelection(CString& strCurrent, int & nSelection)
  240. {
  241.   GetWindowText(strCurrent);
  242.   
  243.   int nStartSel, nEndSel;
  244.   GetSel(nStartSel, nEndSel);
  245.   nSelection = nStartSel;
  246. }
  247. void CFloatEdit::setStringAndSelection(CString strNew, int nSelection)
  248. {
  249.   SetWindowText(strNew.GetBuffer(1));
  250.   
  251.   // adjusting the selection value
  252.   nSelection = nSelection < 0 ? 0 : nSelection;
  253.   nSelection = nSelection > strNew.GetLength() ? strNew.GetLength() : nSelection;
  254.   SetSel(nSelection, nSelection);
  255.   // recalculate the value for m_dblCurrentValue
  256.   CString strWork = strNew;
  257.   CString strInteger, strDecimal;
  258.   // will work by default with the decimal point
  259.   removeCommas(strWork);
  260.   getIntegerAndDecimal(strWork, strInteger, strDecimal);
  261.   strWork = strInteger + CString(".") + strDecimal;
  262.   m_dblCurrentValue = atof(strWork.GetBuffer(1));
  263. }
  264. void CFloatEdit::getIntegerAndDecimal(CString strCurrent, 
  265.                                       CString & strInteger, 
  266.                                       CString & strDecimal)
  267. {
  268.   CString strWork;
  269.   strWork = strCurrent;
  270.   // remove the commas from the given string
  271.   removeCommas(strWork);
  272.   // search for the decimal point
  273.   int nPos = strWork.Find(CString(m_cDecimalPoint));
  274.   if(nPos == -1)
  275.   {
  276.     strInteger = strWork;
  277.     strDecimal = CString("");
  278.   }
  279.   else
  280.   {
  281.     strInteger = strWork.Left(nPos);
  282.     strDecimal = midString(strWork, nPos + 1, strWork.GetLength());
  283.   }
  284. }
  285. void CFloatEdit::saveStringAndSelection(void)
  286. {
  287.   CString strCurrent;
  288.   int nStartSel, nEndSel;
  289.   GetSel(nStartSel, nEndSel);
  290.   GetWindowText(strCurrent);
  291.   m_strSavedString = strCurrent;
  292.   m_nSavedSelection = nStartSel;
  293. }
  294. void CFloatEdit::deleteSelection(void)
  295. {
  296.   // get the current selection
  297.   int nSelStart, nSelEnd;
  298.   GetSel(nSelStart, nSelEnd);
  299.   // remove it
  300.   if(nSelEnd > nSelStart)
  301.   {
  302.     ReplaceSel("");
  303.   }
  304.   
  305.   // recalculate the value stored in the filled number
  306.   CString strControl;
  307.   GetWindowText(strControl);
  308.   
  309.   setStringAndSelection(strControl, nSelStart);
  310.   SetValue(m_dblCurrentValue);
  311.   SetSel(nSelStart, nSelStart);
  312. }
  313. void CFloatEdit::processCharacter( UINT cCurrent)
  314. {
  315.   // will process the input character inserted there
  316.   // the character can be 
  317.   // - digit
  318.   if(cCurrent == MINUS_SIGN || cCurrent == PLUS_SIGN)
  319.   {
  320.     deleteSelection();
  321.     processSign(cCurrent);
  322.   }
  323.   if(cCurrent >= '0' && cCurrent <= '9')
  324.   {
  325.     deleteSelection();
  326.     processDigit(cCurrent);
  327.   }
  328.   // - backspace
  329.   if(cCurrent == 8)
  330.   {
  331.     if(hasSelection())
  332.       deleteSelection();
  333.     else
  334.       processBackspace();
  335.   }
  336.   // - decimal point
  337.   if(cCurrent == (UINT) m_cDecimalPoint)
  338.   {
  339.     deleteSelection();
  340.     processDecimalPoint();
  341.   }
  342. }
  343. void CFloatEdit::processDigit(UINT cCurrent)
  344. {
  345.   // get the current text and selection
  346.   CString strCurrent;
  347.   int nSelection;
  348.   getStringAndSelection(strCurrent, nSelection);
  349.   // if the selection is on the left of the decimal point
  350.   if(bIsLeftDecimal(strCurrent, nSelection))
  351.   { 
  352.     int nDelta;
  353.     CString strDestination;
  354.     nDelta = strCurrent.GetLength() - nSelection;
  355.     
  356.     strCurrent = strCurrent.Left(nSelection) + 
  357.       CString((char) cCurrent) + 
  358.       midString(strCurrent, nSelection, strCurrent.GetLength());
  359.     putCommas(strCurrent, strDestination);
  360.     strCurrent = strDestination;
  361.     nSelection = strCurrent.GetLength() - nDelta;
  362.   }
  363.   // if the selection is on the right of the decimal point
  364.   else
  365.   { 
  366.     // if there are digits available, will overwrite the next character
  367.     if(nSelection < strCurrent.GetLength())
  368.     {
  369.       strCurrent.SetAt(nSelection, (char) cCurrent);
  370.       nSelection++;
  371.     } // if
  372.   }
  373.   // if the current selection is on the left of the comma, will jump
  374.   if(bIsRightCharacterComma(strCurrent, nSelection))
  375.   {
  376.     nSelection ++;
  377.   }
  378.   
  379.   // set the current text and selection
  380.   setStringAndSelection(strCurrent, nSelection);
  381. }
  382. void CFloatEdit::processDelete()
  383. {
  384.   CString strCurrent;
  385.   int nSelection;
  386.   getStringAndSelection(strCurrent, nSelection);
  387.   if(bIsRightCharacterDecimal(strCurrent, nSelection))
  388.     nSelection ++;
  389.   if(bIsLeftDecimal(strCurrent, nSelection))
  390.   {    // on the left side of the decimal point
  391.     int nDelta = strCurrent.GetLength() - nSelection;
  392.     
  393.     if(bIsRightCharacterComma(strCurrent, nSelection))
  394.     {
  395.       deleteCharRight(strCurrent, nSelection, 2);
  396.       nDelta -= 2;
  397.     }
  398.     else
  399.     {
  400.       deleteCharRight(strCurrent, nSelection, 1);
  401.       nDelta -= 1;
  402.     }
  403.     
  404.     CString strDestination;
  405.     putCommas(strCurrent, strDestination);
  406.     strCurrent = strDestination;
  407.     nSelection = strCurrent.GetLength() - nDelta;
  408.   }
  409.   else // on the right side of the decimal point
  410.   {
  411.     if(nSelection < strCurrent.GetLength())
  412.     {
  413.       strCurrent = strCurrent.Left(nSelection) + 
  414.         midString(strCurrent, nSelection + 1, strCurrent.GetLength()) +
  415.         CString("0");
  416.     } 
  417.   }
  418.   setStringAndSelection(strCurrent, nSelection);
  419. }
  420. void CFloatEdit::processBackspace()
  421. {
  422.   CString strCurrent;
  423.   int nSelection;
  424.   getStringAndSelection(strCurrent, nSelection);
  425.   if(bIsLeftCharacterDecimal(strCurrent, nSelection))
  426.     nSelection --;
  427.   if(bIsLeftDecimal(strCurrent, nSelection))
  428.   {    // on the left side of the decimal point
  429.     int nDelta = strCurrent.GetLength() - nSelection;
  430.     
  431.     if(bIsLeftCharacterComma(strCurrent, nSelection))
  432.     {
  433.       deleteCharLeft(strCurrent, nSelection, 2);
  434.     }
  435.     else
  436.     {
  437.       deleteCharLeft(strCurrent, nSelection, 1);
  438.     }
  439.     
  440.     CString strDestination;
  441.     putCommas(strCurrent, strDestination);
  442.     strCurrent = strDestination;
  443.     nSelection = strCurrent.GetLength() - nDelta;
  444.   }
  445.   else // on the right side of the decimal point
  446.   {
  447.     strCurrent = strCurrent.Left(nSelection-1) + 
  448.       midString(strCurrent, nSelection, strCurrent.GetLength()) +
  449.       CString("0");
  450.     nSelection --;
  451.   }
  452.   setStringAndSelection(strCurrent, nSelection);
  453. }
  454. void CFloatEdit::processDecimalPoint()
  455. {
  456.   CString strCurrent;
  457.   int nSelection;
  458.   getStringAndSelection(strCurrent, nSelection);
  459.   // if on the left side, delete all through the decimal point
  460.   // if on the right side, not delete anything
  461.   // afterwards, will move the selection after the decimal point
  462.   int nPos = strCurrent.Find(CString(m_cDecimalPoint));
  463.   nPos = nPos == -1 ? strCurrent.GetLength() : nPos;
  464.   if(bIsLeftDecimal(strCurrent, nSelection))
  465.   {
  466.     strCurrent = strCurrent.Left(nSelection) + midString(strCurrent, nPos, strCurrent.GetLength());
  467.   }
  468.   CString strDestination;
  469.   putCommas(strCurrent, strDestination);
  470.   strCurrent = strDestination;
  471.   nPos = strCurrent.Find(CString(m_cDecimalPoint));
  472.   nPos = nPos == -1 ? strCurrent.GetLength() : nPos;
  473.   nSelection = nPos + 1;
  474.   nSelection = 
  475.     nSelection > strCurrent.GetLength() ? 
  476.     strCurrent.GetLength() : nSelection;
  477.   setStringAndSelection(strCurrent, nSelection);
  478. }
  479. void CFloatEdit::processSign(UINT nCharacter)
  480. {
  481.   // toggles the sign character on the first position
  482.   CString strCurrent;
  483.   int nSelection;
  484.   getStringAndSelection(strCurrent, nSelection);
  485.   if(nCharacter == MINUS_SIGN)
  486.   {
  487.     // toggles the sign
  488.     if(bIsRightCharacterSign(strCurrent, 0))
  489.     {
  490.       strCurrent = strCurrent.Right(strCurrent.GetLength() - 1);
  491.       nSelection --;
  492.     }
  493.     else
  494.     {
  495.       strCurrent = CString(MINUS_SIGN) + strCurrent;
  496.       nSelection ++;
  497.     }
  498.   }
  499.   if(nCharacter == PLUS_SIGN)
  500.   {  
  501.     // removes the sign character if existent
  502.     if(bIsRightCharacterSign(strCurrent, 0))
  503.     {
  504.       strCurrent = strCurrent.Right(strCurrent.GetLength() - 1);
  505.       nSelection --;
  506.     }
  507.   }
  508.   setStringAndSelection(strCurrent, nSelection);
  509. }
  510. void CFloatEdit::OnChar(UINT nChar, UINT, UINT) 
  511. {
  512.   processCharacter(nChar);
  513. }
  514. BOOL CFloatEdit::PreTranslateMessage(MSG* pMsg) 
  515. {
  516. if(pMsg->message == WM_KEYDOWN && pMsg->wParam == INT_KEY_DELETE)
  517.   {
  518.     if(hasSelection())
  519.       deleteSelection();
  520.     else
  521.       processDelete();
  522.     return TRUE;
  523.   }
  524.   if(pMsg->message == WM_KEYDOWN && pMsg->wParam == INT_KEY_INSERT)
  525.   {
  526.     return TRUE;
  527.   }
  528. return CEdit::PreTranslateMessage(pMsg);
  529. }
  530. void CFloatEdit::removeFirstComma(CString & strDestination)
  531. {
  532.   BOOL bSign = FALSE;
  533.   CString strWork = strDestination;
  534.   if(strWork.GetLength() > 0)
  535.   {
  536.     if(strWork.GetAt(0) == MINUS_SIGN)
  537.     {
  538.       bSign = TRUE;
  539.       strWork = strWork.Right(strWork.GetLength() - 1);
  540.     }
  541.   }
  542.   if(strWork.Left(1) == CString(m_cComma))
  543.     strWork = strWork.Right(strWork.GetLength() - 1);
  544.   if(bSign)
  545.     strWork = CString(MINUS_SIGN) + strWork;
  546.   strDestination = strWork;
  547. }
  548. BOOL CFloatEdit::bIsRightCharacterSign(CString strCurrent, int nSelection)
  549. {
  550.   // return true whether the right character is the sign character
  551.   BOOL bResult = FALSE; 
  552.   
  553.   if(nSelection < strCurrent.GetLength() && nSelection >= 0)
  554.   {
  555.     if(strCurrent.GetAt(nSelection) == MINUS_SIGN)
  556.       bResult = TRUE;
  557.   } // if
  558.   return bResult;
  559. }
  560. void CFloatEdit::OnRButtonDown(UINT, CPoint) 
  561. {
  562.   // disable the right menu button menu
  563. }
  564. CString CFloatEdit::midString(CString strIn, int nStart, int nLength)
  565. {
  566.   // getting hte mid string, as Mid member from CString does
  567.   CString strResult = CString("");
  568.   if(nStart >= 0 && nStart < strIn.GetLength())
  569.   {
  570.     strResult = strIn.Mid(nStart, nLength);
  571.   }
  572.   return strResult;
  573. }
  574. BOOL CFloatEdit::hasSelection(void)
  575. {
  576.   BOOL bResult = FALSE;
  577.   // getting the current selection
  578.   int nSelStart, nSelEnd;
  579.   GetSel(nSelStart, nSelEnd);
  580.   // setting the result
  581.   if(nSelEnd > nSelStart)
  582.     bResult = TRUE;
  583.   return bResult;
  584. }
  585. void CFloatEdit::getLocaleInformation(char & cDecimal, char & cComma)
  586. {
  587.   struct lconv * pLocalStructure;
  588.   pLocalStructure = localeconv();
  589.   
  590.   cDecimal = * (pLocalStructure->decimal_point);
  591.   cComma = *( pLocalStructure->currency_symbol);
  592. }
  593. void CFloatEdit::LocalSettings(void)
  594. {
  595.   char cDecimal[RETRIEVE_BUFFER_SPACE];
  596.   char cComma[RETRIEVE_BUFFER_SPACE];
  597.   ::GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SDECIMAL, cDecimal, RETRIEVE_BUFFER_SPACE);
  598.   ::GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_STHOUSAND, cComma, RETRIEVE_BUFFER_SPACE);
  599.   m_cDecimalPoint = *cDecimal;
  600.   // decimal point and thousands separator should be different
  601.   if(*cDecimal != *cComma)
  602.     m_cComma = *cComma;
  603. }