www.pudn.com > SCIDE.rar > sizecbar.cpp


///////////////////////////////////////////////////////////////////////// 
// Copyright (C) 1998, 1999 by Cristi Posea 
// All rights reserved 
// 
// Use and distribute freely, except: don't remove my name from the 
// source or documentation (don't take credit for my work), mark your 
// changes (don't get me blamed for your possible bugs), don't alter 
// or remove this notice. 
// No warrantee of any kind, express or implied, is included with this 
// software; use at your own risk, responsibility for damages (if any) to 
// anyone resulting from the use of this software rests entirely with the 
// user. 
// 
// This class is intended to be used as a base class. Do not simply add 
// your code to this file - instead create a new class derived from 
// CSizingControlBar and put there what you need. 
// Modify this file only to fix bugs, and don't forget to send me a copy. 
// 
// Send bug reports, bug fixes, enhancements, requests, flames, etc., 
// and I'll try to keep a version up to date.  I can be reached at: 
//    cristip@dundas.com 
// 
// More details at MFC Programmer's SourceBook 
// http://www.codeguru.com/docking/docking_window.shtml or search 
// www.codeguru.com for my name if the article was moved. 
// 
///////////////////////////////////////////////////////////////////////// 
// 
// Acknowledgements: 
//  o   Thanks to Harlan R. Seymour (harlans@dundas.com) for his continuous 
//      support during development of this code. 
//  o   Thanks to Dundas Software for the opportunity to test this code 
//      on real-life applications. 
//      If you don't know who they are, visit them at www.dundas.com . 
//      Their award winning components and development suites are 
//      a pile of gold. 
//  o   Thanks to Chris Maunder (chrism@dundas.com) who came with the 
//      simplest way to query "Show window content while dragging" system 
//      setting. 
//  o   Thanks to Zafir Anjum (zafir@codeguru.com) for publishing this 
//      code on his cool site (www.codeguru.com). 
//  o   Some ideas for the gripper came from the CToolBarEx flat toolbar 
//      by Joerg Koenig (Joerg.Koenig@rhein-neckar.de). Also he inspired 
//      me on writing this notice:) . Thanks, Joerg! 
//  o   Thanks to Jakawan Ratiwanich (jack@alpha.fsec.ucf.edu) and to 
//      Udo Schaefer (Udo.Schaefer@vcase.de) for the dwStyle bug fix under 
//      VC++ 6.0. 
//  o   And, of course, many thanks to all of you who used this code, 
//      for the invaluable feedback I received. 
//       
///////////////////////////////////////////////////////////////////////// 
 
 
// sizecbar.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "sizecbar.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////// 
// CSizingControlBar 
 
CSCBArray CSizingControlBar::m_arrBars; // static member 
 
IMPLEMENT_DYNAMIC(CSizingControlBar, baseCSizingControlBar); 
 
CSizingControlBar::CSizingControlBar() 
{ 
    m_szMin = CSize(33, 32); 
    m_szHorz = CSize(200, 200); 
    m_szVert = CSize(200, 200); 
    m_szFloat = CSize(200, 200); 
    m_bTracking = FALSE; 
    m_bKeepSize = FALSE; 
    m_bParentSizing = FALSE; 
    m_cxEdge = 5; 
    m_bDragShowContent = FALSE; 
    m_nDockBarID = 0; 
    m_dwSCBStyle = 0; 
} 
 
CSizingControlBar::~CSizingControlBar() 
{ 
} 
 
BEGIN_MESSAGE_MAP(CSizingControlBar, baseCSizingControlBar) 
    //{{AFX_MSG_MAP(CSizingControlBar) 
    ON_WM_CREATE() 
    ON_WM_PAINT() 
    ON_WM_NCPAINT() 
    ON_WM_NCCALCSIZE() 
    ON_WM_WINDOWPOSCHANGING() 
    ON_WM_CAPTURECHANGED() 
    ON_WM_SETTINGCHANGE() 
    ON_WM_LBUTTONUP() 
    ON_WM_MOUSEMOVE() 
    ON_WM_NCLBUTTONDOWN() 
    ON_WM_LBUTTONDOWN() 
    ON_WM_LBUTTONDBLCLK() 
    ON_WM_RBUTTONDOWN() 
    ON_WM_NCLBUTTONUP() 
    ON_WM_NCMOUSEMOVE() 
    ON_WM_NCHITTEST() 
    //}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
BOOL CSizingControlBar::Create(LPCTSTR lpszWindowName, CWnd* pParentWnd, 
                               CSize sizeDefault, BOOL bHasGripper, 
                               UINT nID, DWORD dwStyle) 
{ 
    // must have a parent 
    ASSERT_VALID(pParentWnd); 
    // cannot be both fixed and dynamic 
    // (CBRS_SIZE_DYNAMIC is used for resizng when floating) 
    ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && 
              (dwStyle & CBRS_SIZE_DYNAMIC))); 
 
    m_dwStyle = dwStyle & CBRS_ALL; // save the control bar styles 
 
    m_szHorz = sizeDefault; // set the size members 
    m_szVert = sizeDefault; 
    m_szFloat = sizeDefault; 
 
    m_cyGripper = bHasGripper ? 12 : 0; // set the gripper width 
 
    // register and create the window - skip CControlBar::Create() 
    CString wndclass = ::AfxRegisterWndClass(CS_DBLCLKS, 
        ::LoadCursor(NULL, IDC_ARROW), 
        ::GetSysColorBrush(COLOR_BTNFACE), 0); 
 
    dwStyle &= ~CBRS_ALL; // keep only the generic window styles 
    dwStyle |= WS_CLIPCHILDREN; // prevents flashing 
    if (!CWnd::Create(wndclass, lpszWindowName, dwStyle, 
        CRect(0, 0, 0, 0), pParentWnd, nID)) 
        return FALSE; 
 
    return TRUE; 
} 
 
///////////////////////////////////////////////////////////////////////// 
// CSizingControlBar message handlers 
 
int CSizingControlBar::OnCreate(LPCREATESTRUCT lpCreateStruct)  
{ 
    if (baseCSizingControlBar::OnCreate(lpCreateStruct) == -1) 
        return -1; 
     
    // querry SPI_GETDRAGFULLWINDOWS system parameter 
    // OnSettingChange() will update m_bDragShowContent 
    m_bDragShowContent = FALSE; 
    ::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, 
        &m_bDragShowContent, 0); 
 
    m_arrBars.Add(this);        // register 
     
    m_dwSCBStyle |= SCBS_SHOWEDGES;//原来是注释掉的,蒋勇恢复 
 
    return 0; 
} 
 
BOOL CSizingControlBar::DestroyWindow()  
{ 
    int nPos = FindSizingBar(this); 
    ASSERT(nPos >= 0); 
 
    m_arrBars.RemoveAt(nPos);   // unregister 
 
    return baseCSizingControlBar::DestroyWindow(); 
} 
 
const BOOL CSizingControlBar::IsFloating() const 
{ 
    return !IsHorzDocked() && !IsVertDocked(); 
} 
 
const BOOL CSizingControlBar::IsHorzDocked() const 
{ 
    return (m_nDockBarID == AFX_IDW_DOCKBAR_TOP || 
        m_nDockBarID == AFX_IDW_DOCKBAR_BOTTOM); 
} 
 
const BOOL CSizingControlBar::IsVertDocked() const 
{ 
    return (m_nDockBarID == AFX_IDW_DOCKBAR_LEFT || 
        m_nDockBarID == AFX_IDW_DOCKBAR_RIGHT); 
} 
 
const BOOL CSizingControlBar::IsSideTracking() const 
{ 
    // don't call this when not tracking 
    ASSERT(m_bTracking && !IsFloating()); 
 
    return (m_htEdge == HTLEFT || m_htEdge == HTRIGHT) ? 
        IsHorzDocked() : IsVertDocked(); 
} 
 
CSize CSizingControlBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz) 
{ 
    if (bStretch) // the bar is stretched (is not the child of a dockbar) 
        if (bHorz) 
            return CSize(32767, m_szHorz.cy); 
        else 
            return CSize(m_szVert.cx, 32767); 
 
    // dirty cast - using CSCBDockBar to access protected CDockBar members 
    CSCBDockBar* pDockBar = (CSCBDockBar*) m_pDockBar; 
 
    // force imediate RecalcDelayShow() for all sizing bars on the row 
    // with delayShow/delayHide flags set to avoid IsVisible() problems 
    CSCBArray arrSCBars; 
    GetRowSizingBars(arrSCBars); 
    AFX_SIZEPARENTPARAMS layout; 
    layout.hDWP = pDockBar->m_bLayoutQuery ? 
        NULL : ::BeginDeferWindowPos(arrSCBars.GetSize()); 
    for (int i = 0; i < arrSCBars.GetSize(); i++) 
        arrSCBars[i]->RecalcDelayShow(&layout); 
    if (layout.hDWP != NULL) 
        ::EndDeferWindowPos(layout.hDWP); 
 
    // get available length 
    CRect rc = pDockBar->m_rectLayout; 
    if (rc.IsRectEmpty()) 
        m_pDockSite->GetClientRect(&rc); 
    int nLengthAvail = bHorz ? rc.Width() + 2 : rc.Height() - 2; 
 
    if (IsVisible() && !IsFloating() && 
        m_bParentSizing && arrSCBars[0] == this) 
        if (NegociateSpace(nLengthAvail, (bHorz != FALSE))) 
            AlignControlBars(); 
 
    m_bParentSizing = FALSE; 
     
    CSize szRet = bHorz ? m_szHorz : m_szVert; 
    szRet.cx = max(m_szMin.cx, szRet.cx); 
    szRet.cy = max(m_szMin.cy, szRet.cy); 
 
    return szRet; 
} 
 
CSize CSizingControlBar::CalcDynamicLayout(int nLength, DWORD dwMode) 
{ 
    if (dwMode & (LM_HORZDOCK | LM_VERTDOCK)) // docked ? 
    { 
        if (nLength == -1) 
            m_bParentSizing = TRUE; 
 
        return baseCSizingControlBar::CalcDynamicLayout(nLength, dwMode); 
    } 
 
    if (dwMode & LM_MRUWIDTH) return m_szFloat; 
    if (dwMode & LM_COMMIT) return m_szFloat; // already committed 
 
    ((dwMode & LM_LENGTHY) ? m_szFloat.cy : m_szFloat.cx) = nLength; 
 
    m_szFloat.cx = max(m_szFloat.cx, m_szMin.cx); 
    m_szFloat.cy = max(m_szFloat.cy, m_szMin.cy); 
 
    return m_szFloat; 
} 
 
void CSizingControlBar::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) 
{ 
    // force non-client recalc if moved or resized 
    lpwndpos->flags |= SWP_FRAMECHANGED; 
 
    baseCSizingControlBar::OnWindowPosChanging(lpwndpos); 
 
    // find on which side are we docked 
    UINT nOldDockBarID = m_nDockBarID; 
    m_nDockBarID = GetParent()->GetDlgCtrlID(); 
 
    if (!IsFloating()) 
        if (lpwndpos->flags & SWP_SHOWWINDOW) 
            m_bKeepSize = TRUE; 
} 
 
///////////////////////////////////////////////////////////////////////// 
// Mouse Handling 
// 
void CSizingControlBar::OnLButtonDown(UINT nFlags, CPoint point)  
{ 
    if (m_pDockBar != NULL) 
    { 
        // start the drag 
        ASSERT(m_pDockContext != NULL); 
        ClientToScreen(&point); 
        m_pDockContext->StartDrag(point); 
    } 
    else 
        CWnd::OnLButtonDown(nFlags, point); 
} 
 
void CSizingControlBar::OnLButtonDblClk(UINT nFlags, CPoint point)  
{ 
    if (m_pDockBar != NULL) 
    { 
        // toggle docking 
        ASSERT(m_pDockContext != NULL); 
        m_pDockContext->ToggleDocking(); 
    } 
    else 
        CWnd::OnLButtonDblClk(nFlags, point); 
} 
 
void CSizingControlBar::OnNcLButtonDown(UINT nHitTest, CPoint point)  
{ 
    if (IsFloating()) 
    { 
        baseCSizingControlBar::OnNcLButtonDown(nHitTest, point); 
        return; 
    } 
 
    if (m_bTracking) return; 
 
    if ((nHitTest >= HTSIZEFIRST) && (nHitTest <= HTSIZELAST)) 
        StartTracking(nHitTest); // sizing edge hit 
} 
 
void CSizingControlBar::OnNcLButtonUp(UINT nHitTest, CPoint point)  
{ 
    if (nHitTest == HTCLOSE) 
        m_pDockSite->ShowControlBar(this, FALSE, FALSE); // hide 
 
    baseCSizingControlBar::OnNcLButtonUp(nHitTest, point); 
} 
 
void CSizingControlBar::OnLButtonUp(UINT nFlags, CPoint point)  
{ 
    if (m_bTracking) 
        StopTracking(); 
 
    baseCSizingControlBar::OnLButtonUp(nFlags, point); 
} 
 
void CSizingControlBar::OnRButtonDown(UINT nFlags, CPoint point)  
{ 
    if (m_bTracking) 
        StopTracking(); 
     
    baseCSizingControlBar::OnRButtonDown(nFlags, point); 
} 
 
void CSizingControlBar::OnMouseMove(UINT nFlags, CPoint point)  
{ 
    if (m_bTracking) 
        OnTrackUpdateSize(point); 
     
    baseCSizingControlBar::OnMouseMove(nFlags, point); 
} 
 
void CSizingControlBar::OnCaptureChanged(CWnd *pWnd)  
{ 
    if (m_bTracking && (pWnd != this)) 
        StopTracking(); 
 
    baseCSizingControlBar::OnCaptureChanged(pWnd); 
} 
 
void CSizingControlBar::OnNcCalcSize(BOOL bCalcValidRects, 
                                     NCCALCSIZE_PARAMS FAR* lpncsp)  
{ 
    // compute the the client area 
    CRect rcClient = lpncsp->rgrc[0]; 
    rcClient.DeflateRect(5, 5); 
 
    m_dwSCBStyle &= ~SCBS_EDGEALL; 
 
    switch(m_nDockBarID) 
    { 
    case AFX_IDW_DOCKBAR_TOP: 
        m_dwSCBStyle |= SCBS_EDGEBOTTOM; 
        rcClient.DeflateRect(m_cyGripper, 0, 0, 0); 
        break; 
    case AFX_IDW_DOCKBAR_BOTTOM: 
        m_dwSCBStyle |= SCBS_EDGETOP; 
        rcClient.DeflateRect(m_cyGripper, 0, 0, 0); 
        break; 
    case AFX_IDW_DOCKBAR_LEFT: 
        m_dwSCBStyle |= SCBS_EDGERIGHT; 
        rcClient.DeflateRect(0, m_cyGripper, 0, 0); 
        break; 
    case AFX_IDW_DOCKBAR_RIGHT: 
        m_dwSCBStyle |= SCBS_EDGELEFT; 
        rcClient.DeflateRect(0, m_cyGripper, 0, 0); 
        break; 
    default: 
        break; 
    } 
 
    if (!IsFloating() && m_pDockBar != NULL) 
    { 
        CSCBArray arrSCBars; 
        GetRowSizingBars(arrSCBars); 
 
        for (int i = 0; i < arrSCBars.GetSize(); i++) 
            if (arrSCBars[i] == this) 
            { 
                if (i > 0) 
                    m_dwSCBStyle |= IsHorzDocked() ? 
                        SCBS_EDGELEFT : SCBS_EDGETOP; 
                if (i < arrSCBars.GetSize() - 1) 
                    m_dwSCBStyle |= IsHorzDocked() ? 
                        SCBS_EDGERIGHT : SCBS_EDGEBOTTOM; 
            } 
    } 
 
    // make room for edges only if they will be painted 
    if (m_dwSCBStyle & SCBS_SHOWEDGES) 
        rcClient.DeflateRect( 
            (m_dwSCBStyle & SCBS_EDGELEFT) ? m_cxEdge : 0, 
            (m_dwSCBStyle & SCBS_EDGETOP) ? m_cxEdge : 0, 
            (m_dwSCBStyle & SCBS_EDGERIGHT) ? m_cxEdge : 0, 
            (m_dwSCBStyle & SCBS_EDGEBOTTOM) ? m_cxEdge : 0); 
 
    // "hide" button positioning 
    CPoint ptOrgBtn; 
    if (IsHorzDocked()) 
        ptOrgBtn = CPoint(rcClient.left - m_cyGripper - 1, 
            rcClient.top - 1); 
    else 
        ptOrgBtn = CPoint(rcClient.right - 11, 
            rcClient.top - m_cyGripper - 1); 
 
    m_biHide.Move(ptOrgBtn - CRect(lpncsp->rgrc[0]).TopLeft()); 
 
    lpncsp->rgrc[0] = rcClient; 
} 
 
void CSizingControlBar::OnNcPaint()  
{ 
    // get window DC that is clipped to the non-client area 
    CWindowDC dc(this); 
 
    CRect rcClient, rcBar; 
    GetClientRect(rcClient); 
    ClientToScreen(rcClient); 
    GetWindowRect(rcBar); 
    rcClient.OffsetRect(-rcBar.TopLeft()); 
    rcBar.OffsetRect(-rcBar.TopLeft()); 
 
    // client area is not our bussiness :) 
    dc.ExcludeClipRect(rcClient); 
 
    // draw borders in non-client area 
    CRect rcDraw = rcBar; 
    DrawBorders(&dc, rcDraw); 
 
    // erase parts not drawn 
    dc.IntersectClipRect(rcDraw); 
 
    // erase NC background the hard way 
    HBRUSH hbr = (HBRUSH)GetClassLong(m_hWnd, GCL_HBRBACKGROUND); 
    ::FillRect(dc.m_hDC, rcDraw, hbr); 
 
    if (m_dwSCBStyle & SCBS_SHOWEDGES) 
    { 
        CRect rcEdge; // paint the sizing edges 
        for (int i = 0; i < 4; i++) 
            if (GetEdgeRect(rcBar, GetEdgeHTCode(i), rcEdge)) 
                dc.Draw3dRect(rcEdge, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
                    ::GetSysColor(COLOR_BTNSHADOW)); 
    } 
 
    if (m_cyGripper && !IsFloating()) 
        NcPaintGripper(&dc, rcClient); 
 
    ReleaseDC(&dc); 
} 
 
void CSizingControlBar::NcPaintGripper(CDC* pDC, CRect rcClient) 
{ 
    // paints a simple "two raised lines" gripper 
    // override this if you want a more sophisticated gripper 
    CRect gripper = rcClient; 
    CRect rcbtn = m_biHide.GetRect(); 
    BOOL bHorz = IsHorzDocked(); 
     
    gripper.DeflateRect(1, 1); 
    if (bHorz) 
    {   // gripper at left 
        gripper.left -= m_cyGripper; 
        gripper.right = gripper.left + 3; 
        gripper.top = rcbtn.bottom + 3; 
    } 
    else 
    {   // gripper at top 
        gripper.top -= m_cyGripper; 
        gripper.bottom = gripper.top + 3; 
        gripper.right = rcbtn.left - 3; 
    } 
 
    pDC->Draw3dRect(gripper, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
        ::GetSysColor(COLOR_BTNSHADOW)); 
 
    gripper.OffsetRect(bHorz ? 3 : 0, bHorz ? 0 : 3); 
     
    pDC->Draw3dRect(gripper, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
        ::GetSysColor(COLOR_BTNSHADOW)); 
 
    m_biHide.Paint(pDC); 
} 
 
void CSizingControlBar::OnPaint() 
{ 
    // overridden to skip border painting based on clientrect 
    CPaintDC dc(this); 
} 
 
UINT CSizingControlBar::OnNcHitTest(CPoint point) 
{ 
    if (IsFloating()) 
        return baseCSizingControlBar::OnNcHitTest(point); 
 
    CRect rcBar, rcEdge; 
    GetWindowRect(rcBar); 
 
    for (int i = 0; i < 4; i++) 
        if (GetEdgeRect(rcBar, GetEdgeHTCode(i), rcEdge)) 
            if (rcEdge.PtInRect(point)) return GetEdgeHTCode(i); 
 
    CRect rc = m_biHide.GetRect(); 
    rc.OffsetRect(rcBar.TopLeft()); 
    if (rc.PtInRect(point)) 
        return HTCLOSE; 
 
    return HTCLIENT; 
} 
 
void CSizingControlBar::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)  
{ 
    baseCSizingControlBar::OnSettingChange(uFlags, lpszSection); 
 
    m_bDragShowContent = FALSE; 
    ::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, 
        &m_bDragShowContent, 0); // update 
} 
 
///////////////////////////////////////////////////////////////////////// 
// CSizingControlBar implementation helpers 
 
void CSizingControlBar::StartTracking(UINT nHitTest) 
{ 
    SetCapture(); 
 
    // make sure no updates are pending 
    RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW); 
     
    BOOL bHorz = IsHorzDocked(); 
 
    m_szOld = bHorz ? m_szHorz : m_szVert; 
 
    CRect rc; 
    GetWindowRect(&rc); 
    CRect rcEdge; 
    VERIFY(GetEdgeRect(rc, nHitTest, rcEdge)); 
    m_ptOld = rcEdge.CenterPoint(); 
 
    m_htEdge = nHitTest; 
    m_bTracking = TRUE; 
 
    CSCBArray arrSCBars; 
    GetRowSizingBars(arrSCBars); 
 
    // compute the minsize as the max minsize of the sizing bars on row 
    m_szMinT = m_szMin; 
    for (int i = 0; i < arrSCBars.GetSize(); i++) 
        if (bHorz) 
            m_szMinT.cy = max(m_szMinT.cy, arrSCBars[i]->m_szMin.cy); 
        else 
            m_szMinT.cx = max(m_szMinT.cx, arrSCBars[i]->m_szMin.cx); 
 
    if (!IsSideTracking()) 
    { 
        // the control bar cannot grow with more than the size of  
        // remaining client area of the mainframe 
        m_pDockSite->RepositionBars(0, 0xFFFF, AFX_IDW_PANE_FIRST, 
            reposQuery, &rc, NULL, TRUE); 
        m_szMaxT = m_szOld + rc.Size() - CSize(4, 4); 
    } 
    else 
    { 
        // side tracking: max size is the actual size plus the amount 
        // the neighbour bar can be decreased to reach its minsize 
        for (int i = 0; i < arrSCBars.GetSize(); i++) 
            if (arrSCBars[i] == this) break; 
 
        CSizingControlBar* pBar = arrSCBars[i + 
            ((m_htEdge == HTTOP || m_htEdge == HTLEFT) ? -1 : 1)]; 
 
        m_szMaxT = m_szOld + (bHorz ? pBar->m_szHorz : 
            pBar->m_szVert) - pBar->m_szMin; 
    } 
 
    OnTrackInvertTracker(); // draw tracker 
} 
 
void CSizingControlBar::StopTracking() 
{ 
    OnTrackInvertTracker(); // erase tracker 
 
    m_bTracking = FALSE; 
    ReleaseCapture(); 
     
    m_pDockSite->DelayRecalcLayout(); 
} 
 
void CSizingControlBar::OnTrackUpdateSize(CPoint& point) 
{ 
    ASSERT(!IsFloating()); 
 
    CPoint pt = point; 
    ClientToScreen(&pt); 
    CSize szDelta = pt - m_ptOld; 
 
    CSize sizeNew = m_szOld; 
    switch (m_htEdge) 
    { 
    case HTLEFT:    sizeNew -= CSize(szDelta.cx, 0); break; 
    case HTTOP:     sizeNew -= CSize(0, szDelta.cy); break; 
    case HTRIGHT:   sizeNew += CSize(szDelta.cx, 0); break; 
    case HTBOTTOM:  sizeNew += CSize(0, szDelta.cy); break; 
    } 
 
    // enforce the limits 
    sizeNew.cx = max(m_szMinT.cx, min(m_szMaxT.cx, sizeNew.cx)); 
    sizeNew.cy = max(m_szMinT.cy, min(m_szMaxT.cy, sizeNew.cy)); 
 
    BOOL bHorz = IsHorzDocked(); 
    szDelta = sizeNew - (bHorz ? m_szHorz : m_szVert); 
     
    if (szDelta == CSize(0, 0)) return; // no size change 
 
    OnTrackInvertTracker(); // erase tracker 
 
    (bHorz ? m_szHorz : m_szVert) = sizeNew; // save the new size 
 
    CSCBArray arrSCBars; 
    GetRowSizingBars(arrSCBars); 
 
    for (int i = 0; i < arrSCBars.GetSize(); i++) 
        if (!IsSideTracking()) 
        {   // track simultaneously 
            CSizingControlBar* pBar = arrSCBars[i]; 
            (bHorz ? pBar->m_szHorz.cy : pBar->m_szVert.cx) = 
                bHorz ? sizeNew.cy : sizeNew.cx; 
        } 
        else 
        {   // adjust the neighbour's size too 
            if (arrSCBars[i] != this) continue; 
 
            CSizingControlBar* pBar = arrSCBars[i + 
                ((m_htEdge == HTTOP || m_htEdge == HTLEFT) ? -1 : 1)]; 
 
            (bHorz ? pBar->m_szHorz.cx : pBar->m_szVert.cy) -= 
                bHorz ? szDelta.cx : szDelta.cy; 
        } 
 
    OnTrackInvertTracker(); // redraw tracker at new pos 
 
    if (m_bDragShowContent) 
        m_pDockSite->DelayRecalcLayout(); 
} 
 
void CSizingControlBar::OnTrackInvertTracker() 
{ 
    ASSERT(m_bTracking); 
 
    if (m_bDragShowContent) 
        return; // don't show tracker if DragFullWindows is on 
 
    BOOL bHorz = IsHorzDocked(); 
    CRect rc, rcBar, rcDock, rcFrame; 
    GetWindowRect(rcBar); 
    m_pDockBar->GetWindowRect(rcDock); 
    m_pDockSite->GetWindowRect(rcFrame); 
    VERIFY(GetEdgeRect(rcBar, m_htEdge, rc)); 
    if (!IsSideTracking()) 
        rc = bHorz ?  
            CRect(rcDock.left + 1, rc.top, rcDock.right - 1, rc.bottom) : 
            CRect(rc.left, rcDock.top + 1, rc.right, rcDock.bottom - 1); 
 
    rc.OffsetRect(-rcFrame.TopLeft()); 
 
    CSize sizeNew = bHorz ? m_szHorz : m_szVert; 
    CSize sizeDelta = sizeNew - m_szOld; 
    if (m_nDockBarID == AFX_IDW_DOCKBAR_LEFT && m_htEdge == HTTOP || 
        m_nDockBarID == AFX_IDW_DOCKBAR_RIGHT && m_htEdge != HTBOTTOM || 
        m_nDockBarID == AFX_IDW_DOCKBAR_TOP && m_htEdge == HTLEFT || 
        m_nDockBarID == AFX_IDW_DOCKBAR_BOTTOM && m_htEdge != HTRIGHT) 
        sizeDelta = -sizeDelta; 
    rc.OffsetRect(sizeDelta); 
 
    CDC *pDC = m_pDockSite->GetDCEx(NULL, 
        DCX_WINDOW | DCX_CACHE | DCX_LOCKWINDOWUPDATE); 
    CBrush* pBrush = CDC::GetHalftoneBrush(); 
    CBrush* pBrushOld = pDC->SelectObject(pBrush); 
 
    pDC->PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATINVERT); 
     
    pDC->SelectObject(pBrushOld); 
    m_pDockSite->ReleaseDC(pDC); 
} 
 
BOOL CSizingControlBar::GetEdgeRect(CRect rcWnd, UINT nHitTest, 
                                    CRect& rcEdge) 
{ 
    rcEdge = rcWnd; 
    if (m_dwSCBStyle & SCBS_SHOWEDGES) 
        rcEdge.DeflateRect(1, 1); 
    BOOL bHorz = IsHorzDocked(); 
 
    switch (nHitTest) 
    { 
    case HTLEFT: 
        if (!(m_dwSCBStyle & SCBS_EDGELEFT)) return FALSE; 
        rcEdge.right = rcEdge.left + m_cxEdge; 
        rcEdge.DeflateRect(0, bHorz ? m_cxEdge: 0); 
        break; 
    case HTTOP: 
        if (!(m_dwSCBStyle & SCBS_EDGETOP)) return FALSE; 
        rcEdge.bottom = rcEdge.top + m_cxEdge; 
        rcEdge.DeflateRect(bHorz ? 0 : m_cxEdge, 0); 
        break; 
    case HTRIGHT: 
        if (!(m_dwSCBStyle & SCBS_EDGERIGHT)) return FALSE; 
        rcEdge.left = rcEdge.right - m_cxEdge; 
        rcEdge.DeflateRect(0, bHorz ? m_cxEdge: 0); 
        break; 
    case HTBOTTOM: 
        if (!(m_dwSCBStyle & SCBS_EDGEBOTTOM)) return FALSE; 
        rcEdge.top = rcEdge.bottom - m_cxEdge; 
        rcEdge.DeflateRect(bHorz ? 0 : m_cxEdge, 0); 
        break; 
    default: 
        ASSERT(FALSE); // invalid hit test code 
    } 
    return TRUE; 
} 
 
UINT CSizingControlBar::GetEdgeHTCode(int nEdge) 
{ 
    if (nEdge == 0) return HTLEFT; 
    if (nEdge == 1) return HTTOP; 
    if (nEdge == 2) return HTRIGHT; 
    if (nEdge == 3) return HTBOTTOM; 
    ASSERT(FALSE); // invalid edge no 
    return HTNOWHERE; 
} 
 
void CSizingControlBar::GetRowInfo(int& nFirst, int& nLast, int& nThis) 
{ 
    ASSERT_VALID(m_pDockBar); // verify bounds 
 
    nThis = m_pDockBar->FindBar(this); 
    ASSERT(nThis != -1); 
 
    int i, nBars = m_pDockBar->m_arrBars.GetSize(); 
 
    // find the first and the last bar in row 
    for (nFirst = -1, i = nThis - 1; i >= 0 && nFirst == -1; i--) 
        if (m_pDockBar->m_arrBars[i] == NULL) 
            nFirst = i + 1; 
    for (nLast = -1, i = nThis + 1; i < nBars && nLast == -1; i++) 
        if (m_pDockBar->m_arrBars[i] == NULL) 
            nLast = i - 1; 
 
    ASSERT((nLast != -1) && (nFirst != -1)); 
} 
 
void CSizingControlBar::GetRowSizingBars(CSCBArray& arrSCBars) 
{ 
    arrSCBars.RemoveAll(); 
 
    int nFirst, nLast, nThis; 
    GetRowInfo(nFirst, nLast, nThis); 
 
    for (int i = nFirst; i <= nLast; i++) 
    { 
        CControlBar* pBar = (CControlBar*)m_pDockBar->m_arrBars[i]; 
        if (HIWORD(pBar) == 0) continue; // placeholder 
        if (!pBar->IsVisible()) continue; 
        if (FindSizingBar(pBar) >= 0) 
            arrSCBars.Add((CSizingControlBar*)pBar); 
    } 
} 
 
const int CSizingControlBar::FindSizingBar(CControlBar* pBar) const 
{ 
    for (int nPos = 0; nPos < m_arrBars.GetSize(); nPos++) 
        if (m_arrBars[nPos] == pBar) 
            return nPos; // got it 
 
    return -1; // not found 
} 
 
BOOL CSizingControlBar::NegociateSpace(int nLengthAvail, BOOL bHorz) 
{ 
    ASSERT(bHorz == IsHorzDocked()); 
 
    int nFirst, nLast, nThis; 
    GetRowInfo(nFirst, nLast, nThis); 
 
    // step 1: subtract the visible fixed bars' lengths 
    for (int i = nFirst; i <= nLast; i++) 
    { 
        CControlBar* pFBar = (CControlBar*)m_pDockBar->m_arrBars[i]; 
        if (HIWORD(pFBar) == 0) continue; // placeholder 
        if (!pFBar->IsVisible() || (FindSizingBar(pFBar) >= 0)) continue; 
 
        CRect rcBar; 
        pFBar->GetWindowRect(&rcBar); 
 
        nLengthAvail -= (bHorz ? rcBar.Width() - 2 : rcBar.Height() - 2); 
    } 
 
    CSCBArray arrSCBars; 
    GetRowSizingBars(arrSCBars); 
    CSizingControlBar* pBar; 
 
    // step 2: compute actual and min lengths; also the common width 
    int nActualLength = 0; 
    int nMinLength = 2; 
    int nWidth = 0; 
    for (i = 0; i < arrSCBars.GetSize(); i++) 
    { 
        pBar = arrSCBars[i]; 
        nActualLength += bHorz ? pBar->m_szHorz.cx - 2 : 
            pBar->m_szVert.cy - 2; 
        nMinLength += bHorz ? pBar->m_szMin.cx - 2: 
            pBar->m_szMin.cy - 2; 
        nWidth = max(nWidth, bHorz ? pBar->m_szHorz.cy : 
            pBar->m_szVert.cx); 
    } 
     
    // step 3: pop the bar out of the row if not enough room 
    if (nMinLength > nLengthAvail) 
    { 
        if (nFirst < nThis || nThis < nLast) 
        {   // not enough room - create a new row 
            m_pDockBar->m_arrBars.InsertAt(nLast + 1, this); 
            m_pDockBar->m_arrBars.InsertAt(nLast + 1, (CControlBar*) NULL); 
            m_pDockBar->m_arrBars.RemoveAt(nThis); 
        } 
        return FALSE; 
    } 
 
    // step 4: make the bars same width 
    for (i = 0; i < arrSCBars.GetSize(); i++) 
        if (bHorz) 
            arrSCBars[i]->m_szHorz.cy = nWidth; 
        else 
            arrSCBars[i]->m_szVert.cx = nWidth; 
 
    if (nActualLength == nLengthAvail) 
        return TRUE; // no change 
 
    // step 5: distribute the difference between the bars, but 
    //         don't shrink them below minsize 
    int nDelta = nLengthAvail - nActualLength; 
 
    while (nDelta != 0) 
    { 
        int nDeltaOld = nDelta; 
        for (i = 0; i < arrSCBars.GetSize(); i++) 
        { 
            pBar = arrSCBars[i]; 
            int nLMin = bHorz ? pBar->m_szMin.cx : pBar->m_szMin.cy; 
            int nL = bHorz ? pBar->m_szHorz.cx : pBar->m_szVert.cy; 
             
            if ((nL == nLMin) && (nDelta < 0) || // already at min length 
                pBar->m_bKeepSize) // or wants to keep its size 
                continue; 
             
            // sign of nDelta 
            int nDelta2 = (nDelta == 0) ? 0 : ((nDelta < 0) ? -1 : 1); 
 
            (bHorz ? pBar->m_szHorz.cx : pBar->m_szVert.cy) += nDelta2; 
            nDelta -= nDelta2; 
            if (nDelta == 0) break; 
        } 
        // clear m_bKeepSize flags 
        if ((nDeltaOld == nDelta) || (nDelta == 0)) 
            for (i = 0; i < arrSCBars.GetSize(); i++) 
                arrSCBars[i]->m_bKeepSize = FALSE; 
    } 
 
    return TRUE; 
} 
 
void CSizingControlBar::AlignControlBars() 
{ 
    int nFirst, nLast, nThis; 
    GetRowInfo(nFirst, nLast, nThis); 
 
    BOOL bHorz = IsHorzDocked(); 
    BOOL bNeedRecalc = FALSE; 
    int nPos, nAlign = bHorz ? -2 : 0; 
 
    CRect rc, rcDock; 
    m_pDockBar->GetWindowRect(&rcDock); 
 
    for (int i = nFirst; i <= nLast; i++) 
    { 
        CControlBar* pBar = (CControlBar*)m_pDockBar->m_arrBars[i]; 
        if (HIWORD(pBar) == 0) continue; // placeholder 
        if (!pBar->IsVisible()) continue; 
 
        pBar->GetWindowRect(&rc); 
        rc.OffsetRect(-rcDock.TopLeft()); 
 
        if ((nPos = FindSizingBar(pBar)) >= 0) 
            rc = CRect(rc.TopLeft(), bHorz ? 
                m_arrBars[nPos]->m_szHorz : m_arrBars[nPos]->m_szVert); 
 
        if ((bHorz ? rc.left : rc.top) != nAlign) 
        { 
            if (!bHorz) 
                rc.OffsetRect(0, nAlign - rc.top - 2); 
            else if (m_nDockBarID == AFX_IDW_DOCKBAR_TOP) 
                rc.OffsetRect(nAlign - rc.left, -2); 
            else 
                rc.OffsetRect(nAlign - rc.left, 0); 
            pBar->MoveWindow(rc); 
            bNeedRecalc = TRUE; 
        } 
        nAlign += (bHorz ? rc.Width() : rc.Height()) - 2; 
    } 
 
    if (bNeedRecalc) 
    { 
        m_pDockSite->DelayRecalcLayout(); 
        TRACE("ccc\n"); 
    } 
} 
 
void CSizingControlBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) 
{ 
    BOOL bNeedPaint = FALSE; 
 
    CPoint pt; 
    ::GetCursorPos(&pt); 
    BOOL bHit = (OnNcHitTest(pt) == HTCLOSE); 
    BOOL bLButtonDown = (::GetKeyState(VK_LBUTTON) < 0); 
 
    BOOL bWasPushed = m_biHide.bPushed; 
    m_biHide.bPushed = bHit && bLButtonDown; 
 
    BOOL bWasRaised = m_biHide.bRaised; 
    m_biHide.bRaised = bHit && !bLButtonDown; 
 
    bNeedPaint |= (m_biHide.bPushed ^ bWasPushed) ||  
                  (m_biHide.bRaised ^ bWasRaised); 
 
    if (bNeedPaint) 
        SendMessage(WM_NCPAINT); 
} 
 
void CSizingControlBar::LoadState(LPCTSTR lpszProfileName) 
{ 
    ASSERT_VALID(this); 
    ASSERT(GetSafeHwnd()); // must be called after Create() 
 
    CWinApp* pApp = AfxGetApp(); 
 
    TCHAR szSection[256]; 
    wsprintf(szSection, _T("%s-SCBar-%d"), lpszProfileName, 
        GetDlgCtrlID()); 
 
    m_szHorz.cx = max(m_szMin.cx, (int) pApp->GetProfileInt(szSection, 
        _T("sizeHorzCX"), m_szHorz.cx)); 
    m_szHorz.cy = max(m_szMin.cy, (int) pApp->GetProfileInt(szSection,  
        _T("sizeHorzCY"), m_szHorz.cy)); 
 
    m_szVert.cx = max(m_szMin.cx, (int) pApp->GetProfileInt(szSection,  
        _T("sizeVertCX"), m_szVert.cx)); 
    m_szVert.cy = max(m_szMin.cy, (int) pApp->GetProfileInt(szSection,  
        _T("sizeVertCY"), m_szVert.cy)); 
 
    m_szFloat.cx = max(m_szMin.cx, (int) pApp->GetProfileInt(szSection, 
        _T("sizeFloatCX"), m_szFloat.cx)); 
    m_szFloat.cy = max(m_szMin.cy, (int) pApp->GetProfileInt(szSection, 
        _T("sizeFloatCY"), m_szFloat.cy)); 
} 
 
void CSizingControlBar::SaveState(LPCTSTR lpszProfileName) 
{ 
    // place your SaveState or GlobalSaveState call in 
    // CMainFrame::DestroyWindow(), not in OnDestroy() 
    ASSERT_VALID(this); 
    ASSERT(GetSafeHwnd()); 
 
    CWinApp* pApp = AfxGetApp(); 
 
    TCHAR szSection[256]; 
    wsprintf(szSection, _T("%s-SCBar-%d"), lpszProfileName, 
        GetDlgCtrlID()); 
 
    pApp->WriteProfileInt(szSection, _T("sizeHorzCX"), m_szHorz.cx); 
    pApp->WriteProfileInt(szSection, _T("sizeHorzCY"), m_szHorz.cy); 
 
    pApp->WriteProfileInt(szSection, _T("sizeVertCX"), m_szVert.cx); 
    pApp->WriteProfileInt(szSection, _T("sizeVertCY"), m_szVert.cy); 
 
    pApp->WriteProfileInt(szSection, _T("sizeFloatCX"), m_szFloat.cx); 
    pApp->WriteProfileInt(szSection, _T("sizeFloatCY"), m_szFloat.cy); 
} 
 
void CSizingControlBar::GlobalLoadState(LPCTSTR lpszProfileName) 
{ 
    for (int i = 0; i < m_arrBars.GetSize(); i++) 
        ((CSizingControlBar*) m_arrBars[i])->LoadState(lpszProfileName); 
} 
 
void CSizingControlBar::GlobalSaveState(LPCTSTR lpszProfileName) 
{ 
    for (int i = 0; i < m_arrBars.GetSize(); i++) 
        ((CSizingControlBar*) m_arrBars[i])->SaveState(lpszProfileName); 
} 
 
///////////////////////////////////////////////////////////////////////// 
// CSCBButton 
 
CSCBButton::CSCBButton() 
{ 
    bRaised = FALSE; 
    bPushed = FALSE; 
} 
 
void CSCBButton::Paint(CDC* pDC) 
{ 
    CRect rc = GetRect(); 
 
    if (bPushed) 
        pDC->Draw3dRect(rc, ::GetSysColor(COLOR_BTNSHADOW), 
            ::GetSysColor(COLOR_BTNHIGHLIGHT)); 
    else 
        if (bRaised) 
            pDC->Draw3dRect(rc, ::GetSysColor(COLOR_BTNHIGHLIGHT), 
                ::GetSysColor(COLOR_BTNSHADOW)); 
 
    COLORREF clrOldTextColor = pDC->GetTextColor(); 
    pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT)); 
    int nPrevBkMode = pDC->SetBkMode(TRANSPARENT); 
    CFont font; 
    int ppi = pDC->GetDeviceCaps(LOGPIXELSX); 
    int pointsize = MulDiv(68, 96, ppi); // 6 points at 96 ppi,蒋勇将60改为68 
    font.CreatePointFont(pointsize, _T("Marlett")); 
    CFont* oldfont = pDC->SelectObject(&font); 
 
    pDC->TextOut(ptOrg.x + 2, ptOrg.y + 2, CString(_T("r"))); // x-like 
     
    pDC->SelectObject(oldfont); 
    pDC->SetBkMode(nPrevBkMode); 
    pDC->SetTextColor(clrOldTextColor); 
}