/** * @file XTPReBar.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/XTPTypeId.h" #include "Common/XTPCasting.h" #include "Common/XTPFramework.h" #include "Common/XTPSystemHelpers.h" #include "Common/XTPSynchro.h" #include "Common/XTPApplication.h" #include "Common/XTPSingleton.h" #include "Common/XTPGdiObjects.h" #include "Common/XTPVC80Helpers.h" #include "Common/XTPDrawHelpers.h" #include "Common/XTPHookManager.h" #include "Common/XTPColorManager.h" #include "CommandBars/XTPCommandBarsDefines.h" #include "CommandBars/XTPPaintManager.h" #include "CommandBars/XTPCommandBar.h" #include "CommandBars/XTPToolBar.h" #include "CommandBars/XTPDockBar.h" #include "CommandBars/XTPReBar.h" #ifndef VERSION_IE401 # define VERSION_IE401 MAKELONG(72, 4) #endif #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif const LPCTSTR CXTPReBar::m_lpszStateInfoEntry = _T("RebarStateInfo"); const LPCTSTR CXTPReBar::m_lpszStateInfoFormat = _T("wID=%04X, cx=%d, fStyle=%08X"); ////////////////////////////////////////////////////////////////////////// // #if _MSC_VER < 1200 // MFC 6.0 ///////////////////////////////////////////////////////////////////////////// // CXTPReBarBase IMPLEMENT_DYNAMIC(CXTPReBarBase, CControlBar) BEGIN_MESSAGE_MAP(CXTPReBarBase, CControlBar) //{{AFX_MSG_MAP(CXTPReBarBase) ON_WM_NCCREATE() ON_WM_NCCALCSIZE() ON_WM_NCPAINT() ON_NOTIFY_REFLECT(RBN_HEIGHTCHANGE, OnHeightChange) ON_NOTIFY_REFLECT(RBN_ENDDRAG, OnHeightChange) ON_MESSAGE(RB_SHOWBAND, OnShowBand) ON_MESSAGE_VOID(WM_RECALCPARENT, OnRecalcParent) //}}AFX_MSG_MAP END_MESSAGE_MAP() CXTPReBarBase::CXTPReBarBase() { m_cxLeftBorder = m_cxRightBorder = m_cyTopBorder = m_cyBottomBorder = 0; m_iComCtlVersion = XTPSystemVersion()->GetComCtlVersion(); } void CXTPReBarBase::OnRecalcParent() { CFrameWnd* pFrameWnd = GetParentFrame(); _ASSERTE(pFrameWnd != NULL); pFrameWnd->RecalcLayout(); } void CXTPReBarBase::OnHeightChange(NMHDR* /*pNMHDR*/, LRESULT* pResult) { // gives us access to protected member m_bInRecalcLayout. class CFriendFrameWnd : public CFrameWnd { friend class CXTPReBarBase; }; // does the CXTPReBarBase have a frame ? CFriendFrameWnd* pFrameWnd = (CFriendFrameWnd*)GetParentFrame(); if (pFrameWnd != NULL) { // it does -- tell it to recalc its layout if (!pFrameWnd->m_bInRecalcLayout) pFrameWnd->RecalcLayout(); else PostMessage(WM_RECALCPARENT); } *pResult = 0; } LRESULT CXTPReBarBase::OnShowBand(WPARAM wParam, LPARAM) { LRESULT lResult = Default(); if (lResult) { // keep window visible state in sync with band visible state REBARBANDINFO rbBand; rbBand.cbSize = sizeof(rbBand); rbBand.fMask = RBBIM_CHILD | RBBIM_STYLE; VERIFY(DefWindowProc(RB_GETBANDINFO, wParam, (LPARAM)&rbBand)); CControlBar* pBar = DYNAMIC_DOWNCAST(CControlBar, CWnd::FromHandlePermanent(rbBand.hwndChild)); BOOL bWindowVisible; if (pBar != NULL) bWindowVisible = pBar->IsVisible(); else bWindowVisible = (::GetWindowLong(rbBand.hwndChild, GWL_STYLE) & WS_VISIBLE) != 0; BOOL bBandVisible = (rbBand.fStyle & RBBS_HIDDEN) == 0; if (bWindowVisible != bBandVisible) VERIFY(::ShowWindow(rbBand.hwndChild, bBandVisible ? SW_SHOW : SW_HIDE)); } return lResult; } BOOL CXTPReBarBase::_AddBar(CWnd* pBar, REBARBANDINFO* pRBBI) { _ASSERTE(this); _ASSERTE(::IsWindow(m_hWnd)); _ASSERTE(pBar != NULL); _ASSERTE(::IsWindow(pBar->m_hWnd)); pRBBI->cbSize = sizeof(REBARBANDINFO); pRBBI->fMask |= RBBIM_CHILD | RBBIM_CHILDSIZE; pRBBI->hwndChild = pBar->m_hWnd; CSize size; CControlBar* pTemp = DYNAMIC_DOWNCAST(CControlBar, pBar); if (pTemp != NULL) { size = pTemp->CalcFixedLayout(FALSE, m_dwStyle & CBRS_ORIENT_HORZ); } else { CRect rect; pBar->GetWindowRect(&rect); size = rect.Size(); } // WINBUG: COMCTL32.DLL is off by 4 pixels in its sizing logic. Whatever // is specified as the minimum size, the system rebar will allow that band // to be 4 actual pixels smaller! That's why we add 4 to the size here. _ASSERTE(m_iComCtlVersion != -1); pRBBI->cxMinChild = size.cx + (m_iComCtlVersion < VERSION_IE401 ? XTP_DPI_X(4) : 0); pRBBI->cyMinChild = size.cy; BOOL bResult = (BOOL)DefWindowProc(RB_INSERTBAND, (WPARAM)-1, (LPARAM)pRBBI); CFrameWnd* pFrameWnd = GetParentFrame(); if (pFrameWnd != NULL) pFrameWnd->RecalcLayout(); return bResult; } BOOL CXTPReBarBase::AddBar(CWnd* pBar, LPCTSTR pszText, CBitmap* pbmp, DWORD dwStyle) { REBARBANDINFO rbBand; rbBand.fMask = RBBIM_STYLE; rbBand.fStyle = dwStyle; if (pszText != NULL) { rbBand.fMask |= RBBIM_TEXT; rbBand.lpText = const_cast(pszText); } if (pbmp != NULL) { rbBand.fMask |= RBBIM_BACKGROUND; rbBand.hbmBack = (HBITMAP)*pbmp; } return _AddBar(pBar, &rbBand); } BOOL CXTPReBarBase::AddBar(CWnd* pBar, COLORREF clrFore, COLORREF clrBack, LPCTSTR pszText, DWORD dwStyle) { REBARBANDINFO rbBand; rbBand.fMask = RBBIM_STYLE | RBBIM_COLORS; rbBand.fStyle = dwStyle; rbBand.clrFore = clrFore; rbBand.clrBack = clrBack; if (pszText != NULL) { rbBand.fMask |= RBBIM_TEXT; rbBand.lpText = const_cast(pszText); } return _AddBar(pBar, &rbBand); } CSize CXTPReBarBase::CalcFixedLayout(BOOL bStretch, BOOL bHorz) { _ASSERTE(this); _ASSERTE(::IsWindow(m_hWnd)); // the union of the band rectangles is the total bounding rect int nCount = DefWindowProc(RB_GETBANDCOUNT, 0, 0); REBARBANDINFO rbBand; rbBand.cbSize = sizeof(rbBand); int nTemp; // sync up hidden state of the bands for (nTemp = nCount; nTemp--;) { rbBand.fMask = RBBIM_CHILD | RBBIM_STYLE; VERIFY(DefWindowProc(RB_GETBANDINFO, nTemp, (LPARAM)&rbBand)); CControlBar* pBar = DYNAMIC_DOWNCAST(CControlBar, CWnd::FromHandlePermanent(rbBand.hwndChild)); BOOL bWindowVisible; if (pBar != NULL) bWindowVisible = pBar->IsVisible(); else bWindowVisible = (::GetWindowLong(rbBand.hwndChild, GWL_STYLE) & WS_VISIBLE) != 0; BOOL bBandVisible = (rbBand.fStyle & RBBS_HIDDEN) == 0; if (bWindowVisible != bBandVisible) VERIFY(DefWindowProc(RB_SHOWBAND, nTemp, bWindowVisible)); } // determine bounding rect of all visible bands CRect rectBound; rectBound.SetRectEmpty(); for (nTemp = nCount; nTemp--;) { rbBand.fMask = RBBIM_STYLE; VERIFY(DefWindowProc(RB_GETBANDINFO, nTemp, (LPARAM)&rbBand)); if ((rbBand.fStyle & RBBS_HIDDEN) == 0) { CRect rect; VERIFY(DefWindowProc(RB_GETRECT, nTemp, (LPARAM)&rect)); rectBound |= rect; } } // add borders as part of bounding rect if (!rectBound.IsRectEmpty()) { CRect rect; rect.SetRectEmpty(); CalcInsideRect(rect, bHorz); rectBound.right -= rect.Width(); rectBound.bottom -= rect.Height(); } return CSize((bHorz && bStretch) ? 32767 : rectBound.Width(), (!bHorz && bStretch) ? 32767 : rectBound.Height()); } CSize CXTPReBarBase::CalcDynamicLayout(int /*nLength*/, DWORD dwMode) { if (dwMode & LM_HORZDOCK) _ASSERTE(dwMode & LM_HORZ); return CalcFixedLayout(dwMode & LM_STRETCH, dwMode & LM_HORZ); } BOOL CXTPReBarBase::Create(CWnd* pParentWnd, DWORD dwCtrlStyle, DWORD dwStyle, UINT nID) { _ASSERTE(pParentWnd); // must have a parent _ASSERTE(!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC))); // save the style m_dwStyle = (dwStyle & CBRS_ALL); if (nID == AFX_IDW_REBAR) m_dwStyle |= CBRS_HIDE_INPLACE; dwStyle &= ~CBRS_ALL; dwStyle |= CCS_NOPARENTALIGN | CCS_NOMOVEY | CCS_NODIVIDER | CCS_NORESIZE | RBS_VARHEIGHT; dwStyle |= dwCtrlStyle; // initialize common controls INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(icex); icex.dwICC = ICC_COOL_CLASSES; VERIFY(InitCommonControlsEx(&icex)); // create the HWND CRect rect; rect.SetRectEmpty(); if (!CWnd::Create(REBARCLASSNAME, NULL, dwStyle, rect, pParentWnd, nID)) return FALSE; // Note: Parent must resize itself for control bar to be resized return TRUE; } void CXTPReBarBase::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHandler) { UpdateDialogControls(pTarget, bDisableIfNoHandler); } BOOL CXTPReBarBase::OnNcCreate(LPCREATESTRUCT lpCreateStruct) { if (!CControlBar::OnNcCreate(lpCreateStruct)) return FALSE; // if the owner was set before the rebar was created, set it now if (m_hWndOwner != NULL) DefWindowProc(RB_SETPARENT, (WPARAM)m_hWndOwner, 0); return TRUE; } void CXTPReBarBase::OnNcCalcSize(BOOL /*bCalcValidRects*/, NCCALCSIZE_PARAMS* lpncsp) { // calculate border space (will add to top/bottom, subtract from right/bottom) CRect rect; rect.SetRectEmpty(); BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0; CControlBar::CalcInsideRect(rect, bHorz); // adjust non-client area for border space lpncsp->rgrc[0].left += rect.left; lpncsp->rgrc[0].top += rect.top; lpncsp->rgrc[0].right += rect.right; lpncsp->rgrc[0].bottom += rect.bottom; } void CXTPReBarBase::OnNcPaint() { EraseNonClient(); } static HWND _xtpAfxChildWindowFromPoint(HWND hWnd, POINT pt) { _ASSERTE(hWnd != NULL); // check child windows ::ClientToScreen(hWnd, &pt); HWND hWndChild = ::GetWindow(hWnd, GW_CHILD); for (; hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT)) { if ((UINT)(WORD)::GetDlgCtrlID(hWndChild) != (WORD)-1 && (::GetWindowLong(hWndChild, GWL_STYLE) & WS_VISIBLE)) { // see if point hits the child window CRect rect; ::GetWindowRect(hWndChild, rect); if (rect.PtInRect(pt)) return hWndChild; } } return NULL; // not found } INT_PTR CXTPReBarBase::OnToolHitTest(CPoint point, TOOLINFO* pTI) const { _ASSERTE(this); _ASSERTE(::IsWindow(m_hWnd)); HWND hWndChild = _xtpAfxChildWindowFromPoint(m_hWnd, point); CWnd* pWnd = CWnd::FromHandlePermanent(hWndChild); if (pWnd == NULL) return CControlBar::OnToolHitTest(point, pTI); _ASSERTE(pWnd->m_hWnd == hWndChild); return pWnd->OnToolHitTest(point, pTI); } LRESULT CXTPReBarBase::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // special handling for certain messages (forwarding to owner/parent) switch (message) { case WM_POPMESSAGESTRING: case WM_SETMESSAGESTRING: return GetOwner()->SendMessage(message, wParam, lParam); } return CControlBar::WindowProc(message, wParam, lParam); } #endif ///////////////////////////////////////////////////////////////////////////// // CXTPReBar CXTPReBar::CXTPReBar() { } CXTPReBar::~CXTPReBar() { } IMPLEMENT_DYNAMIC(CXTPReBar, CXTPReBarBase) BOOL CXTPReBar::AddToolBar(CXTPToolBar* pToolBar, DWORD dwStyle) { CSize sz = pToolBar->CalcDockingLayout(32000, LM_HORZDOCK | LM_HORZ | LM_COMMIT); pToolBar->MoveWindow(0, 0, sz.cx, sz.cy); if (pToolBar->GetDockBar()) { pToolBar->GetDockBar()->RemoveCommandBar(pToolBar, -1); pToolBar->SetFlags(0, xtpFlagAlignAny | xtpFlagFloating); pToolBar->ModifyBarStyle(CBRS_GRIPPER, 0); } if (!AddBar(pToolBar, 0, 0, dwStyle)) return FALSE; UINT nID = XTPToUInt(pToolBar->GetBarID()); int nCount = static_cast(DefWindowProc(RB_GETBANDCOUNT, 0, 0)); REBARBANDINFO rbBand; rbBand.cbSize = sizeof(rbBand); rbBand.fMask = RBBIM_ID; rbBand.wID = nID; VERIFY(DefWindowProc(RB_SETBANDINFO, XTPToWPARAM(nCount - 1), XTPToLPARAM(&rbBand))); return TRUE; } void CXTPReBar::DeleteToolBar(CXTPToolBar* pToolBar) { int nBand = FindBand(pToolBar); if (nBand != -1) { DefWindowProc(RB_DELETEBAND, XTPToWPARAM(nBand), 0); } } CXTPToolBar* CXTPReBar::GetToolBar(int nBand) { REBARBANDINFO rbBand; rbBand.cbSize = sizeof(rbBand); rbBand.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE; VERIFY(DefWindowProc(RB_GETBANDINFO, XTPToWPARAM(nBand), XTPToLPARAM(&rbBand))); return DYNAMIC_DOWNCAST(CXTPToolBar, CWnd::FromHandlePermanent(rbBand.hwndChild)); } int CXTPReBar::FindBand(CXTPToolBar* pToolBar) { int nCount = (int)DefWindowProc(RB_GETBANDCOUNT, 0, 0); // sync up hidden state of the bands for (int nTemp = nCount; nTemp--;) { if (GetToolBar(nTemp) == pToolBar) return nTemp; } return -1; } #include "Common/Base/Diagnostic/XTPBeginAfxMap.h" BEGIN_MESSAGE_MAP(CXTPReBar, CXTPReBarBase) //{{AFX_MSG_MAP(CXTPReBar) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP ON_NOTIFY_REFLECT(RBN_CHILDSIZE, OnChildSize) ON_WM_PAINT() ON_WM_ERASEBKGND() END_MESSAGE_MAP() #include "Common/Base/Diagnostic/XTPEndAfxMap.h" void CXTPReBar::OnPaint() { // background is already filled in gray CPaintDC dc(this); // Get the client rect. CXTPClientRect rectClient(this); // Paint to a memory device context to help // eliminate screen flicker. CXTPBufferDC memDC(dc, rectClient); memDC.FillSolidRect(rectClient, GetSysColor(COLOR_3DFACE)); // Now let the window do its default painting... CWnd::DefWindowProc(WM_ERASEBKGND, (WPARAM)memDC.m_hDC, 0); CWnd::DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0); } BOOL CXTPReBar::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; } CSize CXTPReBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz) { CSize szResult = CXTPReBarBase::CalcFixedLayout(bStretch, bHorz); Invalidate(FALSE); int nCount = (int)DefWindowProc(RB_GETBANDCOUNT, 0, 0); // sync up hidden state of the bands for (int nTemp = nCount; nTemp--;) { CXTPToolBar* pToolBar = GetToolBar(nTemp); if (pToolBar) { CXTPWindowRect rc(pToolBar); CSize szMin = pToolBar->CalcDockingLayout(32000, LM_HORZDOCK | LM_HORZ); CSize sz = pToolBar->CalcDockingLayout( rc.Width(), DWORD(LM_HORZDOCK | LM_HORZ | LM_COMMIT | (pToolBar->GetFlags() & xtpFlagStretched ? LM_STRETCH : LM_HIDEWRAP))); REBARBANDINFO rbbi; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; rbbi.cxIdeal = XTPToUIntChecked(sz.cx); rbbi.cxMinChild = XTPToUIntChecked(szMin.cx); rbbi.cyMinChild = XTPToUIntChecked(sz.cy); VERIFY(DefWindowProc(RB_SETBANDINFO, XTPToWPARAM(nTemp), XTPToLPARAM(&rbbi))); pToolBar->Redraw(); } } return szResult; } void CXTPReBar::OnChildSize(NMHDR* pNotifyStruct, LRESULT* result) { OnHeightChange(pNotifyStruct, result); } void CXTPReBar::LoadState(LPCTSTR lpszProfileName) { // This function restores index, width and style from the registry for // each band in the rebar. CString strValue = AfxGetApp()->GetProfileString(lpszProfileName, m_lpszStateInfoEntry); if (!strValue.IsEmpty()) { REBARBANDINFO rbbi; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_STYLE | RBBIM_SIZE | RBBIM_ID; int nCount = static_cast(DefWindowProc(RB_GETBANDCOUNT, 0, 0)); for (int nBand = 0; nBand < nCount; nBand++) { CString strBandState; VERIFY(AfxExtractSubString(strBandState, strValue, nBand, _T('\n'))); UINT nID, cx, nStyle; #pragma warning(push) #pragma warning( \ disable : 4774) // 'swscanf_s' : format string expected in argument 2 is not a string literal int nResult = SCANF_S(strBandState, m_lpszStateInfoFormat, &nID, &cx, &nStyle); #pragma warning(pop) _ASSERTE(nResult == 3); UNUSED(nResult); DefWindowProc(RB_MOVEBAND, XTPToWPARAM(DefWindowProc(RB_IDTOINDEX, nID, 0)), nBand); VERIFY(DefWindowProc(RB_GETBANDINFO, XTPToWPARAM(nBand), XTPToLPARAM(&rbbi))); rbbi.cx = cx; rbbi.fStyle = (rbbi.fStyle & ~(RBBS_HIDDEN | RBBS_BREAK)) | nStyle; VERIFY(DefWindowProc(RB_SETBANDINFO, XTPToWPARAM(nBand), XTPToLPARAM(&rbbi))); CXTPToolBar* pToolBar = GetToolBar(nBand); if (pToolBar && (nStyle & RBBS_HIDDEN)) { pToolBar->SetVisible(FALSE); } } } } void CXTPReBar::SaveState(LPCTSTR lpszProfileName) { // This function saves index, width and style in the registry for each // band in the rebar, so that it could be possible to restore all these // settings when the user runs the program next time. CString strValue; REBARBANDINFO rbbi; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_STYLE | RBBIM_SIZE | RBBIM_ID; int nCount = static_cast(DefWindowProc(RB_GETBANDCOUNT, 0, 0)); for (int nBand = 0; nBand < nCount; nBand++) { VERIFY(DefWindowProc(RB_GETBANDINFO, XTPToWPARAM(nBand), XTPToLPARAM(&rbbi))); CString strBandState; strBandState.Format(m_lpszStateInfoFormat, rbbi.wID, rbbi.cx, rbbi.fStyle); strValue += (strValue.IsEmpty() ? _T("") : _T("\n")) + strBandState; } VERIFY(AfxGetApp()->WriteProfileString(lpszProfileName, m_lpszStateInfoEntry, strValue)); }