Reactos
1/*
2 * PROJECT: ReactOS Clipboard Viewer
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Scrolling related helper functions.
5 * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke
6 * Copyright 2015-2018 Hermes Belusca-Maito
7 */
8
9#include "precomp.h"
10
11void OnKeyScroll(HWND hWnd, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state)
12{
13 // NOTE: Windows uses an offset of 16 pixels
14 switch (wParam)
15 {
16 case VK_UP:
17 OnScroll(hWnd, SB_VERT, MAKELONG(SB_LINEUP, 0), 5, state);
18 break;
19
20 case VK_DOWN:
21 OnScroll(hWnd, SB_VERT, MAKELONG(SB_LINEDOWN, 0), 5, state);
22 break;
23
24 case VK_LEFT:
25 OnScroll(hWnd, SB_HORZ, MAKELONG(SB_LINELEFT, 0), 5, state);
26 break;
27
28 case VK_RIGHT:
29 OnScroll(hWnd, SB_HORZ, MAKELONG(SB_LINERIGHT, 0), 5, state);
30 break;
31
32 case VK_PRIOR:
33 OnScroll(hWnd, SB_VERT, MAKELONG(SB_PAGEUP, 0), state->nPageY, state);
34 break;
35
36 case VK_NEXT:
37 OnScroll(hWnd, SB_VERT, MAKELONG(SB_PAGEDOWN, 0), state->nPageY, state);
38 break;
39
40 case VK_HOME:
41 {
42 OnScroll(hWnd, SB_HORZ, MAKELONG(SB_LEFT, 0), 0, state);
43 if (GetKeyState(VK_CONTROL) & 0x8000)
44 OnScroll(hWnd, SB_VERT, MAKELONG(SB_TOP, 0), 0, state);
45 break;
46 }
47
48 case VK_END:
49 {
50 OnScroll(hWnd, SB_HORZ, MAKELONG(SB_RIGHT, 0), 0, state);
51 if (GetKeyState(VK_CONTROL) & 0x8000)
52 OnScroll(hWnd, SB_VERT, MAKELONG(SB_BOTTOM, 0), 0, state);
53 break;
54 }
55
56 default:
57 break;
58 }
59}
60
61void OnMouseScroll(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state)
62{
63 INT nBar;
64 INT nPage;
65 INT iDelta;
66 UINT uLinesToScroll = state->uLinesToScroll;
67 INT zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
68 WORD sbCode;
69
70 assert(uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL);
71
72 if (uMsg == WM_MOUSEWHEEL)
73 {
74 nBar = SB_VERT;
75 nPage = state->nPageY;
76
77 /* Accumulate wheel rotation ticks */
78 zDelta += state->iWheelCarryoverY;
79 state->iWheelCarryoverY = zDelta % WHEEL_DELTA;
80 }
81 else // if (uMsg == WM_MOUSEHWHEEL)
82 {
83 nBar = SB_HORZ;
84 nPage = state->nPageX;
85
86 /* Accumulate wheel rotation ticks */
87 zDelta += state->iWheelCarryoverX;
88 state->iWheelCarryoverX = zDelta % WHEEL_DELTA;
89 }
90
91 /*
92 * If the user specified scrolling by pages, do so.
93 * Due to a bug on Windows where, if the window height is
94 * less than the scroll lines delta default value (== 3),
95 * several lines would be skipped when scrolling if we
96 * used the WHEEL_PAGESCROLL value. Instead of this, use
97 * the number of lines per page as the limiting value.
98 * See https://www.strchr.com/corrections_to_raymond_chen_s_wheel_scrolling_code
99 * for more details.
100 */
101 if (uLinesToScroll > nPage) // (uLinesToScroll == WHEEL_PAGESCROLL)
102 uLinesToScroll = nPage;
103 /* If the user specified no wheel scrolling, don't do anything */
104 else if (uLinesToScroll == 0)
105 return;
106
107 /* Compute the scroll direction and the absolute delta value */
108 if (zDelta > 0)
109 {
110 sbCode = SB_LINEUP;
111 }
112 else
113 {
114 sbCode = SB_LINEDOWN;
115 zDelta = -zDelta;
116 }
117
118 /* Compute how many lines we should scroll (in absolute value) */
119 iDelta = (INT)uLinesToScroll * zDelta / WHEEL_DELTA;
120
121 OnScroll(hWnd, nBar, MAKELONG(sbCode, 0), iDelta, state);
122}
123
124void OnScroll(HWND hWnd, INT nBar, WPARAM wParam, INT iDelta, LPSCROLLSTATE state)
125{
126 SCROLLINFO si;
127 PINT pCurrent;
128 INT Maximum;
129 INT NewPos;
130 INT OldX, OldY;
131
132 assert(nBar == SB_HORZ || nBar == SB_VERT);
133
134 if (Globals.uDisplayFormat == CF_OWNERDISPLAY)
135 {
136 if (nBar == SB_HORZ)
137 {
138 SendClipboardOwnerMessage(TRUE, WM_HSCROLLCLIPBOARD,
139 (WPARAM)hWnd, (LPARAM)wParam);
140 }
141 else // if (nBar == SB_VERT)
142 {
143 SendClipboardOwnerMessage(TRUE, WM_VSCROLLCLIPBOARD,
144 (WPARAM)hWnd, (LPARAM)wParam);
145 }
146 return;
147 }
148
149 if (nBar == SB_HORZ)
150 {
151 pCurrent = &state->CurrentX;
152 Maximum = state->MaxX;
153 }
154 else // if (nBar == SB_VERT)
155 {
156 pCurrent = &state->CurrentY;
157 Maximum = state->MaxY;
158 }
159
160 ZeroMemory(&si, sizeof(si));
161 si.cbSize = sizeof(si);
162 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
163 GetScrollInfo(hWnd, nBar, &si);
164
165 switch (LOWORD(wParam))
166 {
167 case SB_THUMBPOSITION:
168 case SB_THUMBTRACK:
169 {
170 NewPos = si.nTrackPos;
171 break;
172 }
173
174 case SB_LINEUP: // SB_LINELEFT:
175 {
176 NewPos = si.nPos - iDelta;
177 break;
178 }
179
180 case SB_LINEDOWN: // SB_LINERIGHT:
181 {
182 NewPos = si.nPos + iDelta;
183 break;
184 }
185
186 case SB_PAGEUP: // SB_PAGELEFT:
187 {
188 NewPos = si.nPos - si.nPage;
189 break;
190 }
191
192 case SB_PAGEDOWN: // SB_PAGERIGHT:
193 {
194 NewPos = si.nPos + si.nPage;
195 break;
196 }
197
198 case SB_TOP: // SB_LEFT:
199 {
200 NewPos = si.nMin;
201 break;
202 }
203
204 case SB_BOTTOM: // SB_RIGHT:
205 {
206 NewPos = si.nMax;
207 break;
208 }
209
210 default:
211 {
212 NewPos = si.nPos;
213 break;
214 }
215 }
216
217 NewPos = min(max(NewPos, 0), Maximum);
218
219 if (si.nPos == NewPos)
220 return;
221
222 OldX = state->CurrentX;
223 OldY = state->CurrentY;
224 *pCurrent = NewPos;
225
226 ScrollWindowEx(hWnd,
227 OldX - state->CurrentX,
228 OldY - state->CurrentY,
229 NULL,
230 NULL,
231 NULL,
232 NULL,
233 SW_ERASE | SW_INVALIDATE);
234 UpdateWindow(hWnd);
235
236 si.fMask = SIF_POS;
237 si.nPos = NewPos;
238 SetScrollInfo(hWnd, nBar, &si, TRUE);
239}
240
241void UpdateLinesToScroll(LPSCROLLSTATE state)
242{
243 UINT uLinesToScroll;
244
245 if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uLinesToScroll, 0))
246 {
247 /* Default value on Windows */
248 state->uLinesToScroll = 3;
249 }
250 else
251 {
252 state->uLinesToScroll = uLinesToScroll;
253 }
254}
255
256void UpdateWindowScrollState(HWND hWnd, INT nMaxWidth, INT nMaxHeight, LPSCROLLSTATE lpState)
257{
258 RECT rc;
259 SCROLLINFO si;
260
261 if (!GetClientRect(hWnd, &rc))
262 SetRectEmpty(&rc);
263
264 ZeroMemory(&si, sizeof(si));
265 si.cbSize = sizeof(si);
266 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
267
268 lpState->nMaxWidth = nMaxWidth;
269 lpState->MaxX = max(nMaxWidth - rc.right, 0);
270 lpState->CurrentX = min(lpState->CurrentX, lpState->MaxX);
271 lpState->nPageX = rc.right;
272 si.nMin = 0;
273 si.nMax = nMaxWidth;
274 si.nPage = lpState->nPageX;
275 si.nPos = lpState->CurrentX;
276 SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
277
278 lpState->nMaxHeight = nMaxHeight;
279 lpState->MaxY = max(nMaxHeight - rc.bottom, 0);
280 lpState->CurrentY = min(lpState->CurrentY, lpState->MaxY);
281 lpState->nPageY = rc.bottom;
282 si.nMin = 0;
283 si.nMax = nMaxHeight;
284 si.nPage = lpState->nPageY;
285 si.nPos = lpState->CurrentY;
286 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
287}