root/third_party/wtl/include/atlscrl.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 __ATLSCRL_H__
#define __ATLSCRL_H__

#pragma once

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

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

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

#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
  #include <zmouse.h>
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)

#ifndef GET_WHEEL_DELTA_WPARAM
  #define GET_WHEEL_DELTA_WPARAM(wParam)  ((short)HIWORD(wParam))
#endif

#ifndef WM_MOUSEHWHEEL
  #define WM_MOUSEHWHEEL                  0x020E
#endif


///////////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// CScrollImpl<T>
// CScrollWindowImpl<T, TBase, TWinTraits>
// CMapScrollImpl<T>
// CMapScrollWindowImpl<T, TBase, TWinTraits>
// CFSBWindowT<TBase>
// CZoomScrollImpl<T>
// CZoomScrollWindowImpl<T, TBase, TWinTraits>
// CScrollContainerImpl<T, TBase, TWinTraits>
// CScrollContainer

namespace WTL
{

///////////////////////////////////////////////////////////////////////////////
// CScrollImpl - Provides scrolling support to any window

// Scroll extended styles
#define SCRL_SCROLLCHILDREN     0x00000001
#define SCRL_ERASEBACKGROUND    0x00000002
#define SCRL_NOTHUMBTRACKING    0x00000004
#if (WINVER >= 0x0500)
#define SCRL_SMOOTHSCROLL       0x00000008
#endif // (WINVER >= 0x0500)
#define SCRL_DISABLENOSCROLLV   0x00000010
#define SCRL_DISABLENOSCROLLH   0x00000020
#define SCRL_DISABLENOSCROLL    (SCRL_DISABLENOSCROLLV | SCRL_DISABLENOSCROLLH)


template <class T>
class CScrollImpl
{
public:
        enum { uSCROLL_FLAGS = SW_INVALIDATE };

        POINT m_ptOffset;
        SIZE m_sizeAll;
        SIZE m_sizeLine;
        SIZE m_sizePage;
        SIZE m_sizeClient;
        int m_zDelta;              // current wheel value
        int m_nWheelLines;         // number of lines to scroll on wheel
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
        // Note that this message must be forwarded from a top level window
        UINT m_uMsgMouseWheel;     // MSH_MOUSEWHEEL
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
        int m_zHDelta;              // current horizontal wheel value
        int m_nHWheelChars;         // number of chars to scroll on horizontal wheel
        UINT m_uScrollFlags;
        DWORD m_dwExtendedStyle;   // scroll specific extended styles

// Constructor
        CScrollImpl() : m_zDelta(0), m_nWheelLines(3), 
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
                        m_uMsgMouseWheel(0U), 
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
                        m_zHDelta(0), m_nHWheelChars(3), 
                        m_uScrollFlags(0U), m_dwExtendedStyle(0)
        {
                m_ptOffset.x = 0;
                m_ptOffset.y = 0;
                m_sizeAll.cx = 0;
                m_sizeAll.cy = 0;
                m_sizePage.cx = 0;
                m_sizePage.cy = 0;
                m_sizeLine.cx = 0;
                m_sizeLine.cy = 0;
                m_sizeClient.cx = 0;
                m_sizeClient.cy = 0;

                SetScrollExtendedStyle(SCRL_SCROLLCHILDREN | SCRL_ERASEBACKGROUND);
        }

// Attributes & Operations
        DWORD GetScrollExtendedStyle() const
        {
                return m_dwExtendedStyle;
        }

        DWORD SetScrollExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
        {
                DWORD dwPrevStyle = m_dwExtendedStyle;
                if(dwMask == 0)
                        m_dwExtendedStyle = dwExtendedStyle;
                else
                        m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
                // cache scroll flags
                T* pT = static_cast<T*>(this);
                pT;   // avoid level 4 warning
                m_uScrollFlags = pT->uSCROLL_FLAGS | (IsScrollingChildren() ? SW_SCROLLCHILDREN : 0) | (IsErasingBackground() ? SW_ERASE : 0);
#if (WINVER >= 0x0500) && !defined(_WIN32_WCE)
                m_uScrollFlags |= (IsSmoothScroll() ? SW_SMOOTHSCROLL : 0);
#endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE)
                return dwPrevStyle;
        }

        // offset operations
        void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

                pT->AdjustScrollOffset(x, y);

                int dx = m_ptOffset.x - x;
                int dy = m_ptOffset.y - y;
                m_ptOffset.x = x;
                m_ptOffset.y = y;

                // block: set horizontal scroll bar
                {
                        SCROLLINFO si = { sizeof(SCROLLINFO) };
                        si.fMask = SIF_POS;
                        if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
                                si.fMask |= SIF_DISABLENOSCROLL;
                        si.nPos = m_ptOffset.x;
                        pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
                }

                // block: set vertical scroll bar
                {
                        SCROLLINFO si = { sizeof(SCROLLINFO) };
                        si.fMask = SIF_POS;
                        if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
                                si.fMask |= SIF_DISABLENOSCROLL;
                        si.nPos = m_ptOffset.y;
                        pT->SetScrollInfo(SB_VERT, &si, bRedraw);
                }

                // Move all children if needed
                if(IsScrollingChildren() && (dx != 0 || dy != 0))
                {
                        for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
                        {
                                RECT rect = { 0 };
                                ::GetWindowRect(hWndChild, &rect);
                                ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
                                ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
                        }
                }

                if(bRedraw)
                        pT->Invalidate();
        }

        void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE)
        {
                SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw);
        }

        void GetScrollOffset(POINT& ptOffset) const
        {
                ptOffset = m_ptOffset;
        }

        // size operations
        void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

                m_sizeAll.cx = cx;
                m_sizeAll.cy = cy;

                int x = 0;
                int y = 0;
                if(!bResetOffset)
                {
                        x = m_ptOffset.x;
                        y = m_ptOffset.y;
                        pT->AdjustScrollOffset(x, y);
                }

                int dx = m_ptOffset.x - x;
                int dy = m_ptOffset.y - y;
                m_ptOffset.x = x;
                m_ptOffset.y = y;

                // block: set horizontal scroll bar
                {
                        SCROLLINFO si = { sizeof(SCROLLINFO) };
                        si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
                        if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
                                si.fMask |= SIF_DISABLENOSCROLL;
                        si.nMin = 0;
                        si.nMax = m_sizeAll.cx - 1;
                        si.nPage = m_sizeClient.cx;
                        si.nPos = m_ptOffset.x;
                        pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
                }

                // block: set vertical scroll bar
                {
                        SCROLLINFO si = { sizeof(SCROLLINFO) };
                        si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
                        if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
                                si.fMask |= SIF_DISABLENOSCROLL;
                        si.nMin = 0;
                        si.nMax = m_sizeAll.cy - 1;
                        si.nPage = m_sizeClient.cy;
                        si.nPos = m_ptOffset.y;
                        pT->SetScrollInfo(SB_VERT, &si, bRedraw);
                }

                // Move all children if needed
                if(IsScrollingChildren() && (dx != 0 || dy != 0))
                {
                        for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
                        {
                                RECT rect = { 0 };
                                ::GetWindowRect(hWndChild, &rect);
                                ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
                                ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
                        }
                }

                SetScrollLine(0, 0);
                SetScrollPage(0, 0);

                if(bRedraw)
                        pT->Invalidate();
        }

        void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                SetScrollSize(size.cx, size.cy, bRedraw, bResetOffset);
        }

        void GetScrollSize(SIZE& sizeWnd) const
        {
                sizeWnd = m_sizeAll;
        }

        // line operations
        void SetScrollLine(int cxLine, int cyLine)
        {
                ATLASSERT(cxLine >= 0 && cyLine >= 0);
                ATLASSERT(m_sizeAll.cx != 0 && m_sizeAll.cy != 0);

                m_sizeLine.cx = T::CalcLineOrPage(cxLine, m_sizeAll.cx, 100);
                m_sizeLine.cy = T::CalcLineOrPage(cyLine, m_sizeAll.cy, 100);
        }

        void SetScrollLine(SIZE sizeLine)
        {
                SetScrollLine(sizeLine.cx, sizeLine.cy);
        }

        void GetScrollLine(SIZE& sizeLine) const
        {
                sizeLine = m_sizeLine;
        }

        // page operations
        void SetScrollPage(int cxPage, int cyPage)
        {
                ATLASSERT(cxPage >= 0 && cyPage >= 0);
                ATLASSERT(m_sizeAll.cx != 0 && m_sizeAll.cy != 0);

                m_sizePage.cx = T::CalcLineOrPage(cxPage, m_sizeAll.cx, 10);
                m_sizePage.cy = T::CalcLineOrPage(cyPage, m_sizeAll.cy, 10);
        }

        void SetScrollPage(SIZE sizePage)
        {
                SetScrollPage(sizePage.cx, sizePage.cy);
        }

        void GetScrollPage(SIZE& sizePage) const
        {
                sizePage = m_sizePage;
        }

        // commands
        void ScrollLineDown()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_VERT, SB_LINEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
        }

        void ScrollLineUp()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_VERT, SB_LINEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
        }

        void ScrollPageDown()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_VERT, SB_PAGEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
        }

        void ScrollPageUp()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_VERT, SB_PAGEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
        }

        void ScrollTop()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_VERT, SB_TOP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
        }

        void ScrollBottom()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_VERT, SB_BOTTOM, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
        }

        void ScrollLineRight()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_HORZ, SB_LINEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
        }

        void ScrollLineLeft()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_HORZ, SB_LINEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
        }

        void ScrollPageRight()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_HORZ, SB_PAGEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
        }

        void ScrollPageLeft()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_HORZ, SB_PAGEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
        }

        void ScrollAllLeft()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_HORZ, SB_TOP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
        }

        void ScrollAllRight()
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_HORZ, SB_BOTTOM, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
        }

        // scroll to make point/view/window visible
        void ScrollToView(POINT pt)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                RECT rect = { pt.x, pt.y, pt.x, pt.y };
                pT->ScrollToView(rect);
        }

        void ScrollToView(RECT& rect)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

                RECT rcClient = { 0 };
                pT->GetClientRect(&rcClient);

                int x = m_ptOffset.x;
                if(rect.left < m_ptOffset.x)
                        x = rect.left;
                else if(rect.right > (m_ptOffset.x + rcClient.right))
                        x = rect.right - rcClient.right;

                int y = m_ptOffset.y;
                if(rect.top < m_ptOffset.y)
                        y = rect.top;
                else if(rect.bottom > (m_ptOffset.y + rcClient.bottom))
                        y = rect.bottom - rcClient.bottom;

                SetScrollOffset(x, y);
        }

        void ScrollToView(HWND hWnd)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

                RECT rect = { 0 };
                ::GetWindowRect(hWnd, &rect);
                ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2);
                ScrollToView(rect);
        }

        BEGIN_MSG_MAP(CScrollImpl)
                MESSAGE_HANDLER(WM_CREATE, OnCreate)
                MESSAGE_HANDLER(WM_VSCROLL, OnVScroll)
                MESSAGE_HANDLER(WM_HSCROLL, OnHScroll)
                MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
                MESSAGE_HANDLER(m_uMsgMouseWheel, OnMouseWheel)
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
                MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnMouseHWheel)
                MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
                MESSAGE_HANDLER(WM_SIZE, OnSize)
                MESSAGE_HANDLER(WM_PAINT, OnPaint)
#ifndef _WIN32_WCE
                MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
#endif // !_WIN32_WCE
        // standard scroll commands
        ALT_MSG_MAP(1)
                COMMAND_ID_HANDLER(ID_SCROLL_UP, OnScrollUp)
                COMMAND_ID_HANDLER(ID_SCROLL_DOWN, OnScrollDown)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, OnScrollPageUp)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, OnScrollPageDown)
                COMMAND_ID_HANDLER(ID_SCROLL_TOP, OnScrollTop)
                COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, OnScrollBottom)
                COMMAND_ID_HANDLER(ID_SCROLL_LEFT, OnScrollLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, OnScrollRight)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, OnScrollPageLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, OnScrollPageRight)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, OnScrollAllLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, OnScrollAllRight)
        END_MSG_MAP()

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

        LRESULT OnVScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_VERT, (int)(short)LOWORD(wParam), (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
                return 0;
        }

        LRESULT OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                pT->DoScroll(SB_HORZ, (int)(short)LOWORD(wParam), (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
                return 0;
        }

        LRESULT OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) || defined(_WIN32_WCE)
                uMsg;
                int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
#else
                int zDelta = (uMsg == WM_MOUSEWHEEL) ? (int)GET_WHEEL_DELTA_WPARAM(wParam) : (int)wParam;
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) || defined(_WIN32_WCE))
                int nScrollCode = (m_nWheelLines == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGEUP : SB_PAGEDOWN) : ((zDelta > 0) ? SB_LINEUP : SB_LINEDOWN);
                m_zDelta += zDelta;   // cumulative
                int zTotal = (m_nWheelLines == WHEEL_PAGESCROLL) ? abs(m_zDelta) : abs(m_zDelta) * m_nWheelLines;
                if(m_sizeAll.cy > m_sizeClient.cy)
                {
                        for(int i = 0; i < zTotal; i += WHEEL_DELTA)
                        {
                                pT->DoScroll(SB_VERT, nScrollCode, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
                                pT->UpdateWindow();
                        }
                }
                else            // can't scroll vertically, scroll horizontally
                {
                        for(int i = 0; i < zTotal; i += WHEEL_DELTA)
                        {
                                pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
                                pT->UpdateWindow();
                        }
                }
                m_zDelta %= WHEEL_DELTA;

                return 0;
        }

        LRESULT OnMouseHWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

                int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
                int nScrollCode = (m_nHWheelChars == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGERIGHT : SB_PAGELEFT) : ((zDelta > 0) ? SB_LINERIGHT : SB_LINELEFT);
                m_zHDelta += zDelta;   // cumulative
                int zTotal = (m_nHWheelChars == WHEEL_PAGESCROLL) ? abs(m_zHDelta) : abs(m_zHDelta) * m_nHWheelChars;
                if(m_sizeAll.cx > m_sizeClient.cx)
                {
                        for(int i = 0; i < zTotal; i += WHEEL_DELTA)
                        {
                                pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
                                pT->UpdateWindow();
                        }
                }
                m_zHDelta %= WHEEL_DELTA;

                return 0;
        }

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

        LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

                m_sizeClient.cx = GET_X_LPARAM(lParam);
                m_sizeClient.cy = GET_Y_LPARAM(lParam);

                // block: set horizontal scroll bar
                {
                        SCROLLINFO si = { sizeof(SCROLLINFO) };
                        si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
                        si.nMin = 0;
                        si.nMax = m_sizeAll.cx - 1;
                        if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
                                si.fMask |= SIF_DISABLENOSCROLL;
                        si.nPage = m_sizeClient.cx;
                        si.nPos = m_ptOffset.x;
                        pT->SetScrollInfo(SB_HORZ, &si, TRUE);
                }

                // block: set vertical scroll bar
                {
                        SCROLLINFO si = { sizeof(SCROLLINFO) };
                        si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
                        si.nMin = 0;
                        si.nMax = m_sizeAll.cy - 1;
                        if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
                                si.fMask |= SIF_DISABLENOSCROLL;
                        si.nPage = m_sizeClient.cy;
                        si.nPos = m_ptOffset.y;
                        pT->SetScrollInfo(SB_VERT, &si, TRUE);
                }

                int x = m_ptOffset.x;
                int y = m_ptOffset.y;
                if(pT->AdjustScrollOffset(x, y))
                {
                        // Children will be moved in SetScrollOffset, if needed
                        pT->ScrollWindowEx(m_ptOffset.x - x, m_ptOffset.y - y, (m_uScrollFlags & ~SCRL_SCROLLCHILDREN));
                        SetScrollOffset(x, y, FALSE);
                }

                bHandled = FALSE;
                return 1;
        }

        LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                if(wParam != NULL)
                {
                        CDCHandle dc = (HDC)wParam;
                        POINT ptViewportOrg = { 0, 0 };
                        dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg);
                        pT->DoPaint(dc);
                        dc.SetViewportOrg(ptViewportOrg);
                }
                else
                {
                        CPaintDC dc(pT->m_hWnd);
                        dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y);
                        pT->DoPaint(dc.m_hDC);
                }
                return 0;
        }

        // scrolling handlers
        LRESULT OnScrollUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollLineUp();
                return 0;
        }

        LRESULT OnScrollDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollLineDown();
                return 0;
        }

        LRESULT OnScrollPageUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollPageUp();
                return 0;
        }

        LRESULT OnScrollPageDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollPageDown();
                return 0;
        }

        LRESULT OnScrollTop(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollTop();
                return 0;
        }

        LRESULT OnScrollBottom(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollBottom();
                return 0;
        }

        LRESULT OnScrollLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollLineLeft();
                return 0;
        }

        LRESULT OnScrollRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollLineRight();
                return 0;
        }

        LRESULT OnScrollPageLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollPageLeft();
                return 0;
        }

        LRESULT OnScrollPageRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollPageRight();
                return 0;
        }

        LRESULT OnScrollAllLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollAllLeft();
                return 0;
        }

        LRESULT OnScrollAllRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                ScrollAllRight();
                return 0;
        }

// Overrideables
        void DoPaint(CDCHandle /*dc*/)
        {
                // must be implemented in a derived class
                ATLASSERT(FALSE);
        }

// Implementation
        void DoScroll(int nType, int nScrollCode, int& cxyOffset, int cxySizeAll, int cxySizePage, int cxySizeLine)
        {
                T* pT = static_cast<T*>(this);
                RECT rect = { 0 };
                pT->GetClientRect(&rect);
                int cxyClient = (nType == SB_VERT) ? rect.bottom : rect.right;
                int cxyMax = cxySizeAll - cxyClient;

                if(cxyMax < 0)   // can't scroll, client area is bigger
                        return;

                bool bUpdate = true;
                int cxyScroll = 0;

                switch(nScrollCode)
                {
                case SB_TOP:            // top or all left
                        cxyScroll = cxyOffset;
                        cxyOffset = 0;
                        break;
                case SB_BOTTOM:         // bottom or all right
                        cxyScroll = cxyOffset - cxyMax;
                        cxyOffset = cxyMax;
                        break;
                case SB_LINEUP:         // line up or line left
                        if(cxyOffset >= cxySizeLine)
                        {
                                cxyScroll = cxySizeLine;
                                cxyOffset -= cxySizeLine;
                        }
                        else
                        {
                                cxyScroll = cxyOffset;
                                cxyOffset = 0;
                        }
                        break;
                case SB_LINEDOWN:       // line down or line right
                        if(cxyOffset < cxyMax - cxySizeLine)
                        {
                                cxyScroll = -cxySizeLine;
                                cxyOffset += cxySizeLine;
                        }
                        else
                        {
                                cxyScroll = cxyOffset - cxyMax;
                                cxyOffset = cxyMax;
                        }
                        break;
                case SB_PAGEUP:         // page up or page left
                        if(cxyOffset >= cxySizePage)
                        {
                                cxyScroll = cxySizePage;
                                cxyOffset -= cxySizePage;
                        }
                        else
                        {
                                cxyScroll = cxyOffset;
                                cxyOffset = 0;
                        }
                        break;
                case SB_PAGEDOWN:       // page down or page right
                        if(cxyOffset < cxyMax - cxySizePage)
                        {
                                cxyScroll = -cxySizePage;
                                cxyOffset += cxySizePage;
                        }
                        else
                        {
                                cxyScroll = cxyOffset - cxyMax;
                                cxyOffset = cxyMax;
                        }
                        break;
                case SB_THUMBTRACK:
                        if(IsNoThumbTracking())
                                break;
                        // else fall through
                case SB_THUMBPOSITION:
                        {
                                SCROLLINFO si = { sizeof(SCROLLINFO), SIF_TRACKPOS };
                                if(pT->GetScrollInfo(nType, &si))
                                {
                                        cxyScroll = cxyOffset - si.nTrackPos;
                                        cxyOffset = si.nTrackPos;
                                }
                        }
                        break;
                case SB_ENDSCROLL:
                default:
                        bUpdate = false;
                        break;
                }

                if(bUpdate && cxyScroll != 0)
                {
                        pT->SetScrollPos(nType, cxyOffset, TRUE);
                        if(nType == SB_VERT)
                                pT->ScrollWindowEx(0, cxyScroll, m_uScrollFlags);
                        else
                                pT->ScrollWindowEx(cxyScroll, 0, m_uScrollFlags);
                }
        }

        static int CalcLineOrPage(int nVal, int nMax, int nDiv)
        {
                if(nVal == 0)
                {
                        nVal = nMax / nDiv;
                        if(nVal < 1)
                                nVal = 1;
                }
                else if(nVal > nMax)
                {
                        nVal = nMax;
                }

                return nVal;
        }

        bool AdjustScrollOffset(int& x, int& y)
        {
                int xOld = x;
                int yOld = y;

                int cxMax = m_sizeAll.cx - m_sizeClient.cx;
                if(x > cxMax)
                        x = (cxMax >= 0) ? cxMax : 0;
                else if(x < 0)
                        x = 0;

                int cyMax = m_sizeAll.cy - m_sizeClient.cy;
                if(y > cyMax)
                        y = (cyMax >= 0) ? cyMax : 0;
                else if(y < 0)
                        y = 0;

                return (x != xOld || y != yOld);
        }

        void GetSystemSettings()
        {
#ifndef _WIN32_WCE
#ifndef SPI_GETWHEELSCROLLLINES
                const UINT SPI_GETWHEELSCROLLLINES = 104;
#endif // !SPI_GETWHEELSCROLLLINES
                ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &m_nWheelLines, 0);

#ifndef SPI_GETWHEELSCROLLCHARS
                const UINT SPI_GETWHEELSCROLLCHARS = 0x006C;
#endif // !SPI_GETWHEELSCROLLCHARS
                ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &m_nHWheelChars, 0);

#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                if(m_uMsgMouseWheel != 0)
                        m_uMsgMouseWheel = ::RegisterWindowMessage(MSH_MOUSEWHEEL);

                HWND hWndWheel = FindWindow(MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE);
                if(::IsWindow(hWndWheel))
                {
                        UINT uMsgScrollLines = ::RegisterWindowMessage(MSH_SCROLL_LINES);
                        if(uMsgScrollLines != 0)
                                m_nWheelLines = (int)::SendMessage(hWndWheel, uMsgScrollLines, 0, 0L);
                }
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
#endif // !_WIN32_WCE
        }

        bool IsScrollingChildren() const
        {
                return (m_dwExtendedStyle & SCRL_SCROLLCHILDREN) != 0;
        }

        bool IsErasingBackground() const
        {
                return (m_dwExtendedStyle & SCRL_ERASEBACKGROUND) != 0;
        }

        bool IsNoThumbTracking() const
        {
                return (m_dwExtendedStyle & SCRL_NOTHUMBTRACKING) != 0;
        }

#if (WINVER >= 0x0500)
        bool IsSmoothScroll() const
        {
                return (m_dwExtendedStyle & SCRL_SMOOTHSCROLL) != 0;
        }
#endif // (WINVER >= 0x0500)
};


///////////////////////////////////////////////////////////////////////////////
// CScrollWindowImpl - Implements a scrollable window

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CScrollWindowImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>, public CScrollImpl< T >
{
public:
        BEGIN_MSG_MAP(CScrollWindowImpl)
                MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
                MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
                MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
                MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel)
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE)
                MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
                MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
                MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
                MESSAGE_HANDLER(WM_PAINT, CScrollImpl< T >::OnPaint)
#ifndef _WIN32_WCE
                MESSAGE_HANDLER(WM_PRINTCLIENT, CScrollImpl< T >::OnPaint)
#endif // !_WIN32_WCE
        ALT_MSG_MAP(1)
                COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
                COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
                COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
                COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
                COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
        END_MSG_MAP()
};


///////////////////////////////////////////////////////////////////////////////
// CMapScrollImpl - Provides mapping and scrolling support to any window

#ifndef _WIN32_WCE

template <class T>
class CMapScrollImpl : public CScrollImpl< T >
{
public:
        int m_nMapMode;
        RECT m_rectLogAll;
        SIZE m_sizeLogLine;
        SIZE m_sizeLogPage;

// Constructor
        CMapScrollImpl() : m_nMapMode(MM_TEXT)
        {
                ::SetRectEmpty(&m_rectLogAll);
                m_sizeLogPage.cx = 0;
                m_sizeLogPage.cy = 0;
                m_sizeLogLine.cx = 0;
                m_sizeLogLine.cy = 0;
        }

// Attributes & Operations
        // mapping mode operations
        void SetScrollMapMode(int nMapMode)
        {
                ATLASSERT(nMapMode >= MM_MIN && nMapMode <= MM_MAX_FIXEDSCALE);
                m_nMapMode = nMapMode;
        }

        int GetScrollMapMode() const
        {
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);
                return m_nMapMode;
        }

        // offset operations
        void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE)
        {
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);
                POINT ptOff = { x, y };
                // block: convert logical to device units
                {
                        CWindowDC dc(NULL);
                        dc.SetMapMode(m_nMapMode);
                        dc.LPtoDP(&ptOff);
                }
                CScrollImpl< T >::SetScrollOffset(ptOff, bRedraw);
        }

        void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE)
        {
                SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw);
        }

        void GetScrollOffset(POINT& ptOffset) const
        {
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);
                ptOffset = m_ptOffset;
                // block: convert device to logical units
                {
                        CWindowDC dc(NULL);
                        dc.SetMapMode(m_nMapMode);
                        dc.DPtoLP(&ptOffset);
                }
        }

        // size operations
        void SetScrollSize(int xMin, int yMin, int xMax, int yMax, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                ATLASSERT(xMax > xMin && yMax > yMin);
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);

                ::SetRect(&m_rectLogAll, xMin, yMin, xMax, yMax);

                SIZE sizeAll = { 0 };
                sizeAll.cx = xMax - xMin + 1;
                sizeAll.cy = yMax - yMin + 1;
                // block: convert logical to device units
                {
                        CWindowDC dc(NULL);
                        dc.SetMapMode(m_nMapMode);
                        dc.LPtoDP(&sizeAll);
                }
                CScrollImpl< T >::SetScrollSize(sizeAll, bRedraw, bResetOffset);
                SetScrollLine(0, 0);
                SetScrollPage(0, 0);
        }

        void SetScrollSize(RECT& rcScroll, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                SetScrollSize(rcScroll.left, rcScroll.top, rcScroll.right, rcScroll.bottom, bRedraw, bResetOffset);
        }

        void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                SetScrollSize(0, 0, cx, cy, bRedraw, bResetOffset);
        }

        void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                SetScrollSize(0, 0, size.cx, size.cy, bRedraw, bResetOffset);
        }

        void GetScrollSize(RECT& rcScroll) const
        {
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);
                rcScroll = m_rectLogAll;
        }

        // line operations
        void SetScrollLine(int cxLine, int cyLine)
        {
                ATLASSERT(cxLine >= 0 && cyLine >= 0);
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);

                m_sizeLogLine.cx = cxLine;
                m_sizeLogLine.cy = cyLine;
                SIZE sizeLine = m_sizeLogLine;
                // block: convert logical to device units
                {
                        CWindowDC dc(NULL);
                        dc.SetMapMode(m_nMapMode);
                        dc.LPtoDP(&sizeLine);
                }
                CScrollImpl< T >::SetScrollLine(sizeLine);
        }

        void SetScrollLine(SIZE sizeLine)
        {
                SetScrollLine(sizeLine.cx, sizeLine.cy);
        }

        void GetScrollLine(SIZE& sizeLine) const
        {
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);
                sizeLine = m_sizeLogLine;
        }

        // page operations
        void SetScrollPage(int cxPage, int cyPage)
        {
                ATLASSERT(cxPage >= 0 && cyPage >= 0);
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);

                m_sizeLogPage.cx = cxPage;
                m_sizeLogPage.cy = cyPage;
                SIZE sizePage = m_sizeLogPage;
                // block: convert logical to device units
                {
                        CWindowDC dc(NULL);
                        dc.SetMapMode(m_nMapMode);
                        dc.LPtoDP(&sizePage);
                }
                CScrollImpl< T >::SetScrollPage(sizePage);
        }

        void SetScrollPage(SIZE sizePage)
        {
                SetScrollPage(sizePage.cx, sizePage.cy);
        }

        void GetScrollPage(SIZE& sizePage) const
        {
                ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE);
                sizePage = m_sizeLogPage;
        }

        BEGIN_MSG_MAP(CMapScrollImpl)
                MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
                MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
                MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel)
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
                MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
                MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
                MESSAGE_HANDLER(WM_PAINT, OnPaint)
                MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
        ALT_MSG_MAP(1)
                COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
                COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
                COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
                COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
                COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
        END_MSG_MAP()

        LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                if(wParam != NULL)
                {
                        CDCHandle dc = (HDC)wParam;
                        int nMapModeSav = dc.GetMapMode();
                        dc.SetMapMode(m_nMapMode);
                        POINT ptViewportOrg = { 0, 0 };
                        if(m_nMapMode == MM_TEXT)
                                dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg);
                        else
                                dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y + m_sizeAll.cy, &ptViewportOrg);
                        POINT ptWindowOrg = { 0, 0 };
                        dc.SetWindowOrg(m_rectLogAll.left, m_rectLogAll.top, &ptWindowOrg);

                        pT->DoPaint(dc);

                        dc.SetMapMode(nMapModeSav);
                        dc.SetViewportOrg(ptViewportOrg);
                        dc.SetWindowOrg(ptWindowOrg);
                }
                else
                {
                        CPaintDC dc(pT->m_hWnd);
                        dc.SetMapMode(m_nMapMode);
                        if(m_nMapMode == MM_TEXT)
                                dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y);
                        else
                                dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y + m_sizeAll.cy);
                        dc.SetWindowOrg(m_rectLogAll.left, m_rectLogAll.top);
                        pT->DoPaint(dc.m_hDC);
                }
                return 0;
        }
};

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CMapScrollWindowImpl - Implements scrolling window with mapping

#ifndef _WIN32_WCE

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CMapScrollWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CMapScrollImpl< T >
{
public:
        BEGIN_MSG_MAP(CMapScrollWindowImpl)
                MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
                MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
                MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel)
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
                MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
                MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
                MESSAGE_HANDLER(WM_PAINT, CMapScrollImpl< T >::OnPaint)
                MESSAGE_HANDLER(WM_PRINTCLIENT, CMapScrollImpl< T >::OnPaint)
        ALT_MSG_MAP(1)
                COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
                COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
                COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
                COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
                COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
        END_MSG_MAP()
};

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CFSBWindow - Use as a base instead of CWindow to get flat scroll bar support

#if defined(__ATLCTRLS_H__) && (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)

template <class TBase = ATL::CWindow>
class CFSBWindowT : public TBase, public CFlatScrollBarImpl<CFSBWindowT< TBase > >
{
public:
// Constructors
        CFSBWindowT(HWND hWnd = NULL) : TBase(hWnd)
        { }

        CFSBWindowT< TBase >& operator =(HWND hWnd)
        {
                m_hWnd = hWnd;
                return *this;
        }

// CWindow overrides that use flat scroll bar API
// (only those methods that are used by scroll window classes)
        int SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE)
        {
                ATLASSERT(::IsWindow(m_hWnd));
                return FlatSB_SetScrollPos(nBar, nPos, bRedraw);
        }

        BOOL GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo)
        {
                ATLASSERT(::IsWindow(m_hWnd));
                return FlatSB_GetScrollInfo(nBar, lpScrollInfo);
        }

        BOOL SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE)
        {
                ATLASSERT(::IsWindow(m_hWnd));
                return FlatSB_SetScrollInfo(nBar, lpScrollInfo, bRedraw);
        }
};

typedef CFSBWindowT<ATL::CWindow>   CFSBWindow;

#endif // defined(__ATLCTRLS_H__) && (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)


///////////////////////////////////////////////////////////////////////////////
// CZoomScrollImpl - Provides zooming and scrolling support to any window

#ifndef _WIN32_WCE

// The zoom modes that can be set with the SetZoomMode method
enum
{
        ZOOMMODE_OFF, 
        ZOOMMODE_IN,   // If left mouse button is clicked or dragged, zoom in on point clicked or rectangle dragged.
        ZOOMMODE_OUT   // If left mouse button clicked, zoom out on point clicked.
};

// Notification to parent that zoom scale changed as a result of user mouse action.
#define ZSN_ZOOMCHANGED (NM_FIRST - 50) 

template <class T>
class CZoomScrollImpl : public CScrollImpl< T >
{
public:
        enum { m_cxyMinZoomRect = 12 };   // min rect size to zoom in on rect.

// Data members
        SIZE m_sizeLogAll;              
        SIZE m_sizeLogLine;     
        SIZE m_sizeLogPage;
        float m_fZoomScale;
        float m_fZoomScaleMin;
        float m_fZoomDelta;   // Used in ZOOMMODE_IN and ZOOMMODE_OUT on left-button click.
        int m_nZoomMode;                
        RECT m_rcTrack;
        bool m_bTracking;

// Constructor
        CZoomScrollImpl():
                        m_fZoomScale(1.0),
                        m_fZoomScaleMin(0.5),
                        m_fZoomDelta(0.5),
                        m_nZoomMode(ZOOMMODE_OFF),
                        m_bTracking(false)
        {
                m_sizeLogAll.cx = 0;
                m_sizeLogAll.cy = 0;
                m_sizeLogPage.cx = 0;
                m_sizeLogPage.cy = 0;
                m_sizeLogLine.cx = 0;
                m_sizeLogLine.cy = 0;
                ::SetRectEmpty(&m_rcTrack);
        }

// Attributes & Operations

        // size operations
        void SetScrollSize(int cxLog, int cyLog, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                ATLASSERT(cxLog >= 0 && cyLog >= 0);

                // Set up the defaults
                if (cxLog == 0 && cyLog == 0)
                {
                        cxLog = 1;
                        cyLog = 1;
                }

                m_sizeLogAll.cx = cxLog;
                m_sizeLogAll.cy = cyLog;
                SIZE sizeAll = { 0 };
                sizeAll.cx = (int)((float)m_sizeLogAll.cx * m_fZoomScale);
                sizeAll.cy = (int)((float)m_sizeLogAll.cy * m_fZoomScale);

                CScrollImpl< T >::SetScrollSize(sizeAll, bRedraw, bResetOffset);
        }

        void SetScrollSize(SIZE sizeLog, BOOL bRedraw = TRUE, bool bResetOffset = true)
        {
                SetScrollSize(sizeLog.cx, sizeLog.cy, bRedraw, bResetOffset);
        }

        void GetScrollSize(SIZE& sizeLog) const
        {
                sizeLog = m_sizeLogAll;
        }

        // line operations
        void SetScrollLine(int cxLogLine, int cyLogLine)
        {
                ATLASSERT(cxLogLine >= 0 && cyLogLine >= 0);

                m_sizeLogLine.cx = cxLogLine;
                m_sizeLogLine.cy = cyLogLine;

                SIZE sizeLine = { 0 };
                sizeLine.cx = (int)((float)m_sizeLogLine.cx * m_fZoomScale);
                sizeLine.cy = (int)((float)m_sizeLogLine.cy * m_fZoomScale);
                CScrollImpl< T >::SetScrollLine(sizeLine);
        }

        void SetScrollLine(SIZE sizeLogLine)
        {
                SetScrollLine(sizeLogLine.cx, sizeLogLine.cy);
        }

        void GetScrollLine(SIZE& sizeLogLine) const
        {
                sizeLogLine = m_sizeLogLine;
        }

        // page operations
        void SetScrollPage(int cxLogPage, int cyLogPage)
        {
                ATLASSERT(cxLogPage >= 0 && cyLogPage >= 0);

                m_sizeLogPage.cx = cxLogPage;
                m_sizeLogPage.cy = cyLogPage;

                SIZE sizePage = { 0 };
                sizePage.cx = (int)((float)m_sizeLogPage.cx * m_fZoomScale);
                sizePage.cy = (int)((float)m_sizeLogPage.cy * m_fZoomScale);

                CScrollImpl< T >::SetScrollPage(sizePage);
        }

        void SetScrollPage(SIZE sizeLogPage)
        {
                SetScrollPage(sizeLogPage.cx, sizeLogPage.cy);
        }

        void GetScrollPage(SIZE& sizeLogPage) const
        {
                sizeLogPage = m_sizeLogPage;
        }

        void SetZoomScale(float fZoomScale)
        {
                ATLASSERT(fZoomScale > 0);

                if(fZoomScale > 0 && fZoomScale >= m_fZoomScaleMin)
                        m_fZoomScale = fZoomScale;
        }

        float GetZoomScale() const
        {
                return m_fZoomScale;
        }

        void SetZoomScaleMin(float fZoomScaleMin)
        {
                m_fZoomScaleMin = fZoomScaleMin;
        }

        float GetZoomScaleMin() const
        {
                return m_fZoomScaleMin;
        }

        void SetZoomDelta(float fZoomDelta)
        {
                ATLASSERT(fZoomDelta >= 0);

                if(fZoomDelta >= 0)
                        m_fZoomDelta = fZoomDelta;
        }

        float GetZoomDelta() const
        {
                return m_fZoomDelta;
        }

        void SetZoomMode(int nZoomMode)
        {
                m_nZoomMode = nZoomMode;
        }

        int GetZoomMode() const
        {
                return m_nZoomMode;
        }

        void Zoom(int x, int y, float fZoomScale)
        {
                if(fZoomScale <= 0)
                        return;

                fZoomScale = __max(fZoomScale, m_fZoomScaleMin);

                T* pT = static_cast<T*>(this);
                POINT pt = { x, y };
                if(!pT->PtInDevRect(pt))
                        return;

                pT->ViewDPtoLP(&pt);
                pT->Zoom(fZoomScale, false);
                pT->CenterOnLogicalPoint(pt);
        }

        void Zoom(POINT pt, float fZoomScale)
        {
                T* pT = static_cast<T*>(this);
                pT->Zoom(pt.x, pt.y, fZoomScale);
        }

        void Zoom(RECT& rc)
        {
                T* pT = static_cast<T*>(this);
                RECT rcZoom = rc;
                pT->NormalizeRect(rcZoom);
                SIZE size = { rcZoom.right - rcZoom.left, rcZoom.bottom - rcZoom.top };
                POINT pt = { rcZoom.left + size.cx / 2, rcZoom.top + size.cy / 2 };
                if(size.cx < m_cxyMinZoomRect || size.cy < m_cxyMinZoomRect)
                {
                        pT->Zoom(pt, m_fZoomScale + m_fZoomDelta);
                        return;
                }

                ATLASSERT(size.cx > 0 && size.cy > 0);
                
                float fScaleH = (float)(m_sizeClient.cx  + 1) / (float)size.cx;
                float fScaleV = (float)(m_sizeClient.cy + 1) / (float)size.cy;
                float fZoomScale = __min(fScaleH, fScaleV) * m_fZoomScale;
                pT->Zoom(pt, fZoomScale);               
        }

        void Zoom(float fZoomScale, bool bCenter = true)
        {
                if(fZoomScale <= 0)
                        return;

                fZoomScale = __max(fZoomScale, m_fZoomScaleMin);


                T* pT = static_cast<T*>(this);
                POINT pt = { 0 };
                if(bCenter)
                {
                        RECT rc;
                        ::GetClientRect(pT->m_hWnd, &rc);
                        pt.x = rc.right / 2;
                        pt.y = rc.bottom / 2;
                        pT->ViewDPtoLP(&pt);
                }

                // Modify the Viewport extent
                m_fZoomScale = fZoomScale;
                SIZE sizeAll = { 0 };
                sizeAll.cx = (int)((float)m_sizeLogAll.cx * fZoomScale);
                sizeAll.cy = (int)((float)m_sizeLogAll.cy * fZoomScale);
                
                // Update scroll bars and window
                CScrollImpl< T >::SetScrollSize(sizeAll);

                if(bCenter)
                        pT->CenterOnLogicalPoint(pt);
        }

        // Helper functions
        void PrepareDC(CDCHandle dc)
        {
                ATLASSERT(m_sizeAll.cx >= 0 && m_sizeAll.cy >= 0);
                dc.SetMapMode(MM_ANISOTROPIC);
                dc.SetWindowExt(m_sizeLogAll);
                dc.SetViewportExt(m_sizeAll);
                dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y);
        }

        void ViewDPtoLP(LPPOINT lpPoints, int nCount = 1)
        {
                ATLASSERT(lpPoints);
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));

                CWindowDC dc(pT->m_hWnd);
                pT->PrepareDC(dc.m_hDC);
                dc.DPtoLP(lpPoints, nCount);
        }

        void ViewLPtoDP(LPPOINT lpPoints, int nCount = 1)
        {
                ATLASSERT(lpPoints);
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
        
                CWindowDC dc(pT->m_hWnd);
                pT->PrepareDC(dc.m_hDC);
                dc.LPtoDP(lpPoints, nCount);
        }

        void ClientToDevice(POINT &pt)
        {
                pt.x += m_ptOffset.x;
                pt.y += m_ptOffset.y;
        }        

        void DeviceToClient(POINT &pt)
        {
                pt.x -= m_ptOffset.x;
                pt.y -= m_ptOffset.y;
        }

        void CenterOnPoint(POINT pt)
        {
                T* pT = static_cast<T*>(this);
                RECT rect;
                pT->GetClientRect(&rect);

                int xOfs = pt.x - (rect.right / 2) + m_ptOffset.x;
                if(xOfs < 0)
                {
                        xOfs = 0;
                }
                else 
                {
                        int xMax = __max((int)(m_sizeAll.cx - rect.right), 0);
                        if(xOfs > xMax)
                                xOfs = xMax;
                }
                
                int yOfs = pt.y - (rect.bottom / 2) + m_ptOffset.y;
                if(yOfs < 0)
                {
                        yOfs = 0;
                }
                else 
                {
                        int yMax = __max((int)(m_sizeAll.cy - rect.bottom), 0);
                        if(yOfs > yMax)
                                yOfs = yMax;
                }

                CScrollImpl< T >::SetScrollOffset(xOfs, yOfs);
        }

        void CenterOnLogicalPoint(POINT ptLog)
        {
                T* pT = static_cast<T*>(this);
                pT->ViewLPtoDP(&ptLog);
                pT->DeviceToClient(ptLog);
                pT->CenterOnPoint(ptLog);
        }

        BOOL PtInDevRect(POINT pt)
        {
                RECT rc = { 0, 0, m_sizeAll.cx, m_sizeAll.cy };
                ::OffsetRect(&rc, -m_ptOffset.x, -m_ptOffset.y);
                return ::PtInRect(&rc, pt);
        }

        void NormalizeRect(RECT& rc)
        {
                if(rc.left > rc.right) 
                {
                        int r = rc.right;
                        rc.right = rc.left;
                        rc.left = r;
                }
                if(rc.top > rc.bottom)
                {
                        int b = rc.bottom;
                        rc.bottom = rc.top;
                        rc.top = b;
                }
        }

        void DrawTrackRect()
        {
                T* pT = static_cast<T*>(this);
                const SIZE sizeLines = { 2, 2 };
                RECT rc = m_rcTrack;
                pT->NormalizeRect(rc);
                if(!::IsRectEmpty(&rc))
                {
                        ::MapWindowPoints(pT->m_hWnd, NULL, (LPPOINT)&rc, 2);
                        CWindowDC dc(NULL);
                        dc.DrawDragRect(&rc, sizeLines, NULL, sizeLines);
                }
        }

        void NotifyParentZoomChanged()
        {
                T* pT = static_cast<T*>(this);
                int nId = pT->GetDlgCtrlID();
                NMHDR nmhdr = { pT->m_hWnd, nId, ZSN_ZOOMCHANGED };
                ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nId, (LPARAM)&nmhdr);
        }

        BEGIN_MSG_MAP(CZoomScrollImpl)
                MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
                MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
                MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
                MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel)
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
                MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
                MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
                MESSAGE_HANDLER(WM_PAINT, OnPaint)
                MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
                MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
                MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
                MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
                MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
        ALT_MSG_MAP(1)
                COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
                COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
                COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
                COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
                COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
        END_MSG_MAP()

        LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                ATLASSERT(::IsWindow(pT->m_hWnd));
                ATLASSERT(m_sizeLogAll.cx >= 0 && m_sizeLogAll.cy >= 0);
                ATLASSERT(m_sizeAll.cx >= 0 && m_sizeAll.cy >= 0);

                if(wParam != NULL)
                {
                        CDCHandle dc = (HDC)wParam;
                        int nMapModeSav = dc.GetMapMode();
                        dc.SetMapMode(MM_ANISOTROPIC);
                        SIZE szWindowExt = { 0, 0 };
                        dc.SetWindowExt(m_sizeLogAll, &szWindowExt);
                        SIZE szViewportExt = { 0, 0 };
                        dc.SetViewportExt(m_sizeAll, &szViewportExt);
                        POINT ptViewportOrg = { 0, 0 };
                        dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg);

                        pT->DoPaint(dc);

                        dc.SetMapMode(nMapModeSav);
                        dc.SetWindowExt(szWindowExt);
                        dc.SetViewportExt(szViewportExt);
                        dc.SetViewportOrg(ptViewportOrg);
                }
                else
                {
                        CPaintDC dc(pT->m_hWnd);
                        pT->PrepareDC(dc.m_hDC);
                        pT->DoPaint(dc.m_hDC);
                }
                return 0;
        }

        LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
        {
                if(m_nZoomMode == ZOOMMODE_IN && !m_bTracking)
                {
                        T* pT = static_cast<T*>(this);
                        POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
                        if(pT->PtInDevRect(pt))
                        {
                                pT->SetCapture();
                                m_bTracking = true;
                                ::SetRect(&m_rcTrack, pt.x, pt.y, pt.x, pt.y);
                        }       
                }
                bHandled = FALSE;
                return 0;
        }

        LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
        {
                if(m_bTracking)
                {
                        T* pT = static_cast<T*>(this);
                        POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
                        if(pT->PtInDevRect(pt))
                        {
                                pT->DrawTrackRect();
                                m_rcTrack.right = pt.x;
                                m_rcTrack.bottom = pt.y;
                                pT->DrawTrackRect();
                        }
                }
                bHandled = FALSE;
                return 0;
        }

        LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
        {
                ::ReleaseCapture();
                if(m_nZoomMode == ZOOMMODE_OUT)
                {
                        T* pT = static_cast<T*>(this);
                        pT->Zoom(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), m_fZoomScale - m_fZoomDelta);
                        pT->NotifyParentZoomChanged();
                }
                bHandled = FALSE;
                return 0;
        }

        LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
        {
                if(m_bTracking)
                {
                        m_bTracking = false;
                        T* pT = static_cast<T*>(this);
                        pT->DrawTrackRect();
                        pT->Zoom(m_rcTrack);
                        pT->NotifyParentZoomChanged();
                        ::SetRectEmpty(&m_rcTrack);
                }
                bHandled = FALSE;
                return 0;
        }       

        LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
                if(LOWORD(lParam) == HTCLIENT && m_nZoomMode != ZOOMMODE_OFF)
                {
                        T* pT = static_cast<T*>(this);
                        if((HWND)wParam == pT->m_hWnd)
                        {
                                DWORD dwPos = ::GetMessagePos();
                                POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
                                pT->ScreenToClient(&pt);
                                if(pT->PtInDevRect(pt))
                                {
                                        ::SetCursor(::LoadCursor(NULL, IDC_CROSS));
                                        return 1;
                                }
                        }
                }
                bHandled = FALSE;
                return 0;
        }
};

///////////////////////////////////////////////////////////////////////////////
// CZoomScrollWindowImpl - Implements scrolling window with zooming

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CZoomScrollWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CZoomScrollImpl< T >
{
public:
        BEGIN_MSG_MAP(CZoomScrollWindowImpl)
                MESSAGE_HANDLER(WM_SETCURSOR, CZoomScrollImpl< T >::OnSetCursor)
                MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
                MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
                MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel)
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
                MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
                MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
                MESSAGE_HANDLER(WM_PAINT, CZoomScrollImpl< T >::OnPaint)
                MESSAGE_HANDLER(WM_PRINTCLIENT, CZoomScrollImpl< T >::OnPaint)
                MESSAGE_HANDLER(WM_LBUTTONDOWN, CZoomScrollImpl< T >::OnLButtonDown)
                MESSAGE_HANDLER(WM_MOUSEMOVE, CZoomScrollImpl< T >::OnMouseMove)
                MESSAGE_HANDLER(WM_LBUTTONUP, CZoomScrollImpl< T >::OnLButtonUp)
                MESSAGE_HANDLER(WM_CAPTURECHANGED, CZoomScrollImpl< T >::OnCaptureChanged)
        ALT_MSG_MAP(1)
                COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
                COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
                COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
                COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
                COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
        END_MSG_MAP()
};

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CScrollContainer

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CScrollContainerImpl : public CScrollWindowImpl< T, TBase, TWinTraits >
{
public:
        DECLARE_WND_CLASS_EX(NULL, 0, -1)

        typedef CScrollWindowImpl< T, TBase, TWinTraits >   _baseClass;

// Data members
        ATL::CWindow m_wndClient;
        bool m_bAutoSizeClient;
        bool m_bDrawEdgeIfEmpty;

// Constructor
        CScrollContainerImpl() : m_bAutoSizeClient(true), m_bDrawEdgeIfEmpty(false)
        {
                // Set CScrollWindowImpl extended style
                SetScrollExtendedStyle(SCRL_SCROLLCHILDREN);
        }

// Attributes
        HWND GetClient() const
        {
                return m_wndClient;
        }

        HWND SetClient(HWND hWndClient, bool bClientSizeAsMin = true)
        {
                ATLASSERT(::IsWindow(m_hWnd));

                HWND hWndOldClient = m_wndClient;
                m_wndClient = hWndClient;

                SetRedraw(FALSE);
                SetScrollSize(1, 1, FALSE);

                if(m_wndClient.m_hWnd != NULL)
                {
                        m_wndClient.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE);

                        if(bClientSizeAsMin)
                        {
                                RECT rect = { 0 };
                                m_wndClient.GetWindowRect(&rect);
                                if((rect.right - rect.left) > 0 && (rect.bottom - rect.top) > 0)
                                        SetScrollSize(rect.right - rect.left, rect.bottom - rect.top, FALSE);
                        }

                        T* pT = static_cast<T*>(this);
                        pT->UpdateLayout();
                }

                SetRedraw(TRUE);
                RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);

                return hWndOldClient;
        }

// Message map and handlers
        BEGIN_MSG_MAP(CScrollContainerImpl)
                MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
                MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
                MESSAGE_HANDLER(WM_SIZE, OnSize)
                CHAIN_MSG_MAP(_baseClass)
                FORWARD_NOTIFICATIONS()
        ALT_MSG_MAP(1)
                CHAIN_MSG_MAP_ALT(_baseClass, 1)
        END_MSG_MAP()

        LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                if(m_wndClient.m_hWnd != NULL)
                        m_wndClient.SetFocus();

                return 0;
        }

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

        LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
        {
                BOOL bTmp = TRUE;
                LRESULT lRet = _baseClass::OnSize(uMsg, wParam, lParam, bTmp);

                T* pT = static_cast<T*>(this);
                pT->UpdateLayout();

                return lRet;
        }

// Overrides for CScrollWindowImpl
        void DoPaint(CDCHandle dc)
        {
                if(!m_bAutoSizeClient || m_wndClient.m_hWnd == NULL)
                {
                        T* pT = static_cast<T*>(this);
                        RECT rect = { 0 };
                        pT->GetContainerRect(rect);

                        if(m_bDrawEdgeIfEmpty && m_wndClient.m_hWnd == NULL)
                                dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);

                        dc.FillRect(&rect, COLOR_APPWORKSPACE);
                }
        }

        void ScrollToView(POINT pt)
        {
                CScrollWindowImpl< T, TBase, TWinTraits >::ScrollToView(pt);
        }

        void ScrollToView(RECT& rect)
        {
                CScrollWindowImpl< T, TBase, TWinTraits >::ScrollToView(rect);
        }

        void ScrollToView(HWND hWnd)   // client window coordinates
        {
                T* pT = static_cast<T*>(this);
                pT;   // avoid level 4 warning
                ATLASSERT(::IsWindow(pT->m_hWnd));
                ATLASSERT(m_wndClient.IsWindow());

                RECT rect = { 0 };
                ::GetWindowRect(hWnd, &rect);
                ::MapWindowPoints(NULL, m_wndClient.m_hWnd, (LPPOINT)&rect, 2);
                ScrollToView(rect);
        }

// Implementation - overrideable methods
        void UpdateLayout()
        {
                ATLASSERT(::IsWindow(m_hWnd));

                if(m_bAutoSizeClient && m_wndClient.m_hWnd != NULL)
                {
                        T* pT = static_cast<T*>(this);
                        RECT rect = { 0 };
                        pT->GetContainerRect(rect);

                        m_wndClient.SetWindowPos(NULL, &rect, SWP_NOZORDER | SWP_NOMOVE);
                }
                else
                {
                        Invalidate();
                }
        }

        void GetContainerRect(RECT& rect)
        {
                GetClientRect(&rect);

                if(rect.right < m_sizeAll.cx)
                        rect.right = m_sizeAll.cx;

                if(rect.bottom < m_sizeAll.cy)
                        rect.bottom = m_sizeAll.cy;
        }
};

class CScrollContainer : public CScrollContainerImpl<CScrollContainer>
{
public:
        DECLARE_WND_CLASS_EX(_T("WTL_ScrollContainer"), 0, -1)
};

}; // namespace WTL

#endif // __ATLSCRL_H__

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