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 11 void 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 61 void 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 124 void 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 241 void 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 256 void 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 } 288