mfplat: Remove duplicated GUID entry from attribute tracing.
[wine/zf.git] / dlls / comctl32 / theme_scrollbar.c
blob27d31bd4ac55fdfc405b6781bcb2bcb8840cf115
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 SCROLLINFO si;
338 unsigned int thumbpos, thumbsize;
339 int uppertrackstate, lowertrackstate, thumbstate;
340 RECT partrect, trackrect;
341 SIZE grippersize;
343 si.cbSize = sizeof(si);
344 si.fMask = SIF_ALL;
345 GetScrollInfo(hwnd, SB_CTL, &si);
347 trackrect = r;
349 if (disabled) {
350 uppertrackstate = SCRBS_DISABLED;
351 lowertrackstate = SCRBS_DISABLED;
352 thumbstate = SCRBS_DISABLED;
353 } else {
354 uppertrackstate = SCRBS_NORMAL;
355 lowertrackstate = SCRBS_NORMAL;
356 thumbstate = SCRBS_NORMAL;
358 if (tracking_win == hwnd) {
359 if (tracking_hot_part == SCROLL_TOP_RECT)
360 uppertrackstate = SCRBS_HOT;
361 else if (tracking_hot_part == SCROLL_BOTTOM_RECT)
362 lowertrackstate = SCRBS_HOT;
363 else if (tracking_hot_part == SCROLL_THUMB)
364 thumbstate = SCRBS_HOT;
368 if (vertical) {
369 SIZE upsize, downsize;
370 int uparrowstate, downarrowstate;
372 if (disabled) {
373 uparrowstate = ABS_UPDISABLED;
374 downarrowstate = ABS_DOWNDISABLED;
375 } else {
376 uparrowstate = ABS_UPNORMAL;
377 downarrowstate = ABS_DOWNNORMAL;
379 if (tracking_win == hwnd) {
380 if (tracking_hot_part == SCROLL_TOP_ARROW)
381 uparrowstate = ABS_UPHOT;
382 else if (tracking_hot_part == SCROLL_BOTTOM_ARROW)
383 downarrowstate = ABS_DOWNHOT;
387 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, uparrowstate, NULL, TS_DRAW, &upsize))) {
388 WARN("Could not get up arrow size.\n");
389 return;
392 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, downarrowstate, NULL, TS_DRAW, &downsize))) {
393 WARN("Could not get down arrow size.\n");
394 return;
397 if (r.bottom - r.top - upsize.cy - downsize.cy < SCROLL_MIN_RECT)
398 upsize.cy = downsize.cy = (r.bottom - r.top - SCROLL_MIN_RECT)/2;
400 partrect = r;
401 partrect.bottom = partrect.top + upsize.cy;
402 DrawThemeBackground(theme, dc, SBP_ARROWBTN, uparrowstate, &partrect, NULL);
404 trackrect.top = partrect.bottom;
406 partrect.bottom = r.bottom;
407 partrect.top = partrect.bottom - downsize.cy;
408 DrawThemeBackground(theme, dc, SBP_ARROWBTN, downarrowstate, &partrect, NULL);
410 trackrect.bottom = partrect.top;
412 calc_thumb_dimensions(trackrect.bottom - trackrect.top, &si, &thumbpos, &thumbsize);
414 if (thumbpos > 0) {
415 partrect.top = trackrect.top;
416 partrect.bottom = partrect.top + thumbpos;
418 DrawThemeBackground(theme, dc, SBP_UPPERTRACKVERT, uppertrackstate, &partrect, NULL);
421 if (thumbsize > 0) {
422 partrect.top = trackrect.top + thumbpos;
423 partrect.bottom = partrect.top + thumbsize;
425 DrawThemeBackground(theme, dc, SBP_THUMBBTNVERT, thumbstate, &partrect, NULL);
427 if (SUCCEEDED(GetThemePartSize(theme, dc, SBP_GRIPPERVERT, thumbstate, NULL, TS_DRAW, &grippersize))) {
428 MARGINS margins;
430 if (SUCCEEDED(GetThemeMargins(theme, dc, SBP_THUMBBTNVERT, thumbstate, TMT_CONTENTMARGINS, &partrect, &margins))) {
431 if (grippersize.cy <= (thumbsize - margins.cyTopHeight - margins.cyBottomHeight))
432 DrawThemeBackground(theme, dc, SBP_GRIPPERVERT, thumbstate, &partrect, NULL);
437 if (thumbpos + thumbsize < trackrect.bottom - trackrect.top) {
438 partrect.bottom = trackrect.bottom;
439 partrect.top = trackrect.top + thumbsize + thumbpos;
441 DrawThemeBackground(theme, dc, SBP_LOWERTRACKVERT, lowertrackstate, &partrect, NULL);
443 } else {
444 SIZE leftsize, rightsize;
445 int leftarrowstate, rightarrowstate;
447 if (disabled) {
448 leftarrowstate = ABS_LEFTDISABLED;
449 rightarrowstate = ABS_RIGHTDISABLED;
450 } else {
451 leftarrowstate = ABS_LEFTNORMAL;
452 rightarrowstate = ABS_RIGHTNORMAL;
454 if (tracking_win == hwnd) {
455 if (tracking_hot_part == SCROLL_TOP_ARROW)
456 leftarrowstate = ABS_LEFTHOT;
457 else if (tracking_hot_part == SCROLL_BOTTOM_ARROW)
458 rightarrowstate = ABS_RIGHTHOT;
462 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, leftarrowstate, NULL, TS_DRAW, &leftsize))) {
463 WARN("Could not get left arrow size.\n");
464 return;
467 if (FAILED(GetThemePartSize(theme, dc, SBP_ARROWBTN, rightarrowstate, NULL, TS_DRAW, &rightsize))) {
468 WARN("Could not get right arrow size.\n");
469 return;
472 if (r.right - r.left - leftsize.cx - rightsize.cx < SCROLL_MIN_RECT)
473 leftsize.cx = rightsize.cx = (r.right - r.left - SCROLL_MIN_RECT)/2;
475 partrect = r;
476 partrect.right = partrect.left + leftsize.cx;
477 DrawThemeBackground(theme, dc, SBP_ARROWBTN, leftarrowstate, &partrect, NULL);
479 trackrect.left = partrect.right;
481 partrect.right = r.right;
482 partrect.left = partrect.right - rightsize.cx;
483 DrawThemeBackground(theme, dc, SBP_ARROWBTN, rightarrowstate, &partrect, NULL);
485 trackrect.right = partrect.left;
487 calc_thumb_dimensions(trackrect.right - trackrect.left, &si, &thumbpos, &thumbsize);
489 if (thumbpos > 0) {
490 partrect.left = trackrect.left;
491 partrect.right = partrect.left + thumbpos;
493 DrawThemeBackground(theme, dc, SBP_UPPERTRACKHORZ, uppertrackstate, &partrect, NULL);
496 if (thumbsize > 0) {
497 partrect.left = trackrect.left + thumbpos;
498 partrect.right = partrect.left + thumbsize;
500 DrawThemeBackground(theme, dc, SBP_THUMBBTNHORZ, thumbstate, &partrect, NULL);
502 if (SUCCEEDED(GetThemePartSize(theme, dc, SBP_GRIPPERHORZ, thumbstate, NULL, TS_DRAW, &grippersize))) {
503 MARGINS margins;
505 if (SUCCEEDED(GetThemeMargins(theme, dc, SBP_THUMBBTNHORZ, thumbstate, TMT_CONTENTMARGINS, &partrect, &margins))) {
506 if (grippersize.cx <= (thumbsize - margins.cxLeftWidth - margins.cxRightWidth))
507 DrawThemeBackground(theme, dc, SBP_GRIPPERHORZ, thumbstate, &partrect, NULL);
512 if (thumbpos + thumbsize < trackrect.right - trackrect.left) {
513 partrect.right = trackrect.right;
514 partrect.left = trackrect.left + thumbsize + thumbpos;
516 DrawThemeBackground(theme, dc, SBP_LOWERTRACKHORZ, lowertrackstate, &partrect, NULL);
521 EndPaint(hwnd, &ps);
524 LRESULT CALLBACK THEMING_ScrollbarSubclassProc (HWND hwnd, UINT msg,
525 WPARAM wParam, LPARAM lParam,
526 ULONG_PTR dwRefData)
528 const WCHAR* themeClass = WC_SCROLLBARW;
529 HTHEME theme;
530 LRESULT result;
531 POINT pt;
533 TRACE("(%p, 0x%x, %lu, %lu, %lu)\n", hwnd, msg, wParam, lParam, dwRefData);
535 switch (msg) {
536 case WM_CREATE:
537 result = THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
538 OpenThemeData(hwnd, themeClass);
539 return result;
541 case WM_DESTROY:
542 theme = GetWindowTheme(hwnd);
543 CloseThemeData(theme);
544 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
546 case WM_THEMECHANGED:
547 theme = GetWindowTheme(hwnd);
548 CloseThemeData(theme);
549 OpenThemeData(hwnd, themeClass);
550 break;
552 case WM_SYSCOLORCHANGE:
553 theme = GetWindowTheme(hwnd);
554 if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
555 /* Do nothing. When themed, a WM_THEMECHANGED will be received, too,
556 * which will do the repaint. */
557 break;
559 case WM_PAINT:
560 theme = GetWindowTheme(hwnd);
561 if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
563 paint_scrollbar(hwnd, theme);
564 break;
566 case WM_MOUSEMOVE:
567 case WM_MOUSELEAVE:
568 theme = GetWindowTheme(hwnd);
569 if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
571 pt.x = (short)LOWORD(lParam);
572 pt.y = (short)HIWORD(lParam);
573 scroll_event(hwnd, theme, msg, pt);
574 break;
576 default:
577 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam);
580 return 0;