1
2 #include "stdafx.h"
3 #include "ScrollHelper.h"
4
5 #ifdef _DEBUG
6 #define new DEBUG_NEW
7 #undef THIS_FILE
8 static char THIS_FILE[] = __FILE__;
9 #endif
10
11 // CScrollHelper /////////////////////////////////////////////////////////////////////
12
CScrollHelper()13 CScrollHelper::CScrollHelper()
14 {
15 m_attachWnd = NULL;
16 m_pageSize = CSize(0,0);
17 m_displaySize = CSize(0,0);
18 m_scrollPos = CSize(0,0);
19 }
20
~CScrollHelper()21 CScrollHelper::~CScrollHelper()
22 {
23 DetachWnd();
24 }
25
AttachWnd(CWnd * pWnd)26 void CScrollHelper::AttachWnd(CWnd* pWnd)
27 {
28 m_attachWnd = pWnd;
29 }
30
DetachWnd()31 void CScrollHelper::DetachWnd()
32 {
33 m_attachWnd = NULL;
34 }
35
SetDisplaySize(int displayWidth,int displayHeight)36 void CScrollHelper::SetDisplaySize(int displayWidth, int displayHeight)
37 {
38 m_displaySize = CSize(displayWidth, displayHeight);
39
40 if ( m_attachWnd != NULL && ::IsWindow(m_attachWnd->m_hWnd) )
41 UpdateScrollInfo();
42 }
43
GetDisplaySize() const44 const CSize& CScrollHelper::GetDisplaySize() const
45 {
46 return m_displaySize;
47 }
48
GetScrollPos() const49 const CSize& CScrollHelper::GetScrollPos() const
50 {
51 return m_scrollPos;
52 }
53
54
GetPageSize() const55 const CSize& CScrollHelper::GetPageSize() const
56 {
57 return m_pageSize;
58 }
59
SetScrollPos(int bar,int newpos,BOOL redraw)60 void CScrollHelper::SetScrollPos(int bar, int newpos, BOOL redraw)
61 {
62 if (m_attachWnd == NULL)
63 return;
64
65 CRect rect;
66 GetClientRectSB(m_attachWnd, rect);
67 CSize windowSize(rect.Width(), rect.Height());
68
69 if ((bar == SB_HORZ) && (windowSize.cx < m_displaySize.cx))
70 {
71 int deltaPos = newpos - m_scrollPos.cx;
72 m_scrollPos.cx += deltaPos;
73 m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, redraw);
74 if (redraw)
75 {
76 m_attachWnd->ScrollWindow(0, -deltaPos);
77 }
78 }
79
80 if ((bar == SB_VERT) && (windowSize.cy < m_displaySize.cy))
81 {
82 int deltaPos = newpos - m_scrollPos.cy;
83 m_scrollPos.cy += deltaPos;
84 m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, redraw);
85
86 if (redraw)
87 {
88 m_attachWnd->ScrollWindow(0, -deltaPos);
89 }
90 }
91 }
92
ScrollToOrigin(bool scrollLeft,bool scrollTop)93 void CScrollHelper::ScrollToOrigin(bool scrollLeft, bool scrollTop)
94 {
95 if ( m_attachWnd == NULL )
96 return;
97
98 if ( scrollLeft )
99 {
100 if ( m_displaySize.cx > 0 && m_pageSize.cx > 0 && m_scrollPos.cx > 0 )
101 {
102 int deltaPos = -m_scrollPos.cx;
103 m_scrollPos.cx += deltaPos;
104 m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, TRUE);
105 m_attachWnd->ScrollWindow(-deltaPos, 0);
106 }
107 }
108
109 if ( scrollTop )
110 {
111 if ( m_displaySize.cy > 0 && m_pageSize.cy > 0 && m_scrollPos.cy > 0 )
112 {
113 int deltaPos = -m_scrollPos.cy;
114 m_scrollPos.cy += deltaPos;
115 m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, TRUE);
116 m_attachWnd->ScrollWindow(0, -deltaPos);
117 }
118 }
119 }
120
OnHScroll(UINT nSBCode,UINT nPos,CScrollBar * pScrollBar)121 void CScrollHelper::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
122 {
123 if ( m_attachWnd == NULL )
124 return;
125
126 const int lineOffset = 60;
127
128 // Compute the desired change or delta in scroll position.
129 int deltaPos = 0;
130 switch( nSBCode )
131 {
132 case SB_LINELEFT:
133 // Left scroll arrow was pressed.
134 deltaPos = -lineOffset;
135 break;
136
137 case SB_LINERIGHT:
138 // Right scroll arrow was pressed.
139 deltaPos = lineOffset;
140 break;
141
142 case SB_PAGELEFT:
143 // User clicked inbetween left arrow and thumb.
144 deltaPos = -m_pageSize.cx;
145 break;
146
147 case SB_PAGERIGHT:
148 // User clicked inbetween thumb and right arrow.
149 deltaPos = m_pageSize.cx;
150 break;
151
152 case SB_THUMBTRACK:
153 // Scrollbar thumb is being dragged.
154 deltaPos = Get32BitScrollPos(SB_HORZ, pScrollBar) - m_scrollPos.cx;
155 break;
156
157 case SB_THUMBPOSITION:
158 // Scrollbar thumb was released.
159 deltaPos = Get32BitScrollPos(SB_HORZ, pScrollBar) - m_scrollPos.cx;
160 break;
161
162 default:
163 // We don't process other scrollbar messages.
164 return;
165 }
166
167 // Compute the new scroll position.
168 int newScrollPos = m_scrollPos.cx + deltaPos;
169
170 // If the new scroll position is negative, we adjust
171 // deltaPos in order to scroll the window back to origin.
172 if ( newScrollPos < 0 )
173 deltaPos = -m_scrollPos.cx;
174
175 // If the new scroll position is greater than the max scroll position,
176 // we adjust deltaPos in order to scroll the window precisely to the
177 // maximum position.
178 int maxScrollPos = m_displaySize.cx - m_pageSize.cx;
179 if ( newScrollPos > maxScrollPos )
180 deltaPos = maxScrollPos - m_scrollPos.cx;
181
182 // Scroll the window if needed.
183 if ( deltaPos != 0 )
184 {
185 m_scrollPos.cx += deltaPos;
186 m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, TRUE);
187 m_attachWnd->ScrollWindow(-deltaPos, 0);
188 }
189 }
190
OnVScroll(UINT nSBCode,UINT nPos,CScrollBar * pScrollBar)191 void CScrollHelper::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
192 {
193 if ( m_attachWnd == NULL )
194 return;
195
196 const int lineOffset = 60;
197
198 // Compute the desired change or delta in scroll position.
199 int deltaPos = 0;
200 switch( nSBCode )
201 {
202 case SB_LINEUP:
203 // Up arrow button on scrollbar was pressed.
204 deltaPos = -lineOffset;
205 break;
206
207 case SB_LINEDOWN:
208 // Down arrow button on scrollbar was pressed.
209 deltaPos = lineOffset;
210 break;
211
212 case SB_PAGEUP:
213 // User clicked inbetween up arrow and thumb.
214 deltaPos = -m_pageSize.cy;
215 break;
216
217 case SB_PAGEDOWN:
218 // User clicked inbetween thumb and down arrow.
219 deltaPos = m_pageSize.cy;
220 break;
221
222 case SB_THUMBTRACK:
223 // Scrollbar thumb is being dragged.
224 deltaPos = Get32BitScrollPos(SB_VERT, pScrollBar) - m_scrollPos.cy;
225 break;
226
227 case SB_THUMBPOSITION:
228 // Scrollbar thumb was released.
229 deltaPos = Get32BitScrollPos(SB_VERT, pScrollBar) - m_scrollPos.cy;
230 break;
231
232 default:
233 // We don't process other scrollbar messages.
234 return;
235 }
236
237 // Compute the new scroll position.
238 int newScrollPos = m_scrollPos.cy + deltaPos;
239
240 // If the new scroll position is negative, we adjust
241 // deltaPos in order to scroll the window back to origin.
242 if ( newScrollPos < 0 )
243 deltaPos = -m_scrollPos.cy;
244
245 // If the new scroll position is greater than the max scroll position,
246 // we adjust deltaPos in order to scroll the window precisely to the
247 // maximum position.
248 int maxScrollPos = m_displaySize.cy - m_pageSize.cy;
249 if ( newScrollPos > maxScrollPos )
250 deltaPos = maxScrollPos - m_scrollPos.cy;
251
252 // Scroll the window if needed.
253 if ( deltaPos != 0 )
254 {
255 m_scrollPos.cy += deltaPos;
256 m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, TRUE);
257 m_attachWnd->ScrollWindow(0, -deltaPos);
258 }
259 }
260
OnMouseWheel(UINT nFlags,short zDelta,CPoint pt)261 BOOL CScrollHelper::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
262 {
263 if ( m_attachWnd == NULL )
264 return FALSE;
265
266 // Don't do anything if the vertical scrollbar is not enabled.
267 int scrollMin = 0, scrollMax = 0;
268 m_attachWnd->GetScrollRange(SB_VERT, &scrollMin, &scrollMax);
269 if ( scrollMin == scrollMax )
270 return FALSE;
271
272 // Compute the number of scrolling increments requested.
273 int numScrollIncrements = abs(zDelta) / WHEEL_DELTA;
274
275 // Each scrolling increment corresponds to a certain number of
276 // scroll lines (one scroll line is like a SB_LINEUP or SB_LINEDOWN).
277 // We need to query the system parameters for this value.
278 int numScrollLinesPerIncrement = 0;
279 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &numScrollLinesPerIncrement, 0);
280
281 // Check if a page scroll was requested.
282 if ( numScrollLinesPerIncrement == WHEEL_PAGESCROLL )
283 {
284 // Call the vscroll message handler to do the work.
285 OnVScroll(zDelta > 0 ? SB_PAGEUP : SB_PAGEDOWN, 0, NULL);
286 return TRUE;
287 }
288
289 // Compute total number of lines to scroll.
290 int numScrollLines = numScrollIncrements * numScrollLinesPerIncrement;
291
292 // Adjust numScrollLines to slow down the scrolling a bit more.
293 numScrollLines = max(numScrollLines/3, 1);
294
295 // Do the scrolling.
296 for(int i = 0; i < numScrollLines; ++i)
297 {
298 // Call the vscroll message handler to do the work.
299 OnVScroll(zDelta > 0 ? SB_LINEUP : SB_LINEDOWN, 0, NULL);
300 }
301
302 return TRUE;
303 }
304
OnSize(UINT nType,int cx,int cy)305 void CScrollHelper::OnSize(UINT nType, int cx, int cy)
306 {
307 UpdateScrollInfo();
308 }
309
Scroll(int bar,int delta)310 BOOL CScrollHelper::Scroll(int bar, int delta)
311 {
312 int pos;
313 int new_pos;
314 int max_pos;
315
316 if (bar == SB_VERT)
317 {
318 pos = m_scrollPos.cy;
319 max_pos = m_displaySize.cy - m_pageSize.cy;
320
321 }
322 else if (bar == SB_HORZ)
323 {
324 pos = m_scrollPos.cx;
325 max_pos = m_displaySize.cx - m_pageSize.cx;
326 }
327 else
328 {
329 return FALSE;
330 }
331
332 new_pos = pos + delta;
333
334 if (delta > 0)
335 {
336 if (new_pos > max_pos)
337 {
338 new_pos = max_pos;
339 }
340 }
341 else
342 {
343 if (new_pos < 0)
344 {
345 new_pos = 0;
346 }
347 }
348
349 if (new_pos != pos)
350 {
351 SetScrollPos(bar, new_pos, TRUE);
352 return TRUE;
353 }
354
355 return FALSE;
356 }
357
Get32BitScrollPos(int bar,CScrollBar * pScrollBar)358 int CScrollHelper::Get32BitScrollPos(int bar, CScrollBar* pScrollBar)
359 {
360 // Code below is from MSDN Article ID 152252, "How To Get
361 // 32-bit Scroll Position During Scroll Messages".
362
363 // First determine if the user scrolled a scroll bar control
364 // on the window or scrolled the window itself.
365 ASSERT( m_attachWnd != NULL );
366 HWND hWndScroll;
367 if ( pScrollBar == NULL )
368 hWndScroll = m_attachWnd->m_hWnd;
369 else
370 hWndScroll = pScrollBar->m_hWnd;
371
372 SCROLLINFO si;
373 si.cbSize = sizeof(SCROLLINFO);
374 si.fMask = SIF_TRACKPOS;
375 ::GetScrollInfo(hWndScroll, bar, &si);
376
377 int scrollPos = si.nTrackPos;
378
379 return scrollPos;
380 }
381
UpdateScrollInfo()382 void CScrollHelper::UpdateScrollInfo()
383 {
384 if ( m_attachWnd == NULL )
385 return;
386
387 // Get the width/height of the attached wnd that includes the area
388 // covered by the scrollbars (if any). The reason we need this is
389 // because when scrollbars are present, both cx/cy and GetClientRect()
390 // when accessed from OnSize() do not include the scrollbar covered
391 // areas. In other words, their values are smaller than what you would
392 // expect.
393 CRect rect;
394 GetClientRectSB(m_attachWnd, rect);
395 CSize windowSize(rect.Width(), rect.Height());
396
397 // Update horizontal scrollbar.
398 CSize deltaPos(0,0);
399 UpdateScrollBar(SB_HORZ, windowSize.cx, m_displaySize.cx,
400 m_pageSize.cx, m_scrollPos.cx, deltaPos.cx);
401
402 // Update vertical scrollbar.
403 UpdateScrollBar(SB_VERT, windowSize.cy, m_displaySize.cy,
404 m_pageSize.cy, m_scrollPos.cy, deltaPos.cy);
405
406 // See if we need to scroll the window back in place.
407 // This is needed to handle the case where the scrollbar is
408 // moved all the way to the right for example, and controls
409 // at the left side disappear from the view. Then the user
410 // resizes the window wider until scrollbars disappear. Without
411 // this code below, the controls off the page will be gone forever.
412 if ( deltaPos.cx != 0 || deltaPos.cy != 0 )
413 {
414 m_attachWnd->ScrollWindow(deltaPos.cx, deltaPos.cy);
415 }
416 }
417
UpdateScrollBar(int bar,int windowSize,int displaySize,LONG & pageSize,LONG & scrollPos,LONG & deltaPos)418 void CScrollHelper::UpdateScrollBar(int bar, int windowSize, int displaySize,
419 LONG& pageSize, LONG& scrollPos, LONG& deltaPos)
420 {
421 int scrollMax = 0;
422 deltaPos = 0;
423 if ( windowSize < displaySize )
424 {
425 scrollMax = displaySize - 1;
426 if ( pageSize > 0 && scrollPos > 0 )
427 {
428 // Adjust the scroll position when the window size is changed.
429 scrollPos = (LONG)(1.0 * scrollPos * windowSize / pageSize);
430 }
431 pageSize = windowSize;
432 scrollPos = min(scrollPos, displaySize - pageSize);
433 deltaPos = m_attachWnd->GetScrollPos(bar) - scrollPos;
434 }
435 else
436 {
437 // Force the scrollbar to go away.
438 pageSize = 0;
439 scrollPos = 0;
440 deltaPos = m_attachWnd->GetScrollPos(bar);
441 }
442
443 SCROLLINFO si;
444 memset(&si, 0, sizeof(SCROLLINFO));
445 si.cbSize = sizeof(SCROLLINFO);
446 si.fMask = SIF_ALL; // SIF_ALL = SIF_PAGE | SIF_RANGE | SIF_POS;
447 si.nMin = 0;
448 si.nMax = scrollMax;
449 si.nPage = pageSize;
450 si.nPos = scrollPos;
451 m_attachWnd->SetScrollInfo(bar, &si, TRUE);
452 }
453
454 // Helper function to get client rect with possible
455 // modification by adding scrollbar width/height.
GetClientRectSB(CWnd * pWnd,CRect & rect)456 void CScrollHelper::GetClientRectSB(CWnd* pWnd, CRect& rect)
457 {
458 ASSERT(pWnd != NULL);
459
460 //CRect winRect;
461 //pWnd->GetWindowRect(&winRect);
462 //pWnd->ScreenToClient(&winRect);
463
464 pWnd->GetClientRect(&rect);
465
466 int cxSB = ::GetSystemMetrics(SM_CXVSCROLL);
467 int cySB = ::GetSystemMetrics(SM_CYHSCROLL);
468
469 int style = pWnd->GetStyle();
470 if (style & WS_HSCROLL)
471 {
472 rect.bottom += cySB;
473 }
474
475 if (style & WS_VSCROLL)
476 {
477 rect.right += cxSB;
478 }
479
480 if (rect.Width() < m_displaySize.cx)
481 {
482 rect.bottom -= cySB;
483 }
484
485 if (rect.Height() < m_displaySize.cy)
486 {
487 rect.right -= cxSB;
488
489 if (rect.Width() < m_displaySize.cx)
490 {
491 rect.bottom -= cySB;
492 }
493 }
494 }
495 // END
496
497