// XTPDockBar.cpp : implementation of the CXTPDockBar class. // // This file is a part of the XTREME COMMANDBARS MFC class library. // (c)1998-2012 Codejock Software, All Rights Reserved. // // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN // CONSENT OF CODEJOCK SOFTWARE. // // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A // SINGLE COMPUTER. // // CONTACT INFORMATION: // support@codejock.com // http://www.codejock.com // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Common/XTPDrawHelpers.h" #include "Common/XTPSystemHelpers.h" #include "Common/XTPHookManager.h" #include "Common/XTPColorManager.h" #include "XTPCommandBarsDefines.h" #include "XTPCommandBar.h" #include "XTPCommandBars.h" #include "XTPCommandBarsFrameHook.h" #include "XTPDockBar.h" #include "XTPToolBar.h" #include "XTPMenuBar.h" #include "XTPCommandBars.h" #include "XTPDockContext.h" #include "XTPPaintManager.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // CXTPDockBar IMPLEMENT_DYNCREATE(CXTPDockBar, CWnd) CXTPDockBar::CXTPDockBar() { m_arrBars.Add(NULL); m_dwStyle = 0; m_pCommandBars = NULL; CXTPDrawHelpers::RegisterWndClass(0, _T("XTPDockBar"), 0); } CXTPDockBar::~CXTPDockBar() { } BEGIN_MESSAGE_MAP(CXTPDockBar, CWnd) ON_MESSAGE(WM_SIZEPARENT, OnSizeParent) ON_WM_PAINT() ON_MESSAGE(WM_PRINTCLIENT, OnPrintClient) ON_WM_ERASEBKGND() ON_WM_RBUTTONDOWN() ON_WM_NCHITTEST_EX() ON_WM_SIZE() END_MESSAGE_MAP() // CXTPDockBar message handlers LRESULT CXTPDockBar::OnNcHitTest(CPoint point) { if (GetPosition() == xtpBarTop && m_pCommandBars->GetMenuBar() != NULL && m_pCommandBars->GetMenuBar()->IsRibbonBar() && FindBar(m_pCommandBars->GetMenuBar()) != -1) { return HTTRANSPARENT; } return (LRESULT)CWnd::OnNcHitTest(point); } BOOL CXTPDockBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID) { ASSERT(pParentWnd != NULL); // save the xtp m_dwStyle = (dwStyle & CBRS_ALL); XTPBarPosition barPosition = GetPosition(); CString strWindowName = barPosition == xtpBarTop ? _T("xtpBarTop") : barPosition == xtpBarLeft ? _T("xtpBarLeft") : barPosition == xtpBarBottom ? _T("xtpBarBottom") : _T("xtpBarRight"); return CWnd::Create(_T("XTPDockBar"), strWindowName, dwStyle, CXTPEmptyRect(), pParentWnd, nID); } BOOL CXTPDockBar::PreCreateWindow(CREATESTRUCT& cs) { if (!CWnd::PreCreateWindow(cs)) return FALSE; // force clipsliblings (otherwise will cause repaint problems) cs.style |= WS_CLIPSIBLINGS; return TRUE; } CSize CXTPDockBar::CalcFixedLayout(BOOL /*bStretch*/, BOOL /*bHorz*/, AFX_SIZEPARENTPARAMS* /*lpLayout*/) { return 0; } struct CXTPDockBar::DOCK_INFO { DOCK_INFO(CXTPToolBar* p = 0, CRect rc = 0, int n = 0) { pBar = p; rcMRUPos = rcBar = rc; nIndex = n; nMinWidth = 0; nTotlaMinWidth = 0; } CXTPToolBar* pBar; CRect rcBar; CRect rcMRUPos; int nIndex; int nMinWidth; int nTotlaMinWidth; }; class CXTPDockBar::CDockInfoArray : public CArray { public: void Sort() { qsort(GetData(), GetSize(), sizeof(DOCK_INFO), CompareFunc); } static int _cdecl CompareFunc(const void* elem1, const void* elem2) { return ((DOCK_INFO*)elem1)->rcBar.left < ((DOCK_INFO*)elem2)->rcBar.left ? -1 : 1; } void _swap(LONG& A, LONG& B) { int C = B; B = A; A = C; } void InvertRects() { for (int nPos = 0; nPos < GetSize(); nPos++) { CRect& rect = ElementAt(nPos).rcBar; _swap(rect.left, rect.top); _swap(rect.right, rect.bottom); } } }; int CXTPDockBar::_GetMode(BOOL bHorz, CXTPToolBar* pBar) { DWORD dwMode = bHorz ? LM_HORZ | LM_HORZDOCK : LM_VERTDOCK; if (pBar->GetFlags() & xtpFlagStretched) dwMode |= LM_STRETCH; if ((!m_pCommandBars->IsCustomizeMode() || m_pCommandBars->IsQuickCustomizeMode()) && pBar->GetFlags() & xtpFlagHideWrap) return dwMode | LM_HIDEWRAP; return dwMode; } int CXTPDockBar::_AdjustRow(CToolBarArray& arrRow, CPoint pt, int nLength, BOOL bHorz, AFX_SIZEPARENTPARAMS* lpLayout, int& nRemove) { CDockInfoArray arrInfo; int nPos; // Step 1. Getting maximum available size; for (nPos = 0; nPos < arrRow.GetSize(); nPos++) { CXTPToolBar* pBar = arrRow[nPos]; CSize sizeBar = pBar->CalcDockingLayout(nLength, _GetMode(bHorz, pBar)); CPoint p = bHorz ? CPoint(pBar->m_pDockContext->m_rectMRUDockPos.left, pt.y) : CPoint(pt.x, pBar->m_pDockContext->m_rectMRUDockPos.top); DOCK_INFO dockInfo(pBar, CRect(p, sizeBar), nPos); arrInfo.Add(dockInfo); } ASSERT(arrInfo.GetSize() == arrRow.GetSize()); if (!bHorz) arrInfo.InvertRects(); arrInfo.Sort(); // Step 2. if Total length is more than available, fill empty area. int nIndex = -1; int nLen = 0; BOOL bMove = TRUE; for (nPos = 0; nPos < arrInfo.GetSize(); nPos++) { CRect& rect = arrInfo[nPos].rcBar; bMove = (rect.left < nLen && (nIndex < arrInfo[nPos].nIndex || bMove)); if (bMove) rect.OffsetRect(nLen - rect.left, 0); nLen = rect.right; nIndex = arrInfo[nPos].nIndex; } nLen = nLength; nIndex = -1; bMove = TRUE; for (nPos = (int)arrInfo.GetSize() - 1; nPos >= 0; nPos--) { CRect& rect = arrInfo[nPos].rcBar; bMove = (rect.right - nLen > 0 && (nIndex < arrInfo[nPos].nIndex || bMove)); if (bMove) rect.OffsetRect(nLen - rect.right, 0); nLen = rect.left; nIndex = arrInfo[nPos].nIndex; } nLen = 0; for (nPos = 0; nPos < arrInfo.GetSize(); nPos++) { CRect& rect = arrInfo[nPos].rcBar; if (rect.left < nLen) rect.OffsetRect(nLen - rect.left, 0); nLen = rect.left + rect.Width(); } // Step 3. if Total length is more than available, make it expandable. if (nLen > nLength) { int nSum = 0; for (nPos = 0; nPos < arrInfo.GetSize(); nPos++) { CXTPToolBar* pBar = arrInfo[nPos].pBar; if (!(_GetMode(bHorz, pBar) & LM_HIDEWRAP)) arrInfo[nPos].nMinWidth = nLength; else { CSize sz = pBar->CalcDockingLayout(1, _GetMode(bHorz, pBar)); arrInfo[nPos].nMinWidth = bHorz ? sz.cx : sz.cy; } arrInfo[nPos].nTotlaMinWidth = nSum; nSum += arrInfo[nPos].nMinWidth; } nLen = nLength; for (nPos = (int)arrInfo.GetSize() - 1; nPos >= 0; nPos--) { CRect& rect = arrInfo[nPos].rcBar; int nLeft = bHorz ? arrInfo[nPos].rcMRUPos.left : arrInfo[nPos].rcMRUPos.top; if (nLeft > rect.left) nLeft = rect.left; if (nLeft < nLen - rect.Width()) nLeft = nLen - rect.Width(); if (nLeft < arrInfo[nPos].nTotlaMinWidth) nLeft = arrInfo[nPos].nTotlaMinWidth; if (nLen - nLeft < arrInfo[nPos].nMinWidth) nLeft = nLen - arrInfo[nPos].nMinWidth; if ((nLen - nLeft < arrInfo[nPos].nMinWidth || nLeft < arrInfo[nPos].nTotlaMinWidth) && arrInfo.GetSize() != 1) { nRemove = arrInfo[arrInfo.GetSize() - 1].nIndex; return -1; } rect.right = nLen; nLen = rect.left = max(0, nLeft); } } if (!bHorz) arrInfo.InvertRects(); int nWidth = 0; // Calculate total width for (nPos = 0; nPos < arrInfo.GetSize(); nPos++) { CXTPToolBar* pBar = arrInfo[nPos].pBar; CRect& rect = arrInfo[nPos].rcBar; CSize sizeBar = pBar->CalcDockingLayout(bHorz ? rect.Width() : rect.Height(), _GetMode(bHorz, pBar)); nWidth = max(nWidth, bHorz ? sizeBar.cy : sizeBar.cx); } if (lpLayout->hDWP == 0) return nWidth; // Step 4. Move it. for (nPos = 0; nPos < arrInfo.GetSize(); nPos++) { CXTPToolBar* pBar = arrInfo[nPos].pBar; CRect& rect = arrInfo[nPos].rcBar; int nMode = _GetMode(bHorz, pBar) | LM_COMMIT; if (pBar->GetFlags() & xtpFlagStretchedShared) { if (bHorz) { rect.right = nPos == arrInfo.GetSize() - 1 ? nLength : arrInfo[nPos + 1].rcBar.left; rect.left = nPos == 0 ? 0 : min(arrInfo[nPos - 1].rcBar.right, rect.left); } else { rect.bottom = nPos == arrInfo.GetSize() - 1 ? nLength : arrInfo[nPos + 1].rcBar.top; rect.top = nPos == 0 ? 0 : min(arrInfo[nPos - 1].rcBar.bottom, rect.top); } nMode |= LM_STRETCH; } CSize sz = pBar->CalcDockingLayout(bHorz ? rect.Width() : rect.Height(), nMode, nWidth); rect = CRect(rect.TopLeft(), sz); pBar->m_pDockContext->m_uMRUDockPosition = GetPosition(); CXTPWindowRect rectOld(pBar); ScreenToClient(&rectOld); if (!::EqualRect(rectOld, rect)) { AfxRepositionWindow(lpLayout, pBar->m_hWnd, &rect); pBar->Redraw(); } } return nWidth; } int CXTPDockBar::AdjustRow(CToolBarArray& arrRow, CPoint pt, int nLength, BOOL bHorz, AFX_SIZEPARENTPARAMS* lpLayout) { int nTotalWidth = 0; CToolBarArray arrNext; for (;;) { int nRemove = -1; ASSERT(arrRow.GetSize() > 0); int nWidth = _AdjustRow(arrRow, pt, nLength, bHorz, lpLayout, nRemove); if (nWidth == -1) { ASSERT(nRemove != -1); arrNext.Add(arrRow[nRemove]); arrRow.RemoveAt(nRemove); } else { nTotalWidth += nWidth; if (bHorz) pt.y += nWidth; else pt.x += nWidth; if (arrNext.GetSize() > 0) { arrRow.Copy(arrNext); arrNext.RemoveAll(); continue; } break; } } return nTotalWidth; } CSize CXTPDockBar::CalcDynamicLayout(int nLength, DWORD nMode, AFX_SIZEPARENTPARAMS* lpLayout) { BOOL bHorz = nMode & LM_HORZ; BOOL bStretch = nMode & LM_STRETCH; CSize sizeFixed (bStretch && bHorz ? 32767 : 0, bStretch && !bHorz ? 32767 : 0); BOOL bLayoutQuery = (lpLayout->hDWP == NULL); // prepare for layout AFX_SIZEPARENTPARAMS layout; layout.hDWP = bLayoutQuery ? 0 : ::BeginDeferWindowPos((int)m_arrBars.GetSize()); CPoint pt(0, 0); // layout all the control bars for (int nPos = 0; nPos < m_arrBars.GetSize(); nPos++) { CToolBarArray lst; CXTPToolBar* pBar = GetDockedCommandBar(nPos); while (pBar) { if (pBar->IsVisible()) lst.Add(pBar); pBar = GetDockedCommandBar(++nPos); } int nWidth = 0; if (lst.GetSize() != 0) { nWidth = AdjustRow(lst, pt, nLength, bHorz, &layout); lst.RemoveAll(); } if (pBar == NULL && nWidth != 0) { // end of row because pBar == NULL if (bHorz) { pt.y += nWidth; sizeFixed.cy = max(sizeFixed.cy, pt.y); } else { pt.x += nWidth; sizeFixed.cx = max(sizeFixed.cx, pt.x); } } } if (!bLayoutQuery) { // move and resize all the windows at once! if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP)) { TRACE0("Warning: DeferWindowPos failed - low system resources.\n"); } } return sizeFixed; } void CXTPDockBar::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); if (GetPosition() == xtpBarTop && m_pCommandBars->IsDwmEnabled()) { m_pCommandBars->m_pFrameHook->UpdateDwmClientArea(); } } LRESULT CXTPDockBar::OnSizeParent(WPARAM, LPARAM lParam) { AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam; DWORD dwStyle = GetStyle(); if (dwStyle & WS_VISIBLE) { // align the control bar CRect rect; rect.CopyRect(&lpLayout->rect); CSize sizeAvail = rect.Size(); // maximum size available // get maximum requested size DWORD dwMode = lpLayout->bStretch ? LM_STRETCH : 0; if ((m_dwStyle & CBRS_SIZE_DYNAMIC) && m_dwStyle & CBRS_FLOATING) dwMode |= LM_HORZ | LM_MRUWIDTH; else if (dwStyle & CBRS_ORIENT_HORZ) dwMode |= LM_HORZ | LM_HORZDOCK; else dwMode |= LM_VERTDOCK; int nLength = (dwStyle & CBRS_ORIENT_HORZ ? sizeAvail.cx : sizeAvail.cy) /*- 2 * GetSystemMetrics(SM_CXBORDER)*/; CSize size = CalcDynamicLayout(nLength, dwMode, lpLayout); size.cx = min(size.cx, sizeAvail.cx); size.cy = min(size.cy, sizeAvail.cy); if (dwStyle & CBRS_ORIENT_HORZ) { lpLayout->sizeTotal.cy += size.cy; lpLayout->sizeTotal.cx = max(lpLayout->sizeTotal.cx, size.cx); if (dwStyle & CBRS_ALIGN_TOP) lpLayout->rect.top += size.cy; else if (dwStyle & CBRS_ALIGN_BOTTOM) { rect.top = rect.bottom - size.cy; lpLayout->rect.bottom -= size.cy; } } else { lpLayout->sizeTotal.cx += size.cx; lpLayout->sizeTotal.cy = max(lpLayout->sizeTotal.cy, size.cy); if (dwStyle & CBRS_ALIGN_LEFT) lpLayout->rect.left += size.cx; else if (dwStyle & CBRS_ALIGN_RIGHT) { rect.left = rect.right - size.cx; lpLayout->rect.right -= size.cx; } } rect.right = rect.left + size.cx; rect.bottom = rect.top + size.cy; // only resize the window if doing layout and not just rect query if (lpLayout->hDWP != NULL) AfxRepositionWindow(lpLayout, m_hWnd, &rect); Invalidate(FALSE); } return 0; } void CXTPDockBar::OnPaint() { CPaintDC dc(this); m_pCommandBars->GetPaintManager()->FillDockBar(&dc, this); } LRESULT CXTPDockBar::OnPrintClient(WPARAM wParam, LPARAM /*lParam*/) { CDC* pDC = CDC::FromHandle((HDC)wParam); if (pDC) { m_pCommandBars->GetPaintManager()->FillDockBar(pDC, this); } return TRUE; } BOOL CXTPDockBar::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; } void CXTPDockBar::AdjustStretchBars() { for (int nPos = 0; nPos < m_arrBars.GetSize(); nPos++) { CXTPToolBar* pBar = GetDockedCommandBar(nPos); if (pBar && pBar->GetFlags() & xtpFlagStretched) { if (nPos > 0 && GetDockedCommandBar(nPos - 1) != NULL) { m_arrBars.InsertAt(nPos, (CXTPToolBar*)NULL); nPos++; } if (nPos < m_arrBars.GetSize() -1 && GetDockedCommandBar(nPos + 1) != NULL) { m_arrBars.InsertAt(nPos + 1, (CXTPToolBar*)NULL); nPos++; } } } } void CXTPDockBar::DockCommandBar(CXTPToolBar* pBar, LPCRECT lpRect) { ASSERT_VALID(this); ASSERT_VALID(pBar); ASSERT_KINDOF(CXTPToolBar, pBar); CRect rectBar; pBar->GetWindowRect(&rectBar); if (pBar->m_pDockBar == this && (lpRect == NULL || rectBar == *lpRect)) { // already docked and no change in position return; } int nPos = -1; if (lpRect != NULL) { // insert into appropriate row CRect rect(lpRect); ScreenToClient(&rect); CPoint ptMid(rect.left + rect.Width()/2, rect.top + rect.Height()/2); nPos = Insert(pBar, rect, ptMid); pBar->m_pDockContext->m_rectMRUDockPos = rect; } else { // always add on current row, then create new one m_arrBars.Add(pBar); m_arrBars.Add(NULL); pBar->m_pDockContext->m_rectMRUDockPos.SetRectEmpty(); } // attach it to the docking site if (pBar->GetParent() != this) pBar->SetParent(this); if (pBar->m_pDockBar == this) pBar->m_pDockBar->RemoveCommandBar(pBar, nPos); else if (pBar->m_pDockBar != NULL) pBar->m_pDockBar->RemoveCommandBar(pBar, -1); pBar->m_pDockBar = this; AdjustStretchBars(); // get parent frame for recalc layout m_pCommandBars->RecalcFrameLayout(TRUE); } BOOL CXTPDockBar::RemoveCommandBar(CXTPToolBar* pBar, int nPosExclude) { ASSERT_VALID(this); ASSERT(pBar != NULL); if (!pBar) return FALSE; int nPos = FindBar(pBar, nPosExclude); ASSERT(nPos > 0); if (nPos <= 0) return FALSE; m_arrBars.RemoveAt(nPos); if (m_arrBars[nPos-1] == NULL && m_arrBars[nPos] == NULL) m_arrBars.RemoveAt(nPos); pBar->m_pDockBar = NULL; // get parent frame for recalc layout/frame destroy m_pCommandBars->RecalcFrameLayout(TRUE); return TRUE; } CXTPToolBar* CXTPDockBar::GetDockedCommandBar(int nPos) const { CXTPToolBar* pResult = (CXTPToolBar*)m_arrBars[nPos]; ASSERT(pResult == 0 || HIWORD(pResult) != 0); // XTPLibrary don't use placeholder return pResult; } int CXTPDockBar::GetDockedCount() const { int nCount = 0; for (int i = 0; i < m_arrBars.GetSize(); i++) { if (GetDockedCommandBar(i) != NULL) nCount++; } return nCount; } int CXTPDockBar::GetDockedVisibleCount() const { int nCount = 0; for (int i = 0; i < m_arrBars.GetSize(); i++) { CXTPToolBar* pBar = GetDockedCommandBar(i); if (pBar != NULL && pBar->IsVisible()) nCount++; } return nCount; } int CXTPDockBar::FindBar(CXTPToolBar* pBar, int nPosExclude) { for (int nPos = 0; nPos < m_arrBars.GetSize(); nPos++) { if (nPos != nPosExclude && m_arrBars[nPos] == pBar) return nPos; } return -1; } int CXTPDockBar::Insert(CXTPToolBar* pBarIns, CRect /*rect*/, CPoint ptMid) { ASSERT_VALID(this); ASSERT(pBarIns != NULL); int nPos = 0; int nPosInsAfter = 0; int nWidth = 0; int nTotalWidth = 0; //int nPosInsBefore = 0; BOOL bHorz = m_dwStyle & CBRS_ORIENT_HORZ; BOOL bAllowTopMost = pBarIns->IsRibbonBar() || m_pCommandBars->GetMenuBar() == NULL || !m_pCommandBars->GetMenuBar()->IsRibbonBar() || FindBar(m_pCommandBars->GetMenuBar()) == -1; for (nPos = 0; nPos < m_arrBars.GetSize(); nPos++) { CXTPToolBar* pBar = GetDockedCommandBar(nPos); if (pBar && !pBar->IsVisible()) continue; if (pBar != NULL) { CRect rectBar; pBar->GetWindowRect(&rectBar); ScreenToClient(&rectBar); nWidth = max(nWidth, bHorz ? rectBar.bottom : rectBar.right); } else // end of row because pBar == NULL { if ((bHorz ? ptMid.y : ptMid.x) < nWidth && (bAllowTopMost || nPosInsAfter > 1)) { if (nPos == 0 || ((bHorz ? ptMid.y : ptMid.x) == nTotalWidth)) // first section m_arrBars.InsertAt(nPosInsAfter + 1, (CXTPToolBar*)NULL); m_arrBars.InsertAt(nPosInsAfter + 1, pBarIns); return nPosInsAfter + 1; } nTotalWidth = nWidth; nWidth = 0; nPosInsAfter = nPos; } } // create a new row m_arrBars.InsertAt(nPosInsAfter + 1, (CXTPToolBar*)NULL); m_arrBars.InsertAt(nPosInsAfter + 1, pBarIns); return nPosInsAfter + 1; } XTPBarPosition CXTPDockBar::GetPosition() const { if (m_dwStyle & CBRS_TOP) return xtpBarTop; if (m_dwStyle & CBRS_BOTTOM) return xtpBarBottom; if (m_dwStyle & CBRS_LEFT) return xtpBarLeft; return xtpBarRight; } BOOL CXTPDockBar::IsVerticalPosition() const { return !(m_dwStyle & CBRS_ORIENT_HORZ); } void CXTPDockBar::OnRButtonDown(UINT /*nFlags*/, CPoint point) { ClientToScreen(&point); m_pCommandBars->ContextMenu(NULL, point); } void CXTPDockBar::GetVisibleToolbars(int nPos, CToolBarArray& arrBars) { ASSERT(nPos != -1); ASSERT(m_arrBars[nPos]); while (m_arrBars[nPos] != NULL && nPos > 0) { nPos--; } nPos++; CDockInfoArray arrInfo; while (m_arrBars[nPos] != NULL) { CXTPToolBar* pBar = m_arrBars[nPos++]; if (pBar->IsVisible()) { DOCK_INFO dockInfo(pBar, CXTPWindowRect(pBar), nPos); arrInfo.Add(dockInfo); } } if (m_dwStyle & CBRS_LEFT || m_dwStyle & CBRS_RIGHT) arrInfo.InvertRects(); arrInfo.Sort(); for (int i = 0; i < arrInfo.GetSize(); i++) { arrBars.Add(arrInfo[i].pBar); } }