Filter Library Camera Interface Physics

ControlFilterEdit.cpp

00001 /*
00002  * ControlFilterEdit.cpp - a filtered edit control.
00003  *
00004  * This file is part of the Camera Filter Library.
00005  * Computer Aided Measurement Environment for Realtime Atomic imaging (Camera)
00006  *
00007  * Copyright (C) 2004, Leiden Probe Microscopy.
00008  * Copyright (C) 2004, Universiteit Leiden.
00009  *
00010  * The filtered edit control is based on:
00011  * CFilterEdit: Use Regular Expressions to Filter Your Input, By Ben Hanson.
00012  * See http://www.codeproject.com/editctrl/FilterEdit.asp
00013  *
00014  * Authors: Martin J. Moene (original)
00015  *
00016  * $Id: ControlFilterEdit.cpp 150 2005-04-25 09:02:30Z moene $
00017  */
00018 
00019 #include "stdafx.h"                     // for common (pre-compiled) headers
00020 //#include <cfl/stdafx.h>                 // for common (pre-compiled) headers
00021 #include <cfl/ControlFilterEdit.h>
00022 
00023 IMPLEMENT_DYNAMIC( CFilterEdit, CEdit )
00024 
00025 /**
00026  * destructor.
00027  */
00028 
00029 CFilterEdit::~CFilterEdit()
00030 {
00031    ; // do nothing
00032 }
00033 
00034 /**
00035  * (default) constructor.
00036  */
00037 
00038 CFilterEdit::CFilterEdit( const bool bProcessChars/* = true*/ ) :
00039    m_bProcessChars( bProcessChars ),
00040    m_bAutoValidate( true  ),
00041    m_bModal       ( true  ),
00042    m_bShowInvalid ( true  ),
00043    m_bSetBkOnError( false ),
00044    m_bWavyLineOnError ( true ),
00045    m_bBeepOnInvalid( true  ),
00046    m_bValid        ( true  ),
00047    m_bCharValid    ( false ),
00048    m_bControlDown  ( false ),
00049    m_crFgOK        ( RGB (  0,   0,   0 ) ),
00050    m_crBkOK        ( RGB (255, 255, 255 ) ),
00051    m_crFgError     ( RGB (255, 255, 255 ) ),
00052    m_crBkError     ( RGB (255,   0 ,  0 ) )
00053 {
00054    m_brOK.CreateSolidBrush   ( m_crBkOK    );
00055    m_brError.CreateSolidBrush( m_crBkError );
00056 }
00057 
00058 /**
00059  * validate as the user types (also settable in the constructor).
00060  */
00061 
00062 void CFilterEdit::SetProcessChars( const bool bProcessChars )
00063 {
00064    m_bProcessChars = bProcessChars;
00065 }
00066 
00067 /**
00068  * check that the entire input is valid when the control loses focus 
00069  * (bAutoValidate), set focus back to the control if the input is invalid 
00070  * when the control loses focus (bModal).
00071  */
00072 
00073 void CFilterEdit::SetAutoValidate( const bool bAutoValidate, const bool bModal )
00074 {
00075    m_bAutoValidate = bAutoValidate;
00076    m_bModal = bModal;
00077 }
00078 
00079 /**
00080  * show  if the input is invalid (bShowInvalid), via the background color 
00081  * (bSetBkOnError) and with a wavy line under the text (bWavyLineOnError).
00082  */
00083 
00084 void CFilterEdit::SetShowInvalid (
00085    const bool bShowInvalid, const bool bSetBkOnError, const bool bWavyLineOnError )
00086 {
00087    m_bShowInvalid = bShowInvalid;
00088    m_bSetBkOnError = bSetBkOnError;
00089    m_bWavyLineOnError = bWavyLineOnError;
00090 
00091    /*
00092     * Sanity Check: There is no point in setting both error display types 
00093     * to false if bShowInvalid is true.
00094     */
00095 
00096    if ( m_bShowInvalid && !m_bSetBkOnError && !m_bWavyLineOnError )
00097    {
00098       m_bWavyLineOnError = true;
00099    }
00100 }
00101 
00102 /**
00103  * beep when the final validation fails (bBeepOnInvalid).
00104  */
00105 
00106 void CFilterEdit::SetBeepOnInvalid( const bool bBeepOnInvalid )
00107 {
00108    m_bBeepOnInvalid = bBeepOnInvalid;
00109 }
00110 
00111 /**
00112  * set the normal ('OK') background color.
00113  */
00114 
00115 void CFilterEdit::SetBackgroundColourOK( const COLORREF crBkOK )
00116 {
00117    m_crBkOK = crBkOK;
00118    m_brOK.DeleteObject ();
00119    m_brOK.CreateSolidBrush (m_crBkOK);
00120 }
00121 
00122 /**
00123  * set the normal ('OK') foreground color.
00124  */
00125 
00126 void CFilterEdit::SetForegroundColourOK( const COLORREF crFgOK )
00127 {
00128    m_crFgOK = crFgOK;
00129 }
00130 
00131 /**
00132  * set the error background color.
00133  */
00134 
00135 void CFilterEdit::SetBackgroundColourError( const COLORREF crBkError )
00136 {
00137    m_crBkError = crBkError;
00138    m_brError.DeleteObject ();
00139    m_brError.CreateSolidBrush (m_crBkError);
00140 }
00141 
00142 /**
00143  * set the error foreground color.
00144  */
00145 
00146 void CFilterEdit::SetForegroundColourError( const COLORREF crFgError )
00147 {
00148    m_crFgError = crFgError;
00149 }
00150 
00151 /**
00152  * true if last entered character is valid.
00153  */
00154 
00155 bool CFilterEdit::IsValidChar()
00156 {
00157    return m_bCharValid;
00158 }
00159 
00160 /**
00161  * the entered text, so far.
00162  */
00163 
00164 const CString &CFilterEdit::GetProposedText() const
00165 {
00166    return m_strProposedText;
00167 }
00168 
00169 /**
00170  * true if the entered text is complete and valid.
00171  */
00172 
00173 bool CFilterEdit::IsValidInput()
00174 {
00175    CString str;
00176 
00177    GetWindowText (str);
00178 
00179    m_bValid = Check( str );
00180 
00181    Invalidate ();
00182 
00183    if (!m_bValid && m_bBeepOnInvalid)
00184    {
00185       ::MessageBeep( MB_ICONEXCLAMATION );
00186    }
00187 
00188    return m_bValid;
00189 }
00190 
00191 /*
00192  * the message map
00193  */
00194 
00195 BEGIN_MESSAGE_MAP( CFilterEdit, CEdit )
00196    ON_WM_CHAR()
00197    ON_WM_KEYDOWN()
00198    ON_WM_KEYUP()
00199    ON_WM_CTLCOLOR_REFLECT()
00200    ON_WM_SETFOCUS()
00201    ON_WM_KILLFOCUS()
00202    ON_WM_PAINT()
00203 END_MESSAGE_MAP()
00204 
00205 /**
00206  * true if this character is acceptable at this point.
00207  */
00208 
00209 bool CFilterEdit::ValidateChar( UINT nChar )
00210 {
00211    int  iStartIndex = -1;
00212    int  iEndIndex   = -1;
00213    bool bCharValid  = true;
00214 
00215    GetSel (iStartIndex, iEndIndex);
00216    GetWindowText (m_strProposedText);
00217 
00218    if ( nChar == VK_BACK )
00219    {
00220       if ( iStartIndex == iEndIndex )
00221       {
00222          iStartIndex--;
00223       }
00224 
00225       // remove char
00226       m_strProposedText.Delete( iStartIndex, iEndIndex - iStartIndex );
00227       bCharValid = Match( m_strProposedText );
00228    }
00229    else
00230    {
00231       // haven't reached text limit
00232       m_strProposedText.Delete( iStartIndex, iEndIndex - iStartIndex );
00233       m_strProposedText.Insert( iStartIndex, static_cast<TCHAR> (nChar) );
00234       bCharValid = Match( m_strProposedText );
00235    }
00236 
00237    return bCharValid;
00238 }
00239 
00240 #pragma warning( push )
00241 #pragma warning( disable : 4100 )
00242 
00243 /**
00244  * true if input is complete and valid; override in derived classes.
00245  */
00246 
00247 bool CFilterEdit::Check( const CString &csText )
00248 {
00249    return true;
00250 }
00251 
00252 /**
00253  * true if partial input is acceptable; override in derived classes.
00254  */
00255 
00256 bool CFilterEdit::Match( const CString &csText )
00257 {
00258    return true;
00259 }
00260 
00261 #pragma warning( pop )
00262 
00263 /**
00264  * restore valid state.
00265  */
00266 
00267 void CFilterEdit::ResetValid ()
00268 {
00269    if ( !m_bValid )
00270    {
00271       m_bValid = true;
00272       Invalidate();
00273    }
00274 }
00275 
00276 /**
00277  * handle window message, like cut paste etc.
00278  */
00279 
00280 LRESULT CFilterEdit::WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
00281 {
00282    bool bExecute    = true;
00283    bool bResetValid = false;
00284    LRESULT lResult  = 0;
00285 
00286    if ( m_bProcessChars )
00287    {
00288       switch ( message )
00289       {
00290          case EM_REPLACESEL:
00291          {
00292             int iStartIndex = -1;
00293             int iEndIndex   = -1;
00294             CString strReplace;
00295 
00296             GetSel( iStartIndex, iEndIndex );
00297             strReplace = reinterpret_cast<LPCTSTR>( lParam );
00298             GetWindowText( m_strProposedText );
00299             m_strProposedText.Delete( iStartIndex, iEndIndex - iStartIndex );
00300             m_strProposedText.Insert( iStartIndex, strReplace );
00301             // Don't set m_bCharValid as OnChar not called
00302             bExecute    = Match( m_strProposedText );
00303             bResetValid = bExecute;
00304             break;
00305          }
00306    /*
00307          case EM_SETLIMITTEXT:
00308          m_uiMaxChars = static_cast<unsigned int> (wParam);
00309          break;
00310    */
00311          case WM_CLEAR:
00312             bExecute    = CanDelete();
00313             bResetValid = bExecute;
00314             break;
00315 
00316          case WM_CUT:
00317             bExecute    = CanDelete();
00318             bResetValid = bExecute;
00319             break;
00320 
00321          case WM_PASTE:
00322             bExecute    = CanPaste();
00323             bResetValid = bExecute;
00324             break;
00325 
00326          case WM_SETTEXT:
00327          {
00328             const TCHAR *psz = reinterpret_cast<const TCHAR *>( lParam );
00329 
00330             bExecute    = Match( psz );
00331             bResetValid = bExecute;
00332             break;
00333          }
00334 
00335          case WM_UNDO:
00336             bResetValid = true;
00337 
00338          default:
00339             break;
00340       }
00341    }
00342 
00343    if ( bExecute )
00344    {
00345       lResult = CEdit::WindowProc( message, wParam, lParam );
00346 
00347       if ( bResetValid )
00348       {
00349          ResetValid();
00350       }
00351    }
00352 
00353    return lResult;
00354 }
00355 
00356 /**
00357  * handle delete (Ctrl+X).
00358  */
00359 
00360 bool CFilterEdit::CanDelete()
00361 {
00362    CString csText;
00363    int iStartIndex = -1;
00364    int iEndIndex   = -1;
00365 
00366    GetWindowText( csText );
00367    GetSel( iStartIndex, iEndIndex );
00368 
00369    if ( iStartIndex == iEndIndex )
00370    {
00371       iEndIndex++;
00372    }
00373 
00374    csText.Delete( iStartIndex, iEndIndex - iStartIndex );
00375 
00376    return Match( csText );
00377 }
00378 
00379 /**
00380  * handle paste (Ctrl+V).
00381  */
00382 
00383 bool CFilterEdit::CanPaste()
00384 {
00385    bool bSuccess = OpenClipboard () != 0;
00386 
00387    if ( bSuccess )
00388    {
00389 #ifdef _UNICODE
00390       HANDLE hClipMem = ::GetClipboardData( CF_UNICODETEXT );
00391 #else
00392       HANDLE hClipMem = ::GetClipboardData( CF_TEXT );
00393 #endif
00394 
00395       bSuccess = hClipMem != 0;
00396 
00397       if ( bSuccess )
00398       {
00399          const TCHAR *lpClipMem = (const TCHAR *) ::GlobalLock( hClipMem );
00400 
00401          bSuccess = lpClipMem != 0;
00402 
00403          if ( bSuccess )
00404          {
00405             CString strClipText;
00406             CString csText;
00407             int iStartIndex = -1;
00408             int iEndIndex   = -1;
00409 
00410             strClipText.Format( _T("%s"), lpClipMem );
00411             GetWindowText( csText );
00412             GetSel       ( iStartIndex, iEndIndex   );
00413             csText.Delete( iStartIndex, iEndIndex - iStartIndex );
00414             csText.Insert( iStartIndex, strClipText );
00415 
00416             if ( csText.GetLength() > static_cast<int>( GetLimitText() ) )
00417    //         if (csText.GetLength () > static_cast<int> (m_uiMaxChars))
00418             {
00419                csText = csText.Left( GetLimitText() );
00420    //            csText = csText.Left (m_uiMaxChars);
00421             }
00422 
00423             bSuccess = Match( csText );
00424             ::GlobalUnlock( hClipMem );
00425          }
00426       }
00427 
00428       ::CloseClipboard();
00429    }
00430 
00431    return bSuccess;
00432 }
00433 
00434 /**
00435  * validate new character (if SetProcessChars(true)) and let it be further processed, unless its invalid.
00436  */
00437 
00438 void CFilterEdit::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags )
00439 {
00440    m_bCharValid = !m_bProcessChars || ValidateChar( nChar );
00441 
00442    if ( m_bCharValid )
00443    {
00444       CEdit::OnChar( nChar, nRepCnt, nFlags );
00445    }
00446 }
00447 
00448 /**
00449  * handle control-key press (Del, Ctrl+X, Ctrl+V).
00450  */
00451 
00452 void CFilterEdit::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
00453 {
00454    bool bExecute = true;
00455 
00456    ResetValid();
00457 
00458    if ( nChar == VK_CONTROL )
00459    {
00460       m_bControlDown = true;
00461    }
00462    else if ( nChar == VK_DELETE )
00463    {
00464       if ( m_bProcessChars )
00465       {
00466          bExecute = CanDelete();
00467       }
00468    }
00469    else if ( ( nChar == 'x' || nChar == 'X' ) && m_bControlDown )
00470    {
00471       if ( m_bProcessChars )
00472       {
00473          bExecute = CanDelete();
00474 
00475          if ( bExecute )
00476          {
00477             // Will handle cut ourselves, so don't pass char on...
00478             bExecute = false;
00479             SendMessage( WM_CUT, 0, 0 );
00480          }
00481       }
00482    }
00483    // Test for Paste keyboard short-cut
00484    else if ( ( nChar == 'v' || nChar == 'V' ) && m_bControlDown )
00485    {
00486       SendMessage( WM_PASTE, 0, 0 );
00487    }
00488 
00489    if ( bExecute )
00490    {
00491       CEdit::OnKeyDown( nChar, nRepCnt, nFlags );
00492    }
00493 }
00494 
00495 /**
00496  * handle control-key release (Ctrl).
00497  */
00498 
00499 void CFilterEdit::OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags )
00500 {
00501    if ( nChar == VK_CONTROL )
00502    {
00503       m_bControlDown = false;
00504    }
00505 
00506    CEdit::OnKeyUp( nChar, nRepCnt, nFlags );
00507 }
00508 
00509 /**
00510  * handle the ON_WM_CTLCOLOR_REFLECT message.
00511  */
00512 
00513 HBRUSH CFilterEdit::CtlColor( CDC *pDC, UINT /* nCtlColor */ )
00514 {
00515    /*
00516     * See MSDN TN062: Message Reflection for Windows Controls
00517     */
00518 
00519    HBRUSH hbr = m_brOK;
00520 
00521    /*
00522     * If m_bSetBkOnError is not set, then use the 'OK' colours to draw anyway.
00523     */
00524 
00525    if ( m_bValid || !m_bSetBkOnError )
00526    {
00527       pDC->SetBkColor  ( m_crBkOK );
00528       pDC->SetTextColor( m_crFgOK );
00529    }
00530    else if ( m_bShowInvalid && m_bSetBkOnError )
00531    {
00532       /*
00533        * If m_bSetBkOnError is set, then change all the colours.
00534        */
00535 
00536       hbr = m_brError;
00537       pDC->SetBkColor  ( m_crBkError );
00538       pDC->SetTextColor( m_crFgError );
00539    }
00540 
00541    return hbr;
00542 }
00543 
00544 /**
00545  * handle set focus message.
00546  */
00547 
00548 void CFilterEdit::OnSetFocus( CWnd *pOldWnd )
00549 {
00550    CEdit::OnSetFocus( pOldWnd );
00551 }
00552 
00553 /**
00554  * validate on kill focus message.
00555  */
00556 
00557 void CFilterEdit::OnKillFocus( CWnd *pNewWnd )
00558 {
00559    bool bValid = true;
00560 
00561    if ( m_bAutoValidate )
00562    {
00563       /*
00564        * Don't beep on exit...
00565        */
00566 
00567       if ( pNewWnd && pNewWnd != GetParent() && pNewWnd->GetDlgCtrlID() != IDCANCEL )
00568       {
00569          bValid = IsValidInput();
00570       }
00571       else
00572       {
00573          bValid = true;
00574       }
00575    }
00576 
00577    if ( bValid || !m_bModal )
00578    {
00579       CEdit::OnKillFocus( pNewWnd );
00580    }
00581    else
00582    {
00583       SetFocus();
00584    }
00585 }
00586 
00587 /**
00588  * handle paint message.
00589  */
00590 
00591 void CFilterEdit::OnPaint()
00592 {
00593    CClientDC dc (this);
00594 
00595    //  This stops the control from going grey
00596    Default();
00597 
00598    if ( !m_bValid && m_bShowInvalid && m_bWavyLineOnError )
00599    {
00600       CFont *pFont = GetFont();
00601       CPen  *pPen  = dc.GetCurrentPen();
00602 
00603       LOGPEN  lp;
00604       CPen    MyPen;
00605       CRect   rect;
00606       CString csText;
00607       CSize   size;
00608       int iX = 0;
00609       const int iIncrement = 2;
00610 
00611       // We must use the correct font when using GetTextExtent..!
00612 
00613       dc.SelectObject( pFont );
00614 
00615       // We have to create a new pen to set a new colour...
00616 
00617       pPen->GetLogPen( &lp );
00618       lp.lopnColor = m_bSetBkOnError ? m_crFgError : m_crBkError;
00619       MyPen.CreatePenIndirect( &lp );
00620       dc.SelectObject( &MyPen );
00621 
00622       // Get the rect for the entire edit control
00623 
00624       GetRect      ( &rect  );
00625       GetWindowText( csText );
00626 
00627       // This part deals with the *displayed* Text Extent
00628 
00629       size = dc.GetTextExtent( csText.Mid( CharFromPos( CPoint( rect.left + 1, rect.top ) ) ) );
00630 
00631       // Check for case of empty string
00632       if ( size.cx == 0 )
00633          size.cx = iIncrement * 3;
00634       // Dont draw off the end of the edit control
00635       else if ( size.cx > rect.right )
00636          size.cx = rect.right;
00637 
00638       // Starting x co-ordinate to start drawing from
00639 
00640       iX = rect.left;
00641 
00642       // Start at the bottom left of the edit control
00643 
00644       dc.MoveTo( rect.left, rect.bottom );
00645 
00646       // Draw the wavy line like in Microsoft Word
00647 
00648       while ( iX < size.cx + rect.left )
00649       {
00650          iX += iIncrement;
00651 
00652          if ( iX > size.cx + rect.left )
00653             break;
00654 
00655          dc.LineTo( iX, rect.bottom + iIncrement );
00656          iX += iIncrement;
00657 
00658          if ( iX > size.cx + rect.left )
00659             break;
00660 
00661          dc.LineTo( iX, rect.bottom );
00662       }
00663    }
00664 
00665    // Do not call CEdit::OnPaint() for painting messages
00666 }
00667 
00668 /*
00669  * end of file
00670  */

Camera Filter Library documentation © 2004-2007 by Leiden Probe Microscopy