⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 atldock.h

📁 这是一本学习 window编程的很好的参考教材
💻 H
📖 第 1 页 / 共 4 页
字号:
template< class T, class TBase = CWindow, class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE CDockingPaneWindowImpl : 
   public CWindowImpl< T, TBase, TWinTraits >,
   public CSplitterBar<CDockingPaneWindowImpl>
{
public:
   DECLARE_WND_CLASS_EX(NULL, CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, COLOR_WINDOW)

   typedef CDockingPaneWindowImpl< T , TBase, TWinTraits > thisClass;
   
   BEGIN_MSG_MAP(CDockingPaneWindowImpl)
      MESSAGE_HANDLER(WM_PAINT, OnPaint)
      MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
      MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
      MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
      MESSAGE_HANDLER(WM_LBUTTONDOWN, OnButtonDown)
      MESSAGE_HANDLER(WM_SIZE, OnSize)
      MESSAGE_HANDLER(WM_DOCK_UPDATELAYOUT, OnRecalcSpace)
   END_MSG_MAP()

   CDockMap m_map;       // Panels docking in this pane
                         // Pointers are owned by the parent window
   DWORD m_dwExtStyle;   // Mirror of extended style of parent window (dock window)
   int m_cy;             // Current size of panel
   int m_cyOld;          // Last size when panel was visible
   short m_Side;         // The side the panel is shown the
   RECT m_rcSplitter;    // Rectangle of the splitter

   CDockingPaneWindowImpl() : 
      m_cy(0), 
      m_cyOld(DEFAULT_DOCKPANE_SIZE), 
      m_dwExtStyle(0L)
   {
      ::SetRectEmpty(&m_rcSplitter);
   }

   // Implementation

   void DockWindow(DOCKCONTEXT* ctx)
   {
      ATLASSERT(ctx);
      if( m_map.GetSize() == 0 ) {
         m_cy = m_cyOld;
         ShowWindow(SW_SHOWNOACTIVATE);
      }
      ctx->Side = m_Side;
      ctx->LastSide = m_Side;
      ::SetParent(ctx->hwndDocked, m_hWnd);
      ::SetParent(ctx->hwndChild, ctx->hwndDocked);
      ::ShowWindow(ctx->hwndDocked, SW_SHOWNOACTIVATE);
      m_map.Add(ctx);
      PostMessage(WM_DOCK_UPDATELAYOUT);
   }

   void UnDockWindow(DOCKCONTEXT* ctx)
   {
      ATLASSERT(ctx);
      for( int i = 0; i < m_map.GetSize(); i++ ) {
         if( m_map[i] == ctx ) {
            m_map.RemoveAt(i);
            ::ShowWindow(ctx->hwndDocked, SW_HIDE);
            break;
         }
      }
      if( m_map.GetSize() == 0 ) {
         if( m_dwExtStyle & DCK_EX_REMEMBERSIZE ) {
            m_cyOld = m_cy; // remember pane size to until next time
         }
         m_cy = 0;
         ShowWindow(SW_HIDE);
      }
      else {
         SendMessage(WM_DOCK_UPDATELAYOUT);
      }
   }

   void RepositionWindow(DOCKCONTEXT* ctx, int iPos)
   {
      if( iPos >= m_map.GetSize() ) return;
      for( int i = 0; i < m_map.GetSize(); i++ ) {
         if( m_map[i] == ctx ) {
            DOCKCONTEXT* pCtx = m_map[iPos];
            m_map.SetAtIndex(iPos, ctx);
            m_map.SetAtIndex(i, pCtx);
            break;
         }
      }
      SendMessage(WM_DOCK_UPDATELAYOUT);
      Invalidate();
   }

   // Message handlers

   LRESULT OnRecalcSpace(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
   {
      if( m_map.GetSize() == 0 ) return 0;
      for( int i = 0; i < m_map.GetSize(); i++ ) {
         ::SendMessage(m_map[i]->hwndDocked, WM_DOCK_UPDATELAYOUT, 0,0);
      }
      OnSize(0,0,0,bHandled);
      return 0;
   }

   LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      CPaintDC dc(m_hWnd);
      // Draw splitter along the pane side
      T* pT = static_cast<T*>(this);
      pT->DrawSplitterBar((HDC) dc, !IsDockedVertically(m_Side), m_rcSplitter);
      return 0;
   }

   LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      return 1; // handled, no background painting needed
   }

   LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
   {
      bHandled = FALSE;
      if( m_map.GetSize() == 0 ) return 1;
      T* pT = static_cast<T*>(this);
      pT->UpdateLayout();
      HDWP hdwp = BeginDeferWindowPos(m_map.GetSize());
      for( int i = 0; i < m_map.GetSize(); i++ ) {
         RECT& rc = m_map[i]->rcWindow;
         ::DeferWindowPos(hdwp, m_map[i]->hwndDocked, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
      }
      EndDeferWindowPos(hdwp);
      return 1;
   }

   LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
   {
      POINT ptPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
      if( PtInSplitter(ptPos, m_Side, 0, m_rcSplitter) ) {
         bool bVertical = !IsDockedVertically(m_Side);
         ::SetCursor( bVertical ? s_hVertCursor : s_hHorizCursor);
         bHandled = FALSE;
      }
      return 0;
   }

   LRESULT OnButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
   {
      POINT ptPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
      if( PtInSplitter(ptPos, m_Side, 0, m_rcSplitter) ) {
         // Click on the spiltter? Determine how har we can drag the pane and
         // start tracking...
         ::ClientToScreen(m_hWnd,& ptPos);
         m_ptStartDragPoint = m_ptEndDragPoint = m_ptDeltaDragPoint = ptPos;
         //
         RECT rcWin;
         GetWindowRect(&rcWin);
         m_rcTracker = m_rcSplitter;
         ::OffsetRect(&m_rcTracker, rcWin.left, rcWin.top);
         //
         ::GetWindowRect(GetParent(),& m_rcTrackerBounds);
         RECT rcLimit;
         ::SendMessage(GetParent(), WM_DOCK_QUERYRECT, DOCK_INFO_CHILD, (LPARAM)&rcLimit);
         switch( m_Side ) {
         case DOCK_LEFT:   m_rcTrackerBounds.right = rcLimit.right; break;
         case DOCK_RIGHT:  m_rcTrackerBounds.left = rcLimit.left; break;
         case DOCK_TOP:    m_rcTrackerBounds.bottom = rcLimit.bottom; break;
         case DOCK_BOTTOM: m_rcTrackerBounds.top = rcLimit.top; break;
         }
         // The actual dragging area is slightly smaller than the client area
         ::InflateRect(&m_rcTrackerBounds, -MIN_DOCKPANE_SIZE, -MIN_DOCKPANE_SIZE);
         // Enter tracking loop
         bool res = Track(false);
         if( res ) {
            int nOffset = ( !IsDockedVertically(m_Side) ? (m_ptEndDragPoint.y - m_ptStartDragPoint.y) : (m_ptEndDragPoint.x - m_ptStartDragPoint.x) );
            if( m_Side == DOCK_RIGHT || m_Side == DOCK_BOTTOM ) nOffset = -nOffset;
            m_cy += nOffset;
            ::SendMessage(GetParent(), WM_DOCK_UPDATELAYOUT, 0,0);
         }
      }
      return 0;
   }

   // Track methods

   void OnStretch(POINT& pt)
   {
      DrawGhostBar();
      if( !IsDockedVertically(m_Side) ) {
         int nOffset = pt.y - m_ptDeltaDragPoint.y;
         if( m_rcTracker.top + nOffset <= m_rcTrackerBounds.top ) nOffset = m_rcTrackerBounds.top - m_rcTracker.top;
         if( m_rcTracker.bottom + nOffset >= m_rcTrackerBounds.bottom ) nOffset = m_rcTrackerBounds.bottom - m_rcTracker.bottom;
         ::OffsetRect(&m_rcTracker, 0,nOffset);
         m_ptDeltaDragPoint.y += nOffset;
      }
      else {
         int nOffset = pt.x - m_ptDeltaDragPoint.x;
         if( m_rcTracker.left + nOffset <= m_rcTrackerBounds.left ) nOffset = m_rcTrackerBounds.left - m_rcTracker.left;
         if( m_rcTracker.right + nOffset >= m_rcTrackerBounds.right ) nOffset = m_rcTrackerBounds.right - m_rcTracker.right;
         ::OffsetRect(&m_rcTracker, nOffset,0);
         m_ptDeltaDragPoint.x += nOffset;
      }
      DrawGhostBar();
   }

   void OnEndResize()
   {
      m_ptEndDragPoint = m_ptDeltaDragPoint;
   }

   // Overridables

   void UpdateLayout()
   {
      if( m_map.GetSize() == 0 ) return;
      if( !IsWindowVisible() ) return;

      int nPanes = m_map.GetSize();
      bool bVertical = IsDockedVertically(m_Side);

      // Place side splitter for this docking area
      RECT rect; 
      GetClientRect(&rect);
      switch( m_Side ) {
      case DOCK_LEFT:
         ::SetRect(&m_rcSplitter, rect.right - m_cxySplitter, rect.top, rect.right, rect.bottom);
         rect.right -= m_cxySplitter;
         break;
      case DOCK_TOP:
         ::SetRect(&m_rcSplitter, rect.left, rect.bottom - m_cxySplitter, rect.right, rect.bottom);
         rect.bottom -= m_cxySplitter;
         break;
      case DOCK_RIGHT:
         ::SetRect(&m_rcSplitter, rect.left, rect.top, rect.left + m_cxySplitter, rect.bottom);
         rect.left += m_cxySplitter;
         break;
      case DOCK_BOTTOM:
         ::SetRect(&m_rcSplitter, rect.left, rect.top, rect.right, rect.top + m_cxySplitter);
         rect.top += m_cxySplitter;
         break;
      }

      // Place splitters in each child panel (except in the last one)
      for( int i = 0; i < nPanes - 1; i++ ) {
         ::SendMessage(m_map[i]->hwndDocked, WM_DOCK_SETSPLITTER, DEFAULT_SPLITTER_SIZE, 0L);
      }
      // The last panel does not have a splitter
      ::SendMessage(m_map[i]->hwndDocked, WM_DOCK_SETSPLITTER, 0, 0L);

      // Get actual height of all panels
      int nActualHeight = 0;
      for( i = 0; i < nPanes; i++ ) {
         const RECT& rc = m_map[i]->rcWindow;
         int iPaneHeight = (bVertical ? rc.bottom - rc.top : rc.right - rc.left);
         if( iPaneHeight < 10 ) iPaneHeight = 30;
         nActualHeight += iPaneHeight;
      }
      // Get height of docking area
      int nTop, nHeight;
      if( bVertical ) {
         nTop = rect.top;
         nHeight = rect.bottom - rect.top;
      }
      else {
         nTop = rect.left;
         nHeight = rect.right - rect.left;
      }
      // Distribute the difference among panels
      int nDelta = ((nHeight - nActualHeight) / nPanes);
      for( i = 0; i < nPanes; i++ ) {
         const RECT& rc = m_map[i]->rcWindow;
         int nSize = (bVertical ? rc.bottom - rc.top : rc.right - rc.left);
         if( !m_map[i]->bKeepSize ) nSize += nDelta;
         if( nSize < MIN_DOCKPANE_SIZE ) nSize = MIN_DOCKPANE_SIZE;
         if( nTop + nSize >= nHeight ) nSize /= 2;
         if( bVertical ) {
            ::SetRect(&m_map[i]->rcWindow, rect.left, nTop, rect.right, nTop + nSize);
         }
         else {
            ::SetRect(&m_map[i]->rcWindow, nTop, rect.top, nTop + nSize, rect.bottom);
         }
         nTop += nSize;
         m_map[i]->bKeepSize = false;
      }
      // Stretch the last window to the size of the docking window
      (bVertical ? m_map[nPanes - 1]->rcWindow.bottom : m_map[nPanes - 1]->rcWindow.right ) = nHeight;
   }
};

class CDockingPaneWindow : public CDockingPaneWindowImpl<CDockingPaneWindow>
{
public:
   DECLARE_WND_CLASS_EX(_T("WTL_DockingPaneWindow"), CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, COLOR_WINDOW)
};


///////////////////////////////////////////////////////
// CDockingWindow

template< class T, 
          class TPaneWindow = CDockingPaneWindow,
          class TDockWindow = CDockingPaneChildWindow,
          class TFloatWindow = CFloatingWindow,
          class TBase = CWindow, 
          class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE CDockingWindowImpl : 
   public CWindowImpl< T, TBase, TWinTraits >
{
public:
   DECLARE_WND_CLASS_EX(NULL, CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, NULL)

   typedef CDockingWindowImpl< T , TBase, TWinTraits > thisClass;
   
   BEGIN_MSG_MAP(CDockingWindowImpl)
      MESSAGE_HANDLER(WM_CREATE, OnCreate)
      MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
      MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
      MESSAGE_HANDLER(WM_SIZE, OnSize)
      MESSAGE_HANDLER(WM_DOCK_QUERYRECT, OnQueryRect)
      MESSAGE_HANDLER(WM_DOCK_QUERYTRACK, OnQueryTrack)
      MESSAGE_HANDLER(WM_DOCK_DOCK, OnDock)
      MESSAGE_HANDLER(WM_DOCK_FLOAT, OnFloat)
      MESSAGE_HANDLER(WM_DOCK_UNDOCK, OnUnDock)
      MESSAGE_HANDLER(WM_DOCK_UNFLOAT, OnUnFloat)
      MESSAGE_HANDLER(WM_DOCK_UPDATELAYOUT, OnSize)
      MESSAGE_HANDLER(WM_DOCK_REPOSITIONWINDOW, OnRepositionWindow)
      MESSAGE_HANDLER(WM_DOCK_CLIENT_CLOSE, OnClientClose)
   END_MSG_MAP()

   TPaneWindow m_panes[4];  // The 4 panel windows (one for each side)
   CDockMap m_map;          // The master map of dockable windows
   HWND m_hwndClient;       // The client window contained in the center
   DWORD m_dwExtStyle;      // Optional styles
   SIZE m_sizeBorder;       // Size of window borders

   // Operations

   void SetClient(HWND hWnd)
   {
      ATLASSERT(::IsWindow(hWnd));
      ATLASSERT(::GetWindowLong(hWnd, GWL_STYLE) & WS_CHILD);
      m_hwndClient = hWnd;
   }

   DWORD GetExtendedDockStyle() const
   {
      return m_dwExtStyle;
   }

   void SetExtendedDockStyle(DWORD dwStyle)
   {
      ATLASSERT(::IsWindow(m_hWnd));
      m_dwExtStyle = dwStyle;
      // Duplicate style settings to panels
      for( short i = 0; i < 4; i++ ) m_panes[i].m_dwExtStyle = dwStyle;
   }

   BOOL IsDockPane(HWND hWnd) const
   {
      ATLASSERT(::IsWindow(hWnd));
      for( int i = 0; i < m_map.GetSize(); i++ ) if( m_map[i]->hwndChild == hWnd ) return TRUE;      
      return FALSE;
   }

   void AddWindow(HWND hWnd, DWORD dwFlags = 0)
   {
      ATLASSERT(::IsWindow(hWnd));

      // Create docking context
      DOCKCONTEXT* ctx;
      ATLTRY(ctx = new DOCKCONTEXT);
      if( ctx == NULL ) return;
      ctx->Side = DOCK_HIDDEN;
      ctx->LastSide = DOCK_LEFT;
      ctx->hwndChild = hWnd;
      ctx->hwndRoot = m_hWnd;
      ::SetRect(&ctx->rcWindow, 0, 0, MIN_DOCKPANE_SIZE, MIN_DOCKPANE_SIZE);
      ctx->sizeFloat.cx = ctx->sizeFloat.cy = DEFAULT_FLOAT_SIZE;
      ctx->dwFlags = dwFlags;
      ctx->bKeepSize = false;
      ctx->hwndOrigPrnt = ::GetParent(hWnd);

      // Create docking child
      TDockWindow* wndDock;
      ATLTRY(wndDock = new TDockWindow(ctx));
      if( wndDock == NULL ) return;
      wndDock->Create(m_hWnd, rcDefault, NULL);
      ATLASSERT(::IsWindow(wndDock->m_hWnd));
      ctx->hwndDocked = *wndDock;

      // Create floating child
      TFloatWindow* wndFloat;
      TCHAR szCaption[128];    // max text length is 127 for floating caption
      ::GetWindowText(hWnd, szCaption, sizeof(szCaption)/sizeof(TCHAR));
      ATLTRY(wndFloat = new TFloatWindow(ctx));
      if( wndFloat == NULL ) return;
      wndFloat->Create(m_hWnd, rcDefault, szCaption);
      ATLASSERT(::IsWindow(wndFloat->m_hWnd));
      ctx->hwndFloated = *wndFloat;

      ::SetParent(ctx->hwndChild, ctx->hwndDocked);

      // Add Context to master list
      m_map.Add(ctx);
   }

   BOOL RemoveWindow(HWND hWnd)
   {
      ATLASSERT(::IsWindow(hWnd));

      DOCKCONTEXT* pCtx = _GetContext(hWnd);
      ATLASSERT(pCtx);
      if( pCtx == NULL ) return FALSE;

      // Hide and destrow panel

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -