Filter Library | Camera | Interface Physics |
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 */