Filter Library Camera Interface Physics

ControlFilterEdit.h

00001 /*
00002  * ControlFilterEdit.h - 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-2005, Leiden Probe Microscopy.
00008  * Copyright (C) 2004-2005, 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.h 202 2005-05-12 11:00:48Z moene $
00017  */
00018 
00019 #ifndef CFL_CONTROLFILTEREDIT_H
00020 #define CFL_CONTROLFILTEREDIT_H
00021 
00022 #if _MSC_VER > 1000
00023 #pragma once
00024 #endif // _MSC_VER > 1000
00025 
00026 #include <afxwin.h>                     // for class CEdit
00027 
00028 /**
00029  * \addtogroup cfl_controlnumedit Numeric Edit Control
00030  * \brief numeric edit control.
00031  *
00032  * This section contains various classes for numeric edit controls, currently:
00033  * - CFilterEdit
00034  * - CIntegerEdit
00035  * - CRealEdit
00036  *
00037  * The controls are used in dialog classes to:
00038  * - restrict what can be input
00039  * - set the control to a new value
00040  * - obtain the current value
00041  *
00042  * The controls may also be buddied with a \ref cfl_controlnumspincontrol. 
00043  * In that case the numeric edit control is used to only accept allowable input 
00044  * and the spin control is used to set and obtain the value.
00045  *
00046  * Note: do not check the <em>Set buddy integer</em> flag on the spin control.
00047  *
00048  * By using a control class we avoid direct access to the underlying windows dialog 
00049  * item via GetDlgItem. More about that can be read in <em>Avoiding GetDlgItem
00050  * in MFC</em> by Joseph M. Newcomer \ref newcomer_avoidgetdlgitem "[GETDLGITEM]".
00051  *
00052  * Another advantage of the numeric edit controls is that this makes it possible 
00053  * to do direct numeric access, like in:
00054  * \code
00055  * CIntegerEdit c_txtOffset;
00056  * c_txtOffset = -10;
00057  *
00058  * // and
00059  *
00060  * if ( c_txtOffset > 0 )
00061  *    ...
00062  * \endcode
00063  *
00064  * Here is how you may use a filtering edit control of type CIntegerEdit in a
00065  * dialog.
00066  *
00067  * \code
00068  * // mydialog.h - header
00069  *
00070  * class MyDialog
00071  * {
00072  * public:
00073  *    MyDialog( CWnd* pParent = NULL );
00074  *
00075  * protected:
00076  *    virtual void DoDataExchange( CDataExchange* pDX );
00077  *
00078  *    //{{AFX_MSG(MyDialog)
00079  *    virtual BOOL OnInitDialog();
00080  *    afx_msg void OnChangeOffset();
00081  *    //}}AFX_MSG
00082  *    DECLARE_MESSAGE_MAP()
00083  *
00084  * private:
00085  *    CIntegerEdit c_txtOffset;
00086  *    bool         initialized;
00087  * };
00088  *
00089  * \endcode
00090  *
00091  * Please note the member variable \c initialized. Under some circumestances
00092  * messages start coming, while not all controls are fully initialized and
00093  * messages should not be handled yet. This happens for example when you
00094  * auto-buddy an edit control with a spin control.
00095  * (See also \ref newcomer_avoidgetdlgitem "[GETDLGITEM]".)
00096  *
00097  * \code
00098  * // mydialog.cpp - implementation
00099  *
00100  * MyDialog::MyDialog( CWnd* pParent ) :
00101  *    c_txtOffset( true   ),  // signed
00102  *    initialized( false  )   // don't handle messages yet; see OnInitDialog()
00103  * {
00104  *    //{{AFX_DATA_INIT(MyDialog)
00105  *    //}}AFX_DATA_INIT
00106  * }
00107  *
00108  * void MyDialog::DoDataExchange( CDataExchange* pDX )
00109  * {
00110  *    CDialog::DoDataExchange(pDX);
00111  *    //{{AFX_DATA_MAP(MyDialog)
00112  *    DDX_Control(pDX, IDC_MYDIALOG_OFFSET, c_txtOffset );
00113  *    //}}AFX_DATA_MAP
00114  * }
00115  *
00116  * BEGIN_MESSAGE_MAP( MyDialog )
00117  *    //{{AFX_MSG_MAP(MyDialog)
00118  *    ON_EN_CHANGE( IDC_MYDIALOG_OFFSET, OnChangeOffset )
00119  *    //}}AFX_MSG_MAP
00120  * END_MESSAGE_MAP()
00121  *
00122  * BOOL MyDialog::OnInitDialog()
00123  * {
00124  *    CFilterDlg::OnInitDialog();
00125  *
00126  *    c_txtOffset = 0;     // initialize offset value
00127  *
00128  *    UpdateData(FALSE);   // transfer data from variables to controls
00129  *
00130  *    initialized = true;  // dialog initialized
00131  *
00132  *    return TRUE;         // we set the focus
00133  * }
00134  *
00135  * // for example, ensure odd value:
00136  *
00137  * void MyDialog::OnChangeOffset()
00138  * {
00139  *    if ( !initialized )
00140  *       return ;
00141  *
00142  *    if ( 0 == c_txtOffset % 2 )
00143  *       c_txtOffset = c_txtOffset + 1;
00144  * }
00145  * \endcode
00146  *
00147  * Note that the handler OnChangeOffset() is not required to obtain the
00148  * filtering property, that is handled via DoDataExchange().
00149  *
00150  * @{
00151  */
00152 
00153 DECLARE_CLASS( CFilterEdit );           // declare various types for class
00154 
00155 /**
00156  * a base class for filtering edit classes.
00157  *
00158  * CFilterEdit is the base class for edit fields that can filter their input.
00159  * This can be used to limit the input to data that is valid in a certian
00160  * context, for example only positive integer numbers.
00161  *
00162  * Base class CFilterEdit lets you specify several ascpects of its behaviour:
00163  * - validate as the user types (also settable in the constructor), SetProcessChars()
00164  * - check that the entire input is valid when the control loses focus; set
00165  *   focus back to the control if the input is invalid when the control loses
00166  *   focus, SetAutoValidate()
00167  * - show invalid input visually: set the background color to a specified color
00168  *   if the input is invalid, draw a wavy line under the text if the input is
00169  *   invalid, SetShowInvalid()
00170  * - set the error colors, SetBackgroundColourError(), SetForegroundColourError()
00171  * - set the normal ('OK') colors, SetForegroundColourOK(), SetBackgroundColourOK()
00172  * - beep, if, when a final validation is performed, the validation fails, SetBeepOnInvalid()
00173  *
00174  * To implement the checking behaviour, do the following.
00175  * - derive your class publicly from CFilterEdit
00176  * - implement methods Check() and Match()
00177  *
00178  * Note: CFilterEdit can also be used by itself solely with the purpose to create
00179  * a colored edit field.
00180  *
00181  * Make sure that the control is included in method DoDataExchange() of the 
00182  * dialog classes where you use the edit control.
00183  *
00184  * Class CFilterEdit is a modification of the class CFilterEdit by Ben Hanson
00185  * \ref hanson_filteredit "[FILTEREDIT]" in that the regular expression pattern 
00186  * matching \ref hanson_regexp "[REGEXP]" is replaced by a simple lexical scanner
00187  * \ref dragon_1986 "[Aho et al.]".
00188  *
00189  * Another interesting article is: A Validating Edit Control by Joseph M. 
00190  * Newcomer \ref newcomer_valedtctl "[VALEDTCTL]". This article describes the
00191  * use of a tooltip to indicate the reason the contents of an edit control is
00192  * invalid.
00193  *
00194  * The following example shows an edit field that limits its input to signed
00195  * integer numbers in octal, decimal or hexadecimal notation.
00196  *
00197  * The interface.
00198  * \code
00199  * // ControlOctDecHexEdit.h - class declaration.
00200  *
00201  * #ifndef CFL_CONTROLOCTDECHEXEDIT_H
00202  * #define CFL_CONTROLOCTDECHEXEDIT_H
00203  *
00204  * #if _MSC_VER > 1000
00205  * #pragma once
00206  * #endif // _MSC_VER > 1000
00207  *
00208  * #include <cfl/ControlFilterEdit.h>      // for base class CFilterEdit
00209  *
00210  * //
00211  * // COctDecHexEdit
00212  * //
00213  *
00214  * class COctDecHexEdit : public CFilterEdit
00215  * {
00216  * public:
00217  *    ~COctDecHexEdit();
00218  *    COctDecHexEdit( bool bSigned = true );
00219  *
00220  * protected:
00221  *    bool Check( const CString &csText );
00222  *    bool Match( const CString &csText );
00223  *
00224  * private:
00225  *    bool m_bSigned;
00226  * };
00227  *
00228  * //
00229  * // destructor.
00230  * //
00231  *
00232  * inline COctDecHexEdit::~COctDecHexEdit()
00233  * {
00234  *    ; // do nothing
00235  * }
00236  *
00237  * //
00238  * // destructor.
00239  * //
00240  *
00241  * inline COctDecHexEdit::COctDecHexEdit( bool bSigned ) :
00242  *    CFilterEdit(), m_bSigned( bSigned )
00243  * {
00244  *    ; // do nothing
00245  * }
00246  *
00247  * #endif // CFL_CONTROLOCTDECHEXEDIT_H
00248  *
00249  * \endcode
00250  *
00251  * The implementation.
00252  * \code
00253  * // ControlOctDecHexEdit.cpp - class implementation.
00254  *
00255  * #include "stdafx.h"                     // for common (pre-compiled) headers
00256  * #include <cfl/ControlOctDecHexEdit.h>   // header
00257  *
00258  * //
00259  * // true if input is complete and valid.
00260  * //
00261  *
00262  * bool COctDecHexEdit::Check( const CString &csText )
00263  * {
00264  *    TCHAR* ePtr;
00265  *
00266  *    // let strtol() determine the base:
00267  *    (void) _tcstol( csText, &ePtr, 0 );
00268  *
00269  *    // ok if all characters are used for conversion
00270  *    return _TCHAR('\0') == *ePtr;
00271  * }
00272  *
00273  * //
00274  * // true if (partial) input valid.
00275  * //
00276  *
00277  * //      number : sign? ( octal | decimal | hexadecimal )
00278  * //        sign : [+-]
00279  * //       octal : "0" [0-7]+
00280  * //     decimal : [1-9][0-9]*
00281  * // hexadecimal : "0" [xX] [0-9a-fA-F]+
00282  * //
00283  *
00284  * bool COctDecHexEdit::Match( const CString &csText )
00285  * {
00286  *    int base = 10;
00287  *
00288  *    const TCHAR* cPtr = csText;
00289  *
00290  *    // optional sign?
00291  *
00292  *    if ( _TCHAR('+') == *cPtr || ( m_bSigned && _TCHAR('-') == *cPtr ) )
00293  *       ++cPtr;
00294  *
00295  *    // leading octal/hexadecimal 0?
00296  *
00297  *    if ( _TCHAR('0') == *cPtr )
00298  *    {
00299  *       ++cPtr;
00300  *       base = 8;
00301  *    }
00302  *
00303  *    // leading hexadecimal 0x, 0X?
00304  *
00305  *    if ( 8 == base && ( _TCHAR('x') == *cPtr || _TCHAR('X') == *cPtr ) )
00306  *    {
00307  *       ++cPtr;
00308  *       base = 16;
00309  *    }
00310  *
00311  *    // scan remaining digits:
00312  *
00313  *    if ( 8 == base )
00314  *    {
00315  *       while ( *cPtr && _TCHAR('0') <= *cPtr && *cPtr <= _TCHAR('7') )
00316  *         ++cPtr;
00317  *    }
00318  *    else if ( 10 == base )
00319  *    {
00320  *       while ( *cPtr && _istdigit( *cPtr ) )
00321  *         ++cPtr;
00322  *    }
00323  *    else if ( 16 == base )
00324  *    {
00325  *       while ( *cPtr && _istxdigit( *cPtr ) )
00326  *         ++cPtr;
00327  *    }
00328  *
00329  *    // ok if all characters matched:
00330  *    return cPtr == (const TCHAR*) csText + csText.GetLength();
00331  * }
00332  * \endcode
00333  */
00334 
00335 class CFilterEdit : public CEdit
00336 {
00337    typedef CFilterEdit self;            ///< this type
00338 
00339    DECLARE_DYNAMIC( CFilterEdit )
00340 
00341 public:
00342    ///
00343    /// \name Construction
00344    /// @{
00345 
00346    virtual ~CFilterEdit ();
00347    CFilterEdit ( const bool bProcessChars = true );
00348 
00349    /// @}
00350    /// \name Predicates
00351    /// @{
00352 
00353    bool    IsValidChar ();
00354    bool    IsValidInput ();
00355 
00356    /// @}
00357    /// \name Accessors
00358    /// @{
00359 
00360    // Read this to further process text from derived class
00361    const   CString &GetProposedText () const;
00362 
00363    /// @}
00364    /// \name Mutators
00365    /// @{
00366 
00367    void    SetProcessChars         ( const bool bProcessChars );
00368    void    SetAutoValidate         ( const bool bAutoValidate, const bool bModal );
00369    void    SetShowInvalid          ( const bool bShowInvalid, const bool bSetBkOnError, const bool bWavyLineOnError );
00370    void    SetBeepOnInvalid        ( const bool bBeepOnInvalid );
00371 
00372    void    SetBackgroundColourOK   ( const COLORREF crBkOK   );
00373    void    SetForegroundColourOK   ( const COLORREF crFgOK    );
00374    void    SetBackgroundColourError( const COLORREF crBkError );
00375    void    SetForegroundColourError( const COLORREF crFgError );
00376 
00377    void    Select();
00378 
00379    // @}
00380 
00381 protected:
00382    bool            ValidateChar( UINT nChar );
00383    void            ResetValid  ( );
00384 
00385    virtual bool    Check( const CString &csText );
00386    virtual bool    Match( const CString &csText );
00387 
00388    virtual LRESULT WindowProc ( UINT message, WPARAM wParam, LPARAM lParam );
00389 
00390    DECLARE_MESSAGE_MAP ()
00391 
00392    //
00393    // \name Message Handlers
00394    // @{
00395 
00396    afx_msg void    OnChar     ( UINT nChar, UINT nRepCnt, UINT nFlags );
00397    afx_msg void    OnKeyDown  ( UINT nChar, UINT nRepCnt, UINT nFlags );
00398    afx_msg void    OnKeyUp    ( UINT nChar, UINT nRepCnt, UINT nFlags );
00399    afx_msg HBRUSH  CtlColor   ( CDC *pDC, UINT /*nCtlColor*/ );
00400    afx_msg void    OnSetFocus ( CWnd *pOldWnd );
00401    afx_msg void    OnKillFocus( CWnd *pNewWnd );
00402    afx_msg void    OnPaint    ( );
00403 
00404    // @}
00405 
00406 private:
00407    bool CanDelete();                    ///< handle cut, Ctrl+X
00408    bool CanPaste ();                    ///< handle paste, Ctrl+V
00409 
00410    CFilterEdit( CFilterEdit const&   ); ///< prevent copy-assignment construction
00411    self& operator=(CFilterEdit const&); ///< prevent assignment
00412 
00413 private:
00414    // Set this to false to call CEdit::OnChar in derived class
00415    //   unsigned int m_uiMaxChars;
00416    bool m_bProcessChars;                ///< validate characters while typing (flag)
00417 
00418    // These two flags go together
00419    bool m_bAutoValidate;                ///< (flag)
00420    bool m_bModal;                       ///< (flag)
00421 
00422    // These three flags go together
00423    bool m_bShowInvalid;                 ///< indicate invalid input (flag)
00424    bool m_bSetBkOnError;                ///< change background color for invalid input (flag)
00425    bool m_bWavyLineOnError;             ///< draw wavy line if input is invalid (flag)
00426 
00427    bool m_bBeepOnInvalid;               ///< beep if input is invalid (flag)
00428 
00429    // Does the text completely match the regex
00430    bool m_bValid;                       ///< input is valid (flag)
00431    // Set to true or false each time a char is typed
00432    // (read from derived class
00433    bool m_bCharValid;                   ///< current character is valid (flag)
00434    // Latest proposed string (can be read by a derived class)
00435    CString m_strProposedText;           ///< current valid text
00436 
00437    bool m_bControlDown;                 ///< control key is down (flag)
00438 
00439    COLORREF m_crFgOK;                   ///< ok foreground color
00440    COLORREF m_crBkOK;                   ///< ok background color
00441    COLORREF m_crFgError;                ///< error foreground color
00442    COLORREF m_crBkError;                ///< error background color
00443    CBrush   m_brOK;                     ///< ok brush
00444    CBrush   m_brError;                  ///< error brush
00445 };
00446 
00447 /// @} cfl_controlnumedit
00448 
00449 /**
00450  * focus the control and select it content.
00451  */
00452 
00453 inline void CFilterEdit::Select()
00454 {
00455    SetFocus(       );
00456    SetSel  ( 0, -1 );
00457 }
00458 
00459 #endif // CFL_CONTROLFILTEREDIT_H
00460 
00461 /*
00462  * end of file
00463  */

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