Mercurial > foo_out_sdl
diff foosdk/wtl/Include/atlscrl.h @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/wtl/Include/atlscrl.h Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,2127 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. 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 Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLSCRL_H__ +#define __ATLSCRL_H__ + +#pragma once + +#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 + + +/////////////////////////////////////////////////////////////////////////////// +// 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 +#define SCRL_SMOOTHSCROLL 0x00000008 +#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 + 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), + 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); + (void)pT; // avoid level 4 warning + m_uScrollFlags = pT->uSCROLL_FLAGS | (IsScrollingChildren() ? SW_SCROLLCHILDREN : 0) | (IsErasingBackground() ? SW_ERASE : 0); + m_uScrollFlags |= (IsSmoothScroll() ? SW_SMOOTHSCROLL : 0); + 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 = {}; + ::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 = {}; + ::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 = {}; + 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 = {}; + ::GetWindowRect(hWnd, &rect); + ::OffsetRect(&rect, m_ptOffset.x, m_ptOffset.y); + ::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) + MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnMouseHWheel) + MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) + MESSAGE_HANDLER(WM_SIZE, OnSize) + MESSAGE_HANDLER(WM_PAINT, OnPaint) + MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) + // 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) + { + T* pT = static_cast<T*>(this); + pT->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)); + + int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam); + 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 if(m_sizeAll.cx > m_sizeClient.cx) // 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)); + + pT->DoSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + + 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 DoSize(int cx, int cy) + { + m_sizeClient.cx = cx; + m_sizeClient.cy = cy; + + T* pT = static_cast<T*>(this); + + // 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); + } + } + + void DoScroll(int nType, int nScrollCode, int& cxyOffset, int cxySizeAll, int cxySizePage, int cxySizeLine) + { + T* pT = static_cast<T*>(this); + RECT rect = {}; + 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() + { + ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &m_nWheelLines, 0); + +#ifndef SPI_GETWHEELSCROLLCHARS + const UINT SPI_GETWHEELSCROLLCHARS = 0x006C; +#endif + ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &m_nHWheelChars, 0); + } + + 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; + } + + bool IsSmoothScroll() const + { + return (m_dwExtendedStyle & SCRL_SMOOTHSCROLL) != 0; + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// 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: + BOOL SubclassWindow(HWND hWnd) + { + BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); + if(bRet != FALSE) + { + T* pT = static_cast<T*>(this); + pT->GetSystemSettings(); + + RECT rect = {}; + this->GetClientRect(&rect); + pT->DoSize(rect.right, rect.bottom); + } + + return bRet; + } + + 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) + 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) + MESSAGE_HANDLER(WM_PRINTCLIENT, CScrollImpl< 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() +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CMapScrollImpl - Provides mapping and scrolling support to any window + +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 = this->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 = {}; + 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) + 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(-this->m_ptOffset.x, -this->m_ptOffset.y, &ptViewportOrg); + else + dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y + this->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(-this->m_ptOffset.x, -this->m_ptOffset.y); + else + dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y + this->m_sizeAll.cy); + dc.SetWindowOrg(m_rectLogAll.left, m_rectLogAll.top); + pT->DoPaint(dc.m_hDC); + } + return 0; + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CMapScrollWindowImpl - Implements scrolling window with mapping + +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: + BOOL SubclassWindow(HWND hWnd) + { + BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); + if(bRet != FALSE) + { + T* pT = static_cast<T*>(this); + pT->GetSystemSettings(); + + RECT rect = {}; + this->GetClientRect(&rect); + pT->DoSize(rect.right, rect.bottom); + } + + return bRet; + } + + 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) + 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() +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CFSBWindow - Use as a base instead of CWindow to get flat scroll bar support + +#ifdef __ATLCTRLS_H__ + +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) + { + this->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(this->m_hWnd)); + return this->FlatSB_SetScrollPos(nBar, nPos, bRedraw); + } + + BOOL GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return this->FlatSB_GetScrollInfo(nBar, lpScrollInfo); + } + + BOOL SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + return this->FlatSB_SetScrollInfo(nBar, lpScrollInfo, bRedraw); + } +}; + +typedef CFSBWindowT<ATL::CWindow> CFSBWindow; + +#endif // __ATLCTRLS_H__ + + +/////////////////////////////////////////////////////////////////////////////// +// CZoomScrollImpl - Provides zooming and scrolling support to any window + +// 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. + + struct _ChildPlacement + { + HWND hWnd; + int x; + int y; + int cx; + int cy; + + bool operator ==(const _ChildPlacement& cp) const { return (memcmp(this, &cp, sizeof(_ChildPlacement)) == 0); } + }; + +// Data members + SIZE m_sizeLogAll; + SIZE m_sizeLogLine; + SIZE m_sizeLogPage; + float m_fZoomScale; + float m_fZoomScaleMin; + float m_fZoomScaleMax; + float m_fZoomDelta; // Used in ZOOMMODE_IN and ZOOMMODE_OUT on left-button click. + int m_nZoomMode; + RECT m_rcTrack; + bool m_bTracking; + + bool m_bZoomChildren; + ATL::CSimpleArray<_ChildPlacement> m_arrChildren; + +// Constructor + CZoomScrollImpl(): m_fZoomScale(1.0f), m_fZoomScaleMin(0.1f), m_fZoomScaleMax(100.0f), m_fZoomDelta(0.5f), + m_nZoomMode(ZOOMMODE_OFF), m_bTracking(false), m_bZoomChildren(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 = {}; + 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 = {}; + 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 = {}; + 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.0f); + if(fZoomScale <= 0.0f) + return; + + m_fZoomScale = fZoomScale; + if(m_fZoomScale < m_fZoomScaleMin) + m_fZoomScale = m_fZoomScaleMin; + else if(m_fZoomScale > m_fZoomScaleMax) + m_fZoomScale = m_fZoomScaleMax; + } + + float GetZoomScale() const + { + return m_fZoomScale; + } + + void SetZoomScaleMin(float fZoomScaleMin) + { + ATLASSERT(fZoomScaleMin > 0.0f); + ATLASSERT(fZoomScaleMin <= m_fZoomScaleMax); + + m_fZoomScaleMin = fZoomScaleMin; + } + + float GetZoomScaleMin() const + { + return m_fZoomScaleMin; + } + + void SetZoomScaleMax(float fZoomScaleMax) + { + ATLASSERT(fZoomScaleMax > 0.0f); + ATLASSERT(m_fZoomScaleMin <= fZoomScaleMax); + + m_fZoomScaleMax = fZoomScaleMax; + } + + float GetZoomScaleMax() const + { + return m_fZoomScaleMax; + } + + void SetZoomDelta(float fZoomDelta) + { + ATLASSERT(fZoomDelta >= 0.0f); + + if(fZoomDelta >= 0.0f) + m_fZoomDelta = fZoomDelta; + } + + float GetZoomDelta() const + { + return m_fZoomDelta; + } + + void SetZoomMode(int nZoomMode) + { + m_nZoomMode = nZoomMode; + } + + int GetZoomMode() const + { + return m_nZoomMode; + } + + void SetZoomChildren(bool bEnable = true) + { + T* pT = static_cast<T*>(this); + ATLASSERT(::IsWindow(pT->m_hWnd)); + + m_bZoomChildren = bEnable; + + m_arrChildren.RemoveAll(); + if(m_bZoomChildren) + { + for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT)) + { + RECT rect = {}; + ::GetWindowRect(hWndChild, &rect); + ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2); + + _ChildPlacement cp = {}; + cp.hWnd = hWndChild; + cp.x = rect.left; + cp.y = rect.top; + cp.cx = rect.right - rect.left; + cp.cy = rect.bottom - rect.top; + m_arrChildren.Add(cp); + } + } + } + + bool GetZoomChildren() const + { + return m_bZoomChildren; + } + + void Zoom(int x, int y, float fZoomScale) + { + if(fZoomScale <= 0.0f) + return; + + if(fZoomScale < m_fZoomScaleMin) + fZoomScale = m_fZoomScaleMin; + else if(fZoomScale > m_fZoomScaleMax) + fZoomScale = m_fZoomScaleMax; + + 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)(this->m_sizeClient.cx + 1) / (float)size.cx; + float fScaleV = (float)(this->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.0f) + return; + + if(fZoomScale < m_fZoomScaleMin) + fZoomScale = m_fZoomScaleMin; + else if(fZoomScale > m_fZoomScaleMax) + fZoomScale = m_fZoomScaleMax; + + T* pT = static_cast<T*>(this); + POINT pt = { 0, 0 }; + if(bCenter) + { + RECT rcClient = {}; + ::GetClientRect(pT->m_hWnd, &rcClient); + pt.x = rcClient.right / 2; + pt.y = rcClient.bottom / 2; + pT->ViewDPtoLP(&pt); + } + + // Modify the Viewport extent + SIZE sizeAll = {}; + 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); + + // Zoom all children if needed + if(m_bZoomChildren && (m_fZoomScale != fZoomScale)) + { + for(int i = 0; i < m_arrChildren.GetSize(); i++) + { + ATLASSERT(::IsWindow(m_arrChildren[i].hWnd)); + + ::SetWindowPos(m_arrChildren[i].hWnd, NULL, + (int)((float)m_arrChildren[i].x * fZoomScale + 0.5f), + (int)((float)m_arrChildren[i].y * fZoomScale + 0.5f), + (int)((float)m_arrChildren[i].cx * fZoomScale + 0.5f), + (int)((float)m_arrChildren[i].cy * fZoomScale + 0.5f), + SWP_NOZORDER | SWP_NOACTIVATE); + } + } + + // Set new zoom scale + m_fZoomScale = fZoomScale; + + if(bCenter) + pT->CenterOnLogicalPoint(pt); + } + + void ZoomIn(bool bCenter = true) + { + T* pT = static_cast<T*>(this); + pT->Zoom(m_fZoomScale + m_fZoomDelta, bCenter); + } + + void ZoomOut(bool bCenter = true) + { + T* pT = static_cast<T*>(this); + pT->Zoom(m_fZoomScale - m_fZoomDelta, bCenter); + } + + void ZoomDefault(bool bCenter = true) + { + T* pT = static_cast<T*>(this); + pT->Zoom(1.0f, bCenter); + } + + // Helper functions + void PrepareDC(CDCHandle dc) + { + ATLASSERT((this->m_sizeAll.cx >= 0) && (this->m_sizeAll.cy >= 0)); + dc.SetMapMode(MM_ANISOTROPIC); + dc.SetWindowExt(this->m_sizeLogAll); + dc.SetViewportExt(this->m_sizeAll); + dc.SetViewportOrg(-this->m_ptOffset.x, -this->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 += this->m_ptOffset.x; + pt.y += this->m_ptOffset.y; + } + + void DeviceToClient(POINT &pt) + { + pt.x -= this->m_ptOffset.x; + pt.y -= this->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) + this->m_ptOffset.x; + if(xOfs < 0) + { + xOfs = 0; + } + else + { + int xMax = __max((int)(this->m_sizeAll.cx - rect.right), 0); + if(xOfs > xMax) + xOfs = xMax; + } + + int yOfs = pt.y - (rect.bottom / 2) + this->m_ptOffset.y; + if(yOfs < 0) + { + yOfs = 0; + } + else + { + int yMax = __max((int)(this->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, this->m_sizeAll.cx, this->m_sizeAll.cy }; + ::OffsetRect(&rc, -this->m_ptOffset.x, -this->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)) + { + CClientDC dc(pT->m_hWnd); + dc.DrawDragRect(&rc, sizeLines, NULL, sizeLines); + } + } + + void NotifyParentZoomChanged() + { + T* pT = static_cast<T*>(this); + int nId = pT->GetDlgCtrlID(); + NMHDR nmhdr = { pT->m_hWnd, (UINT_PTR)nId, ZSN_ZOOMCHANGED }; + ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nId, (LPARAM)&nmhdr); + } + + void DoWheelZoom(int zDelta) + { + float fZoomScale = m_fZoomScale + ((zDelta > 0) ? m_fZoomDelta : -m_fZoomDelta); + T* pT = static_cast<T*>(this); + pT->Zoom(fZoomScale); + pT->NotifyParentZoomChanged(); + } + + 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, OnMouseWheel) + 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 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; + } + + LRESULT OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if((GET_KEYSTATE_WPARAM(wParam) & MK_CONTROL) != 0) // handle zoom if Ctrl is pressed + { + int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam); + T* pT = static_cast<T*>(this); + pT->DoWheelZoom(zDelta); + } + else + { + CScrollImpl< T >::OnMouseWheel(uMsg, wParam, lParam, bHandled); + } + + return 0; + } + + 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((this->m_sizeAll.cx >= 0) && (this->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(this->m_sizeAll, &szViewportExt); + POINT ptViewportOrg = { 0, 0 }; + dc.SetViewportOrg(-this->m_ptOffset.x, -this->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); + + RECT rcClip; + pT->GetClientRect(&rcClip); + if((this->m_ptOffset.x == 0) && (this->m_ptOffset.y == 0)) + { + if(rcClip.right > this->m_sizeAll.cx) + rcClip.right = this->m_sizeAll.cx; + if(rcClip.bottom > this->m_sizeAll.cy) + rcClip.bottom = this->m_sizeAll.cy; + } + ::MapWindowPoints(pT->m_hWnd, NULL, (LPPOINT)&rcClip, 2); + ::ClipCursor(&rcClip); + } + } + + 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 + 1; + m_rcTrack.bottom = pt.y + 1; + 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); + ::ClipCursor(NULL); + } + + 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: + BOOL SubclassWindow(HWND hWnd) + { + BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); + if(bRet != FALSE) + { + T* pT = static_cast<T*>(this); + pT->GetSystemSettings(); + + RECT rect = {}; + this->GetClientRect(&rect); + pT->DoSize(rect.right, rect.bottom); + } + + return bRet; + } + + 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, CZoomScrollImpl< T >::OnMouseWheel) + 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() +}; + + +/////////////////////////////////////////////////////////////////////////////// +// 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_EX2(NULL, T, 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 + this->SetScrollExtendedStyle(SCRL_SCROLLCHILDREN); + } + +// Attributes + HWND GetClient() const + { + return m_wndClient; + } + + HWND SetClient(HWND hWndClient, bool bClientSizeAsMin = true) + { + ATLASSERT(::IsWindow(this->m_hWnd)); + + HWND hWndOldClient = m_wndClient; + m_wndClient = hWndClient; + + this->SetRedraw(FALSE); + this->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 = {}; + m_wndClient.GetWindowRect(&rect); + if(((rect.right - rect.left) > 0) && ((rect.bottom - rect.top) > 0)) + this->SetScrollSize(rect.right - rect.left, rect.bottom - rect.top, FALSE); + } + + T* pT = static_cast<T*>(this); + pT->UpdateLayout(); + } + + this->SetRedraw(TRUE); + this->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) + 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 + } + +// Overrides for CScrollWindowImpl + void DoSize(int cx, int cy) + { + _baseClass::DoSize(cx, cy); + + T* pT = static_cast<T*>(this); + pT->UpdateLayout(); + } + + void DoPaint(CDCHandle dc) + { + if(!m_bAutoSizeClient || (m_wndClient.m_hWnd == NULL)) + { + T* pT = static_cast<T*>(this); + RECT rect = {}; + 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); + (void)pT; // avoid level 4 warning + ATLASSERT(::IsWindow(pT->m_hWnd)); + ATLASSERT(m_wndClient.IsWindow()); + + RECT rect = {}; + ::GetWindowRect(hWnd, &rect); + ::MapWindowPoints(NULL, m_wndClient.m_hWnd, (LPPOINT)&rect, 2); + ScrollToView(rect); + } + +// Implementation - overrideable methods + void UpdateLayout() + { + ATLASSERT(::IsWindow(this->m_hWnd)); + + if(m_bAutoSizeClient && (m_wndClient.m_hWnd != NULL)) + { + T* pT = static_cast<T*>(this); + RECT rect = {}; + pT->GetContainerRect(rect); + + m_wndClient.SetWindowPos(NULL, &rect, SWP_NOZORDER | SWP_NOMOVE); + } + else + { + this->Invalidate(); + } + } + + void GetContainerRect(RECT& rect) + { + this->GetClientRect(&rect); + + if(rect.right < this->m_sizeAll.cx) + rect.right = this->m_sizeAll.cx; + + if(rect.bottom < this->m_sizeAll.cy) + rect.bottom = this->m_sizeAll.cy; + } +}; + +class CScrollContainer : public CScrollContainerImpl<CScrollContainer> +{ +public: + DECLARE_WND_CLASS_EX(_T("WTL_ScrollContainer"), 0, -1) +}; + +} // namespace WTL + +#endif // __ATLSCRL_H__
