/** * @file XTPScrollBarContainer.cpp * * @copyright * (c) 1998-2025 Codejock Software, All Rights Reserved. * * This source file is the property of Codejock Software and must not be * redistributed by any means without the explicit written permission of * Codejock Software. * * The use of this source code is governed by the terms and conditions specified * in the Toolkit Pro license agreement. Codejock Software grants you, as a * single software developer, the limited right to use this software on one * computer only. * * Contact Information: * support@codejock.com * http://www.codejock.com * */ #include "StdAfx.h" #include "Common/XTPCasting.h" #include "Common/ScrollBar/XTPScrollBase.h" #include "Common/ScrollBar/XTPScrollBarCtrl.h" #include "Common/ScrollBar/XTPScrollBarContainer.h" #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define ASSERT_SCROLLBAR_ID(nID) \ _ASSERTE((AFX_IDW_HSCROLL_FIRST <= (nID) && (nID) < (AFX_IDW_HSCROLL_FIRST + 0x10)) \ || (AFX_IDW_VSCROLL_FIRST <= (nID) && (nID) < (AFX_IDW_VSCROLL_FIRST + 0x10)) \ || AFX_IDW_SIZE_BOX == (nID)) // CXTPScrollBarContainerImpl::STDSCROLLBAR // CXTPScrollBarContainerImpl::STDSCROLLBAR::STDSCROLLBAR() : pCtrl(NULL) , bVisible(FALSE) { } CXTPScrollBarContainerImpl::STDSCROLLBAR::~STDSCROLLBAR() { _ASSERTE(NULL == pCtrl); } CXTPScrollBarCtrl* CXTPScrollBarContainerImpl::STDSCROLLBAR::operator->() throw() { _ASSERTE(NULL != pCtrl); return pCtrl; } const CXTPScrollBarCtrl* CXTPScrollBarContainerImpl::STDSCROLLBAR::operator->() const throw() { _ASSERTE(NULL != pCtrl); return pCtrl; } /////////////////////////////////////////////////////////////////////////////// // CXTPScrollBarContainerImpl CXTPScrollBarContainerImpl::CXTPScrollBarContainerImpl() : m_bCreated(FALSE) , m_pContainer(NULL) , m_nTheme(xtpScrollBarThemeWindowsDefault) , m_pScrollBarClass(NULL) { } CXTPScrollBarContainerImpl::~CXTPScrollBarContainerImpl() { while (!m_ScrollBars.IsEmpty()) { CXTPScrollBarCtrl* pScrollBar = m_ScrollBars.RemoveHead(); if (::IsWindow(pScrollBar->GetSafeHwnd())) { pScrollBar->DestroyWindow(); } delete pScrollBar; } DestroyStdScrollBar(SbGripper); DestroyStdScrollBar(SbHorz); DestroyStdScrollBar(SbVert); } inline CXTPScrollBarContainerImpl::StdScrollBarId AFX_CDECL CXTPScrollBarContainerImpl::SBtoSBId(int nBar) { StdScrollBarId sbId = SbInvalid; switch (nBar) { case SB_VERT: sbId = SbVert; break; case SB_HORZ: sbId = SbHorz; break; } return sbId; } inline UINT AFX_CDECL CXTPScrollBarContainerImpl::SBIdToSBWs(StdScrollBarId nBar) { _ASSERTE(SbInvalid != nBar); static const UINT sbWStyles[] = { WS_HSCROLL, WS_VSCROLL, 0 }; return sbWStyles[nBar]; } inline int AFX_CDECL CXTPScrollBarContainerImpl::SBIdToSB(StdScrollBarId nBar) { _ASSERTE(SbInvalid != nBar && SbGripper != nBar); static const int sbBars[] = { SB_HORZ, SB_VERT }; return sbBars[nBar]; } void CXTPScrollBarContainerImpl::SetContainer(CWnd* pContainer) { m_pContainer = pContainer; } void CXTPScrollBarContainerImpl::SetScrollBarClass(CRuntimeClass* pClass /*= NULL*/) { _ASSERTE("Scroll bar class must be derived from CXTPScrollBarCtrl" && (NULL != pClass ? pClass->IsDerivedFrom(RUNTIME_CLASS(CXTPScrollBarCtrl)) : TRUE)); m_pScrollBarClass = pClass; } CScrollBar* CXTPScrollBarContainerImpl::GetScrollBarCtrl(int nBar) { _ASSERTE(SB_VERT == nBar || SB_HORZ == nBar); CScrollBar* pScrollBar = NULL; StdScrollBarId nBarId = SBtoSBId(nBar); if (HasStdBar(nBarId)) { pScrollBar = m_StdScrollBar[nBar].pCtrl; } //// Make sure the scroll bar requested is created //// because sometimes this method gets invoked before OnCreate //// but when parent is already created. // CXTPScrollBarContainerImpl* pThis = const_cast(this); // CScrollBar* pScrollBar = pThis->CreateStdScrollBar(SBtoSBId(nBar)); return pScrollBar; } void CXTPScrollBarContainerImpl::SetScrollBarTheme(XTPScrollBarTheme theme) { m_nTheme = theme; // Apply theme to standard scroll bars. static const StdScrollBarId nBarIds[] = { SbVert, SbHorz, SbGripper }; for (int i = 0; i < _countof(nBarIds); ++i) { if (HasStdBar(nBarIds[i])) { m_StdScrollBar[nBarIds[i]]->SetTheme(m_nTheme); } } // Apply theme to user scroll bars. POSITION nScrollBarPosition = m_ScrollBars.GetHeadPosition(); while (NULL != nScrollBarPosition) { CXTPScrollBarCtrl* pScrollBar = m_ScrollBars.GetNext(nScrollBarPosition); if (::IsWindow(pScrollBar->GetSafeHwnd())) { pScrollBar->SetTheme(m_nTheme); } } } void CXTPScrollBarContainerImpl::OnWindowProc(UINT message, WPARAM wParam, LPARAM lParam, LRESULT& lResult) { switch (message) { case WM_CREATE: if (-1 != lResult) { LPCREATESTRUCT lpCreateStruct = reinterpret_cast(lParam); OnCreate(lpCreateStruct); } break; case WM_NCCALCSIZE: { LPNCCALCSIZE_PARAMS lpncsp = reinterpret_cast(lParam); OnNcCalcSize((BOOL)wParam, lpncsp); } break; case WM_WINDOWPOSCHANGED: case WM_WINDOWPOSCHANGING: { WINDOWPOS* lpWinPos = reinterpret_cast(lParam); OnWindowPosChange(lpWinPos); } break; case WM_STYLECHANGED: { LPSTYLESTRUCT pStyleStruct = reinterpret_cast(lParam); OnStyleChanged((long)wParam, pStyleStruct); } break; } } void CXTPScrollBarContainerImpl::OnCreate(LPCREATESTRUCT lpCreateStruct) { m_bCreated = TRUE; SyncStdScrollBarStates(); UNREFERENCED_PARAMETER(lpCreateStruct); } void CXTPScrollBarContainerImpl::OnWindowPosChange(WINDOWPOS* lpWndPos) { RepositionStdScrollBars(); UNREFERENCED_PARAMETER(lpWndPos); } void CXTPScrollBarContainerImpl::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp) { if (HasStdBar(SbVert)) { int cxVScroll = ::GetSystemMetrics(SM_CXVSCROLL); if (m_pContainer->GetExStyle() & WS_EX_LAYOUTRTL) { lpncsp->rgrc->left -= cxVScroll; lpncsp->rgrc->left -= GetContainerBorder().cx; } else { lpncsp->rgrc->right += cxVScroll; lpncsp->rgrc->right += GetContainerBorder().cx; } } if (HasStdBar(SbHorz)) { int cyHScroll = ::GetSystemMetrics(SM_CXHSCROLL); lpncsp->rgrc->bottom += cyHScroll; } UNREFERENCED_PARAMETER(bCalcValidRects); } void CXTPScrollBarContainerImpl::OnStyleChanged(LONG nType, LPSTYLESTRUCT pStyleStruct) { _ASSERTE(NULL != pStyleStruct); if (GWL_STYLE == nType && m_bCreated && 0 != ((pStyleStruct->styleNew ^ pStyleStruct->styleOld) & (WS_HSCROLL | WS_VSCROLL))) { SyncStdScrollBarStates(); } } BOOL CXTPScrollBarContainerImpl::HasStdBar(StdScrollBarId nBar) const { _ASSERTE(SbInvalid != nBar); BOOL bHas = (NULL != m_StdScrollBar[nBar].pCtrl && ::IsWindow(m_StdScrollBar[nBar]->m_hWnd)); return bHas; } CScrollBar* CXTPScrollBarContainerImpl::CreateStdScrollBar(StdScrollBarId nBar) { _ASSERTE(NULL != m_pContainer); _ASSERTE(m_bCreated); CScrollBar* pScrollBar = NULL; if (SbInvalid != nBar && NULL == m_StdScrollBar[nBar].pCtrl) { CRuntimeClass* pScrollBarClass = (NULL != m_pScrollBarClass ? m_pScrollBarClass : RUNTIME_CLASS(CXTPScrollBarCtrl)); m_StdScrollBar[nBar].pCtrl = static_cast( pScrollBarClass->CreateObject()); if (NULL != m_StdScrollBar[nBar].pCtrl) { STDSCROLLBAR& stdBarInfo = m_StdScrollBar[nBar]; static const UINT sbStyles[] = { SBS_HORZ, SBS_VERT, SBS_SIZEGRIP }; BOOL bCreated = stdBarInfo->Create(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | sbStyles[nBar], CRect(0, 0, 0, 0), m_pContainer, 0); if (bCreated) { stdBarInfo->SetTheme(m_nTheme); stdBarInfo.bVisible = TRUE; } pScrollBar = stdBarInfo.pCtrl; } } return pScrollBar; } void CXTPScrollBarContainerImpl::DestroyStdScrollBar(StdScrollBarId nBar) { if (SbInvalid != nBar) { if (NULL != m_StdScrollBar[nBar].pCtrl) { if (HasStdBar(nBar)) { m_StdScrollBar[nBar]->DestroyWindow(); } delete m_StdScrollBar[nBar].pCtrl; m_StdScrollBar[nBar].pCtrl = NULL; } m_StdScrollBar[nBar].bVisible = FALSE; } } void CXTPScrollBarContainerImpl::RepositionStdScrollBars(const RECT* lpRect /*= NULL*/) { _ASSERTE(NULL != m_pContainer); CRect rc; if (NULL != lpRect) { rc = *lpRect; } else { m_pContainer->GetClientRect(&rc); } int cyHScroll = ::GetSystemMetrics(SM_CYHSCROLL); int cxVScroll = ::GetSystemMetrics(SM_CXVSCROLL); int cx = rc.Width(); int cy = rc.Height(); BOOL bHScroll = HasStdBar(SbHorz); BOOL bVScroll = HasStdBar(SbVert); if (bHScroll) { m_StdScrollBar[SbHorz]->SetWindowPos( &CWnd::wndTop, rc.left, cy - cyHScroll, (bVScroll && m_StdScrollBar[SbVert].bVisible ? cx - cxVScroll : cx), cyHScroll, XTPToUInt(m_StdScrollBar[SbHorz].bVisible ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); } if (bVScroll) { m_StdScrollBar[SbVert]->SetWindowPos( &CWnd::wndTop, cx - cxVScroll - GetContainerBorder().cx, rc.top, cxVScroll, cy - (bHScroll && m_StdScrollBar[SbHorz].bVisible ? cyHScroll : 0), XTPToUInt(m_StdScrollBar[SbVert].bVisible ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); } if (HasStdBar(SbGripper)) { m_StdScrollBar[SbGripper].bVisible = bVScroll && bHScroll && m_StdScrollBar[SbHorz].bVisible && m_StdScrollBar[SbVert].bVisible; m_StdScrollBar[SbGripper]->SetWindowPos( &CWnd::wndTop, cx - cxVScroll, cy - cyHScroll, cxVScroll, cyHScroll, XTPToUInt(m_StdScrollBar[SbGripper].bVisible ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); } } BOOL CXTPScrollBarContainerImpl::CreateScrollBarCtrl(DWORD dwStyle, UINT nID) { ASSERT_SCROLLBAR_ID(nID); _ASSERTE(NULL != m_pContainer); _ASSERTE(m_bCreated); BOOL result = FALSE; DestroyScrollBarCtrl(nID); if (::IsWindow(m_pContainer->GetSafeHwnd())) { CXTPScrollBarCtrl* pScrollBar = new CXTPScrollBarCtrl(); if (NULL != pScrollBar) { if (pScrollBar->Create(WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | dwStyle, CRect(0, 0, 0, 0), m_pContainer, nID)) { m_ScrollBars.AddTail(pScrollBar); pScrollBar->SetTheme(m_nTheme); result = TRUE; } else { delete pScrollBar; } } } return result; } void CXTPScrollBarContainerImpl::DestroyScrollBarCtrl(UINT nID) { ASSERT_SCROLLBAR_ID(nID); _ASSERTE(NULL != m_pContainer); CWnd* pItem = m_pContainer->GetDlgItem(XTPToInt(nID)); if (NULL != pItem) { ASSERT_KINDOF(CXTPScrollBarCtrl, pItem); CXTPScrollBarCtrl* pScrollBar = static_cast(pItem); POSITION pos = m_ScrollBars.Find(pScrollBar); if (NULL != pos) { m_ScrollBars.RemoveAt(pos); } if (::IsWindow(pScrollBar->GetSafeHwnd())) { pScrollBar->DestroyWindow(); } delete pScrollBar; } } void CXTPScrollBarContainerImpl::ShowScrollBar(UINT nBar, BOOL bShow) { _ASSERTE(SB_HORZ == nBar || SB_VERT == nBar); _ASSERTE(NULL != m_pContainer); if (m_bCreated) { if (SB_HORZ == nBar || SB_BOTH == nBar) { ShowScrollBarInternal(SbHorz, bShow); } if (SB_VERT == nBar || SB_BOTH == nBar) { ShowScrollBarInternal(SbVert, bShow); } BOOL bShowGripper = HasStdBar(SbVert) && HasStdBar(SbHorz); ShowScrollBarInternal(SbGripper, bShowGripper); m_pContainer->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_FRAMECHANGED); RepositionStdScrollBars(); } } void CXTPScrollBarContainerImpl::ShowScrollBarInternal(StdScrollBarId nBar, BOOL bShow) { _ASSERTE(NULL != m_pContainer); if (SbInvalid != nBar) { if (SbGripper != nBar) { m_pContainer->ShowScrollBar(XTPToUInt(SBIdToSB(nBar)), bShow); } if (bShow) { CreateStdScrollBar(nBar); } else { DestroyStdScrollBar(nBar); } } } BOOL CXTPScrollBarContainerImpl::EnableScrollBar(int nSBFlags, UINT nArrowFlags /*= ESB_ENABLE_BOTH*/) { BOOL bEnable = (ESB_ENABLE_BOTH == nArrowFlags); if ((SB_VERT == nSBFlags || SB_BOTH == nSBFlags) && HasStdBar(SbVert)) { m_StdScrollBar[SbVert]->EnableWindow(bEnable); } if ((SB_HORZ == nSBFlags || SB_BOTH == nSBFlags) && HasStdBar(SbHorz)) { m_StdScrollBar[SbHorz]->EnableWindow(bEnable); } return TRUE; } void CXTPScrollBarContainerImpl::SyncStdScrollBarStates() { _ASSERTE(NULL != m_pContainer); UINT sbStyle = m_pContainer->GetStyle() & (WS_HSCROLL | WS_VSCROLL); ShowScrollBar(SB_HORZ, 0 != (sbStyle & WS_HSCROLL)); ShowScrollBar(SB_VERT, 0 != (sbStyle & WS_VSCROLL)); } void CXTPScrollBarContainerImpl::RefreshMetrics() { static const StdScrollBarId nBarIds[] = { SbHorz, SbVert, SbGripper }; for (int i = 0; i < _countof(nBarIds); ++i) { if (HasStdBar(nBarIds[i])) { m_StdScrollBar[nBarIds[i]]->RefreshMetrics(); } } POSITION nScrollBarPosition = m_ScrollBars.GetHeadPosition(); while (NULL != nScrollBarPosition) { CXTPScrollBarCtrl* pScrollBar = m_ScrollBars.GetNext(nScrollBarPosition); if (::IsWindow(pScrollBar->GetSafeHwnd())) { pScrollBar->RefreshMetrics(); } } } void CXTPScrollBarContainerImpl::OptimizeChildrenClipping() { if (XTP_ASSERT_CHECK(NULL != m_pContainer && ::IsWindow(m_pContainer->m_hWnd))) { DWORD dwClipStyles = XTPToDWORD(WS_CLIPCHILDREN | WS_CLIPSIBLINGS); if (dwClipStyles != (m_pContainer->GetStyle() & dwClipStyles)) { m_pContainer->ModifyStyle(0, dwClipStyles); } CWnd* pChild = m_pContainer->GetWindow(GW_CHILD); while (NULL != pChild) { if (dwClipStyles != (pChild->GetStyle() & dwClipStyles)) { pChild->ModifyStyle(0, dwClipStyles); } pChild = pChild->GetWindow(GW_HWNDNEXT); } } } CSize CXTPScrollBarContainerImpl::GetContainerBorder() { int cxBorder = 0; int cyBorder = 0; if (m_pContainer) { cxBorder += m_pContainer->GetStyle() & WS_BORDER ? ::GetSystemMetrics(SM_CXBORDER) : 0; cyBorder += m_pContainer->GetStyle() & WS_BORDER ? ::GetSystemMetrics(SM_CYBORDER) : 0; cxBorder += m_pContainer->GetExStyle() & WS_EX_CLIENTEDGE ? ::GetSystemMetrics(SM_CXEDGE) : 0; cyBorder += m_pContainer->GetExStyle() & WS_EX_CLIENTEDGE ? ::GetSystemMetrics(SM_CYEDGE) : 0; } return CSize(cxBorder, cyBorder); }