No.004 - 番外1 ドッキング・ツールバー ちょっとだけ MFCソース解析 |
MFCドッキングツールバーのドッキング時の処理をちょっとだけ解析する。 MFCの複雑さが理解できると思う。 ソースはMicrosoft Visual Studio\VC98\MFC\SRCフォルダより抜粋 |
CControlBarInfo |
CWnd
|
CFrameWnd::DockControlBar で メインウィンドウ にツールバーを ドッキング する。 このCFrameWnd::DockControlBar関数はドッキング先となるCDockBarを選定しポインタ pDockBarを得て、 そのポインタからCDockBar::DockControlBarを呼ぶ。 よばれたCDockBar::DockControlBar関数ではドッキングさせるツールバーの親ウィンドウを変更し、 メインウィンドウのレイアウト変更フラグをセットする。 |
CFrameWnd::OnIdleUpdateCmdUIは処理メッセージがないとき(?)スレッドクラスから呼ばれる。 レイアウト変更フラグが変更されているときこの関数は、CFrameWnd::RecalcLayoutを呼ぶ。 さらにその中からCWnd::RepositionBarsを呼ぶ CWnd::RepositionBarsは::BeginDeferWindowPos(8) でレイアウト構造体を得て、 各ウィンドウにWM_SIZEPARENTメッセージを送りウィンドウサイズを再計算させる。 |
WM_SIZEPARENTメッセージを送られたCDockBarはウィンドウ・レイアウト構造体を処理し基本クラス(CControlBar)のOnSizeParentを呼ぶ。 CControlBar::OnSizeParentではCDockBar::RecalcDelayShowを呼びウィンドウ表示状態を::DeferWindowPosでセットする。 CDockBar::RecalcDelayShowはさらにグローバル関数のAfxRepositionWindowを呼び、 AfxRepositionWindowが:DeferWindowPosでウィンドウサイズを決定する。 |
include/afxwin2.inl |
_AFXWIN_INLINE void CFrameWnd::DelayRecalcLayout(BOOL bNotify) { m_nIdleFlags |= (idleLayout | (bNotify ? idleNotify : 0)); }; |
winfrm.cpp |
void CFrameWnd::OnIdleUpdateCmdUI() { // update menu if necessary if (m_nIdleFlags & idleMenu) OnUpdateFrameMenu(m_hMenuAlt); // update title if necessary if (m_nIdleFlags & idleTitle) OnUpdateFrameTitle(TRUE); // recalc layout if necessary if (m_nIdleFlags & idleLayout) { RecalcLayout(m_nIdleFlags & idleNotify); UpdateWindow(); } // set the current message string if necessary if (m_nIDTracking != m_nIDLastMessage) { SetMessageText(m_nIDTracking); ASSERT(m_nIDTracking == m_nIDLastMessage); } m_nIdleFlags = 0; } void CFrameWnd::RecalcLayout(BOOL bNotify) { if (m_bInRecalcLayout) return; m_bInRecalcLayout = TRUE; // clear idle flags for recalc layout if called elsewhere if (m_nIdleFlags & idleNotify) bNotify = TRUE; m_nIdleFlags &= ~(idleLayout|idleNotify); #ifndef _AFX_NO_OLE_SUPPORT // call the layout hook -- OLE support uses this hook if (bNotify && m_pNotifyHook != NULL) m_pNotifyHook->OnRecalcLayout(); #endif // reposition all the child windows (regardless of ID) if (GetStyle() & FWS_SNAPTOBARS) { CRect rect(0, 0, 32767, 32767); RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposQuery, &rect, &rect, FALSE); RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposExtra, &m_rectBorder, &rect, TRUE); CalcWindowRect(&rect); SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); } else RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposExtra, &m_rectBorder); m_bInRecalcLayout = FALSE; } |
winfrm2.cpp |
void CFrameWnd::DockControlBar(CControlBar* pBar, UINT nDockBarID, LPCRECT lpRect) { CDockBar* pDockBar = (nDockBarID == 0) ? NULL : (CDockBar*)GetControlBar(nDockBarID); DockControlBar(pBar, pDockBar, lpRect); } void CFrameWnd::DockControlBar(CControlBar* pBar, CDockBar* pDockBar, LPCRECT lpRect) { ASSERT(pBar != NULL); // make sure CControlBar::EnableDocking has been called ASSERT(pBar->m_pDockContext != NULL); if (pDockBar == NULL) { for (int i = 0; i < 4; i++) { if ((dwDockBarMap[i][1] & CBRS_ALIGN_ANY) == (pBar->m_dwStyle & CBRS_ALIGN_ANY)) { pDockBar = (CDockBar*)GetControlBar(dwDockBarMap[i][0]); ASSERT(pDockBar != NULL); // assert fails when initial CBRS_ of bar does not // match available docking sites, as set by EnableDocking() break; } } } ASSERT(pDockBar != NULL); ASSERT(m_listControlBars.Find(pBar) != NULL); ASSERT(pBar->m_pDockSite == this); // if this assertion occurred it is because the parent of pBar was not initially // this CFrameWnd when pBar's OnCreate was called // i.e. this control bar should have been created with a different parent initially pDockBar->DockControlBar(pBar, lpRect); } |
wincore.cpp |
void CWnd::RepositionBars(UINT nIDFirst, UINT nIDLast, UINT nIDLeftOver, UINT nFlags, LPRECT lpRectParam, LPCRECT lpRectClient, BOOL bStretch) { ASSERT(nFlags == 0 || nFlags == reposQuery || nFlags == reposExtra); // walk kids in order, control bars get the resize notification // which allow them to shrink the client area // remaining size goes to the 'nIDLeftOver' pane // NOTE: nIDFirst->nIDLast are usually 0->0xffff AFX_SIZEPARENTPARAMS layout; HWND hWndLeftOver = NULL; layout.bStretch = bStretch; layout.sizeTotal.cx = layout.sizeTotal.cy = 0; if (lpRectClient != NULL) layout.rect = *lpRectClient; // starting rect comes from parameter else GetClientRect(&layout.rect); // starting rect comes from client rect if (nFlags != reposQuery) layout.hDWP = ::BeginDeferWindowPos(8); // reasonable guess else layout.hDWP = NULL; // not actually doing layout for (HWND hWndChild = ::GetTopWindow(m_hWnd); hWndChild != NULL; hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT)) { UINT nIDC = _AfxGetDlgCtrlID(hWndChild); CWnd* pWnd = CWnd::FromHandlePermanent(hWndChild); if (nIDC == nIDLeftOver) hWndLeftOver = hWndChild; else if (nIDC >= nIDFirst && nIDC <= nIDLast && pWnd != NULL) ::SendMessage(hWndChild, WM_SIZEPARENT, 0, (LPARAM)&layout); } // if just getting the available rectangle, return it now... if (nFlags == reposQuery) { ASSERT(lpRectParam != NULL); if (bStretch) ::CopyRect(lpRectParam, &layout.rect); else { lpRectParam->left = lpRectParam->top = 0; lpRectParam->right = layout.sizeTotal.cx; lpRectParam->bottom = layout.sizeTotal.cy; } return; } // the rest is the client size of the left-over pane if (nIDLeftOver != 0 && hWndLeftOver != NULL) { CWnd* pLeftOver = CWnd::FromHandle(hWndLeftOver); // allow extra space as specified by lpRectBorder if (nFlags == reposExtra) { ASSERT(lpRectParam != NULL); layout.rect.left += lpRectParam->left; layout.rect.top += lpRectParam->top; layout.rect.right -= lpRectParam->right; layout.rect.bottom -= lpRectParam->bottom; } // reposition the window pLeftOver->CalcWindowRect(&layout.rect); AfxRepositionWindow(&layout, hWndLeftOver, &layout.rect); } // move and resize all the windows at once! if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP)) TRACE0("Warning: DeferWindowPos failed - low system resources.\n"); } void AFXAPI AfxRepositionWindow(AFX_SIZEPARENTPARAMS* lpLayout, HWND hWnd, LPCRECT lpRect) { ASSERT(hWnd != NULL); ASSERT(lpRect != NULL); HWND hWndParent = ::GetParent(hWnd); ASSERT(hWndParent != NULL); if (lpLayout != NULL && lpLayout->hDWP == NULL) return; // first check if the new rectangle is the same as the current CRect rectOld; ::GetWindowRect(hWnd, rectOld); ::ScreenToClient(hWndParent, &rectOld.TopLeft()); ::ScreenToClient(hWndParent, &rectOld.BottomRight()); if (::EqualRect(rectOld, lpRect)) return; // nothing to do // try to use DeferWindowPos for speed, otherwise use SetWindowPos if (lpLayout != NULL) { lpLayout->hDWP = ::DeferWindowPos(lpLayout->hDWP, hWnd, NULL, lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, SWP_NOACTIVATE|SWP_NOZORDER); } else { ::SetWindowPos(hWnd, NULL, lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, SWP_NOACTIVATE|SWP_NOZORDER); } } |
bardock.cpp |
void CDockBar::DockControlBar(CControlBar* pBar, LPCRECT lpRect) { ASSERT_VALID(this); ASSERT_VALID(pBar); ASSERT_KINDOF(CControlBar, pBar); CRect rectBar; pBar->GetWindowRect(&rectBar); if (pBar->m_pDockBar == this && (lpRect == NULL || rectBar == *lpRect)) { // already docked and no change in position return; } // set CBRS_FLOAT_MULTI style if docking bar has it if (m_bFloating && (pBar->m_dwDockStyle & CBRS_FLOAT_MULTI)) m_dwStyle |= CBRS_FLOAT_MULTI; m_dwStyle &= ~(CBRS_SIZE_FIXED | CBRS_SIZE_DYNAMIC); m_dwStyle |= pBar->m_dwStyle & (CBRS_SIZE_FIXED | CBRS_SIZE_DYNAMIC); if (!(m_dwStyle & CBRS_FLOAT_MULTI)) { TCHAR szTitle[_MAX_PATH]; pBar->GetWindowText(szTitle, _countof(szTitle)); AfxSetWindowText(m_hWnd, szTitle); } // align correctly and turn on all borders DWORD dwStyle = pBar->GetBarStyle(); dwStyle &= ~(CBRS_ALIGN_ANY); dwStyle |= (m_dwStyle & CBRS_ALIGN_ANY) | CBRS_BORDER_ANY; if (m_bFloating) dwStyle |= CBRS_FLOATING; else dwStyle &= ~CBRS_FLOATING; pBar->SetBarStyle(dwStyle); // hide first if changing to a new docking site to avoid flashing BOOL bShow = FALSE; if (pBar->m_pDockBar != this && pBar->IsWindowVisible()) { pBar->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_HIDEWINDOW); bShow = TRUE; } 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); // position at requested position pBar->SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCOPYBITS); } else { // always add on current row, then create new one m_arrBars.Add(pBar); m_arrBars.Add(NULL); // align off the edge initially pBar->SetWindowPos(NULL, -afxData.cxBorder2, -afxData.cyBorder2, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCOPYBITS); } // attach it to the docking site if (pBar->GetParent() != this) pBar->SetParent(this); if (pBar->m_pDockBar == this) pBar->m_pDockBar->RemoveControlBar(pBar, nPos); else if (pBar->m_pDockBar != NULL) pBar->m_pDockBar->RemoveControlBar(pBar, -1, m_bFloating && !pBar->m_pDockBar->m_bFloating); pBar->m_pDockBar = this; if (bShow) { ASSERT(!pBar->IsWindowVisible()); pBar->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW); } // remove any place holder for pBar in this dockbar RemovePlaceHolder(pBar); // get parent frame for recalc layout CFrameWnd* pFrameWnd = GetDockingFrame(); pFrameWnd->DelayRecalcLayout(); } LRESULT CDockBar::OnSizeParent(WPARAM wParam, LPARAM lParam) { AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam; // set m_bLayoutQuery to TRUE if lpLayout->hDWP == NULL BOOL bLayoutQuery = m_bLayoutQuery; CRect rectLayout = m_rectLayout; m_bLayoutQuery = (lpLayout->hDWP == NULL); m_rectLayout = lpLayout->rect; LRESULT lResult = CControlBar::OnSizeParent(wParam, lParam); // restore m_bLayoutQuery m_bLayoutQuery = bLayoutQuery; m_rectLayout = rectLayout; return lResult; } |
barcore.cpp |
DWORD CControlBar::RecalcDelayShow(AFX_SIZEPARENTPARAMS* lpLayout) { ASSERT(lpLayout != NULL); // resize and reposition this control bar based on styles DWORD dwStyle = (m_dwStyle & (CBRS_ALIGN_ANY|CBRS_BORDER_ANY)) | (GetStyle() & WS_VISIBLE); // handle delay hide/show if (m_nStateFlags & (delayHide|delayShow)) { UINT swpFlags = 0; if (m_nStateFlags & delayHide) { ASSERT((m_nStateFlags & delayShow) == 0); if (dwStyle & WS_VISIBLE) swpFlags = SWP_HIDEWINDOW; } else { ASSERT(m_nStateFlags & delayShow); if ((dwStyle & WS_VISIBLE) == 0) swpFlags = SWP_SHOWWINDOW; } if (swpFlags != 0) { // make the window seem visible/hidden dwStyle ^= WS_VISIBLE; if (lpLayout->hDWP != NULL) { // clear delay flags m_nStateFlags &= ~(delayShow|delayHide); // hide/show the window if actually doing layout lpLayout->hDWP = ::DeferWindowPos(lpLayout->hDWP, m_hWnd, NULL, 0, 0, 0, 0, swpFlags| SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); } } else { // clear delay flags -- window is already in correct state m_nStateFlags &= ~(delayShow|delayHide); } } return dwStyle; // return new style } LRESULT CControlBar::OnSizeParent(WPARAM, LPARAM lParam) { AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam; DWORD dwStyle = RecalcDelayShow(lpLayout); if ((dwStyle & WS_VISIBLE) && (dwStyle & CBRS_ALIGN_ANY) != 0) { // 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; CSize size = CalcDynamicLayout(-1, dwMode); 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 if (dwStyle & CBRS_ORIENT_VERT) { 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; } } else { ASSERT(FALSE); // can never happen } 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); } return 0; } |