comparison 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
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 // Windows Template Library - WTL version 10.0
2 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
3 //
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Public License (http://opensource.org/licenses/MS-PL)
7 // which can be found in the file MS-PL.txt at the root folder.
8
9 #ifndef __ATLSCRL_H__
10 #define __ATLSCRL_H__
11
12 #pragma once
13
14 #ifndef __ATLAPP_H__
15 #error atlscrl.h requires atlapp.h to be included first
16 #endif
17
18 #ifndef __ATLWIN_H__
19 #error atlscrl.h requires atlwin.h to be included first
20 #endif
21
22
23 ///////////////////////////////////////////////////////////////////////////////
24 // Classes in this file:
25 //
26 // CScrollImpl<T>
27 // CScrollWindowImpl<T, TBase, TWinTraits>
28 // CMapScrollImpl<T>
29 // CMapScrollWindowImpl<T, TBase, TWinTraits>
30 // CFSBWindowT<TBase>
31 // CZoomScrollImpl<T>
32 // CZoomScrollWindowImpl<T, TBase, TWinTraits>
33 // CScrollContainerImpl<T, TBase, TWinTraits>
34 // CScrollContainer
35
36 namespace WTL
37 {
38
39 ///////////////////////////////////////////////////////////////////////////////
40 // CScrollImpl - Provides scrolling support to any window
41
42 // Scroll extended styles
43 #define SCRL_SCROLLCHILDREN 0x00000001
44 #define SCRL_ERASEBACKGROUND 0x00000002
45 #define SCRL_NOTHUMBTRACKING 0x00000004
46 #define SCRL_SMOOTHSCROLL 0x00000008
47 #define SCRL_DISABLENOSCROLLV 0x00000010
48 #define SCRL_DISABLENOSCROLLH 0x00000020
49 #define SCRL_DISABLENOSCROLL (SCRL_DISABLENOSCROLLV | SCRL_DISABLENOSCROLLH)
50
51
52 template <class T>
53 class CScrollImpl
54 {
55 public:
56 enum { uSCROLL_FLAGS = SW_INVALIDATE };
57
58 POINT m_ptOffset;
59 SIZE m_sizeAll;
60 SIZE m_sizeLine;
61 SIZE m_sizePage;
62 SIZE m_sizeClient;
63 int m_zDelta; // current wheel value
64 int m_nWheelLines; // number of lines to scroll on wheel
65 int m_zHDelta; // current horizontal wheel value
66 int m_nHWheelChars; // number of chars to scroll on horizontal wheel
67 UINT m_uScrollFlags;
68 DWORD m_dwExtendedStyle; // scroll specific extended styles
69
70 // Constructor
71 CScrollImpl() : m_zDelta(0), m_nWheelLines(3),
72 m_zHDelta(0), m_nHWheelChars(3),
73 m_uScrollFlags(0U), m_dwExtendedStyle(0)
74 {
75 m_ptOffset.x = 0;
76 m_ptOffset.y = 0;
77 m_sizeAll.cx = 0;
78 m_sizeAll.cy = 0;
79 m_sizePage.cx = 0;
80 m_sizePage.cy = 0;
81 m_sizeLine.cx = 0;
82 m_sizeLine.cy = 0;
83 m_sizeClient.cx = 0;
84 m_sizeClient.cy = 0;
85
86 SetScrollExtendedStyle(SCRL_SCROLLCHILDREN | SCRL_ERASEBACKGROUND);
87 }
88
89 // Attributes & Operations
90 DWORD GetScrollExtendedStyle() const
91 {
92 return m_dwExtendedStyle;
93 }
94
95 DWORD SetScrollExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
96 {
97 DWORD dwPrevStyle = m_dwExtendedStyle;
98 if(dwMask == 0)
99 m_dwExtendedStyle = dwExtendedStyle;
100 else
101 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
102 // cache scroll flags
103 T* pT = static_cast<T*>(this);
104 (void)pT; // avoid level 4 warning
105 m_uScrollFlags = pT->uSCROLL_FLAGS | (IsScrollingChildren() ? SW_SCROLLCHILDREN : 0) | (IsErasingBackground() ? SW_ERASE : 0);
106 m_uScrollFlags |= (IsSmoothScroll() ? SW_SMOOTHSCROLL : 0);
107 return dwPrevStyle;
108 }
109
110 // offset operations
111 void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE)
112 {
113 T* pT = static_cast<T*>(this);
114 ATLASSERT(::IsWindow(pT->m_hWnd));
115
116 pT->AdjustScrollOffset(x, y);
117
118 int dx = m_ptOffset.x - x;
119 int dy = m_ptOffset.y - y;
120 m_ptOffset.x = x;
121 m_ptOffset.y = y;
122
123 // block: set horizontal scroll bar
124 {
125 SCROLLINFO si = { sizeof(SCROLLINFO) };
126 si.fMask = SIF_POS;
127 if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
128 si.fMask |= SIF_DISABLENOSCROLL;
129 si.nPos = m_ptOffset.x;
130 pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
131 }
132
133 // block: set vertical scroll bar
134 {
135 SCROLLINFO si = { sizeof(SCROLLINFO) };
136 si.fMask = SIF_POS;
137 if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
138 si.fMask |= SIF_DISABLENOSCROLL;
139 si.nPos = m_ptOffset.y;
140 pT->SetScrollInfo(SB_VERT, &si, bRedraw);
141 }
142
143 // Move all children if needed
144 if(IsScrollingChildren() && ((dx != 0) || (dy != 0)))
145 {
146 for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
147 {
148 RECT rect = {};
149 ::GetWindowRect(hWndChild, &rect);
150 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
151 ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
152 }
153 }
154
155 if(bRedraw)
156 pT->Invalidate();
157 }
158
159 void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE)
160 {
161 SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw);
162 }
163
164 void GetScrollOffset(POINT& ptOffset) const
165 {
166 ptOffset = m_ptOffset;
167 }
168
169 // size operations
170 void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true)
171 {
172 T* pT = static_cast<T*>(this);
173 ATLASSERT(::IsWindow(pT->m_hWnd));
174
175 m_sizeAll.cx = cx;
176 m_sizeAll.cy = cy;
177
178 int x = 0;
179 int y = 0;
180 if(!bResetOffset)
181 {
182 x = m_ptOffset.x;
183 y = m_ptOffset.y;
184 pT->AdjustScrollOffset(x, y);
185 }
186
187 int dx = m_ptOffset.x - x;
188 int dy = m_ptOffset.y - y;
189 m_ptOffset.x = x;
190 m_ptOffset.y = y;
191
192 // block: set horizontal scroll bar
193 {
194 SCROLLINFO si = { sizeof(SCROLLINFO) };
195 si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
196 if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
197 si.fMask |= SIF_DISABLENOSCROLL;
198 si.nMin = 0;
199 si.nMax = m_sizeAll.cx - 1;
200 si.nPage = m_sizeClient.cx;
201 si.nPos = m_ptOffset.x;
202 pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
203 }
204
205 // block: set vertical scroll bar
206 {
207 SCROLLINFO si = { sizeof(SCROLLINFO) };
208 si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
209 if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
210 si.fMask |= SIF_DISABLENOSCROLL;
211 si.nMin = 0;
212 si.nMax = m_sizeAll.cy - 1;
213 si.nPage = m_sizeClient.cy;
214 si.nPos = m_ptOffset.y;
215 pT->SetScrollInfo(SB_VERT, &si, bRedraw);
216 }
217
218 // Move all children if needed
219 if(IsScrollingChildren() && ((dx != 0) || (dy != 0)))
220 {
221 for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
222 {
223 RECT rect = {};
224 ::GetWindowRect(hWndChild, &rect);
225 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
226 ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
227 }
228 }
229
230 SetScrollLine(0, 0);
231 SetScrollPage(0, 0);
232
233 if(bRedraw)
234 pT->Invalidate();
235 }
236
237 void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true)
238 {
239 SetScrollSize(size.cx, size.cy, bRedraw, bResetOffset);
240 }
241
242 void GetScrollSize(SIZE& sizeWnd) const
243 {
244 sizeWnd = m_sizeAll;
245 }
246
247 // line operations
248 void SetScrollLine(int cxLine, int cyLine)
249 {
250 ATLASSERT((cxLine >= 0) && (cyLine >= 0));
251 ATLASSERT((m_sizeAll.cx != 0) && (m_sizeAll.cy != 0));
252
253 m_sizeLine.cx = T::CalcLineOrPage(cxLine, m_sizeAll.cx, 100);
254 m_sizeLine.cy = T::CalcLineOrPage(cyLine, m_sizeAll.cy, 100);
255 }
256
257 void SetScrollLine(SIZE sizeLine)
258 {
259 SetScrollLine(sizeLine.cx, sizeLine.cy);
260 }
261
262 void GetScrollLine(SIZE& sizeLine) const
263 {
264 sizeLine = m_sizeLine;
265 }
266
267 // page operations
268 void SetScrollPage(int cxPage, int cyPage)
269 {
270 ATLASSERT((cxPage >= 0) && (cyPage >= 0));
271 ATLASSERT((m_sizeAll.cx != 0) && (m_sizeAll.cy != 0));
272
273 m_sizePage.cx = T::CalcLineOrPage(cxPage, m_sizeAll.cx, 10);
274 m_sizePage.cy = T::CalcLineOrPage(cyPage, m_sizeAll.cy, 10);
275 }
276
277 void SetScrollPage(SIZE sizePage)
278 {
279 SetScrollPage(sizePage.cx, sizePage.cy);
280 }
281
282 void GetScrollPage(SIZE& sizePage) const
283 {
284 sizePage = m_sizePage;
285 }
286
287 // commands
288 void ScrollLineDown()
289 {
290 T* pT = static_cast<T*>(this);
291 ATLASSERT(::IsWindow(pT->m_hWnd));
292 pT->DoScroll(SB_VERT, SB_LINEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
293 }
294
295 void ScrollLineUp()
296 {
297 T* pT = static_cast<T*>(this);
298 ATLASSERT(::IsWindow(pT->m_hWnd));
299 pT->DoScroll(SB_VERT, SB_LINEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
300 }
301
302 void ScrollPageDown()
303 {
304 T* pT = static_cast<T*>(this);
305 ATLASSERT(::IsWindow(pT->m_hWnd));
306 pT->DoScroll(SB_VERT, SB_PAGEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
307 }
308
309 void ScrollPageUp()
310 {
311 T* pT = static_cast<T*>(this);
312 ATLASSERT(::IsWindow(pT->m_hWnd));
313 pT->DoScroll(SB_VERT, SB_PAGEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
314 }
315
316 void ScrollTop()
317 {
318 T* pT = static_cast<T*>(this);
319 ATLASSERT(::IsWindow(pT->m_hWnd));
320 pT->DoScroll(SB_VERT, SB_TOP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
321 }
322
323 void ScrollBottom()
324 {
325 T* pT = static_cast<T*>(this);
326 ATLASSERT(::IsWindow(pT->m_hWnd));
327 pT->DoScroll(SB_VERT, SB_BOTTOM, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
328 }
329
330 void ScrollLineRight()
331 {
332 T* pT = static_cast<T*>(this);
333 ATLASSERT(::IsWindow(pT->m_hWnd));
334 pT->DoScroll(SB_HORZ, SB_LINEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
335 }
336
337 void ScrollLineLeft()
338 {
339 T* pT = static_cast<T*>(this);
340 ATLASSERT(::IsWindow(pT->m_hWnd));
341 pT->DoScroll(SB_HORZ, SB_LINEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
342 }
343
344 void ScrollPageRight()
345 {
346 T* pT = static_cast<T*>(this);
347 ATLASSERT(::IsWindow(pT->m_hWnd));
348 pT->DoScroll(SB_HORZ, SB_PAGEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
349 }
350
351 void ScrollPageLeft()
352 {
353 T* pT = static_cast<T*>(this);
354 ATLASSERT(::IsWindow(pT->m_hWnd));
355 pT->DoScroll(SB_HORZ, SB_PAGEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
356 }
357
358 void ScrollAllLeft()
359 {
360 T* pT = static_cast<T*>(this);
361 ATLASSERT(::IsWindow(pT->m_hWnd));
362 pT->DoScroll(SB_HORZ, SB_TOP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
363 }
364
365 void ScrollAllRight()
366 {
367 T* pT = static_cast<T*>(this);
368 ATLASSERT(::IsWindow(pT->m_hWnd));
369 pT->DoScroll(SB_HORZ, SB_BOTTOM, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
370 }
371
372 // scroll to make point/view/window visible
373 void ScrollToView(POINT pt)
374 {
375 T* pT = static_cast<T*>(this);
376 ATLASSERT(::IsWindow(pT->m_hWnd));
377 RECT rect = { pt.x, pt.y, pt.x, pt.y };
378 pT->ScrollToView(rect);
379 }
380
381 void ScrollToView(RECT& rect)
382 {
383 T* pT = static_cast<T*>(this);
384 ATLASSERT(::IsWindow(pT->m_hWnd));
385
386 RECT rcClient = {};
387 pT->GetClientRect(&rcClient);
388
389 int x = m_ptOffset.x;
390 if(rect.left < m_ptOffset.x)
391 x = rect.left;
392 else if(rect.right > (m_ptOffset.x + rcClient.right))
393 x = rect.right - rcClient.right;
394
395 int y = m_ptOffset.y;
396 if(rect.top < m_ptOffset.y)
397 y = rect.top;
398 else if(rect.bottom > (m_ptOffset.y + rcClient.bottom))
399 y = rect.bottom - rcClient.bottom;
400
401 SetScrollOffset(x, y);
402 }
403
404 void ScrollToView(HWND hWnd)
405 {
406 T* pT = static_cast<T*>(this);
407 ATLASSERT(::IsWindow(pT->m_hWnd));
408
409 RECT rect = {};
410 ::GetWindowRect(hWnd, &rect);
411 ::OffsetRect(&rect, m_ptOffset.x, m_ptOffset.y);
412 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2);
413 ScrollToView(rect);
414 }
415
416 BEGIN_MSG_MAP(CScrollImpl)
417 MESSAGE_HANDLER(WM_CREATE, OnCreate)
418 MESSAGE_HANDLER(WM_VSCROLL, OnVScroll)
419 MESSAGE_HANDLER(WM_HSCROLL, OnHScroll)
420 MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
421 MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnMouseHWheel)
422 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
423 MESSAGE_HANDLER(WM_SIZE, OnSize)
424 MESSAGE_HANDLER(WM_PAINT, OnPaint)
425 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
426 // standard scroll commands
427 ALT_MSG_MAP(1)
428 COMMAND_ID_HANDLER(ID_SCROLL_UP, OnScrollUp)
429 COMMAND_ID_HANDLER(ID_SCROLL_DOWN, OnScrollDown)
430 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, OnScrollPageUp)
431 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, OnScrollPageDown)
432 COMMAND_ID_HANDLER(ID_SCROLL_TOP, OnScrollTop)
433 COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, OnScrollBottom)
434 COMMAND_ID_HANDLER(ID_SCROLL_LEFT, OnScrollLeft)
435 COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, OnScrollRight)
436 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, OnScrollPageLeft)
437 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, OnScrollPageRight)
438 COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, OnScrollAllLeft)
439 COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, OnScrollAllRight)
440 END_MSG_MAP()
441
442 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
443 {
444 T* pT = static_cast<T*>(this);
445 pT->GetSystemSettings();
446
447 bHandled = FALSE;
448 return 1;
449 }
450
451 LRESULT OnVScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
452 {
453 T* pT = static_cast<T*>(this);
454 ATLASSERT(::IsWindow(pT->m_hWnd));
455 pT->DoScroll(SB_VERT, (int)(short)LOWORD(wParam), (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
456 return 0;
457 }
458
459 LRESULT OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
460 {
461 T* pT = static_cast<T*>(this);
462 ATLASSERT(::IsWindow(pT->m_hWnd));
463 pT->DoScroll(SB_HORZ, (int)(short)LOWORD(wParam), (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
464 return 0;
465 }
466
467 LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
468 {
469 T* pT = static_cast<T*>(this);
470 ATLASSERT(::IsWindow(pT->m_hWnd));
471
472 int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
473 int nScrollCode = (m_nWheelLines == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGEUP : SB_PAGEDOWN) : ((zDelta > 0) ? SB_LINEUP : SB_LINEDOWN);
474 m_zDelta += zDelta; // cumulative
475 int zTotal = (m_nWheelLines == WHEEL_PAGESCROLL) ? abs(m_zDelta) : abs(m_zDelta) * m_nWheelLines;
476 if(m_sizeAll.cy > m_sizeClient.cy)
477 {
478 for(int i = 0; i < zTotal; i += WHEEL_DELTA)
479 {
480 pT->DoScroll(SB_VERT, nScrollCode, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
481 pT->UpdateWindow();
482 }
483 }
484 else if(m_sizeAll.cx > m_sizeClient.cx) // can't scroll vertically, scroll horizontally
485 {
486 for(int i = 0; i < zTotal; i += WHEEL_DELTA)
487 {
488 pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
489 pT->UpdateWindow();
490 }
491 }
492 m_zDelta %= WHEEL_DELTA;
493
494 return 0;
495 }
496
497 LRESULT OnMouseHWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
498 {
499 T* pT = static_cast<T*>(this);
500 ATLASSERT(::IsWindow(pT->m_hWnd));
501
502 int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
503 int nScrollCode = (m_nHWheelChars == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGERIGHT : SB_PAGELEFT) : ((zDelta > 0) ? SB_LINERIGHT : SB_LINELEFT);
504 m_zHDelta += zDelta; // cumulative
505 int zTotal = (m_nHWheelChars == WHEEL_PAGESCROLL) ? abs(m_zHDelta) : abs(m_zHDelta) * m_nHWheelChars;
506 if(m_sizeAll.cx > m_sizeClient.cx)
507 {
508 for(int i = 0; i < zTotal; i += WHEEL_DELTA)
509 {
510 pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
511 pT->UpdateWindow();
512 }
513 }
514 m_zHDelta %= WHEEL_DELTA;
515
516 return 0;
517 }
518
519 LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
520 {
521 GetSystemSettings();
522 return 0;
523 }
524
525 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
526 {
527 T* pT = static_cast<T*>(this);
528 ATLASSERT(::IsWindow(pT->m_hWnd));
529
530 pT->DoSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
531
532 bHandled = FALSE;
533 return 1;
534 }
535
536 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
537 {
538 T* pT = static_cast<T*>(this);
539 ATLASSERT(::IsWindow(pT->m_hWnd));
540 if(wParam != NULL)
541 {
542 CDCHandle dc = (HDC)wParam;
543 POINT ptViewportOrg = { 0, 0 };
544 dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg);
545 pT->DoPaint(dc);
546 dc.SetViewportOrg(ptViewportOrg);
547 }
548 else
549 {
550 CPaintDC dc(pT->m_hWnd);
551 dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y);
552 pT->DoPaint(dc.m_hDC);
553 }
554 return 0;
555 }
556
557 // scrolling handlers
558 LRESULT OnScrollUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
559 {
560 ScrollLineUp();
561 return 0;
562 }
563
564 LRESULT OnScrollDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
565 {
566 ScrollLineDown();
567 return 0;
568 }
569
570 LRESULT OnScrollPageUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
571 {
572 ScrollPageUp();
573 return 0;
574 }
575
576 LRESULT OnScrollPageDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
577 {
578 ScrollPageDown();
579 return 0;
580 }
581
582 LRESULT OnScrollTop(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
583 {
584 ScrollTop();
585 return 0;
586 }
587
588 LRESULT OnScrollBottom(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
589 {
590 ScrollBottom();
591 return 0;
592 }
593
594 LRESULT OnScrollLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
595 {
596 ScrollLineLeft();
597 return 0;
598 }
599
600 LRESULT OnScrollRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
601 {
602 ScrollLineRight();
603 return 0;
604 }
605
606 LRESULT OnScrollPageLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
607 {
608 ScrollPageLeft();
609 return 0;
610 }
611
612 LRESULT OnScrollPageRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
613 {
614 ScrollPageRight();
615 return 0;
616 }
617
618 LRESULT OnScrollAllLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
619 {
620 ScrollAllLeft();
621 return 0;
622 }
623
624 LRESULT OnScrollAllRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
625 {
626 ScrollAllRight();
627 return 0;
628 }
629
630 // Overrideables
631 void DoPaint(CDCHandle /*dc*/)
632 {
633 // must be implemented in a derived class
634 ATLASSERT(FALSE);
635 }
636
637 // Implementation
638 void DoSize(int cx, int cy)
639 {
640 m_sizeClient.cx = cx;
641 m_sizeClient.cy = cy;
642
643 T* pT = static_cast<T*>(this);
644
645 // block: set horizontal scroll bar
646 {
647 SCROLLINFO si = { sizeof(SCROLLINFO) };
648 si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
649 si.nMin = 0;
650 si.nMax = m_sizeAll.cx - 1;
651 if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
652 si.fMask |= SIF_DISABLENOSCROLL;
653 si.nPage = m_sizeClient.cx;
654 si.nPos = m_ptOffset.x;
655 pT->SetScrollInfo(SB_HORZ, &si, TRUE);
656 }
657
658 // block: set vertical scroll bar
659 {
660 SCROLLINFO si = { sizeof(SCROLLINFO) };
661 si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
662 si.nMin = 0;
663 si.nMax = m_sizeAll.cy - 1;
664 if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
665 si.fMask |= SIF_DISABLENOSCROLL;
666 si.nPage = m_sizeClient.cy;
667 si.nPos = m_ptOffset.y;
668 pT->SetScrollInfo(SB_VERT, &si, TRUE);
669 }
670
671 int x = m_ptOffset.x;
672 int y = m_ptOffset.y;
673 if(pT->AdjustScrollOffset(x, y))
674 {
675 // Children will be moved in SetScrollOffset, if needed
676 pT->ScrollWindowEx(m_ptOffset.x - x, m_ptOffset.y - y, (m_uScrollFlags & ~SCRL_SCROLLCHILDREN));
677 SetScrollOffset(x, y, FALSE);
678 }
679 }
680
681 void DoScroll(int nType, int nScrollCode, int& cxyOffset, int cxySizeAll, int cxySizePage, int cxySizeLine)
682 {
683 T* pT = static_cast<T*>(this);
684 RECT rect = {};
685 pT->GetClientRect(&rect);
686 int cxyClient = (nType == SB_VERT) ? rect.bottom : rect.right;
687 int cxyMax = cxySizeAll - cxyClient;
688
689 if(cxyMax < 0) // can't scroll, client area is bigger
690 return;
691
692 bool bUpdate = true;
693 int cxyScroll = 0;
694
695 switch(nScrollCode)
696 {
697 case SB_TOP: // top or all left
698 cxyScroll = cxyOffset;
699 cxyOffset = 0;
700 break;
701 case SB_BOTTOM: // bottom or all right
702 cxyScroll = cxyOffset - cxyMax;
703 cxyOffset = cxyMax;
704 break;
705 case SB_LINEUP: // line up or line left
706 if(cxyOffset >= cxySizeLine)
707 {
708 cxyScroll = cxySizeLine;
709 cxyOffset -= cxySizeLine;
710 }
711 else
712 {
713 cxyScroll = cxyOffset;
714 cxyOffset = 0;
715 }
716 break;
717 case SB_LINEDOWN: // line down or line right
718 if(cxyOffset < cxyMax - cxySizeLine)
719 {
720 cxyScroll = -cxySizeLine;
721 cxyOffset += cxySizeLine;
722 }
723 else
724 {
725 cxyScroll = cxyOffset - cxyMax;
726 cxyOffset = cxyMax;
727 }
728 break;
729 case SB_PAGEUP: // page up or page left
730 if(cxyOffset >= cxySizePage)
731 {
732 cxyScroll = cxySizePage;
733 cxyOffset -= cxySizePage;
734 }
735 else
736 {
737 cxyScroll = cxyOffset;
738 cxyOffset = 0;
739 }
740 break;
741 case SB_PAGEDOWN: // page down or page right
742 if(cxyOffset < cxyMax - cxySizePage)
743 {
744 cxyScroll = -cxySizePage;
745 cxyOffset += cxySizePage;
746 }
747 else
748 {
749 cxyScroll = cxyOffset - cxyMax;
750 cxyOffset = cxyMax;
751 }
752 break;
753 case SB_THUMBTRACK:
754 if(IsNoThumbTracking())
755 break;
756 // else fall through
757 case SB_THUMBPOSITION:
758 {
759 SCROLLINFO si = { sizeof(SCROLLINFO), SIF_TRACKPOS };
760 if(pT->GetScrollInfo(nType, &si))
761 {
762 cxyScroll = cxyOffset - si.nTrackPos;
763 cxyOffset = si.nTrackPos;
764 }
765 }
766 break;
767 case SB_ENDSCROLL:
768 default:
769 bUpdate = false;
770 break;
771 }
772
773 if(bUpdate && (cxyScroll != 0))
774 {
775 pT->SetScrollPos(nType, cxyOffset, TRUE);
776 if(nType == SB_VERT)
777 pT->ScrollWindowEx(0, cxyScroll, m_uScrollFlags);
778 else
779 pT->ScrollWindowEx(cxyScroll, 0, m_uScrollFlags);
780 }
781 }
782
783 static int CalcLineOrPage(int nVal, int nMax, int nDiv)
784 {
785 if(nVal == 0)
786 {
787 nVal = nMax / nDiv;
788 if(nVal < 1)
789 nVal = 1;
790 }
791 else if(nVal > nMax)
792 {
793 nVal = nMax;
794 }
795
796 return nVal;
797 }
798
799 bool AdjustScrollOffset(int& x, int& y)
800 {
801 int xOld = x;
802 int yOld = y;
803
804 int cxMax = m_sizeAll.cx - m_sizeClient.cx;
805 if(x > cxMax)
806 x = (cxMax >= 0) ? cxMax : 0;
807 else if(x < 0)
808 x = 0;
809
810 int cyMax = m_sizeAll.cy - m_sizeClient.cy;
811 if(y > cyMax)
812 y = (cyMax >= 0) ? cyMax : 0;
813 else if(y < 0)
814 y = 0;
815
816 return ((x != xOld) || (y != yOld));
817 }
818
819 void GetSystemSettings()
820 {
821 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &m_nWheelLines, 0);
822
823 #ifndef SPI_GETWHEELSCROLLCHARS
824 const UINT SPI_GETWHEELSCROLLCHARS = 0x006C;
825 #endif
826 ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &m_nHWheelChars, 0);
827 }
828
829 bool IsScrollingChildren() const
830 {
831 return (m_dwExtendedStyle & SCRL_SCROLLCHILDREN) != 0;
832 }
833
834 bool IsErasingBackground() const
835 {
836 return (m_dwExtendedStyle & SCRL_ERASEBACKGROUND) != 0;
837 }
838
839 bool IsNoThumbTracking() const
840 {
841 return (m_dwExtendedStyle & SCRL_NOTHUMBTRACKING) != 0;
842 }
843
844 bool IsSmoothScroll() const
845 {
846 return (m_dwExtendedStyle & SCRL_SMOOTHSCROLL) != 0;
847 }
848 };
849
850
851 ///////////////////////////////////////////////////////////////////////////////
852 // CScrollWindowImpl - Implements a scrollable window
853
854 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
855 class ATL_NO_VTABLE CScrollWindowImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>, public CScrollImpl< T >
856 {
857 public:
858 BOOL SubclassWindow(HWND hWnd)
859 {
860 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
861 if(bRet != FALSE)
862 {
863 T* pT = static_cast<T*>(this);
864 pT->GetSystemSettings();
865
866 RECT rect = {};
867 this->GetClientRect(&rect);
868 pT->DoSize(rect.right, rect.bottom);
869 }
870
871 return bRet;
872 }
873
874 BEGIN_MSG_MAP(CScrollWindowImpl)
875 MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
876 MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
877 MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
878 MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
879 MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
880 MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
881 MESSAGE_HANDLER(WM_PAINT, CScrollImpl< T >::OnPaint)
882 MESSAGE_HANDLER(WM_PRINTCLIENT, CScrollImpl< T >::OnPaint)
883 ALT_MSG_MAP(1)
884 COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
885 COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
886 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
887 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
888 COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
889 COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
890 COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
891 COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
892 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
893 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
894 COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
895 COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
896 END_MSG_MAP()
897 };
898
899
900 ///////////////////////////////////////////////////////////////////////////////
901 // CMapScrollImpl - Provides mapping and scrolling support to any window
902
903 template <class T>
904 class CMapScrollImpl : public CScrollImpl< T >
905 {
906 public:
907 int m_nMapMode;
908 RECT m_rectLogAll;
909 SIZE m_sizeLogLine;
910 SIZE m_sizeLogPage;
911
912 // Constructor
913 CMapScrollImpl() : m_nMapMode(MM_TEXT)
914 {
915 ::SetRectEmpty(&m_rectLogAll);
916 m_sizeLogPage.cx = 0;
917 m_sizeLogPage.cy = 0;
918 m_sizeLogLine.cx = 0;
919 m_sizeLogLine.cy = 0;
920 }
921
922 // Attributes & Operations
923 // mapping mode operations
924 void SetScrollMapMode(int nMapMode)
925 {
926 ATLASSERT((nMapMode >= MM_MIN) && (nMapMode <= MM_MAX_FIXEDSCALE));
927 m_nMapMode = nMapMode;
928 }
929
930 int GetScrollMapMode() const
931 {
932 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
933 return m_nMapMode;
934 }
935
936 // offset operations
937 void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE)
938 {
939 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
940 POINT ptOff = { x, y };
941 // block: convert logical to device units
942 {
943 CWindowDC dc(NULL);
944 dc.SetMapMode(m_nMapMode);
945 dc.LPtoDP(&ptOff);
946 }
947 CScrollImpl< T >::SetScrollOffset(ptOff, bRedraw);
948 }
949
950 void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE)
951 {
952 SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw);
953 }
954
955 void GetScrollOffset(POINT& ptOffset) const
956 {
957 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
958 ptOffset = this->m_ptOffset;
959 // block: convert device to logical units
960 {
961 CWindowDC dc(NULL);
962 dc.SetMapMode(m_nMapMode);
963 dc.DPtoLP(&ptOffset);
964 }
965 }
966
967 // size operations
968 void SetScrollSize(int xMin, int yMin, int xMax, int yMax, BOOL bRedraw = TRUE, bool bResetOffset = true)
969 {
970 ATLASSERT((xMax > xMin) && (yMax > yMin));
971 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
972
973 ::SetRect(&m_rectLogAll, xMin, yMin, xMax, yMax);
974
975 SIZE sizeAll = {};
976 sizeAll.cx = xMax - xMin + 1;
977 sizeAll.cy = yMax - yMin + 1;
978 // block: convert logical to device units
979 {
980 CWindowDC dc(NULL);
981 dc.SetMapMode(m_nMapMode);
982 dc.LPtoDP(&sizeAll);
983 }
984 CScrollImpl< T >::SetScrollSize(sizeAll, bRedraw, bResetOffset);
985 SetScrollLine(0, 0);
986 SetScrollPage(0, 0);
987 }
988
989 void SetScrollSize(RECT& rcScroll, BOOL bRedraw = TRUE, bool bResetOffset = true)
990 {
991 SetScrollSize(rcScroll.left, rcScroll.top, rcScroll.right, rcScroll.bottom, bRedraw, bResetOffset);
992 }
993
994 void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true)
995 {
996 SetScrollSize(0, 0, cx, cy, bRedraw, bResetOffset);
997 }
998
999 void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true)
1000 {
1001 SetScrollSize(0, 0, size.cx, size.cy, bRedraw, bResetOffset);
1002 }
1003
1004 void GetScrollSize(RECT& rcScroll) const
1005 {
1006 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
1007 rcScroll = m_rectLogAll;
1008 }
1009
1010 // line operations
1011 void SetScrollLine(int cxLine, int cyLine)
1012 {
1013 ATLASSERT((cxLine >= 0) && (cyLine >= 0));
1014 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
1015
1016 m_sizeLogLine.cx = cxLine;
1017 m_sizeLogLine.cy = cyLine;
1018 SIZE sizeLine = m_sizeLogLine;
1019 // block: convert logical to device units
1020 {
1021 CWindowDC dc(NULL);
1022 dc.SetMapMode(m_nMapMode);
1023 dc.LPtoDP(&sizeLine);
1024 }
1025 CScrollImpl< T >::SetScrollLine(sizeLine);
1026 }
1027
1028 void SetScrollLine(SIZE sizeLine)
1029 {
1030 SetScrollLine(sizeLine.cx, sizeLine.cy);
1031 }
1032
1033 void GetScrollLine(SIZE& sizeLine) const
1034 {
1035 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
1036 sizeLine = m_sizeLogLine;
1037 }
1038
1039 // page operations
1040 void SetScrollPage(int cxPage, int cyPage)
1041 {
1042 ATLASSERT((cxPage >= 0) && (cyPage >= 0));
1043 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
1044
1045 m_sizeLogPage.cx = cxPage;
1046 m_sizeLogPage.cy = cyPage;
1047 SIZE sizePage = m_sizeLogPage;
1048 // block: convert logical to device units
1049 {
1050 CWindowDC dc(NULL);
1051 dc.SetMapMode(m_nMapMode);
1052 dc.LPtoDP(&sizePage);
1053 }
1054 CScrollImpl< T >::SetScrollPage(sizePage);
1055 }
1056
1057 void SetScrollPage(SIZE sizePage)
1058 {
1059 SetScrollPage(sizePage.cx, sizePage.cy);
1060 }
1061
1062 void GetScrollPage(SIZE& sizePage) const
1063 {
1064 ATLASSERT((m_nMapMode >= MM_MIN) && (m_nMapMode <= MM_MAX_FIXEDSCALE));
1065 sizePage = m_sizeLogPage;
1066 }
1067
1068 BEGIN_MSG_MAP(CMapScrollImpl)
1069 MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
1070 MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
1071 MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
1072 MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
1073 MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
1074 MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
1075 MESSAGE_HANDLER(WM_PAINT, OnPaint)
1076 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
1077 ALT_MSG_MAP(1)
1078 COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
1079 COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
1080 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
1081 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
1082 COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
1083 COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
1084 COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
1085 COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
1086 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
1087 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
1088 COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
1089 COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
1090 END_MSG_MAP()
1091
1092 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1093 {
1094 T* pT = static_cast<T*>(this);
1095 ATLASSERT(::IsWindow(pT->m_hWnd));
1096 if(wParam != NULL)
1097 {
1098 CDCHandle dc = (HDC)wParam;
1099 int nMapModeSav = dc.GetMapMode();
1100 dc.SetMapMode(m_nMapMode);
1101 POINT ptViewportOrg = { 0, 0 };
1102 if(m_nMapMode == MM_TEXT)
1103 dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y, &ptViewportOrg);
1104 else
1105 dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y + this->m_sizeAll.cy, &ptViewportOrg);
1106 POINT ptWindowOrg = { 0, 0 };
1107 dc.SetWindowOrg(m_rectLogAll.left, m_rectLogAll.top, &ptWindowOrg);
1108
1109 pT->DoPaint(dc);
1110
1111 dc.SetMapMode(nMapModeSav);
1112 dc.SetViewportOrg(ptViewportOrg);
1113 dc.SetWindowOrg(ptWindowOrg);
1114 }
1115 else
1116 {
1117 CPaintDC dc(pT->m_hWnd);
1118 dc.SetMapMode(m_nMapMode);
1119 if(m_nMapMode == MM_TEXT)
1120 dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y);
1121 else
1122 dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y + this->m_sizeAll.cy);
1123 dc.SetWindowOrg(m_rectLogAll.left, m_rectLogAll.top);
1124 pT->DoPaint(dc.m_hDC);
1125 }
1126 return 0;
1127 }
1128 };
1129
1130
1131 ///////////////////////////////////////////////////////////////////////////////
1132 // CMapScrollWindowImpl - Implements scrolling window with mapping
1133
1134 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
1135 class ATL_NO_VTABLE CMapScrollWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CMapScrollImpl< T >
1136 {
1137 public:
1138 BOOL SubclassWindow(HWND hWnd)
1139 {
1140 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
1141 if(bRet != FALSE)
1142 {
1143 T* pT = static_cast<T*>(this);
1144 pT->GetSystemSettings();
1145
1146 RECT rect = {};
1147 this->GetClientRect(&rect);
1148 pT->DoSize(rect.right, rect.bottom);
1149 }
1150
1151 return bRet;
1152 }
1153
1154 BEGIN_MSG_MAP(CMapScrollWindowImpl)
1155 MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
1156 MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
1157 MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
1158 MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
1159 MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
1160 MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
1161 MESSAGE_HANDLER(WM_PAINT, CMapScrollImpl< T >::OnPaint)
1162 MESSAGE_HANDLER(WM_PRINTCLIENT, CMapScrollImpl< T >::OnPaint)
1163 ALT_MSG_MAP(1)
1164 COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
1165 COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
1166 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
1167 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
1168 COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
1169 COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
1170 COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
1171 COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
1172 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
1173 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
1174 COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
1175 COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
1176 END_MSG_MAP()
1177 };
1178
1179
1180 ///////////////////////////////////////////////////////////////////////////////
1181 // CFSBWindow - Use as a base instead of CWindow to get flat scroll bar support
1182
1183 #ifdef __ATLCTRLS_H__
1184
1185 template <class TBase = ATL::CWindow>
1186 class CFSBWindowT : public TBase, public CFlatScrollBarImpl<CFSBWindowT< TBase > >
1187 {
1188 public:
1189 // Constructors
1190 CFSBWindowT(HWND hWnd = NULL) : TBase(hWnd)
1191 { }
1192
1193 CFSBWindowT< TBase >& operator =(HWND hWnd)
1194 {
1195 this->m_hWnd = hWnd;
1196 return *this;
1197 }
1198
1199 // CWindow overrides that use flat scroll bar API
1200 // (only those methods that are used by scroll window classes)
1201 int SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE)
1202 {
1203 ATLASSERT(::IsWindow(this->m_hWnd));
1204 return this->FlatSB_SetScrollPos(nBar, nPos, bRedraw);
1205 }
1206
1207 BOOL GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo)
1208 {
1209 ATLASSERT(::IsWindow(this->m_hWnd));
1210 return this->FlatSB_GetScrollInfo(nBar, lpScrollInfo);
1211 }
1212
1213 BOOL SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE)
1214 {
1215 ATLASSERT(::IsWindow(this->m_hWnd));
1216 return this->FlatSB_SetScrollInfo(nBar, lpScrollInfo, bRedraw);
1217 }
1218 };
1219
1220 typedef CFSBWindowT<ATL::CWindow> CFSBWindow;
1221
1222 #endif // __ATLCTRLS_H__
1223
1224
1225 ///////////////////////////////////////////////////////////////////////////////
1226 // CZoomScrollImpl - Provides zooming and scrolling support to any window
1227
1228 // The zoom modes that can be set with the SetZoomMode method
1229 enum
1230 {
1231 ZOOMMODE_OFF,
1232 ZOOMMODE_IN, // If left mouse button is clicked or dragged, zoom in on point clicked or rectangle dragged.
1233 ZOOMMODE_OUT // If left mouse button clicked, zoom out on point clicked.
1234 };
1235
1236 // Notification to parent that zoom scale changed as a result of user mouse action.
1237 #define ZSN_ZOOMCHANGED (NM_FIRST - 50)
1238
1239 template <class T>
1240 class CZoomScrollImpl : public CScrollImpl< T >
1241 {
1242 public:
1243 enum { m_cxyMinZoomRect = 12 }; // min rect size to zoom in on rect.
1244
1245 struct _ChildPlacement
1246 {
1247 HWND hWnd;
1248 int x;
1249 int y;
1250 int cx;
1251 int cy;
1252
1253 bool operator ==(const _ChildPlacement& cp) const { return (memcmp(this, &cp, sizeof(_ChildPlacement)) == 0); }
1254 };
1255
1256 // Data members
1257 SIZE m_sizeLogAll;
1258 SIZE m_sizeLogLine;
1259 SIZE m_sizeLogPage;
1260 float m_fZoomScale;
1261 float m_fZoomScaleMin;
1262 float m_fZoomScaleMax;
1263 float m_fZoomDelta; // Used in ZOOMMODE_IN and ZOOMMODE_OUT on left-button click.
1264 int m_nZoomMode;
1265 RECT m_rcTrack;
1266 bool m_bTracking;
1267
1268 bool m_bZoomChildren;
1269 ATL::CSimpleArray<_ChildPlacement> m_arrChildren;
1270
1271 // Constructor
1272 CZoomScrollImpl(): m_fZoomScale(1.0f), m_fZoomScaleMin(0.1f), m_fZoomScaleMax(100.0f), m_fZoomDelta(0.5f),
1273 m_nZoomMode(ZOOMMODE_OFF), m_bTracking(false), m_bZoomChildren(false)
1274 {
1275 m_sizeLogAll.cx = 0;
1276 m_sizeLogAll.cy = 0;
1277 m_sizeLogPage.cx = 0;
1278 m_sizeLogPage.cy = 0;
1279 m_sizeLogLine.cx = 0;
1280 m_sizeLogLine.cy = 0;
1281 ::SetRectEmpty(&m_rcTrack);
1282 }
1283
1284 // Attributes & Operations
1285 // size operations
1286 void SetScrollSize(int cxLog, int cyLog, BOOL bRedraw = TRUE, bool bResetOffset = true)
1287 {
1288 ATLASSERT((cxLog >= 0) && (cyLog >= 0));
1289
1290 // Set up the defaults
1291 if((cxLog == 0) && (cyLog == 0))
1292 {
1293 cxLog = 1;
1294 cyLog = 1;
1295 }
1296
1297 m_sizeLogAll.cx = cxLog;
1298 m_sizeLogAll.cy = cyLog;
1299 SIZE sizeAll = {};
1300 sizeAll.cx = (int)((float)m_sizeLogAll.cx * m_fZoomScale);
1301 sizeAll.cy = (int)((float)m_sizeLogAll.cy * m_fZoomScale);
1302
1303 CScrollImpl< T >::SetScrollSize(sizeAll, bRedraw, bResetOffset);
1304 }
1305
1306 void SetScrollSize(SIZE sizeLog, BOOL bRedraw = TRUE, bool bResetOffset = true)
1307 {
1308 SetScrollSize(sizeLog.cx, sizeLog.cy, bRedraw, bResetOffset);
1309 }
1310
1311 void GetScrollSize(SIZE& sizeLog) const
1312 {
1313 sizeLog = m_sizeLogAll;
1314 }
1315
1316 // line operations
1317 void SetScrollLine(int cxLogLine, int cyLogLine)
1318 {
1319 ATLASSERT((cxLogLine >= 0) && (cyLogLine >= 0));
1320
1321 m_sizeLogLine.cx = cxLogLine;
1322 m_sizeLogLine.cy = cyLogLine;
1323
1324 SIZE sizeLine = {};
1325 sizeLine.cx = (int)((float)m_sizeLogLine.cx * m_fZoomScale);
1326 sizeLine.cy = (int)((float)m_sizeLogLine.cy * m_fZoomScale);
1327 CScrollImpl< T >::SetScrollLine(sizeLine);
1328 }
1329
1330 void SetScrollLine(SIZE sizeLogLine)
1331 {
1332 SetScrollLine(sizeLogLine.cx, sizeLogLine.cy);
1333 }
1334
1335 void GetScrollLine(SIZE& sizeLogLine) const
1336 {
1337 sizeLogLine = m_sizeLogLine;
1338 }
1339
1340 // page operations
1341 void SetScrollPage(int cxLogPage, int cyLogPage)
1342 {
1343 ATLASSERT((cxLogPage >= 0) && (cyLogPage >= 0));
1344
1345 m_sizeLogPage.cx = cxLogPage;
1346 m_sizeLogPage.cy = cyLogPage;
1347
1348 SIZE sizePage = {};
1349 sizePage.cx = (int)((float)m_sizeLogPage.cx * m_fZoomScale);
1350 sizePage.cy = (int)((float)m_sizeLogPage.cy * m_fZoomScale);
1351
1352 CScrollImpl< T >::SetScrollPage(sizePage);
1353 }
1354
1355 void SetScrollPage(SIZE sizeLogPage)
1356 {
1357 SetScrollPage(sizeLogPage.cx, sizeLogPage.cy);
1358 }
1359
1360 void GetScrollPage(SIZE& sizeLogPage) const
1361 {
1362 sizeLogPage = m_sizeLogPage;
1363 }
1364
1365 void SetZoomScale(float fZoomScale)
1366 {
1367 ATLASSERT(fZoomScale > 0.0f);
1368 if(fZoomScale <= 0.0f)
1369 return;
1370
1371 m_fZoomScale = fZoomScale;
1372 if(m_fZoomScale < m_fZoomScaleMin)
1373 m_fZoomScale = m_fZoomScaleMin;
1374 else if(m_fZoomScale > m_fZoomScaleMax)
1375 m_fZoomScale = m_fZoomScaleMax;
1376 }
1377
1378 float GetZoomScale() const
1379 {
1380 return m_fZoomScale;
1381 }
1382
1383 void SetZoomScaleMin(float fZoomScaleMin)
1384 {
1385 ATLASSERT(fZoomScaleMin > 0.0f);
1386 ATLASSERT(fZoomScaleMin <= m_fZoomScaleMax);
1387
1388 m_fZoomScaleMin = fZoomScaleMin;
1389 }
1390
1391 float GetZoomScaleMin() const
1392 {
1393 return m_fZoomScaleMin;
1394 }
1395
1396 void SetZoomScaleMax(float fZoomScaleMax)
1397 {
1398 ATLASSERT(fZoomScaleMax > 0.0f);
1399 ATLASSERT(m_fZoomScaleMin <= fZoomScaleMax);
1400
1401 m_fZoomScaleMax = fZoomScaleMax;
1402 }
1403
1404 float GetZoomScaleMax() const
1405 {
1406 return m_fZoomScaleMax;
1407 }
1408
1409 void SetZoomDelta(float fZoomDelta)
1410 {
1411 ATLASSERT(fZoomDelta >= 0.0f);
1412
1413 if(fZoomDelta >= 0.0f)
1414 m_fZoomDelta = fZoomDelta;
1415 }
1416
1417 float GetZoomDelta() const
1418 {
1419 return m_fZoomDelta;
1420 }
1421
1422 void SetZoomMode(int nZoomMode)
1423 {
1424 m_nZoomMode = nZoomMode;
1425 }
1426
1427 int GetZoomMode() const
1428 {
1429 return m_nZoomMode;
1430 }
1431
1432 void SetZoomChildren(bool bEnable = true)
1433 {
1434 T* pT = static_cast<T*>(this);
1435 ATLASSERT(::IsWindow(pT->m_hWnd));
1436
1437 m_bZoomChildren = bEnable;
1438
1439 m_arrChildren.RemoveAll();
1440 if(m_bZoomChildren)
1441 {
1442 for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
1443 {
1444 RECT rect = {};
1445 ::GetWindowRect(hWndChild, &rect);
1446 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2);
1447
1448 _ChildPlacement cp = {};
1449 cp.hWnd = hWndChild;
1450 cp.x = rect.left;
1451 cp.y = rect.top;
1452 cp.cx = rect.right - rect.left;
1453 cp.cy = rect.bottom - rect.top;
1454 m_arrChildren.Add(cp);
1455 }
1456 }
1457 }
1458
1459 bool GetZoomChildren() const
1460 {
1461 return m_bZoomChildren;
1462 }
1463
1464 void Zoom(int x, int y, float fZoomScale)
1465 {
1466 if(fZoomScale <= 0.0f)
1467 return;
1468
1469 if(fZoomScale < m_fZoomScaleMin)
1470 fZoomScale = m_fZoomScaleMin;
1471 else if(fZoomScale > m_fZoomScaleMax)
1472 fZoomScale = m_fZoomScaleMax;
1473
1474 T* pT = static_cast<T*>(this);
1475 POINT pt = { x, y };
1476 if(!pT->PtInDevRect(pt))
1477 return;
1478
1479 pT->ViewDPtoLP(&pt);
1480 pT->Zoom(fZoomScale, false);
1481 pT->CenterOnLogicalPoint(pt);
1482 }
1483
1484 void Zoom(POINT pt, float fZoomScale)
1485 {
1486 T* pT = static_cast<T*>(this);
1487 pT->Zoom(pt.x, pt.y, fZoomScale);
1488 }
1489
1490 void Zoom(RECT& rc)
1491 {
1492 T* pT = static_cast<T*>(this);
1493 RECT rcZoom = rc;
1494 pT->NormalizeRect(rcZoom);
1495 SIZE size = { rcZoom.right - rcZoom.left, rcZoom.bottom - rcZoom.top };
1496 POINT pt = { rcZoom.left + size.cx / 2, rcZoom.top + size.cy / 2 };
1497 if((size.cx < m_cxyMinZoomRect) || (size.cy < m_cxyMinZoomRect))
1498 {
1499 pT->Zoom(pt, m_fZoomScale + m_fZoomDelta);
1500 return;
1501 }
1502
1503 ATLASSERT((size.cx > 0) && (size.cy > 0));
1504
1505 float fScaleH = (float)(this->m_sizeClient.cx + 1) / (float)size.cx;
1506 float fScaleV = (float)(this->m_sizeClient.cy + 1) / (float)size.cy;
1507 float fZoomScale = __min(fScaleH, fScaleV) * m_fZoomScale;
1508 pT->Zoom(pt, fZoomScale);
1509 }
1510
1511 void Zoom(float fZoomScale, bool bCenter = true)
1512 {
1513 if(fZoomScale <= 0.0f)
1514 return;
1515
1516 if(fZoomScale < m_fZoomScaleMin)
1517 fZoomScale = m_fZoomScaleMin;
1518 else if(fZoomScale > m_fZoomScaleMax)
1519 fZoomScale = m_fZoomScaleMax;
1520
1521 T* pT = static_cast<T*>(this);
1522 POINT pt = { 0, 0 };
1523 if(bCenter)
1524 {
1525 RECT rcClient = {};
1526 ::GetClientRect(pT->m_hWnd, &rcClient);
1527 pt.x = rcClient.right / 2;
1528 pt.y = rcClient.bottom / 2;
1529 pT->ViewDPtoLP(&pt);
1530 }
1531
1532 // Modify the Viewport extent
1533 SIZE sizeAll = {};
1534 sizeAll.cx = (int)((float)m_sizeLogAll.cx * fZoomScale);
1535 sizeAll.cy = (int)((float)m_sizeLogAll.cy * fZoomScale);
1536
1537 // Update scroll bars and window
1538 CScrollImpl< T >::SetScrollSize(sizeAll);
1539
1540 // Zoom all children if needed
1541 if(m_bZoomChildren && (m_fZoomScale != fZoomScale))
1542 {
1543 for(int i = 0; i < m_arrChildren.GetSize(); i++)
1544 {
1545 ATLASSERT(::IsWindow(m_arrChildren[i].hWnd));
1546
1547 ::SetWindowPos(m_arrChildren[i].hWnd, NULL,
1548 (int)((float)m_arrChildren[i].x * fZoomScale + 0.5f),
1549 (int)((float)m_arrChildren[i].y * fZoomScale + 0.5f),
1550 (int)((float)m_arrChildren[i].cx * fZoomScale + 0.5f),
1551 (int)((float)m_arrChildren[i].cy * fZoomScale + 0.5f),
1552 SWP_NOZORDER | SWP_NOACTIVATE);
1553 }
1554 }
1555
1556 // Set new zoom scale
1557 m_fZoomScale = fZoomScale;
1558
1559 if(bCenter)
1560 pT->CenterOnLogicalPoint(pt);
1561 }
1562
1563 void ZoomIn(bool bCenter = true)
1564 {
1565 T* pT = static_cast<T*>(this);
1566 pT->Zoom(m_fZoomScale + m_fZoomDelta, bCenter);
1567 }
1568
1569 void ZoomOut(bool bCenter = true)
1570 {
1571 T* pT = static_cast<T*>(this);
1572 pT->Zoom(m_fZoomScale - m_fZoomDelta, bCenter);
1573 }
1574
1575 void ZoomDefault(bool bCenter = true)
1576 {
1577 T* pT = static_cast<T*>(this);
1578 pT->Zoom(1.0f, bCenter);
1579 }
1580
1581 // Helper functions
1582 void PrepareDC(CDCHandle dc)
1583 {
1584 ATLASSERT((this->m_sizeAll.cx >= 0) && (this->m_sizeAll.cy >= 0));
1585 dc.SetMapMode(MM_ANISOTROPIC);
1586 dc.SetWindowExt(this->m_sizeLogAll);
1587 dc.SetViewportExt(this->m_sizeAll);
1588 dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y);
1589 }
1590
1591 void ViewDPtoLP(LPPOINT lpPoints, int nCount = 1)
1592 {
1593 ATLASSERT(lpPoints);
1594 T* pT = static_cast<T*>(this);
1595 ATLASSERT(::IsWindow(pT->m_hWnd));
1596
1597 CWindowDC dc(pT->m_hWnd);
1598 pT->PrepareDC(dc.m_hDC);
1599 dc.DPtoLP(lpPoints, nCount);
1600 }
1601
1602 void ViewLPtoDP(LPPOINT lpPoints, int nCount = 1)
1603 {
1604 ATLASSERT(lpPoints);
1605 T* pT = static_cast<T*>(this);
1606 ATLASSERT(::IsWindow(pT->m_hWnd));
1607
1608 CWindowDC dc(pT->m_hWnd);
1609 pT->PrepareDC(dc.m_hDC);
1610 dc.LPtoDP(lpPoints, nCount);
1611 }
1612
1613 void ClientToDevice(POINT &pt)
1614 {
1615 pt.x += this->m_ptOffset.x;
1616 pt.y += this->m_ptOffset.y;
1617 }
1618
1619 void DeviceToClient(POINT &pt)
1620 {
1621 pt.x -= this->m_ptOffset.x;
1622 pt.y -= this->m_ptOffset.y;
1623 }
1624
1625 void CenterOnPoint(POINT pt)
1626 {
1627 T* pT = static_cast<T*>(this);
1628 RECT rect = {};
1629 pT->GetClientRect(&rect);
1630
1631 int xOfs = pt.x - (rect.right / 2) + this->m_ptOffset.x;
1632 if(xOfs < 0)
1633 {
1634 xOfs = 0;
1635 }
1636 else
1637 {
1638 int xMax = __max((int)(this->m_sizeAll.cx - rect.right), 0);
1639 if(xOfs > xMax)
1640 xOfs = xMax;
1641 }
1642
1643 int yOfs = pt.y - (rect.bottom / 2) + this->m_ptOffset.y;
1644 if(yOfs < 0)
1645 {
1646 yOfs = 0;
1647 }
1648 else
1649 {
1650 int yMax = __max((int)(this->m_sizeAll.cy - rect.bottom), 0);
1651 if(yOfs > yMax)
1652 yOfs = yMax;
1653 }
1654
1655 CScrollImpl< T >::SetScrollOffset(xOfs, yOfs);
1656 }
1657
1658 void CenterOnLogicalPoint(POINT ptLog)
1659 {
1660 T* pT = static_cast<T*>(this);
1661 pT->ViewLPtoDP(&ptLog);
1662 pT->DeviceToClient(ptLog);
1663 pT->CenterOnPoint(ptLog);
1664 }
1665
1666 BOOL PtInDevRect(POINT pt)
1667 {
1668 RECT rc = { 0, 0, this->m_sizeAll.cx, this->m_sizeAll.cy };
1669 ::OffsetRect(&rc, -this->m_ptOffset.x, -this->m_ptOffset.y);
1670 return ::PtInRect(&rc, pt);
1671 }
1672
1673 void NormalizeRect(RECT& rc)
1674 {
1675 if(rc.left > rc.right)
1676 {
1677 int r = rc.right;
1678 rc.right = rc.left;
1679 rc.left = r;
1680 }
1681
1682 if(rc.top > rc.bottom)
1683 {
1684 int b = rc.bottom;
1685 rc.bottom = rc.top;
1686 rc.top = b;
1687 }
1688 }
1689
1690 void DrawTrackRect()
1691 {
1692 T* pT = static_cast<T*>(this);
1693 const SIZE sizeLines = { 2, 2 };
1694 RECT rc = m_rcTrack;
1695 pT->NormalizeRect(rc);
1696 if(!::IsRectEmpty(&rc))
1697 {
1698 CClientDC dc(pT->m_hWnd);
1699 dc.DrawDragRect(&rc, sizeLines, NULL, sizeLines);
1700 }
1701 }
1702
1703 void NotifyParentZoomChanged()
1704 {
1705 T* pT = static_cast<T*>(this);
1706 int nId = pT->GetDlgCtrlID();
1707 NMHDR nmhdr = { pT->m_hWnd, (UINT_PTR)nId, ZSN_ZOOMCHANGED };
1708 ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nId, (LPARAM)&nmhdr);
1709 }
1710
1711 void DoWheelZoom(int zDelta)
1712 {
1713 float fZoomScale = m_fZoomScale + ((zDelta > 0) ? m_fZoomDelta : -m_fZoomDelta);
1714 T* pT = static_cast<T*>(this);
1715 pT->Zoom(fZoomScale);
1716 pT->NotifyParentZoomChanged();
1717 }
1718
1719 BEGIN_MSG_MAP(CZoomScrollImpl)
1720 MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
1721 MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
1722 MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
1723 MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
1724 MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
1725 MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
1726 MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
1727 MESSAGE_HANDLER(WM_PAINT, OnPaint)
1728 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
1729 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
1730 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
1731 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
1732 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
1733 ALT_MSG_MAP(1)
1734 COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
1735 COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
1736 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
1737 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
1738 COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
1739 COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
1740 COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
1741 COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
1742 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
1743 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
1744 COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
1745 COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
1746 END_MSG_MAP()
1747
1748 LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1749 {
1750 if((LOWORD(lParam) == HTCLIENT) && (m_nZoomMode != ZOOMMODE_OFF))
1751 {
1752 T* pT = static_cast<T*>(this);
1753 if((HWND)wParam == pT->m_hWnd)
1754 {
1755 DWORD dwPos = ::GetMessagePos();
1756 POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
1757 pT->ScreenToClient(&pt);
1758 if(pT->PtInDevRect(pt))
1759 {
1760 ::SetCursor(::LoadCursor(NULL, IDC_CROSS));
1761 return 1;
1762 }
1763 }
1764 }
1765
1766 bHandled = FALSE;
1767 return 0;
1768 }
1769
1770 LRESULT OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1771 {
1772 if((GET_KEYSTATE_WPARAM(wParam) & MK_CONTROL) != 0) // handle zoom if Ctrl is pressed
1773 {
1774 int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
1775 T* pT = static_cast<T*>(this);
1776 pT->DoWheelZoom(zDelta);
1777 }
1778 else
1779 {
1780 CScrollImpl< T >::OnMouseWheel(uMsg, wParam, lParam, bHandled);
1781 }
1782
1783 return 0;
1784 }
1785
1786 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1787 {
1788 T* pT = static_cast<T*>(this);
1789 ATLASSERT(::IsWindow(pT->m_hWnd));
1790 ATLASSERT((m_sizeLogAll.cx >= 0) && (m_sizeLogAll.cy >= 0));
1791 ATLASSERT((this->m_sizeAll.cx >= 0) && (this->m_sizeAll.cy >= 0));
1792
1793 if(wParam != NULL)
1794 {
1795 CDCHandle dc = (HDC)wParam;
1796 int nMapModeSav = dc.GetMapMode();
1797 dc.SetMapMode(MM_ANISOTROPIC);
1798 SIZE szWindowExt = { 0, 0 };
1799 dc.SetWindowExt(m_sizeLogAll, &szWindowExt);
1800 SIZE szViewportExt = { 0, 0 };
1801 dc.SetViewportExt(this->m_sizeAll, &szViewportExt);
1802 POINT ptViewportOrg = { 0, 0 };
1803 dc.SetViewportOrg(-this->m_ptOffset.x, -this->m_ptOffset.y, &ptViewportOrg);
1804
1805 pT->DoPaint(dc);
1806
1807 dc.SetMapMode(nMapModeSav);
1808 dc.SetWindowExt(szWindowExt);
1809 dc.SetViewportExt(szViewportExt);
1810 dc.SetViewportOrg(ptViewportOrg);
1811 }
1812 else
1813 {
1814 CPaintDC dc(pT->m_hWnd);
1815 pT->PrepareDC(dc.m_hDC);
1816 pT->DoPaint(dc.m_hDC);
1817 }
1818
1819 return 0;
1820 }
1821
1822 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1823 {
1824 if((m_nZoomMode == ZOOMMODE_IN) && !m_bTracking)
1825 {
1826 T* pT = static_cast<T*>(this);
1827 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1828 if(pT->PtInDevRect(pt))
1829 {
1830 pT->SetCapture();
1831 m_bTracking = true;
1832 ::SetRect(&m_rcTrack, pt.x, pt.y, pt.x, pt.y);
1833
1834 RECT rcClip;
1835 pT->GetClientRect(&rcClip);
1836 if((this->m_ptOffset.x == 0) && (this->m_ptOffset.y == 0))
1837 {
1838 if(rcClip.right > this->m_sizeAll.cx)
1839 rcClip.right = this->m_sizeAll.cx;
1840 if(rcClip.bottom > this->m_sizeAll.cy)
1841 rcClip.bottom = this->m_sizeAll.cy;
1842 }
1843 ::MapWindowPoints(pT->m_hWnd, NULL, (LPPOINT)&rcClip, 2);
1844 ::ClipCursor(&rcClip);
1845 }
1846 }
1847
1848 bHandled = FALSE;
1849 return 0;
1850 }
1851
1852 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1853 {
1854 if(m_bTracking)
1855 {
1856 T* pT = static_cast<T*>(this);
1857 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1858 if(pT->PtInDevRect(pt))
1859 {
1860 pT->DrawTrackRect();
1861 m_rcTrack.right = pt.x + 1;
1862 m_rcTrack.bottom = pt.y + 1;
1863 pT->DrawTrackRect();
1864 }
1865 }
1866
1867 bHandled = FALSE;
1868 return 0;
1869 }
1870
1871 LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1872 {
1873 ::ReleaseCapture();
1874 if(m_nZoomMode == ZOOMMODE_OUT)
1875 {
1876 T* pT = static_cast<T*>(this);
1877 pT->Zoom(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), m_fZoomScale - m_fZoomDelta);
1878 pT->NotifyParentZoomChanged();
1879 }
1880
1881 bHandled = FALSE;
1882 return 0;
1883 }
1884
1885 LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1886 {
1887 if(m_bTracking)
1888 {
1889 m_bTracking = false;
1890 T* pT = static_cast<T*>(this);
1891 pT->DrawTrackRect();
1892 pT->Zoom(m_rcTrack);
1893 pT->NotifyParentZoomChanged();
1894 ::SetRectEmpty(&m_rcTrack);
1895 ::ClipCursor(NULL);
1896 }
1897
1898 bHandled = FALSE;
1899 return 0;
1900 }
1901 };
1902
1903 ///////////////////////////////////////////////////////////////////////////////
1904 // CZoomScrollWindowImpl - Implements scrolling window with zooming
1905
1906 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
1907 class ATL_NO_VTABLE CZoomScrollWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CZoomScrollImpl< T >
1908 {
1909 public:
1910 BOOL SubclassWindow(HWND hWnd)
1911 {
1912 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
1913 if(bRet != FALSE)
1914 {
1915 T* pT = static_cast<T*>(this);
1916 pT->GetSystemSettings();
1917
1918 RECT rect = {};
1919 this->GetClientRect(&rect);
1920 pT->DoSize(rect.right, rect.bottom);
1921 }
1922
1923 return bRet;
1924 }
1925
1926 BEGIN_MSG_MAP(CZoomScrollWindowImpl)
1927 MESSAGE_HANDLER(WM_SETCURSOR, CZoomScrollImpl< T >::OnSetCursor)
1928 MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
1929 MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
1930 MESSAGE_HANDLER(WM_MOUSEWHEEL, CZoomScrollImpl< T >::OnMouseWheel)
1931 MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel)
1932 MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
1933 MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize)
1934 MESSAGE_HANDLER(WM_PAINT, CZoomScrollImpl< T >::OnPaint)
1935 MESSAGE_HANDLER(WM_PRINTCLIENT, CZoomScrollImpl< T >::OnPaint)
1936 MESSAGE_HANDLER(WM_LBUTTONDOWN, CZoomScrollImpl< T >::OnLButtonDown)
1937 MESSAGE_HANDLER(WM_MOUSEMOVE, CZoomScrollImpl< T >::OnMouseMove)
1938 MESSAGE_HANDLER(WM_LBUTTONUP, CZoomScrollImpl< T >::OnLButtonUp)
1939 MESSAGE_HANDLER(WM_CAPTURECHANGED, CZoomScrollImpl< T >::OnCaptureChanged)
1940 ALT_MSG_MAP(1)
1941 COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
1942 COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
1943 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
1944 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
1945 COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
1946 COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
1947 COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
1948 COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
1949 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
1950 COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
1951 COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
1952 COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
1953 END_MSG_MAP()
1954 };
1955
1956
1957 ///////////////////////////////////////////////////////////////////////////////
1958 // CScrollContainer
1959
1960 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
1961 class ATL_NO_VTABLE CScrollContainerImpl : public CScrollWindowImpl< T, TBase, TWinTraits >
1962 {
1963 public:
1964 DECLARE_WND_CLASS_EX2(NULL, T, 0, -1)
1965
1966 typedef CScrollWindowImpl< T, TBase, TWinTraits > _baseClass;
1967
1968 // Data members
1969 ATL::CWindow m_wndClient;
1970 bool m_bAutoSizeClient;
1971 bool m_bDrawEdgeIfEmpty;
1972
1973 // Constructor
1974 CScrollContainerImpl() : m_bAutoSizeClient(true), m_bDrawEdgeIfEmpty(false)
1975 {
1976 // Set CScrollWindowImpl extended style
1977 this->SetScrollExtendedStyle(SCRL_SCROLLCHILDREN);
1978 }
1979
1980 // Attributes
1981 HWND GetClient() const
1982 {
1983 return m_wndClient;
1984 }
1985
1986 HWND SetClient(HWND hWndClient, bool bClientSizeAsMin = true)
1987 {
1988 ATLASSERT(::IsWindow(this->m_hWnd));
1989
1990 HWND hWndOldClient = m_wndClient;
1991 m_wndClient = hWndClient;
1992
1993 this->SetRedraw(FALSE);
1994 this->SetScrollSize(1, 1, FALSE);
1995
1996 if(m_wndClient.m_hWnd != NULL)
1997 {
1998 m_wndClient.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
1999
2000 if(bClientSizeAsMin)
2001 {
2002 RECT rect = {};
2003 m_wndClient.GetWindowRect(&rect);
2004 if(((rect.right - rect.left) > 0) && ((rect.bottom - rect.top) > 0))
2005 this->SetScrollSize(rect.right - rect.left, rect.bottom - rect.top, FALSE);
2006 }
2007
2008 T* pT = static_cast<T*>(this);
2009 pT->UpdateLayout();
2010 }
2011
2012 this->SetRedraw(TRUE);
2013 this->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
2014
2015 return hWndOldClient;
2016 }
2017
2018 // Message map and handlers
2019 BEGIN_MSG_MAP(CScrollContainerImpl)
2020 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
2021 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2022 CHAIN_MSG_MAP(_baseClass)
2023 FORWARD_NOTIFICATIONS()
2024 ALT_MSG_MAP(1)
2025 CHAIN_MSG_MAP_ALT(_baseClass, 1)
2026 END_MSG_MAP()
2027
2028 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2029 {
2030 if(m_wndClient.m_hWnd != NULL)
2031 m_wndClient.SetFocus();
2032
2033 return 0;
2034 }
2035
2036 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2037 {
2038 return 1; // no background needed
2039 }
2040
2041 // Overrides for CScrollWindowImpl
2042 void DoSize(int cx, int cy)
2043 {
2044 _baseClass::DoSize(cx, cy);
2045
2046 T* pT = static_cast<T*>(this);
2047 pT->UpdateLayout();
2048 }
2049
2050 void DoPaint(CDCHandle dc)
2051 {
2052 if(!m_bAutoSizeClient || (m_wndClient.m_hWnd == NULL))
2053 {
2054 T* pT = static_cast<T*>(this);
2055 RECT rect = {};
2056 pT->GetContainerRect(rect);
2057
2058 if(m_bDrawEdgeIfEmpty && (m_wndClient.m_hWnd == NULL))
2059 dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
2060
2061 dc.FillRect(&rect, COLOR_APPWORKSPACE);
2062 }
2063 }
2064
2065 void ScrollToView(POINT pt)
2066 {
2067 CScrollWindowImpl< T, TBase, TWinTraits >::ScrollToView(pt);
2068 }
2069
2070 void ScrollToView(RECT& rect)
2071 {
2072 CScrollWindowImpl< T, TBase, TWinTraits >::ScrollToView(rect);
2073 }
2074
2075 void ScrollToView(HWND hWnd) // client window coordinates
2076 {
2077 T* pT = static_cast<T*>(this);
2078 (void)pT; // avoid level 4 warning
2079 ATLASSERT(::IsWindow(pT->m_hWnd));
2080 ATLASSERT(m_wndClient.IsWindow());
2081
2082 RECT rect = {};
2083 ::GetWindowRect(hWnd, &rect);
2084 ::MapWindowPoints(NULL, m_wndClient.m_hWnd, (LPPOINT)&rect, 2);
2085 ScrollToView(rect);
2086 }
2087
2088 // Implementation - overrideable methods
2089 void UpdateLayout()
2090 {
2091 ATLASSERT(::IsWindow(this->m_hWnd));
2092
2093 if(m_bAutoSizeClient && (m_wndClient.m_hWnd != NULL))
2094 {
2095 T* pT = static_cast<T*>(this);
2096 RECT rect = {};
2097 pT->GetContainerRect(rect);
2098
2099 m_wndClient.SetWindowPos(NULL, &rect, SWP_NOZORDER | SWP_NOMOVE);
2100 }
2101 else
2102 {
2103 this->Invalidate();
2104 }
2105 }
2106
2107 void GetContainerRect(RECT& rect)
2108 {
2109 this->GetClientRect(&rect);
2110
2111 if(rect.right < this->m_sizeAll.cx)
2112 rect.right = this->m_sizeAll.cx;
2113
2114 if(rect.bottom < this->m_sizeAll.cy)
2115 rect.bottom = this->m_sizeAll.cy;
2116 }
2117 };
2118
2119 class CScrollContainer : public CScrollContainerImpl<CScrollContainer>
2120 {
2121 public:
2122 DECLARE_WND_CLASS_EX(_T("WTL_ScrollContainer"), 0, -1)
2123 };
2124
2125 } // namespace WTL
2126
2127 #endif // __ATLSCRL_H__