ddraw/tests: Remove p8_primary_test.
[wine.git] / dlls / comctl32 / theme_scrollbar.c
blobbb42267bf21021684f3fd7db26fcf02d636ae24c
1 /*
2 * Theming - Scrollbar control
4 * Copyright (c) 2015 Mark Harmstone
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <string.h>
24 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "uxtheme.h"
31 #include "vssym32.h"
32 #include "comctl32.h"
33 #include "wine/debug.h"
35 /* Minimum size of the thumb in pixels */
36 #define SCROLL_MIN_THUMB 6
38 /* Minimum size of the rectangle between the arrows */
39 #define SCROLL_MIN_RECT 4
41 enum SCROLL_HITTEST
43 SCROLL_NOWHERE, /* Outside the scroll bar */
44 SCROLL_TOP_ARROW, /* Top or left arrow */
45 SCROLL_TOP_RECT, /* Rectangle between the top arrow and the thumb */
46 SCROLL_THUMB, /* Thumb rectangle */
47 SCROLL_BOTTOM_RECT, /* Rectangle between the thumb and the bottom arrow */
48 SCROLL_BOTTOM_ARROW /* Bottom or right arrow */
51 static HWND tracking_win = 0;
52 static enum SCROLL_HITTEST tracking_hot_part = SCROLL_NOWHERE;
54 WINE_DEFAULT_DEBUG_CHANNEL(theme_scroll);
56 static void calc_thumb_dimensions(unsigned int size, SCROLLINFO *si, unsigned int *thumbpos, unsigned int *thumbsize)
58 if (size <= SCROLL_MIN_RECT)
59 *thumbpos = *thumbsize = 0;
60 else if (si->nPage > si->nMax - si->nMin)
61 *thumbpos = *thumbsize = 0;
62 else {
63 if (si->nPage > 0) {
64 *thumbsize = MulDiv(size, si->nPage, si->nMax - si->nMin + 1);
65 if (*thumbsize < SCROLL_MIN_THUMB) *thumbsize = SCROLL_MIN_THUMB;
67 else *thumbsize = GetSystemMetrics(SM_CXVSCROLL);
69 if (size < *thumbsize)
70 *thumbpos = *thumbsize = 0;
71 else {
72 int max = si->nMax - max(si->nPage - 1, 0);
73 size -= *thumbsize;
74 if (si->nMin >= max)
75 *thumbpos = 0;
76 else
77 *thumbpos = MulDiv(size, si->nTrackPos - si->nMin, max - si->nMin);
82 static enum SCROLL_HITTEST hit_test(HWND hwnd, HTHEME theme, POINT pt)
84 RECT r;
85 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
86 BOOL vertical = style & SBS_VERT;
87 SIZE sz;
88 SCROLLINFO si;
89 unsigned int offset, size, upsize, downsize, thumbpos, thumbsize;
91 GetWindowRect(hwnd, &r);
92 OffsetRect(&r, -r.left, -r.top);
94 if (vertical) {
95 offset = pt.y;
96 size = r.bottom;
98 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_UPNORMAL, NULL, TS_DRAW, &sz))) {
99 WARN("Could not get up arrow size.\n");
100 upsize = 0;
101 } else
102 upsize = sz.cy;
104 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_DOWNNORMAL, NULL, TS_DRAW, &sz))) {
105 WARN("Could not get down arrow size.\n");
106 downsize = 0;
107 } else
108 downsize = sz.cy;
109 } else {
110 offset = pt.x;
111 size = r.right;
113 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_LEFTNORMAL, NULL, TS_DRAW, &sz))) {
114 WARN("Could not get left arrow size.\n");
115 upsize = 0;
116 } else
117 upsize = sz.cx;
119 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_RIGHTNORMAL, NULL, TS_DRAW, &sz))) {
120 WARN("Could not get right arrow size.\n");
121 downsize = 0;
122 } else
123 downsize = sz.cx;
126 if (pt.x < 0 || pt.x > r.right || pt.y < 0 || pt.y > r.bottom)
127 return SCROLL_NOWHERE;
129 if (size < SCROLL_MIN_RECT + upsize + downsize)
130 upsize = downsize = (size - SCROLL_MIN_RECT)/2;
132 if (offset < upsize)
133 return SCROLL_TOP_ARROW;
135 if (offset > size - downsize)
136 return SCROLL_BOTTOM_ARROW;
138 si.cbSize = sizeof(si);
139 si.fMask = SIF_ALL;
140 if (!GetScrollInfo(hwnd, SB_CTL, &si)) {
141 WARN("GetScrollInfo failed.\n");
142 return SCROLL_NOWHERE;
145 calc_thumb_dimensions(size - upsize - downsize, &si, &thumbpos, &thumbsize);
147 if (offset < upsize + thumbpos)
148 return SCROLL_TOP_RECT;
149 else if (offset < upsize + thumbpos + thumbsize)
150 return SCROLL_THUMB;
151 else
152 return SCROLL_BOTTOM_RECT;
155 static void redraw_part(HWND hwnd, HTHEME theme, enum SCROLL_HITTEST part)
157 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
158 BOOL vertical = style & SBS_VERT;
159 SIZE sz;
160 RECT r, partrect;
161 unsigned int size, upsize, downsize;
163 if (part == SCROLL_NOWHERE) { /* redraw everything */
164 InvalidateRect(hwnd, NULL, TRUE);
165 return;
168 GetWindowRect(hwnd, &r);
169 OffsetRect(&r, -r.left, -r.top);
171 if (vertical) {
172 size = r.bottom;
174 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_UPNORMAL, NULL, TS_DRAW, &sz))) {
175 WARN("Could not get up arrow size.\n");
176 upsize = 0;
177 } else
178 upsize = sz.cy;
180 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_DOWNNORMAL, NULL, TS_DRAW, &sz))) {
181 WARN("Could not get down arrow size.\n");
182 downsize = 0;
183 } else
184 downsize = sz.cy;
185 } else {
186 size = r.right;
188 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_LEFTNORMAL, NULL, TS_DRAW, &sz))) {
189 WARN("Could not get left arrow size.\n");
190 upsize = 0;
191 } else
192 upsize = sz.cx;
194 if (FAILED(GetThemePartSize(theme, NULL, SBP_ARROWBTN, ABS_RIGHTNORMAL, NULL, TS_DRAW, &sz))) {
195 WARN("Could not get right arrow size.\n");
196 downsize = 0;
197 } else
198 downsize = sz.cx;
201 if (size < SCROLL_MIN_RECT + upsize + downsize)
202 upsize = downsize = (size - SCROLL_MIN_RECT)/2;
204 partrect = r;
206 if (part == SCROLL_TOP_ARROW) {
207 if (vertical)
208 partrect.bottom = partrect.top + upsize;
209 else
210 partrect.right = partrect.left + upsize;
211 } else if (part == SCROLL_BOTTOM_ARROW) {
212 if (vertical)
213 partrect.top = partrect.bottom - downsize;
214 else
215 partrect.left = partrect.right - downsize;
216 } else {
217 unsigned int thumbpos, thumbsize;
218 SCROLLINFO si;
220 si.cbSize = sizeof(si);
221 si.fMask = SIF_ALL;
222 if (!GetScrollInfo(hwnd, SB_CTL, &si)) {
223 WARN("GetScrollInfo failed.\n");
224 return;
227 calc_thumb_dimensions(size - upsize - downsize, &si, &thumbpos, &thumbsize);
229 if (part == SCROLL_TOP_RECT) {
230 if (vertical) {
231 partrect.top = r.top + upsize;
232 partrect.bottom = partrect.top + thumbpos;
233 } else {
234 partrect.left = r.left + upsize;
235 partrect.right = partrect.left + thumbpos;
237 } else if (part == SCROLL_THUMB) {
238 if (vertical) {
239 partrect.top = r.top + upsize + thumbpos;
240 partrect.bottom = partrect.top + thumbsize;
241 } else {
242 partrect.left = r.left + upsize + thumbpos;
243 partrect.right = partrect.left + thumbsize;
245 } else if (part == SCROLL_BOTTOM_RECT) {
246 if (vertical) {
247 partrect.top = r.top + upsize + thumbpos + thumbsize;
248 partrect.bottom = r.bottom - downsize;
249 } else {
250 partrect.left = r.left + upsize + thumbpos + thumbsize;
251 partrect.right = r.right - downsize;
256 InvalidateRect(hwnd, &partrect, TRUE);
259 static void scroll_event(HWND hwnd, HTHEME theme, UINT msg, POINT pt)
261 enum SCROLL_HITTEST hittest;
262 TRACKMOUSEEVENT tme;
264 if (GetWindowLongW(hwnd, GWL_STYLE) & (SBS_SIZEGRIP | SBS_SIZEBOX))
265 return;
267 hittest = hit_test(hwnd, theme, pt);
269 switch (msg)
271 case WM_MOUSEMOVE:
272 hittest = hit_test(hwnd, theme, pt);
273 tracking_win = hwnd;
274 break;
276 case WM_MOUSELEAVE:
277 if (tracking_win == hwnd) {
278 hittest = SCROLL_NOWHERE;
280 break;
283 tme.cbSize = sizeof(tme);
284 tme.dwFlags = TME_QUERY;
285 TrackMouseEvent(&tme);
287 if (!(tme.dwFlags & TME_LEAVE) || tme.hwndTrack != hwnd) {
288 tme.dwFlags = TME_LEAVE;
289 tme.hwndTrack = hwnd;
290 TrackMouseEvent(&tme);
293 if (tracking_win != hwnd && msg == WM_MOUSELEAVE) {
294 redraw_part(hwnd, theme, SCROLL_NOWHERE);
295 return;
298 if (tracking_win == hwnd && hittest != tracking_hot_part) {
299 enum SCROLL_HITTEST oldhotpart = tracking_hot_part;
301 tracking_hot_part = hittest;
303 if (hittest != SCROLL_NOWHERE)
304 redraw_part(hwnd, theme, hittest);
305 else
306 tracking_win = 0;
308 if (oldhotpart != SCROLL_NOWHERE)
309 redraw_part(hwnd, theme, oldhotpart);
313 static void paint_scrollbar(HWND hwnd, HTHEME theme)
315 HDC dc;
316 PAINTSTRUCT ps;
317 RECT r;
318 DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
319 BOOL vertical = style & SBS_VERT;
320 BOOL disabled = !IsWindowEnabled(hwnd);
322 GetWindowRect(hwnd, &r);
323 OffsetRect(&r, -r.left, -r.top);
325 dc = BeginPaint(hwnd, &ps);
327 if (style & SBS_SIZEBOX || style & SBS_SIZEGRIP) {
328 int state;
330 if (style & SBS_SIZEBOXTOPLEFTALIGN)
331 state = SZB_TOPLEFTALIGN;
332 else
333 state = SZB_RIGHTALIGN;
335 DrawThemeBackground(theme, dc, SBP_SIZEBOX, state, &r, NULL);
336 } else {
337 SCROLLBARINFO sbi;
338 SCROLLINFO si;
339 unsigned int thumbpos, thumbsize;
340 int uppertrackstate, lowertrackstate, thumbstate;
341 RECT partrect, trackrect;
342 SIZE grippersize;
344 sbi.cbSize = sizeof(sbi);
345 GetScrollBarInfo(hwnd, OBJID_CLIENT, &sbi);
347 si.cbSize = sizeof(si);
348 si.fMask = SIF_ALL;
349 GetScrollInfo(hwnd, SB_CTL, &si);
351 trackrect = r;
353 if (disabled) {
354 uppertrackstate = SCRBS_DISABLED;
355 lowertrackstate = SCRBS_DISABLED;
356 thumbstate = SCRBS_DISABLED;
357 } else {
358 uppertrackstate = SCRBS_NORMAL;
359 lowertrackstate = SCRBS_NORMAL;
360 thumbstate = SCRBS_NORMAL;
362 if (tracking_win == hwnd) {
363 if (tracking_hot_part == SCROLL_TOP_RECT)
364 uppertrackstate = SCRBS_HOT;
365 else if (tracking_hot_part == SCROLL_BOTTOM_RECT)
366 lowertrackstate = SCRBS_HOT;
367 else if (tracking_hot_part == SCROLL_THUMB)
368 thumbstate = SCRBS_HOT;
372 if (vertical) {
373 SIZE upsize, downsize;
374 int uparrowstate, downarrowstate;
376 if (disabled) {
377 uparrowstate = ABS_UPDISABLED;
378 downarrowstate = ABS_DOWNDISABLED;
379 } else {
380 uparrowstate = ABS_UPNORMAL;
381 downarrowstate = ABS_DOWNNORMAL;
383 if (tracking_win == hwnd) {
384 if (tracking_hot_part == SCROLL_TOP_ARROW)
385 uparrowstate = ABS_UPHOT;
386 else if (tracking_hot_part == SCROLL_BOTTOM_ARROW)
387 downarrowstate = ABS_DOWNHOT;
391 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, uparrowstate, NULL, TS_DRAW, &upsize))) {
392 WARN("Could not get up arrow size.\n");
393 return;
396 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, downarrowstate, NULL, TS_DRAW, &downsize))) {
397 WARN("Could not get down arrow size.\n");
398 return;
401 if (r.bottom - r.top - upsize.cy - downsize.cy < SCROLL_MIN_RECT)
402 upsize.cy = downsize.cy = (r.bottom - r.top - SCROLL_MIN_RECT)/2;
404 partrect = r;
405 partrect.bottom = partrect.top + upsize.cy;
406 DrawThemeBackground(theme, dc, SBP_ARROWBTN, uparrowstate, &partrect, NULL);
408 trackrect.top = partrect.bottom;
410 partrect.bottom = r.bottom;
411 partrect.top = partrect.bottom - downsize.cy;
412 DrawThemeBackground(theme, dc, SBP_ARROWBTN, downarrowstate, &partrect, NULL);
414 trackrect.bottom = partrect.top;
416 calc_thumb_dimensions(trackrect.bottom - trackrect.top, &si, &thumbpos, &thumbsize);
418 if (thumbpos > 0) {
419 partrect.top = trackrect.top;
420 partrect.bottom = partrect.top + thumbpos;
422 DrawThemeBackground(theme, dc, SBP_UPPERTRACKVERT, uppertrackstate, &partrect, NULL);
425 if (thumbsize > 0) {
426 partrect.top = trackrect.top + thumbpos;
427 partrect.bottom = partrect.top + thumbsize;
429 DrawThemeBackground(theme, dc, SBP_THUMBBTNVERT, thumbstate, &partrect, NULL);
431 if (SUCCEEDED(GetThemePartSize(theme, dc, SBP_GRIPPERVERT, thumbstate, NULL, TS_DRAW, &grippersize))) {
432 MARGINS margins;
434 if (SUCCEEDED(GetThemeMargins(theme, dc, SBP_THUMBBTNVERT, thumbstate, TMT_CONTENTMARGINS, &partrect, &margins))) {
435 if (grippersize.cy <= (thumbsize - margins.cyTopHeight - margins.cyBottomHeight))
436 DrawThemeBackground(theme, dc, SBP_GRIPPERVERT, thumbstate, &partrect, NULL);
441 if (thumbpos + thumbsize < trackrect.bottom - trackrect.top) {
442 partrect.bottom = trackrect.bottom;
443 partrect.top = trackrect.top + thumbsize + thumbpos;
445 DrawThemeBackground(theme, dc, SBP_LOWERTRACKVERT, lowertrackstate, &partrect, NULL);
447 } else {
448 SIZE leftsize, rightsize;
449 int leftarrowstate, rightarrowstate;
451 if (disabled) {
452 leftarrowstate = ABS_LEFTDISABLED;
453 rightarrowstate = ABS_RIGHTDISABLED;
454 } else {
455 leftarrowstate = ABS_LEFTNORMAL;
456 rightarrowstate = ABS_RIGHTNORMAL;
458 if (tracking_win == hwnd) {
459 if (tracking_hot_part == SCROLL_TOP_ARROW)
460 leftarrowstate = ABS_LEFTHOT;
461 else if (tracking_hot_part == SCROLL_BOTTOM_ARROW)
462 rightarrowstate = ABS_RIGHTHOT;
466 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, leftarrowstate, NULL, TS_DRAW, &leftsize))) {
467 WARN("Could not get left arrow size.\n");
468 return;
471 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, rightarrowstate, NULL, TS_DRAW, &rightsize))) {
472 WARN("Could not get right arrow size.\n");
473 return;
476 if (r.right - r.left - leftsize.cx - rightsize.cx < SCROLL_MIN_RECT)
477 leftsize.cx = rightsize.cx = (r.right - r.left - SCROLL_MIN_RECT)/2;
479 partrect = r;
480 partrect.right = partrect.left + leftsize.cx;
481 DrawThemeBackground(theme, dc, SBP_ARROWBTN, leftarrowstate, &partrect, NULL);
483 trackrect.left = partrect.right;
485 partrect.right = r.right;
486 partrect.left = partrect.right - rightsize.cx;
487 DrawThemeBackground(theme, dc, SBP_ARROWBTN, rightarrowstate, &partrect, NULL);
489 trackrect.right = partrect.left;
491 calc_thumb_dimensions(trackrect.right - trackrect.left, &si, &thumbpos, &thumbsize);
493 if (thumbpos > 0) {
494 partrect.left = trackrect.left;
495 partrect.right = partrect.left + thumbpos;
497 DrawThemeBackground(theme, dc, SBP_UPPERTRACKHORZ, uppertrackstate, &partrect, NULL);
500 if (thumbsize > 0) {
501 partrect.left = trackrect.left + thumbpos;
502 partrect.right = partrect.left + thumbsize;
504 DrawThemeBackground(theme, dc, SBP_THUMBBTNHORZ, thumbstate, &partrect, NULL);
506 if (SUCCEEDED(GetThemePartSize(theme, dc, SBP_GRIPPERHORZ, thumbstate, NULL, TS_DRAW, &grippersize))) {
507 MARGINS margins;
509 if (SUCCEEDED(GetThemeMargins(theme, dc, SBP_THUMBBTNHORZ, thumbstate, TMT_CONTENTMARGINS, &partrect, &margins))) {
510 if (grippersize.cx <= (thumbsize - margins.cxLeftWidth - margins.cxRightWidth))
511 DrawThemeBackground(theme, dc, SBP_GRIPPERHORZ, thumbstate, &partrect, NULL);
516 if (thumbpos + thumbsize < trackrect.right - trackrect.left) {
517 partrect.right = trackrect.right;
518 partrect.left = trackrect.left + thumbsize + thumbpos;
520 DrawThemeBackground(theme, dc, SBP_LOWERTRACKHORZ, lowertrackstate, &partrect, NULL);
525 EndPaint(hwnd, &ps);
528 LRESULT CALLBACK THEMING_ScrollbarSubclassProc (HWND hwnd, UINT msg,
529 WPARAM wParam, LPARAM lParam,
530 ULONG_PTR dwRefData)
532 const WCHAR* themeClass = WC_SCROLLBARW;
533 HTHEME theme;
534 LRESULT result;
535 POINT pt;
537 TRACE("(%p, 0x%x, %lu, %lu, %lu)\n", hwnd, msg, wParam, lParam, dwRefData);
539 switch (msg) {
540 case WM_CREATE:
541 result = THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
542 OpenThemeData(hwnd, themeClass);
543 return result;
545 case WM_DESTROY:
546 theme = GetWindowTheme(hwnd);
547 CloseThemeData(theme);
548 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
550 case WM_THEMECHANGED:
551 theme = GetWindowTheme(hwnd);
552 CloseThemeData(theme);
553 OpenThemeData(hwnd, themeClass);
554 break;
556 case WM_SYSCOLORCHANGE:
557 theme = GetWindowTheme(hwnd);
558 if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
559 /* Do nothing. When themed, a WM_THEMECHANGED will be received, too,
560 * which will do the repaint. */
561 break;
563 case WM_PAINT:
564 theme = GetWindowTheme(hwnd);
565 if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
567 paint_scrollbar(hwnd, theme);
568 break;
570 case WM_MOUSEMOVE:
571 case WM_MOUSELEAVE:
572 theme = GetWindowTheme(hwnd);
573 if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
575 pt.x = (short)LOWORD(lParam);
576 pt.y = (short)HIWORD(lParam);
577 scroll_event(hwnd, theme, msg, pt);
578 break;
580 default:
581 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
584 return 0;