📄 spinedit.cpp
字号:
// SpinEdit.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
// SpinEdit.h : header file
//
// SpinEdit Control header file
//
// This code is free for personal and commercial use.
//
// Written by Huang Chaoyi (ahaa007@263.net)
// Please use and enjoy. Please let me know if you find bugs.
//
// There are some code blocks copied from Chris Maunder's CGridCtrl class.
// Thank you, Chris Maunder.
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SpinEdit.h"
#include "TCHAR.h"
#include "seedit.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define IDC_SEEDIT 1 // ID of inplace edit controls
IMPLEMENT_DYNCREATE(CSpinEdit, CWnd)
//在对话框中使用的函数
void AFXAPI DDX_SpinEditControl(CDataExchange* pDX, int nIDC, CSpinEdit& rControl)
{
if (rControl.GetSafeHwnd() == NULL) // not subclassed yet
{
ASSERT(!pDX->m_bSaveAndValidate);
HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
if (!rControl.SubclassWindow(hWndCtrl))
{
ASSERT(FALSE); // possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
#ifndef _AFX_NO_OCC_SUPPORT
else
{
// If the control has reparented itself (e.g., invisible control),
// make sure that the CWnd gets properly wired to its control site.
if (pDX->m_pDlgWnd->GetSafeHwnd() != ::GetParent(rControl.GetSafeHwnd()))
rControl.AttachControlSite(pDX->m_pDlgWnd);
}
#endif //!_AFX_NO_OCC_SUPPORT
}
}
// Get the number of lines to scroll with each mouse wheel notch
// Why doesn't windows give us this function???
UINT GetMouseScrollLines()
{
int nScrollLines = 3; // reasonable default
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"),
0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{
TCHAR szData[128];
DWORD dwKeyDataType;
DWORD dwDataBufSize = sizeof(szData);
if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
(LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
{
nScrollLines = _tcstoul(szData, NULL, 10);
}
RegCloseKey(hKey);
}
return nScrollLines;
}
/////////////////////////////////////////////////////////////////////////////
// CSpinEdit
CSpinEdit::CSpinEdit()
{
RegisterWindowClass( );
m_dbValue = 0;
m_dbBase = 0;
m_dbLower = 0;
m_dbUpper = 100;
m_dbStep = 1;
m_bValueType = SE_INT;
m_szExtraText = _T("");
m_bPointPos = 0;
m_bEditShow = FALSE;
m_bLowerBtnPushed = FALSE;
m_bUpperBtnPushed = FALSE;
m_bNeedExtraText = TRUE;
m_nTimerID = 0;
m_nTimerInterval = 100;
m_nStepsPerWheelNotch = GetMouseScrollLines(); // Get the number of steps
m_dbOldValue = 0;
m_pEdit = NULL;
}
// Register the window class if it has not already been registered.
BOOL CSpinEdit::RegisterWindowClass()
{
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();
if (!(::GetClassInfo(hInst, SPINEDITCTRL_CLASSNAME, &wndcls)))
{
// otherwise we need to register a new class
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = NULL;
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = SPINEDITCTRL_CLASSNAME;
if (!AfxRegisterClass(&wndcls)) {
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}
CSpinEdit::~CSpinEdit()
{
if( m_pEdit != NULL )
delete m_pEdit;
}
BOOL CSpinEdit::Create( CWnd* pParentWnd, CRect rect, UINT nID, DWORD dwStyle )
{
DWORD dwSpinEditStyle = WS_BORDER|WS_CHILD | WS_TABSTOP|dwStyle;
if (!CWnd::Create(SPINEDITCTRL_CLASSNAME, NULL, dwSpinEditStyle, rect, pParentWnd, nID))
return FALSE;
SetFont( pParentWnd->GetFont() );
try
{
if( m_pEdit == NULL )
{
m_pEdit = new CSEEdit;
m_pEdit->Create( 0, this, IDC_SEEDIT );
}
}
catch(CMemoryException *pEx )
{
TRACE( "exception at create CSpinEdit, memory not enough!" );
pEx->Delete();
return FALSE;
}
return TRUE;
}
BOOL CSpinEdit::SubclassWindow(HWND hWnd)
{
if (!CWnd::SubclassWindow(hWnd)) return FALSE;
try
{
if ( m_pEdit == NULL)
{
m_pEdit = new CSEEdit;
m_pEdit->Create( 0, this, IDC_SEEDIT );
}
}
catch(CMemoryException *pEx )
{
TRACE( "exception when creating CSpinEdit, memory not enough!\n" );
pEx->Delete();
return FALSE;
}
return TRUE;
}
BEGIN_MESSAGE_MAP(CSpinEdit, CWnd)
//{{AFX_MSG_MAP(CSpinEdit)
ON_WM_KILLFOCUS()
ON_WM_KEYUP()
ON_WM_PAINT()
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_CHAR()
ON_WM_CAPTURECHANGED()
ON_WM_TIMER()
ON_WM_GETDLGCODE()
ON_WM_SETCURSOR()
ON_WM_SETFOCUS()
ON_WM_MOUSEWHEEL()
ON_NOTIFY(SEN_ENDLABELEDIT, IDC_SEEDIT, OnEndInplaceEdit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSpinEdit message handlers
/////////////////////////////////////////////////////////////////////////////
// CSpinEdit message handlers
void CSpinEdit::OnKillFocus(CWnd* pNewWnd)
{
CWnd::OnKillFocus(pNewWnd );
Invalidate();
}
// Handles mouse wheel notifications
// Note - if this doesn't work for win95 then use OnRegisteredMouseWheel instead
BOOL CSpinEdit::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// A m_nRowsPerWheelNotch value less than 0 indicates that the mouse
// wheel scrolls whole pages, not just lines.
if (m_nStepsPerWheelNotch == -1)
{
int nPagesScrolled = zDelta / 120;
if (nPagesScrolled > 0)
for (int i = 0; i < nPagesScrolled; i++)
AddValue( m_nStepsPerWheelNotch );
else
for (int i = 0; i > nPagesScrolled; i--)
SubValue( m_nStepsPerWheelNotch );
}
else
{
int nRowsScrolled = m_nStepsPerWheelNotch * zDelta / 120;
if (nRowsScrolled > 0)
for (int i = 0; i < nRowsScrolled; i++)
AddValue( );
else
for (int i = 0; i > nRowsScrolled; i--)
SubValue( );
}
return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}
/////////////////////////////////////////////////////////////////////////////
void CSpinEdit::OnPaint()
{
CPaintDC dc(this); // device context for painting
OnDraw( &dc );
}
void CSpinEdit::OnDraw(CDC* pDC)
{
CRect interrect, rect;
CRect clipRect;
if (pDC->GetClipBox(&clipRect) == ERROR) return;
DrawFrame( pDC );
if( GetUpperBtnRect( rect ) )
{
if( interrect.IntersectRect( rect, clipRect ) )
{
DrawUpperBtn( pDC );
}
}
if( GetLowerBtnRect( rect ) )
{
if( interrect.IntersectRect( rect, clipRect ) )
{
DrawLowerBtn( pDC );
}
}
if( GetTextRect( rect ) )
{
if( interrect.IntersectRect( rect, clipRect ) )
{
DrawText( pDC );
}
}
}
UINT CSpinEdit::OnGetDlgCode()
{
UINT nCode = DLGC_WANTARROWS | DLGC_WANTCHARS;
if ( !IsCTRLpressed())
nCode |= DLGC_WANTTAB;
return nCode;
}
BOOL CSpinEdit::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (nHitTest == HTCLIENT)
{
SetCursor(::LoadCursor(NULL, IDC_ARROW));
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
// make a focused frame
void CSpinEdit::OnSetFocus(CWnd* pOldWnd)
{
CWnd::OnSetFocus(pOldWnd );
Invalidate();
}
//stop changing value automaticly
void CSpinEdit::OnCaptureChanged(CWnd *pWnd)
{
if (pWnd->GetSafeHwnd() == GetSafeHwnd()) return;
// kill timer if active
if (m_nTimerID != 0)
{
KillTimer(m_nTimerID);
m_nTimerID = 0;
}
}
//if the lower button or upper button was pressed, decrease or increase the value automaticly
void CSpinEdit::OnTimer(UINT nIDEvent)
{
ASSERT(nIDEvent == WM_LBUTTONDOWN);
if (nIDEvent != WM_LBUTTONDOWN) return;
if (m_bUpperBtnPushed) AddValue();
if (m_bLowerBtnPushed) SubValue();
}
// Instant editing of cells when keys are pressed
void CSpinEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (!IsCTRLpressed())
{
ShowEdit( nChar );
}
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
void CSpinEdit::OnLButtonDblClk(UINT nFlags, CPoint point)
{
CRect rect;
GetClientRect(rect);
// If outside client area, return
if ( !rect.PtInRect(point)) return;
//Inclease the current value if the mouse point in the rect of upper button
if( GetUpperBtnRect( rect ) )
{
if( rect.PtInRect( point ) )
{
SetFocus(); // Auto-destroy any InPlaceEdit's
m_bUpperBtnPushed = TRUE;
AddValue();
}
}
//Declease the current value if the mouse point in the rect of lower button
if( GetLowerBtnRect( rect ) )
{
if( rect.PtInRect( point ) )
{
SetFocus(); // Auto-destroy any InPlaceEdit's
m_bLowerBtnPushed = TRUE;
SubValue( );
}
}
//Edit the current value if the mouse point in the rect of Text Area
if( GetTextRect( rect ) )
{
if( rect.PtInRect( point ) )
{
ShowEdit( VK_LBUTTON );
}
}
CWnd::OnLButtonDblClk(nFlags, point);
}
void CSpinEdit::OnLButtonDown(UINT nFlags, CPoint point)
{
SetFocus(); // Auto-destroy any InPlaceEdit's
SetCapture();
CRect rect;
GetClientRect(rect);
// If outside client area, return
if ( !rect.PtInRect(point)) return;
//Inclease the current value if the mouse point in the rect of upper button
if( GetUpperBtnRect( rect ) )
{
if( rect.PtInRect( point ) )
{
m_bUpperBtnPushed = TRUE;
AddValue( );
m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);
}
}
//Declease the current value if the mouse point in the rect of lower button
if( GetLowerBtnRect( rect ) )
{
if( rect.PtInRect( point ) )
{
m_bLowerBtnPushed = TRUE;
SubValue( );
m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);
}
}
//如果鼠标在文本区则直接进入编辑状态
if( GetTextRect( rect ) )
{
if( rect.PtInRect( point ) )
{
ShowEdit( VK_LBUTTON );
}
}
CWnd::OnLButtonDown(nFlags, point);
}
void CSpinEdit::OnLButtonUp(UINT nFlags, CPoint point)
{
CWnd::OnLButtonUp(nFlags, point);
if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
{
ReleaseCapture();
if( m_nTimerID != 0 )
{
KillTimer(m_nTimerID);
m_nTimerID = 0;
}
}
m_bUpperBtnPushed = FALSE;
m_bLowerBtnPushed = FALSE;
RedrawLowerBtn();
RedrawUpperBtn();
RedrawText();
//notify the parent that the spintedit ctrl was clicked
CWnd *pOwner = GetOwner();
if (pOwner && IsWindow(pOwner->m_hWnd))
pOwner->PostMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED),
(LPARAM) GetSafeHwnd());
}
void CSpinEdit::OnEndInplaceEdit(NMHDR* pNMHDR, LRESULT* pResult)
{
SE_NOTIFYMSG *pMsg = (SE_NOTIFYMSG *)pNMHDR;
if (!IsWindow(GetSafeHwnd()))
return;
m_bEditShow = FALSE;
if( strtod(pMsg->szText, NULL ) != m_dbValue )
{
double tempValue;
tempValue = _tcstod(pMsg->szText, NULL );
SetCurValue( tempValue );
}
if( pMsg->lParam == VK_TAB )
{
OnChar( VK_TAB, 1, 0 );
}
*pResult = 0;
}
//add curruent by dtStepNum*dbStep
BOOL CSpinEdit::AddValue( DWORD dwStepNum )
{
double tempValue;
tempValue = m_dbValue + dwStepNum*m_dbStep;
if( ( tempValue < m_dbLower ) || ( tempValue > m_dbUpper ) )
{
tempValue = DoubleModValue( ( tempValue-m_dbLower ), ( m_dbUpper-m_dbLower ), m_bPointPos );
if( m_dbValue == m_dbUpper )
{
m_dbValue = m_dbLower;
}
else
{
m_dbValue = m_dbLower+tempValue;
}
}
else
{
m_dbValue = tempValue;
}
if( IsWindow ( m_hWnd ) )
{
RedrawText();
RedrawUpperBtn();
}
return TRUE;
}
//subtract curruent by dtStepNum*dbStep
BOOL CSpinEdit::SubValue( DWORD dwStepNum )
{
double tempValue;
tempValue = m_dbValue - dwStepNum*m_dbStep;
if( ( tempValue < m_dbLower ) || ( tempValue > m_dbUpper ) )
{
tempValue = DoubleModValue( ( tempValue-m_dbLower ), ( m_dbUpper-m_dbLower ), m_bPointPos );
if( m_dbValue == m_dbLower )
{
m_dbValue = m_dbUpper;
}
else
{
m_dbValue = m_dbUpper+tempValue;
}
}
else
{
m_dbValue = tempValue;
}
if( IsWindow ( m_hWnd ) )
{
RedrawText();
RedrawLowerBtn();
}
return TRUE;
}
// Get the value and extra-text
CString CSpinEdit::GetShowText( BOOL bNeedExtraText )
{
CString strShow;
switch( m_bValueType )
{
case SE_INT:
{
if( bNeedExtraText && m_bNeedExtraText )
strShow.Format( _T("%d%s"), (int)m_dbValue, m_szExtraText );
else
strShow.Format( _T("%d"), (int)m_dbValue );
break;
}
case SE_FLOAT:
case SE_DOUBLE:
{
CString strFormat;
if( bNeedExtraText && m_bNeedExtraText )
{
strFormat.Format( _T("%%.%df%%s"), m_bPointPos ); //strFormat = "%.xf%s"
strShow.Format( strFormat, m_dbValue, m_szExtraText );
}
else
{
strFormat.Format( _T("%%.%df"), m_bPointPos ); //strFormat = "%.xf"
strShow.Format( strFormat, m_dbValue );
}
break;
}
} //OBTAIN strShow
return strShow;
}
// draw value and extra-text
BOOL CSpinEdit::DrawText( CDC* pDC)
{
BOOL bResult = TRUE;
BOOL bMustReleaseDC = FALSE;
if( m_bEditShow ) return bResult;
CRect rect;
if (!GetTextRect( rect)) return FALSE;
if (!pDC)
{
pDC = GetDC();
if (pDC) bMustReleaseDC = TRUE;
}
if (pDC)
{
int nSavedDC = pDC->SaveDC();
pDC->SetBkMode(TRANSPARENT);
CFont ft;
// Create the appropriate font and select into DC
if (!ft.CreateStockObject(DEFAULT_GUI_FONT))
if (!ft.CreatePointFont(80, "MS Sans Serif"))
return FALSE;
pDC->SelectObject(&ft);
rect.DeflateRect(2, 0);
CString strShow = GetShowText( );
CBrush brush(RGB(255,255,255));
pDC->FillRect( rect, &brush );
pDC->DrawText(strShow, -1, rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
pDC->RestoreDC(nSavedDC);
} else
InvalidateRect(rect, TRUE); // Could not get a DC - invalidate it anyway
// and hope that OnPaint manages to get one
if (bMustReleaseDC)
ReleaseDC(pDC);
return bResult;
}
// mainly drawing the black triangle on upper button
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -