root/third_party/wtl/include/atlsplit.h

/* [<][>][^][v][top][bottom][index][help] */
// Windows Template Library - WTL version 8.0
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// This file is a part of the Windows Template Library.
// The use and distribution terms for this software are covered by the
// Microsoft Permissive License (Ms-PL) which can be found in the file
// Ms-PL.txt at the root of this distribution.

#ifndef __ATLSPLIT_H__
#define __ATLSPLIT_H__

#pragma once

#ifndef __cplusplus
        #error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLAPP_H__
        #error atlsplit.h requires atlapp.h to be included first
#endif

#ifndef __ATLWIN_H__
        #error atlsplit.h requires atlwin.h to be included first
#endif


///////////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// CSplitterImpl<T, t_bVertical>
// CSplitterWindowImpl<T, t_bVertical, TBase, TWinTraits>
// CSplitterWindowT<t_bVertical>


namespace WTL
{

///////////////////////////////////////////////////////////////////////////////
// CSplitterImpl - Provides splitter support to any window

// Splitter panes constants
#define SPLIT_PANE_LEFT                  0
#define SPLIT_PANE_RIGHT                 1
#define SPLIT_PANE_TOP                   SPLIT_PANE_LEFT
#define SPLIT_PANE_BOTTOM                SPLIT_PANE_RIGHT
#define SPLIT_PANE_NONE                 -1

// Splitter extended styles
#define SPLIT_PROPORTIONAL              0x00000001
#define SPLIT_NONINTERACTIVE            0x00000002
#define SPLIT_RIGHTALIGNED              0x00000004
#define SPLIT_BOTTOMALIGNED             SPLIT_RIGHTALIGNED

// Note: SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED/SPLIT_BOTTOMALIGNED are 
// mutually exclusive. If both are set, splitter defaults to SPLIT_PROPORTIONAL


template <class T, bool t_bVertical = true>
class CSplitterImpl
{
public:
        enum { m_nPanesCount = 2, m_nPropMax = 10000 };

        HWND m_hWndPane[m_nPanesCount];
        RECT m_rcSplitter;
        int m_xySplitterPos;
        int m_nDefActivePane;
        int m_cxySplitBar;              // splitter bar width/height
        static HCURSOR m_hCursor;
        int m_cxyMin;                   // minimum pane size
        int m_cxyBarEdge;               // splitter bar edge
        bool m_bFullDrag;
        int m_cxyDragOffset;
        int m_nProportionalPos;
        bool m_bUpdateProportionalPos;
        DWORD m_dwExtendedStyle;       // splitter specific extended styles
        int m_nSinglePane;             // single pane mode

// Constructor
        CSplitterImpl() :
                        m_xySplitterPos(-1), m_nDefActivePane(SPLIT_PANE_NONE), 
                        m_cxySplitBar(0), m_cxyMin(0), m_cxyBarEdge(0), m_bFullDrag(true), 
                        m_cxyDragOffset(0), m_nProportionalPos(0), m_bUpdateProportionalPos(true),
                        m_dwExtendedStyle(SPLIT_PROPORTIONAL),
                        m_nSinglePane(SPLIT_PANE_NONE)
        {
                m_hWndPane[SPLIT_PANE_LEFT] = NULL;
                m_hWndPane[SPLIT_PANE_RIGHT] = NULL;

                ::SetRectEmpty(&m_rcSplitter);

                if(m_hCursor == NULL)
                {
                        CStaticDataInitCriticalSectionLock lock;
                        if(FAILED(lock.Lock()))
                        {
                                ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CSplitterImpl::CSplitterImpl.\n"));
                                ATLASSERT(FALSE);
                                return;
                        }

                        if(m_hCursor == NULL)
                                m_hCursor = ::LoadCursor(NULL, t_bVertical ? IDC_SIZEWE : IDC_SIZENS);

                        lock.Unlock();
                }
        }

// Attributes
        void SetSplitterRect(LPRECT lpRect = NULL, bool bUpdate = true)
        {
                if(lpRect == NULL)
                {
                        T* pT = static_cast<T*>(this);
                        pT->GetClientRect(&m_rcSplitter);
                }
                else
                {
                        m_rcSplitter = *lpRect;
                }

                if(IsProportional())
                        UpdateProportionalPos();
                else if(IsRightAligned())
                        UpdateRightAlignPos();

                if(bUpdate)
                        UpdateSplitterLayout();
        }

        void GetSplitterRect(LPRECT lpRect) const
        {
                ATLASSERT(lpRect != NULL);
                *lpRect = m_rcSplitter;
        }

        bool SetSplitterPos(int xyPos = -1, bool bUpdate = true)
        {
                if(xyPos == -1)   // -1 == middle
                {
                        if(t_bVertical)
                                xyPos = (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) / 2;
                        else
                                xyPos = (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge) / 2;
                }

                // Adjust if out of valid range
                int cxyMax = 0;
                if(t_bVertical)
                        cxyMax = m_rcSplitter.right - m_rcSplitter.left;
                else
                        cxyMax = m_rcSplitter.bottom - m_rcSplitter.top;

                if(xyPos < m_cxyMin + m_cxyBarEdge)
                        xyPos = m_cxyMin;
                else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin))
                        xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin;

                // Set new position and update if requested
                bool bRet = (m_xySplitterPos != xyPos);
                m_xySplitterPos = xyPos;

                if(m_bUpdateProportionalPos)
                {
                        if(IsProportional())
                                StoreProportionalPos();
                        else if(IsRightAligned())
                                StoreRightAlignPos();
                }
                else
                {
                        m_bUpdateProportionalPos = true;
                }

                if(bUpdate && bRet)
                        UpdateSplitterLayout();

                return bRet;
        }

        void SetSplitterPosPct(int nPct, bool bUpdate = true)
        {
                ATLASSERT(nPct >= 0 && nPct <= 100);

                m_nProportionalPos = ::MulDiv(nPct, m_nPropMax, 100);
                UpdateProportionalPos();

                if(bUpdate)
                        UpdateSplitterLayout();
        }

        int GetSplitterPos() const
        {
                return m_xySplitterPos;
        }

        bool SetSinglePaneMode(int nPane = SPLIT_PANE_NONE)
        {
                ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT || nPane == SPLIT_PANE_NONE);
                if(!(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT || nPane == SPLIT_PANE_NONE))
                        return false;

                if(nPane != SPLIT_PANE_NONE)
                {
                        if(!::IsWindowVisible(m_hWndPane[nPane]))
                                ::ShowWindow(m_hWndPane[nPane], SW_SHOW);
                        int nOtherPane = (nPane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT;
                        ::ShowWindow(m_hWndPane[nOtherPane], SW_HIDE);
                        if(m_nDefActivePane != nPane)
                                m_nDefActivePane = nPane;
                }
                else if(m_nSinglePane != SPLIT_PANE_NONE)
                {
                        int nOtherPane = (m_nSinglePane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT;
                        ::ShowWindow(m_hWndPane[nOtherPane], SW_SHOW);
                }

                m_nSinglePane = nPane;
                UpdateSplitterLayout();
                return true;
        }

        int GetSinglePaneMode() const
        {
                return m_nSinglePane;
        }

        DWORD GetSplitterExtendedStyle() const
        {
                return m_dwExtendedStyle;
        }

        DWORD SetSplitterExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
        {
                DWORD dwPrevStyle = m_dwExtendedStyle;
                if(dwMask == 0)
                        m_dwExtendedStyle = dwExtendedStyle;
                else
                        m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
#ifdef _DEBUG
                if(IsProportional() && IsRightAligned())
                        ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::SetSplitterExtendedStyle - SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED are mutually exclusive, defaulting to SPLIT_PROPORTIONAL.\n"));
#endif // _DEBUG
                return dwPrevStyle;
        }

// Splitter operations
        void SetSplitterPanes(HWND hWndLeftTop, HWND hWndRightBottom, bool bUpdate = true)
        {
                m_hWndPane[SPLIT_PANE_LEFT] = hWndLeftTop;
                m_hWndPane[SPLIT_PANE_RIGHT] = hWndRightBottom;
                ATLASSERT(m_hWndPane[SPLIT_PANE_LEFT] == NULL || m_hWndPane[SPLIT_PANE_RIGHT] == NULL || m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT]);
                if(bUpdate)
                        UpdateSplitterLayout();
        }

        bool SetSplitterPane(int nPane, HWND hWnd, bool bUpdate = true)
        {
                ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);

                if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)
                        return false;
                m_hWndPane[nPane] = hWnd;
                ATLASSERT(m_hWndPane[SPLIT_PANE_LEFT] == NULL || m_hWndPane[SPLIT_PANE_RIGHT] == NULL || m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT]);
                if(bUpdate)
                        UpdateSplitterLayout();
                return true;
        }

        HWND GetSplitterPane(int nPane) const
        {
                ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);

                if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)
                        return false;
                return m_hWndPane[nPane];
        }

        bool SetActivePane(int nPane)
        {
                ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);

                if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)
                        return false;
                if(m_nSinglePane != SPLIT_PANE_NONE && nPane != m_nSinglePane)
                        return false;
                ::SetFocus(m_hWndPane[nPane]);
                m_nDefActivePane = nPane;
                return true;
        }

        int GetActivePane() const
        {
                int nRet = SPLIT_PANE_NONE;
                HWND hWndFocus = ::GetFocus();
                if(hWndFocus != NULL)
                {
                        for(int nPane = 0; nPane < m_nPanesCount; nPane++)
                        {
                                if(hWndFocus == m_hWndPane[nPane] || ::IsChild(m_hWndPane[nPane], hWndFocus))
                                {
                                        nRet = nPane;
                                        break;
                                }
                        }
                }
                return nRet;
        }

        bool ActivateNextPane(bool bNext = true)
        {
                int nPane = m_nSinglePane;
                if(nPane == SPLIT_PANE_NONE)
                {
                        switch(GetActivePane())
                        {
                        case SPLIT_PANE_LEFT:
                                nPane = SPLIT_PANE_RIGHT;
                                break;
                        case SPLIT_PANE_RIGHT:
                                nPane = SPLIT_PANE_LEFT;
                                break;
                        default:
                                nPane = bNext ? SPLIT_PANE_LEFT : SPLIT_PANE_RIGHT;
                                break;
                        }
                }
                return SetActivePane(nPane);
        }

        bool SetDefaultActivePane(int nPane)
        {
                ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);

                if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)
                        return false;
                m_nDefActivePane = nPane;
                return true;
        }

        bool SetDefaultActivePane(HWND hWnd)
        {
                for(int nPane = 0; nPane < m_nPanesCount; nPane++)
                {
                        if(hWnd == m_hWndPane[nPane])
                        {
                                m_nDefActivePane = nPane;
                                return true;
                        }
                }
                return false;   // not found
        }

        int GetDefaultActivePane() const
        {
                return m_nDefActivePane;
        }

        void DrawSplitter(CDCHandle dc)
        {
                ATLASSERT(dc.m_hDC != NULL);
                if(m_nSinglePane == SPLIT_PANE_NONE && m_xySplitterPos == -1)
                        return;

                T* pT = static_cast<T*>(this);
                if(m_nSinglePane == SPLIT_PANE_NONE)
                {
                        pT->DrawSplitterBar(dc);

                        for(int nPane = 0; nPane < m_nPanesCount; nPane++)
                        {
                                if(m_hWndPane[nPane] == NULL)
                                        pT->DrawSplitterPane(dc, nPane);
                        }
                }
                else
                {
                        if(m_hWndPane[m_nSinglePane] == NULL)
                                pT->DrawSplitterPane(dc, m_nSinglePane);
                }
        }

// Overrideables
        void DrawSplitterBar(CDCHandle dc)
        {
                RECT rect;
                if(GetSplitterBarRect(&rect))
                {
                        dc.FillRect(&rect, COLOR_3DFACE);
                        // draw 3D edge if needed
                        T* pT = static_cast<T*>(this);
                        if((pT->GetExStyle() & WS_EX_CLIENTEDGE) != 0)
                                dc.DrawEdge(&rect, EDGE_RAISED, t_bVertical ? (BF_LEFT | BF_RIGHT) : (BF_TOP | BF_BOTTOM));
                }
        }

        // called only if pane is empty
        void DrawSplitterPane(CDCHandle dc, int nPane)
        {
                RECT rect;
                if(GetSplitterPaneRect(nPane, &rect))
                {
                        T* pT = static_cast<T*>(this);
                        if((pT->GetExStyle() & WS_EX_CLIENTEDGE) == 0)
                                dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
                        dc.FillRect(&rect, COLOR_APPWORKSPACE);
                }
        }

// Message map and handlers
        BEGIN_MSG_MAP(CSplitterImpl)
                MESSAGE_HANDLER(WM_CREATE, OnCreate)
                MESSAGE_HANDLER(WM_PAINT, OnPaint)
#ifndef _WIN32_WCE
                MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
#endif // !_WIN32_WCE
                if(IsInteractive())
                {
                        MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
                        MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
                        MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
                        MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
                        MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDoubleClick)
                        MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
                }
                MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
#ifndef _WIN32_WCE
                MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
#endif // !_WIN32_WCE
                MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
        END_MSG_MAP()

        LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
        {
                GetSystemSettings(false);
                bHandled = FALSE;
                return 1;
        }

        LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                // try setting position if not set
                if(m_nSinglePane == SPLIT_PANE_NONE && m_xySplitterPos == -1)
                        pT->SetSplitterPos();
                // do painting
                CPaintDC dc(pT->m_hWnd);
                pT->DrawSplitter(dc.m_hDC);
                return 0;
        }

        LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
                T* pT = static_cast<T*>(this);
                if((HWND)wParam == pT->m_hWnd && LOWORD(lParam) == HTCLIENT)
                {
                        DWORD dwPos = ::GetMessagePos();
                        POINT ptPos = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
                        pT->ScreenToClient(&ptPos);
                        if(IsOverSplitterBar(ptPos.x, ptPos.y))
                                return 1;
                }

                bHandled = FALSE;
                return 0;
        }

        LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
                T* pT = static_cast<T*>(this);
                int xPos = GET_X_LPARAM(lParam);
                int yPos = GET_Y_LPARAM(lParam);
                if((wParam & MK_LBUTTON) && ::GetCapture() == pT->m_hWnd)
                {
                        int xyNewSplitPos = 0;
                        if(t_bVertical)
                                xyNewSplitPos = xPos - m_rcSplitter.left - m_cxyDragOffset;
                        else
                                xyNewSplitPos = yPos - m_rcSplitter.top - m_cxyDragOffset;

                        if(xyNewSplitPos == -1)   // avoid -1, that means middle
                                xyNewSplitPos = -2;

                        if(m_xySplitterPos != xyNewSplitPos)
                        {
                                if(m_bFullDrag)
                                {
                                        if(pT->SetSplitterPos(xyNewSplitPos, true))
                                                pT->UpdateWindow();
                                }
                                else
                                {
                                        DrawGhostBar();
                                        pT->SetSplitterPos(xyNewSplitPos, false);
                                        DrawGhostBar();
                                }
                        }
                }
                else            // not dragging, just set cursor
                {
                        if(IsOverSplitterBar(xPos, yPos))
                                ::SetCursor(m_hCursor);
                        bHandled = FALSE;
                }

                return 0;
        }

        LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
        {
                int xPos = GET_X_LPARAM(lParam);
                int yPos = GET_Y_LPARAM(lParam);
                if(IsOverSplitterBar(xPos, yPos))
                {
                        T* pT = static_cast<T*>(this);
                        pT->SetCapture();
                        ::SetCursor(m_hCursor);
                        if(!m_bFullDrag)
                                DrawGhostBar();
                        if(t_bVertical)
                                m_cxyDragOffset = xPos - m_rcSplitter.left - m_xySplitterPos;
                        else
                                m_cxyDragOffset = yPos - m_rcSplitter.top - m_xySplitterPos;
                }
                bHandled = FALSE;
                return 1;
        }

        LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
        {
                ::ReleaseCapture();
                bHandled = FALSE;
                return 1;
        }

        LRESULT OnLButtonDoubleClick(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                pT->SetSplitterPos();   // middle
                return 0;
        }

        LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                if(!m_bFullDrag)
                {
                        DrawGhostBar();
                        UpdateSplitterLayout();
                        T* pT = static_cast<T*>(this);
                        pT->UpdateWindow();
                }
                return 0;
        }

        LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM, BOOL& bHandled)
        {
                if(m_nSinglePane == SPLIT_PANE_NONE)
                {
                        if(m_nDefActivePane == SPLIT_PANE_LEFT || m_nDefActivePane == SPLIT_PANE_RIGHT)
                                ::SetFocus(m_hWndPane[m_nDefActivePane]);
                }
                else
                {
                        ::SetFocus(m_hWndPane[m_nSinglePane]);
                }
                bHandled = FALSE;
                return 1;
        }

#ifndef _WIN32_WCE
        LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
                if(lRet == MA_ACTIVATE || lRet == MA_ACTIVATEANDEAT)
                {
                        DWORD dwPos = ::GetMessagePos();
                        POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
                        pT->ScreenToClient(&pt);
                        RECT rcPane;
                        for(int nPane = 0; nPane < m_nPanesCount; nPane++)
                        {
                                if(GetSplitterPaneRect(nPane, &rcPane) && ::PtInRect(&rcPane, pt))
                                {
                                        m_nDefActivePane = nPane;
                                        break;
                                }
                        }
                }
                return lRet;
        }
#endif // !_WIN32_WCE

        LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                GetSystemSettings(true);
                return 0;
        }

// Implementation - internal helpers
        void UpdateSplitterLayout()
        {
                if(m_nSinglePane == SPLIT_PANE_NONE && m_xySplitterPos == -1)
                        return;

                T* pT = static_cast<T*>(this);
                RECT rect = { 0, 0, 0, 0 };
                if(m_nSinglePane == SPLIT_PANE_NONE)
                {
                        if(GetSplitterBarRect(&rect))
                                pT->InvalidateRect(&rect);

                        for(int nPane = 0; nPane < m_nPanesCount; nPane++)
                        {
                                if(GetSplitterPaneRect(nPane, &rect))
                                {
                                        if(m_hWndPane[nPane] != NULL)
                                                ::SetWindowPos(m_hWndPane[nPane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
                                        else
                                                pT->InvalidateRect(&rect);
                                }
                        }
                }
                else
                {
                        if(GetSplitterPaneRect(m_nSinglePane, &rect))
                        {
                                if(m_hWndPane[m_nSinglePane] != NULL)
                                        ::SetWindowPos(m_hWndPane[m_nSinglePane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
                                else
                                        pT->InvalidateRect(&rect);
                        }
                }
        }

        bool GetSplitterBarRect(LPRECT lpRect) const
        {
                ATLASSERT(lpRect != NULL);
                if(m_nSinglePane != SPLIT_PANE_NONE || m_xySplitterPos == -1)
                        return false;

                if(t_bVertical)
                {
                        lpRect->left = m_rcSplitter.left + m_xySplitterPos;
                        lpRect->top = m_rcSplitter.top;
                        lpRect->right = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
                        lpRect->bottom = m_rcSplitter.bottom;
                }
                else
                {
                        lpRect->left = m_rcSplitter.left;
                        lpRect->top = m_rcSplitter.top + m_xySplitterPos;
                        lpRect->right = m_rcSplitter.right;
                        lpRect->bottom = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
                }

                return true;
        }

        bool GetSplitterPaneRect(int nPane, LPRECT lpRect) const
        {
                ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);
                ATLASSERT(lpRect != NULL);
                bool bRet = true;
                if(m_nSinglePane != SPLIT_PANE_NONE)
                {
                        if(nPane == m_nSinglePane)
                                *lpRect = m_rcSplitter;
                        else
                                bRet = false;
                }
                else if(nPane == SPLIT_PANE_LEFT)
                {
                        if(t_bVertical)
                        {
                                lpRect->left = m_rcSplitter.left;
                                lpRect->top = m_rcSplitter.top;
                                lpRect->right = m_rcSplitter.left + m_xySplitterPos;
                                lpRect->bottom = m_rcSplitter.bottom;
                        }
                        else
                        {
                                lpRect->left = m_rcSplitter.left;
                                lpRect->top = m_rcSplitter.top;
                                lpRect->right = m_rcSplitter.right;
                                lpRect->bottom = m_rcSplitter.top + m_xySplitterPos;
                        }
                }
                else if(nPane == SPLIT_PANE_RIGHT)
                {
                        if(t_bVertical)
                        {
                                lpRect->left = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
                                lpRect->top = m_rcSplitter.top;
                                lpRect->right = m_rcSplitter.right;
                                lpRect->bottom = m_rcSplitter.bottom;
                        }
                        else
                        {
                                lpRect->left = m_rcSplitter.left;
                                lpRect->top = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
                                lpRect->right = m_rcSplitter.right;
                                lpRect->bottom = m_rcSplitter.bottom;
                        }
                }
                else
                {
                        bRet = false;
                }
                return bRet;
        }

        bool IsOverSplitterRect(int x, int y) const
        {
                // -1 == don't check
                return ((x == -1 || (x >= m_rcSplitter.left && x <= m_rcSplitter.right)) &&
                        (y == -1 || (y >= m_rcSplitter.top && y <= m_rcSplitter.bottom)));
        }

        bool IsOverSplitterBar(int x, int y) const
        {
                if(m_nSinglePane != SPLIT_PANE_NONE)
                        return false;
                if(m_xySplitterPos == -1 || !IsOverSplitterRect(x, y))
                        return false;
                int xy = t_bVertical ? x : y;
                int xyOff = t_bVertical ? m_rcSplitter.left : m_rcSplitter.top;
                return ((xy >= (xyOff + m_xySplitterPos)) && (xy < xyOff + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge));
        }

        void DrawGhostBar()
        {
                RECT rect = { 0, 0, 0, 0 };
                if(GetSplitterBarRect(&rect))
                {
                        // invert the brush pattern (looks just like frame window sizing)
                        T* pT = static_cast<T*>(this);
                        CWindowDC dc(pT->m_hWnd);
                        CBrush brush = CDCHandle::GetHalftoneBrush();
                        if(brush.m_hBrush != NULL)
                        {
                                CBrushHandle brushOld = dc.SelectBrush(brush);
                                dc.PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT);
                                dc.SelectBrush(brushOld);
                        }
                }
        }

        void GetSystemSettings(bool bUpdate)
        {
#ifndef _WIN32_WCE
                m_cxySplitBar = ::GetSystemMetrics(t_bVertical ? SM_CXSIZEFRAME : SM_CYSIZEFRAME);
#else // CE specific
                m_cxySplitBar = 2 * ::GetSystemMetrics(t_bVertical ? SM_CXEDGE : SM_CYEDGE);
#endif // _WIN32_WCE

                T* pT = static_cast<T*>(this);
                if((pT->GetExStyle() & WS_EX_CLIENTEDGE))
                {
                        m_cxyBarEdge = 2 * ::GetSystemMetrics(t_bVertical ? SM_CXEDGE : SM_CYEDGE);
                        m_cxyMin = 0;
                }
                else
                {
                        m_cxyBarEdge = 0;
                        m_cxyMin = 2 * ::GetSystemMetrics(t_bVertical ? SM_CXEDGE : SM_CYEDGE);
                }

#ifndef _WIN32_WCE
                ::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &m_bFullDrag, 0);
#endif // !_WIN32_WCE

                if(bUpdate)
                        UpdateSplitterLayout();
        }

        bool IsProportional() const
        {
                return ((m_dwExtendedStyle & SPLIT_PROPORTIONAL) != 0);
        }

        void StoreProportionalPos()
        {
                int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
                if(cxyTotal > 0)
                        m_nProportionalPos = ::MulDiv(m_xySplitterPos, m_nPropMax, cxyTotal);
                else
                        m_nProportionalPos = 0;
                ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreProportionalPos - %i\n"), m_nProportionalPos);
        }

        void UpdateProportionalPos()
        {
                int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
                if(cxyTotal > 0)
                {
                        int xyNewPos = ::MulDiv(m_nProportionalPos, cxyTotal, m_nPropMax);
                        m_bUpdateProportionalPos = false;
                        T* pT = static_cast<T*>(this);
                        pT->SetSplitterPos(xyNewPos, false);
                }
        }

        bool IsRightAligned() const
        {
                return ((m_dwExtendedStyle & SPLIT_RIGHTALIGNED) != 0);
        }

        void StoreRightAlignPos()
        {
                int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
                if(cxyTotal > 0)
                        m_nProportionalPos = cxyTotal - m_xySplitterPos;
                else
                        m_nProportionalPos = 0;
                ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreRightAlignPos - %i\n"), m_nProportionalPos);
        }

        void UpdateRightAlignPos()
        {
                int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
                if(cxyTotal > 0)
                {
                        m_bUpdateProportionalPos = false;
                        T* pT = static_cast<T*>(this);
                        pT->SetSplitterPos(cxyTotal - m_nProportionalPos, false);
                }
        }

        bool IsInteractive() const
        {
                return ((m_dwExtendedStyle & SPLIT_NONINTERACTIVE) == 0);
        }
};

template <class T, bool t_bVertical> HCURSOR CSplitterImpl< T, t_bVertical>::m_hCursor = NULL;


///////////////////////////////////////////////////////////////////////////////
// CSplitterWindowImpl - Implements a splitter window

template <class T, bool t_bVertical = true, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CSplitterWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CSplitterImpl< T , t_bVertical >
{
public:
        DECLARE_WND_CLASS_EX(NULL, CS_DBLCLKS, COLOR_WINDOW)

        typedef CSplitterImpl< T , t_bVertical >   _baseClass;

        BEGIN_MSG_MAP(CSplitterWindowImpl)
                MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
                MESSAGE_HANDLER(WM_SIZE, OnSize)
                CHAIN_MSG_MAP(_baseClass)
                FORWARD_NOTIFICATIONS()
        END_MSG_MAP()

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

        LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
        {
                if(wParam != SIZE_MINIMIZED)
                        SetSplitterRect();

                bHandled = FALSE;
                return 1;
        }
};


///////////////////////////////////////////////////////////////////////////////
// CSplitterWindow - Implements a splitter window to be used as is

template <bool t_bVertical = true>
class CSplitterWindowT : public CSplitterWindowImpl<CSplitterWindowT<t_bVertical>, t_bVertical>
{
public:
        DECLARE_WND_CLASS_EX(_T("WTL_SplitterWindow"), CS_DBLCLKS, COLOR_WINDOW)
};

typedef CSplitterWindowT<true>    CSplitterWindow;
typedef CSplitterWindowT<false>   CHorSplitterWindow;

}; // namespace WTL

#endif // __ATLSPLIT_H__

/* [<][>][^][v][top][bottom][index][help] */