xref: /reactos/dll/win32/comctl32/trackbar.c (revision 0707475f)
1 /*
2  * Trackbar control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1998, 1999 Alex Priem
6  * Copyright 2002 Dimitrie O. Paun
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28 
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "winnls.h"
34 #include "commctrl.h"
35 #include "uxtheme.h"
36 #include "vssym32.h"
37 #include "wine/debug.h"
38 
39 #include "comctl32.h"
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(trackbar);
42 
43 typedef struct
44 {
45     HWND hwndSelf;
46     DWORD dwStyle;
47     LONG lRangeMin;
48     LONG lRangeMax;
49     LONG lLineSize;
50     LONG lPageSize;
51     LONG lSelMin;
52     LONG lSelMax;
53     LONG lPos;
54     UINT uThumbLen;
55     UINT uNumTics;
56     UINT uTicFreq;
57     HWND hwndNotify;
58     HWND hwndToolTip;
59     HWND hwndBuddyLA;
60     HWND hwndBuddyRB;
61     INT  fLocation;
62     DWORD flags;
63     BOOL bUnicode;
64     RECT rcChannel;
65     RECT rcSelection;
66     RECT rcThumb;
67     LPLONG tics;
68 } TRACKBAR_INFO;
69 
70 #define TB_REFRESH_TIMER	1
71 #define TB_REFRESH_DELAY	500
72 
73 #define TOOLTIP_OFFSET		2     /* distance from ctrl edge to tooltip */
74 
75 #define TB_DEFAULTPAGESIZE	20
76 
77 /* Used by TRACKBAR_Refresh to find out which parts of the control
78    need to be recalculated */
79 
80 #define TB_THUMBPOSCHANGED      0x00000001
81 #define TB_THUMBSIZECHANGED     0x00000002
82 #define TB_THUMBCHANGED        (TB_THUMBPOSCHANGED | TB_THUMBSIZECHANGED)
83 #define TB_SELECTIONCHANGED     0x00000004
84 #define TB_DRAG_MODE            0x00000008     /* we're dragging the slider */
85 #define TB_AUTO_PAGE_LEFT       0x00000010
86 #define TB_AUTO_PAGE_RIGHT      0x00000020
87 #define TB_AUTO_PAGE           (TB_AUTO_PAGE_LEFT | TB_AUTO_PAGE_RIGHT)
88 #define TB_THUMB_HOT            0x00000040    /* mouse hovers above thumb */
89 
90 /* Page was set with TBM_SETPAGESIZE */
91 #define TB_USER_PAGE            0x00000080
92 #define TB_IS_FOCUSED           0x00000100
93 
94 /* helper defines for TRACKBAR_DrawTic */
95 #define TIC_EDGE                0x20
96 #define TIC_SELECTIONMARKMAX    0x80
97 #define TIC_SELECTIONMARKMIN    0x100
98 #define TIC_SELECTIONMARK       (TIC_SELECTIONMARKMAX | TIC_SELECTIONMARKMIN)
99 
100 static const WCHAR themeClass[] = { 'T','r','a','c','k','b','a','r',0 };
101 
102 static inline int
notify_customdraw(const TRACKBAR_INFO * infoPtr,NMCUSTOMDRAW * pnmcd,int stage)103 notify_customdraw (const TRACKBAR_INFO *infoPtr, NMCUSTOMDRAW *pnmcd, int stage)
104 {
105     pnmcd->dwDrawStage = stage;
106     return SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
107 		         pnmcd->hdr.idFrom, (LPARAM)pnmcd);
108 }
109 
notify_hdr(const TRACKBAR_INFO * infoPtr,INT code,LPNMHDR pnmh)110 static LRESULT notify_hdr (const TRACKBAR_INFO *infoPtr, INT code, LPNMHDR pnmh)
111 {
112     LRESULT result;
113 
114     TRACE("(code=%d)\n", code);
115 
116     pnmh->hwndFrom = infoPtr->hwndSelf;
117     pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
118     pnmh->code = code;
119     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
120 
121     TRACE("  <= %ld\n", result);
122 
123     return result;
124 }
125 
notify(const TRACKBAR_INFO * infoPtr,INT code)126 static inline int notify (const TRACKBAR_INFO *infoPtr, INT code)
127 {
128     NMHDR nmh;
129     return notify_hdr(infoPtr, code, &nmh);
130 }
131 
notify_with_scroll(const TRACKBAR_INFO * infoPtr,UINT code)132 static void notify_with_scroll (const TRACKBAR_INFO *infoPtr, UINT code)
133 {
134     UINT scroll = infoPtr->dwStyle & TBS_VERT ? WM_VSCROLL : WM_HSCROLL;
135 
136     TRACE("%x\n", code);
137 
138     SendMessageW (infoPtr->hwndNotify, scroll, code, (LPARAM)infoPtr->hwndSelf);
139 }
140 
TRACKBAR_RecalculateTics(TRACKBAR_INFO * infoPtr)141 static void TRACKBAR_RecalculateTics (TRACKBAR_INFO *infoPtr)
142 {
143     int tic;
144     unsigned nrTics, i;
145 
146     if (infoPtr->uTicFreq && infoPtr->lRangeMax >= infoPtr->lRangeMin) {
147         nrTics=(infoPtr->lRangeMax - infoPtr->lRangeMin)/infoPtr->uTicFreq;
148         /* don't add extra tic if there's no remainder */
149         if (nrTics && ((infoPtr->lRangeMax - infoPtr->lRangeMin) % infoPtr->uTicFreq == 0))
150           nrTics--;
151     }
152     else {
153         Free (infoPtr->tics);
154         infoPtr->tics = NULL;
155         infoPtr->uNumTics = 0;
156         return;
157     }
158 
159     if (nrTics != infoPtr->uNumTics) {
160     	infoPtr->tics=ReAlloc (infoPtr->tics,
161                                         (nrTics+1)*sizeof (DWORD));
162 	if (!infoPtr->tics) {
163 	    infoPtr->uNumTics = 0;
164 	    notify(infoPtr, NM_OUTOFMEMORY);
165 	    return;
166 	}
167     	infoPtr->uNumTics = nrTics;
168     }
169 
170     tic = infoPtr->lRangeMin + infoPtr->uTicFreq;
171     for (i = 0; i < nrTics; i++, tic += infoPtr->uTicFreq)
172         infoPtr->tics[i] = tic;
173 }
174 
175 /* converts from physical (mouse) position to logical position
176    (in range of trackbar) */
177 
178 static inline LONG
TRACKBAR_ConvertPlaceToPosition(const TRACKBAR_INFO * infoPtr,int place)179 TRACKBAR_ConvertPlaceToPosition (const TRACKBAR_INFO *infoPtr, int place)
180 {
181     double range, width, pos, offsetthumb;
182 
183     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
184     if (infoPtr->dwStyle & TBS_VERT) {
185         offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
186         width = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - (offsetthumb * 2) - 1;
187         pos = (range*(place - infoPtr->rcChannel.top - offsetthumb)) / width;
188     } else {
189         offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
190         width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - (offsetthumb * 2) - 1;
191         pos = (range*(place - infoPtr->rcChannel.left - offsetthumb)) / width;
192     }
193     pos += infoPtr->lRangeMin;
194     if (pos > infoPtr->lRangeMax)
195         pos = infoPtr->lRangeMax;
196     else if (pos < infoPtr->lRangeMin)
197         pos = infoPtr->lRangeMin;
198 
199     TRACE("%.2f\n", pos);
200     return (LONG)floor(pos + 0.5);
201 }
202 
203 
204 /* return: 0> prev, 0 none, >0 next */
205 static LONG
TRACKBAR_GetAutoPageDirection(const TRACKBAR_INFO * infoPtr,POINT clickPoint)206 TRACKBAR_GetAutoPageDirection (const TRACKBAR_INFO *infoPtr, POINT clickPoint)
207 {
208     RECT pageRect;
209 
210     if (infoPtr->dwStyle & TBS_VERT) {
211 	pageRect.top = infoPtr->rcChannel.top;
212 	pageRect.bottom = infoPtr->rcChannel.bottom;
213 	pageRect.left = infoPtr->rcThumb.left;
214 	pageRect.right = infoPtr->rcThumb.right;
215     } else {
216 	pageRect.top = infoPtr->rcThumb.top;
217 	pageRect.bottom = infoPtr->rcThumb.bottom;
218 	pageRect.left = infoPtr->rcChannel.left;
219 	pageRect.right = infoPtr->rcChannel.right;
220     }
221 
222 
223     if (PtInRect(&pageRect, clickPoint))
224     {
225 	int clickPlace = (infoPtr->dwStyle & TBS_VERT) ? clickPoint.y : clickPoint.x;
226 
227         LONG clickPos = TRACKBAR_ConvertPlaceToPosition(infoPtr, clickPlace);
228 
229 	return clickPos - infoPtr->lPos;
230     }
231 
232     return 0;
233 }
234 
235 static inline void
TRACKBAR_PageDown(TRACKBAR_INFO * infoPtr)236 TRACKBAR_PageDown (TRACKBAR_INFO *infoPtr)
237 {
238     if (infoPtr->lPos == infoPtr->lRangeMax) return;
239 
240     infoPtr->lPos += infoPtr->lPageSize;
241     if (infoPtr->lPos > infoPtr->lRangeMax)
242 	infoPtr->lPos = infoPtr->lRangeMax;
243     notify_with_scroll (infoPtr, TB_PAGEDOWN);
244 }
245 
246 
247 static inline void
TRACKBAR_PageUp(TRACKBAR_INFO * infoPtr)248 TRACKBAR_PageUp (TRACKBAR_INFO *infoPtr)
249 {
250     if (infoPtr->lPos == infoPtr->lRangeMin) return;
251 
252     infoPtr->lPos -= infoPtr->lPageSize;
253     if (infoPtr->lPos < infoPtr->lRangeMin)
254         infoPtr->lPos = infoPtr->lRangeMin;
255     notify_with_scroll (infoPtr, TB_PAGEUP);
256 }
257 
TRACKBAR_LineUp(TRACKBAR_INFO * infoPtr)258 static inline void TRACKBAR_LineUp(TRACKBAR_INFO *infoPtr)
259 {
260     if (infoPtr->lPos == infoPtr->lRangeMin) return;
261     infoPtr->lPos -= infoPtr->lLineSize;
262     if (infoPtr->lPos < infoPtr->lRangeMin)
263         infoPtr->lPos = infoPtr->lRangeMin;
264     notify_with_scroll (infoPtr, TB_LINEUP);
265 }
266 
TRACKBAR_LineDown(TRACKBAR_INFO * infoPtr)267 static inline void TRACKBAR_LineDown(TRACKBAR_INFO *infoPtr)
268 {
269     if (infoPtr->lPos == infoPtr->lRangeMax) return;
270     infoPtr->lPos += infoPtr->lLineSize;
271     if (infoPtr->lPos > infoPtr->lRangeMax)
272         infoPtr->lPos = infoPtr->lRangeMax;
273     notify_with_scroll (infoPtr, TB_LINEDOWN);
274 }
275 
276 static void
TRACKBAR_CalcChannel(TRACKBAR_INFO * infoPtr)277 TRACKBAR_CalcChannel (TRACKBAR_INFO *infoPtr)
278 {
279     INT cyChannel, offsetthumb, offsetedge;
280     RECT lpRect, *channel = & infoPtr->rcChannel;
281 
282     GetClientRect (infoPtr->hwndSelf, &lpRect);
283 
284     offsetthumb = infoPtr->uThumbLen / 4;
285     offsetedge  = offsetthumb + 3;
286     cyChannel   = (infoPtr->dwStyle & TBS_ENABLESELRANGE) ? offsetthumb*3 : 4;
287     if (infoPtr->dwStyle & TBS_VERT) {
288         channel->top    = lpRect.top + offsetedge;
289         channel->bottom = lpRect.bottom - offsetedge;
290         if (infoPtr->dwStyle & TBS_ENABLESELRANGE)
291             channel->left = lpRect.left + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
292         else
293             channel->left = lpRect.left + (infoPtr->uThumbLen / 2) - 1;
294         if (infoPtr->dwStyle & TBS_BOTH) {
295             if (infoPtr->dwStyle & TBS_NOTICKS)
296                 channel->left += 1;
297             else
298                 channel->left += 9;
299         }
300         else if (infoPtr->dwStyle & TBS_TOP) {
301             if (infoPtr->dwStyle & TBS_NOTICKS)
302                 channel->left += 2;
303             else
304                 channel->left += 10;
305         }
306         channel->right = channel->left + cyChannel;
307     } else {
308         channel->left = lpRect.left + offsetedge;
309         channel->right = lpRect.right - offsetedge;
310         if (infoPtr->dwStyle & TBS_ENABLESELRANGE)
311             channel->top = lpRect.top + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
312         else
313             channel->top = lpRect.top + (infoPtr->uThumbLen / 2) - 1;
314         if (infoPtr->dwStyle & TBS_BOTH) {
315             if (infoPtr->dwStyle & TBS_NOTICKS)
316                 channel->top += 1;
317             else
318                 channel->top += 9;
319         }
320         else if (infoPtr->dwStyle & TBS_TOP) {
321             if (infoPtr->dwStyle & TBS_NOTICKS)
322                 channel->top += 2;
323             else
324                 channel->top += 10;
325         }
326         channel->bottom   = channel->top + cyChannel;
327     }
328 }
329 
330 static void
TRACKBAR_CalcThumb(const TRACKBAR_INFO * infoPtr,LONG lPos,RECT * thumb)331 TRACKBAR_CalcThumb (const TRACKBAR_INFO *infoPtr, LONG lPos, RECT *thumb)
332 {
333     int range, width, height, thumbwidth;
334     RECT lpRect;
335 
336     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
337     thumbwidth = (infoPtr->uThumbLen / 2) | 1;
338 
339     if (!range) range = 1;
340 
341     GetClientRect(infoPtr->hwndSelf, &lpRect);
342     if (infoPtr->dwStyle & TBS_VERT)
343     {
344     	height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - thumbwidth;
345 
346         if ((infoPtr->dwStyle & (TBS_BOTH | TBS_LEFT)) && !(infoPtr->dwStyle & TBS_NOTICKS))
347             thumb->left = 10;
348         else
349             thumb->left = 2;
350         thumb->right = thumb->left + infoPtr->uThumbLen;
351         thumb->top = infoPtr->rcChannel.top +
352                      (height*(lPos - infoPtr->lRangeMin))/range;
353         thumb->bottom = thumb->top + thumbwidth;
354     }
355     else
356     {
357     	width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - thumbwidth;
358 
359         thumb->left = infoPtr->rcChannel.left +
360                       (width*(lPos - infoPtr->lRangeMin))/range;
361         thumb->right = thumb->left + thumbwidth;
362         if ((infoPtr->dwStyle & (TBS_BOTH | TBS_TOP)) && !(infoPtr->dwStyle & TBS_NOTICKS))
363             thumb->top = 10;
364         else
365             thumb->top = 2;
366         thumb->bottom = thumb->top + infoPtr->uThumbLen;
367     }
368 }
369 
370 static inline void
TRACKBAR_UpdateThumb(TRACKBAR_INFO * infoPtr)371 TRACKBAR_UpdateThumb (TRACKBAR_INFO *infoPtr)
372 {
373     TRACKBAR_CalcThumb(infoPtr, infoPtr->lPos, &infoPtr->rcThumb);
374 }
375 
376 static inline void
TRACKBAR_InvalidateAll(const TRACKBAR_INFO * infoPtr)377 TRACKBAR_InvalidateAll (const TRACKBAR_INFO *infoPtr)
378 {
379     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
380 }
381 
382 static void
TRACKBAR_InvalidateThumb(const TRACKBAR_INFO * infoPtr,LONG thumbPos)383 TRACKBAR_InvalidateThumb (const TRACKBAR_INFO *infoPtr, LONG thumbPos)
384 {
385     RECT rcThumb;
386 
387     TRACKBAR_CalcThumb(infoPtr, thumbPos, &rcThumb);
388     InflateRect(&rcThumb, 1, 1);
389     InvalidateRect(infoPtr->hwndSelf, &rcThumb, FALSE);
390 }
391 
392 static inline void
TRACKBAR_InvalidateThumbMove(const TRACKBAR_INFO * infoPtr,LONG oldPos,LONG newPos)393 TRACKBAR_InvalidateThumbMove (const TRACKBAR_INFO *infoPtr, LONG oldPos, LONG newPos)
394 {
395     TRACKBAR_InvalidateThumb (infoPtr, oldPos);
396     if (newPos != oldPos)
397         TRACKBAR_InvalidateThumb (infoPtr, newPos);
398 }
399 
400 static inline BOOL
TRACKBAR_HasSelection(const TRACKBAR_INFO * infoPtr)401 TRACKBAR_HasSelection (const TRACKBAR_INFO *infoPtr)
402 {
403     return infoPtr->lSelMin != infoPtr->lSelMax;
404 }
405 
406 static void
TRACKBAR_CalcSelection(TRACKBAR_INFO * infoPtr)407 TRACKBAR_CalcSelection (TRACKBAR_INFO *infoPtr)
408 {
409     RECT *selection = &infoPtr->rcSelection;
410     int range = infoPtr->lRangeMax - infoPtr->lRangeMin;
411     int offsetthumb, height, width;
412 
413     if (range <= 0) {
414         SetRectEmpty (selection);
415     } else {
416         if (infoPtr->dwStyle & TBS_VERT) {
417             offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
418             height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - offsetthumb*2;
419             selection->top    = infoPtr->rcChannel.top + offsetthumb +
420                 (height*infoPtr->lSelMin)/range;
421             selection->bottom = infoPtr->rcChannel.top + offsetthumb +
422                 (height*infoPtr->lSelMax)/range;
423             selection->left   = infoPtr->rcChannel.left + 3;
424             selection->right  = infoPtr->rcChannel.right - 3;
425         } else {
426             offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
427             width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
428             selection->left   = infoPtr->rcChannel.left + offsetthumb +
429                 (width*infoPtr->lSelMin)/range;
430             selection->right  = infoPtr->rcChannel.left + offsetthumb +
431                 (width*infoPtr->lSelMax)/range;
432             selection->top    = infoPtr->rcChannel.top + 3;
433             selection->bottom = infoPtr->rcChannel.bottom - 3;
434         }
435     }
436 
437     TRACE("selection[%s]\n", wine_dbgstr_rect(selection));
438 }
439 
440 static BOOL
TRACKBAR_AutoPage(TRACKBAR_INFO * infoPtr,POINT clickPoint)441 TRACKBAR_AutoPage (TRACKBAR_INFO *infoPtr, POINT clickPoint)
442 {
443     LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
444     LONG prevPos = infoPtr->lPos;
445 
446     TRACE("clickPoint=%s, dir=%d\n", wine_dbgstr_point(&clickPoint), dir);
447 
448     if (dir > 0 && (infoPtr->flags & TB_AUTO_PAGE_RIGHT))
449 	TRACKBAR_PageDown(infoPtr);
450     else if (dir < 0 && (infoPtr->flags & TB_AUTO_PAGE_LEFT))
451 	TRACKBAR_PageUp(infoPtr);
452     else return FALSE;
453 
454     TRACKBAR_UpdateThumb (infoPtr);
455     TRACKBAR_InvalidateThumbMove (infoPtr, prevPos, infoPtr->lPos);
456 
457     return TRUE;
458 }
459 
460 /* Trackbar drawing code. I like my spaghetti done milanese.  */
461 
462 static void
TRACKBAR_DrawChannel(const TRACKBAR_INFO * infoPtr,HDC hdc)463 TRACKBAR_DrawChannel (const TRACKBAR_INFO *infoPtr, HDC hdc)
464 {
465     RECT rcChannel = infoPtr->rcChannel;
466     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
467 
468     if (theme)
469     {
470         DrawThemeBackground (theme, hdc,
471             (infoPtr->dwStyle & TBS_VERT) ?
472                 TKP_TRACKVERT : TKP_TRACK, TKS_NORMAL, &rcChannel, 0);
473     }
474     else
475     {
476         DrawEdge (hdc, &rcChannel, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
477         if (infoPtr->dwStyle & TBS_ENABLESELRANGE) {		 /* fill the channel */
478             FillRect (hdc, &rcChannel, GetStockObject(WHITE_BRUSH));
479             if (TRACKBAR_HasSelection(infoPtr))
480                 FillRect (hdc, &infoPtr->rcSelection, GetSysColorBrush(COLOR_HIGHLIGHT));
481         }
482     }
483 }
484 
485 static void
TRACKBAR_DrawOneTic(const TRACKBAR_INFO * infoPtr,HDC hdc,LONG ticPos,int flags)486 TRACKBAR_DrawOneTic (const TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
487 {
488     int x, y, ox, oy, range, side, indent = 0, len = 3;
489     int offsetthumb;
490     RECT rcTics;
491 
492     if (flags & TBS_VERT) {
493         offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
494         SetRect(&rcTics, infoPtr->rcThumb.left - 2, infoPtr->rcChannel.top + offsetthumb,
495                 infoPtr->rcThumb.right + 2, infoPtr->rcChannel.bottom - offsetthumb - 1);
496     } else {
497         offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
498         SetRect(&rcTics, infoPtr->rcChannel.left + offsetthumb, infoPtr->rcThumb.top - 2,
499                 infoPtr->rcChannel.right - offsetthumb - 1, infoPtr->rcThumb.bottom + 2);
500     }
501 
502     if (flags & (TBS_TOP | TBS_LEFT)) {
503 	x = rcTics.left;
504 	y = rcTics.top;
505 	side = -1;
506     } else {
507   	x = rcTics.right;
508   	y = rcTics.bottom;
509 	side = 1;
510     }
511 
512     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
513     if (range <= 0)
514       range = 1; /* to avoid division by zero */
515 
516     if (flags & TIC_SELECTIONMARK) {
517   	indent = (flags & TIC_SELECTIONMARKMIN) ? -1 : 1;
518     } else if (flags & TIC_EDGE) {
519 	len++;
520     }
521 
522     if (flags & TBS_VERT) {
523 	int height = rcTics.bottom - rcTics.top;
524 	y = rcTics.top + (height*(ticPos - infoPtr->lRangeMin))/range;
525     } else {
526         int width = rcTics.right - rcTics.left;
527         x = rcTics.left + (width*(ticPos - infoPtr->lRangeMin))/range;
528     }
529 
530     ox = x;
531     oy = y;
532     MoveToEx(hdc, x, y, 0);
533     if (flags & TBS_VERT) x += len * side;
534     else y += len * side;
535     LineTo(hdc, x, y);
536 
537     if (flags & TIC_SELECTIONMARK) {
538 	if (flags & TBS_VERT) {
539 	    x -= side;
540 	} else {
541 	    y -= side;
542 	}
543 	MoveToEx(hdc, x, y, 0);
544 	if (flags & TBS_VERT) {
545 	    y += 2 * indent;
546 	} else {
547 	    x += 2 * indent;
548 	}
549 
550 	LineTo(hdc, x, y);
551 	LineTo(hdc, ox, oy);
552     }
553 }
554 
555 
556 static inline void
TRACKBAR_DrawTic(const TRACKBAR_INFO * infoPtr,HDC hdc,LONG ticPos,int flags)557 TRACKBAR_DrawTic (const TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
558 {
559     if ((flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
560         TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags | TBS_LEFT);
561 
562     if (!(flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
563         TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags & ~TBS_LEFT);
564 }
565 
566 static void
TRACKBAR_DrawTics(const TRACKBAR_INFO * infoPtr,HDC hdc)567 TRACKBAR_DrawTics (const TRACKBAR_INFO *infoPtr, HDC hdc)
568 {
569     unsigned int i;
570     int ticFlags = infoPtr->dwStyle & 0x0f;
571     LOGPEN ticPen = { PS_SOLID, {1, 0}, GetSysColor (COLOR_3DDKSHADOW) };
572     HPEN hOldPen, hTicPen;
573     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
574 
575     if (theme)
576     {
577         int part = (infoPtr->dwStyle & TBS_VERT) ? TKP_TICSVERT : TKP_TICS;
578         GetThemeColor (theme, part, TSS_NORMAL, TMT_COLOR, &ticPen.lopnColor);
579     }
580     /* create the pen to draw the tics with */
581     hTicPen = CreatePenIndirect(&ticPen);
582     hOldPen = hTicPen ? SelectObject(hdc, hTicPen) : 0;
583 
584     /* actually draw the tics */
585     for (i=0; i<infoPtr->uNumTics; i++)
586         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->tics[i], ticFlags);
587 
588     TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMin, ticFlags | TIC_EDGE);
589     TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMax, ticFlags | TIC_EDGE);
590 
591     if ((infoPtr->dwStyle & TBS_ENABLESELRANGE) && TRACKBAR_HasSelection(infoPtr)) {
592         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMin,
593                           ticFlags | TIC_SELECTIONMARKMIN);
594         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMax,
595                           ticFlags | TIC_SELECTIONMARKMAX);
596     }
597 
598     /* clean up the pen, if we created one */
599     if (hTicPen) {
600 	SelectObject(hdc, hOldPen);
601 	DeleteObject(hTicPen);
602     }
603 }
604 
605 static int
TRACKBAR_FillThumb(const TRACKBAR_INFO * infoPtr,HDC hdc,HBRUSH hbrush)606 TRACKBAR_FillThumb (const TRACKBAR_INFO *infoPtr, HDC hdc, HBRUSH hbrush)
607 {
608     const RECT *thumb = &infoPtr->rcThumb;
609     POINT points[6];
610     int PointDepth;
611     HBRUSH oldbr;
612 
613     if (infoPtr->dwStyle & TBS_BOTH)
614     {
615         FillRect(hdc, thumb, hbrush);
616         return 0;
617     }
618 
619     if (infoPtr->dwStyle & TBS_VERT)
620     {
621         PointDepth = (thumb->bottom - thumb->top) / 2;
622         if (infoPtr->dwStyle & TBS_LEFT)
623         {
624             points[0].x = thumb->right-1;
625             points[0].y = thumb->top;
626             points[1].x = thumb->right-1;
627             points[1].y = thumb->bottom-1;
628             points[2].x = thumb->left + PointDepth;
629             points[2].y = thumb->bottom-1;
630             points[3].x = thumb->left;
631             points[3].y = thumb->top + PointDepth;
632             points[4].x = thumb->left + PointDepth;
633             points[4].y = thumb->top;
634             points[5].x = points[0].x;
635             points[5].y = points[0].y;
636         }
637         else
638         {
639             points[0].x = thumb->right;
640             points[0].y = thumb->top + PointDepth;
641             points[1].x = thumb->right - PointDepth;
642             points[1].y = thumb->bottom-1;
643             points[2].x = thumb->left;
644             points[2].y = thumb->bottom-1;
645             points[3].x = thumb->left;
646             points[3].y = thumb->top;
647             points[4].x = thumb->right - PointDepth;
648             points[4].y = thumb->top;
649             points[5].x = points[0].x;
650             points[5].y = points[0].y;
651         }
652     }
653     else
654     {
655         PointDepth = (thumb->right - thumb->left) / 2;
656         if (infoPtr->dwStyle & TBS_TOP)
657         {
658             points[0].x = thumb->left + PointDepth;
659             points[0].y = thumb->top+1;
660             points[1].x = thumb->right-1;
661             points[1].y = thumb->top + PointDepth + 1;
662             points[2].x = thumb->right-1;
663             points[2].y = thumb->bottom-1;
664             points[3].x = thumb->left;
665             points[3].y = thumb->bottom-1;
666             points[4].x = thumb->left;
667             points[4].y = thumb->top + PointDepth + 1;
668             points[5].x = points[0].x;
669             points[5].y = points[0].y;
670         }
671         else
672         {
673             points[0].x = thumb->right-1;
674             points[0].y = thumb->top;
675             points[1].x = thumb->right-1;
676             points[1].y = thumb->bottom - PointDepth - 1;
677             points[2].x = thumb->left + PointDepth;
678             points[2].y = thumb->bottom-1;
679             points[3].x = thumb->left;
680             points[3].y = thumb->bottom - PointDepth - 1;
681             points[4].x = thumb->left;
682             points[4].y = thumb->top;
683             points[5].x = points[0].x;
684             points[5].y = points[0].y;
685         }
686     }
687 
688     oldbr = SelectObject(hdc, hbrush);
689     SetPolyFillMode(hdc, WINDING);
690     Polygon(hdc, points, ARRAY_SIZE(points));
691     SelectObject(hdc, oldbr);
692 
693     return PointDepth;
694 }
695 
696 static void
TRACKBAR_DrawThumb(TRACKBAR_INFO * infoPtr,HDC hdc)697 TRACKBAR_DrawThumb (TRACKBAR_INFO *infoPtr, HDC hdc)
698 {
699     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
700     int PointDepth;
701     HBRUSH brush;
702 
703     if (theme)
704     {
705         int partId;
706         int stateId;
707         if (infoPtr->dwStyle & TBS_BOTH)
708             partId = (infoPtr->dwStyle & TBS_VERT) ? TKP_THUMBVERT : TKP_THUMB;
709         else if (infoPtr->dwStyle & TBS_LEFT)
710             partId = (infoPtr->dwStyle & TBS_VERT) ? TKP_THUMBLEFT : TKP_THUMBTOP;
711         else
712             partId = (infoPtr->dwStyle & TBS_VERT) ? TKP_THUMBRIGHT : TKP_THUMBBOTTOM;
713 
714         if (infoPtr->dwStyle & WS_DISABLED)
715             stateId = TUS_DISABLED;
716         else if (infoPtr->flags & TB_DRAG_MODE)
717             stateId = TUS_PRESSED;
718         else if (infoPtr->flags & TB_THUMB_HOT)
719             stateId = TUS_HOT;
720         else
721             stateId = TUS_NORMAL;
722 
723         DrawThemeBackground (theme, hdc, partId, stateId, &infoPtr->rcThumb, NULL);
724 
725         return;
726     }
727 
728     if (infoPtr->dwStyle & WS_DISABLED || infoPtr->flags & TB_DRAG_MODE)
729     {
730         if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
731             brush = COMCTL32_hPattern55AABrush;
732         else
733             brush = GetSysColorBrush(COLOR_SCROLLBAR);
734 
735         SetTextColor(hdc, comctl32_color.clr3dFace);
736         SetBkColor(hdc, comctl32_color.clr3dHilight);
737     }
738     else
739         brush = GetSysColorBrush(COLOR_BTNFACE);
740 
741     PointDepth = TRACKBAR_FillThumb(infoPtr, hdc, brush);
742 
743     if (infoPtr->dwStyle & TBS_BOTH)
744     {
745        DrawEdge(hdc, &infoPtr->rcThumb, EDGE_RAISED, BF_RECT | BF_SOFT);
746        return;
747     }
748     else
749     {
750         RECT thumb = infoPtr->rcThumb;
751 
752         if (infoPtr->dwStyle & TBS_VERT)
753         {
754           if (infoPtr->dwStyle & TBS_LEFT)
755           {
756             /* rectangular part */
757             thumb.left += PointDepth;
758             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_TOP | BF_RIGHT | BF_BOTTOM | BF_SOFT);
759 
760             /* light edge */
761             thumb.left -= PointDepth;
762             thumb.right = thumb.left + PointDepth;
763             thumb.bottom = infoPtr->rcThumb.top + PointDepth + 1;
764             thumb.top = infoPtr->rcThumb.top;
765             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_DIAGONAL_ENDTOPRIGHT | BF_SOFT);
766 
767             /* shadowed edge */
768             thumb.top += PointDepth;
769             thumb.bottom += PointDepth;
770             DrawEdge(hdc, &thumb, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT | BF_SOFT);
771             return;
772           }
773           else
774           {
775             /* rectangular part */
776             thumb.right -= PointDepth;
777             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_TOP | BF_LEFT | BF_BOTTOM | BF_SOFT);
778 
779             /* light edge */
780             thumb.left = thumb.right;
781             thumb.right += PointDepth + 1;
782             thumb.bottom = infoPtr->rcThumb.top + PointDepth + 1;
783             thumb.top = infoPtr->rcThumb.top;
784             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_DIAGONAL_ENDTOPLEFT | BF_SOFT);
785 
786             /* shadowed edge */
787             thumb.top += PointDepth;
788             thumb.bottom += PointDepth;
789             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT);
790           }
791         }
792         else
793         {
794           if (infoPtr->dwStyle & TBS_TOP)
795           {
796             /* rectangular part */
797             thumb.top += PointDepth;
798             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_LEFT | BF_BOTTOM | BF_RIGHT | BF_SOFT);
799 
800             /* light edge */
801             thumb.left = infoPtr->rcThumb.left;
802             thumb.right = thumb.left + PointDepth;
803             thumb.bottom = infoPtr->rcThumb.top + PointDepth + 1;
804             thumb.top -= PointDepth;
805             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_DIAGONAL_ENDTOPRIGHT | BF_SOFT);
806 
807             /* shadowed edge */
808             thumb.left += PointDepth;
809             thumb.right += PointDepth;
810             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_DIAGONAL_ENDBOTTOMRIGHT | BF_SOFT);
811           }
812           else
813           {
814             /* rectangular part */
815             thumb.bottom -= PointDepth;
816             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT);
817 
818             /* light edge */
819             thumb.left = infoPtr->rcThumb.left;
820             thumb.right = thumb.left + PointDepth;
821             thumb.top = infoPtr->rcThumb.bottom - PointDepth - 1;
822             thumb.bottom += PointDepth;
823             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_DIAGONAL_ENDTOPLEFT | BF_SOFT);
824 
825             /* shadowed edge */
826             thumb.left += PointDepth;
827             thumb.right += PointDepth;
828             DrawEdge(hdc, &thumb, EDGE_RAISED, BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT);
829           }
830         }
831     }
832 }
833 
834 
835 static inline void
TRACKBAR_ActivateToolTip(const TRACKBAR_INFO * infoPtr,BOOL fShow)836 TRACKBAR_ActivateToolTip (const TRACKBAR_INFO *infoPtr, BOOL fShow)
837 {
838     TTTOOLINFOW ti;
839 
840     if (!infoPtr->hwndToolTip) return;
841 
842     ZeroMemory(&ti, sizeof(ti));
843     ti.cbSize = sizeof(ti);
844     ti.hwnd   = infoPtr->hwndSelf;
845 
846     SendMessageW (infoPtr->hwndToolTip, TTM_TRACKACTIVATE, fShow, (LPARAM)&ti);
847 }
848 
849 
850 static void
TRACKBAR_UpdateToolTip(const TRACKBAR_INFO * infoPtr)851 TRACKBAR_UpdateToolTip (const TRACKBAR_INFO *infoPtr)
852 {
853     WCHAR buf[80];
854     static const WCHAR fmt[] = { '%', 'l', 'd', 0 };
855     TTTOOLINFOW ti;
856     POINT pt;
857     RECT rcClient;
858     LRESULT size;
859 
860     if (!infoPtr->hwndToolTip) return;
861 
862     ZeroMemory(&ti, sizeof(ti));
863     ti.cbSize = sizeof(ti);
864     ti.hwnd   = infoPtr->hwndSelf;
865     ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
866 
867     wsprintfW (buf, fmt, infoPtr->lPos);
868     ti.lpszText = buf;
869     SendMessageW (infoPtr->hwndToolTip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti);
870 
871     GetClientRect (infoPtr->hwndSelf, &rcClient);
872     size = SendMessageW (infoPtr->hwndToolTip, TTM_GETBUBBLESIZE, 0, (LPARAM)&ti);
873     if (infoPtr->dwStyle & TBS_VERT) {
874 	if (infoPtr->fLocation == TBTS_LEFT)
875 	    pt.x = 0 - LOWORD(size) - TOOLTIP_OFFSET;
876 	else
877 	    pt.x = rcClient.right + TOOLTIP_OFFSET;
878     	pt.y = (infoPtr->rcThumb.top + infoPtr->rcThumb.bottom - HIWORD(size))/2;
879     } else {
880 	if (infoPtr->fLocation == TBTS_TOP)
881 	    pt.y = 0 - HIWORD(size) - TOOLTIP_OFFSET;
882 	else
883             pt.y = rcClient.bottom + TOOLTIP_OFFSET;
884         pt.x = (infoPtr->rcThumb.left + infoPtr->rcThumb.right - LOWORD(size))/2;
885     }
886     ClientToScreen(infoPtr->hwndSelf, &pt);
887 
888     SendMessageW (infoPtr->hwndToolTip, TTM_TRACKPOSITION,
889                   0, MAKELPARAM(pt.x, pt.y));
890 }
891 
892 
893 static void
TRACKBAR_Refresh(TRACKBAR_INFO * infoPtr,HDC hdcDst)894 TRACKBAR_Refresh (TRACKBAR_INFO *infoPtr, HDC hdcDst)
895 {
896     RECT rcClient;
897     HDC hdc;
898     HBITMAP hOldBmp = 0, hOffScreenBmp = 0;
899     NMCUSTOMDRAW nmcd;
900     int gcdrf, icdrf;
901 
902     if (infoPtr->flags & TB_THUMBCHANGED) {
903         TRACKBAR_UpdateThumb (infoPtr);
904         if (infoPtr->flags & TB_THUMBSIZECHANGED)
905             TRACKBAR_CalcChannel (infoPtr);
906     }
907     if (infoPtr->flags & TB_SELECTIONCHANGED)
908         TRACKBAR_CalcSelection (infoPtr);
909 
910     if (infoPtr->flags & TB_DRAG_MODE)
911         TRACKBAR_UpdateToolTip (infoPtr);
912 
913     infoPtr->flags &= ~ (TB_THUMBCHANGED | TB_SELECTIONCHANGED);
914 
915     GetClientRect (infoPtr->hwndSelf, &rcClient);
916 
917     /* try to render offscreen, if we fail, carrry onscreen */
918     hdc = CreateCompatibleDC(hdcDst);
919     if (hdc) {
920         hOffScreenBmp = CreateCompatibleBitmap(hdcDst, rcClient.right, rcClient.bottom);
921         if (hOffScreenBmp) {
922 	    hOldBmp = SelectObject(hdc, hOffScreenBmp);
923 	} else {
924 	    DeleteObject(hdc);
925 	    hdc = hdcDst;
926 	}
927     } else {
928 	hdc = hdcDst;
929     }
930 
931     ZeroMemory(&nmcd, sizeof(nmcd));
932     nmcd.hdr.hwndFrom = infoPtr->hwndSelf;
933     nmcd.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
934     nmcd.hdr.code = NM_CUSTOMDRAW;
935     nmcd.hdc = hdc;
936 
937     /* start the paint cycle */
938     nmcd.rc = rcClient;
939     gcdrf = notify_customdraw(infoPtr, &nmcd, CDDS_PREPAINT);
940     if (gcdrf & CDRF_SKIPDEFAULT) goto cleanup;
941 
942     /* Erase background */
943     if (gcdrf == CDRF_DODEFAULT ||
944         notify_customdraw(infoPtr, &nmcd, CDDS_PREERASE) != CDRF_SKIPDEFAULT) {
945         if (GetWindowTheme (infoPtr->hwndSelf)) {
946             DrawThemeParentBackground (infoPtr->hwndSelf, hdc, 0);
947         }
948 #ifndef __REACTOS__
949         else {
950 #else
951         {
952 #endif
953             HBRUSH brush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLORSTATIC,
954                     (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
955             FillRect (hdc, &rcClient, brush ? brush : GetSysColorBrush(COLOR_BTNFACE));
956         }
957         if (gcdrf != CDRF_DODEFAULT)
958 	    notify_customdraw(infoPtr, &nmcd, CDDS_POSTERASE);
959     }
960 
961     /* draw channel */
962     if (gcdrf & CDRF_NOTIFYITEMDRAW) {
963         nmcd.dwItemSpec = TBCD_CHANNEL;
964 	nmcd.uItemState = CDIS_DEFAULT;
965 	nmcd.rc = infoPtr->rcChannel;
966 	icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
967     } else icdrf = CDRF_DODEFAULT;
968     if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
969 	TRACKBAR_DrawChannel (infoPtr, hdc);
970 	if (icdrf & CDRF_NOTIFYPOSTPAINT)
971 	    notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
972     }
973 
974 
975     /* draw tics */
976     if (!(infoPtr->dwStyle & TBS_NOTICKS)) {
977     	if (gcdrf & CDRF_NOTIFYITEMDRAW) {
978             nmcd.dwItemSpec = TBCD_TICS;
979 	    nmcd.uItemState = CDIS_DEFAULT;
980 	    nmcd.rc = rcClient;
981 	    icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
982         } else icdrf = CDRF_DODEFAULT;
983 	if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
984 	    TRACKBAR_DrawTics (infoPtr, hdc);
985 	    if (icdrf & CDRF_NOTIFYPOSTPAINT)
986 		notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
987 	}
988     }
989 
990     /* draw thumb */
991     if (!(infoPtr->dwStyle & TBS_NOTHUMB)) {
992 	if (gcdrf & CDRF_NOTIFYITEMDRAW) {
993 	    nmcd.dwItemSpec = TBCD_THUMB;
994 	    nmcd.uItemState = infoPtr->flags & TB_DRAG_MODE ? CDIS_HOT : CDIS_DEFAULT;
995 	    nmcd.rc = infoPtr->rcThumb;
996 	    icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
997 	} else icdrf = CDRF_DODEFAULT;
998 	if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
999             TRACKBAR_DrawThumb(infoPtr, hdc);
1000 	    if (icdrf & CDRF_NOTIFYPOSTPAINT)
1001 		notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
1002 	}
1003     }
1004 
1005     /* draw focus rectangle */
1006     if (infoPtr->flags & TB_IS_FOCUSED) {
1007 	DrawFocusRect(hdc, &rcClient);
1008     }
1009 
1010     /* finish up the painting */
1011     if (gcdrf & CDRF_NOTIFYPOSTPAINT)
1012 	notify_customdraw(infoPtr, &nmcd, CDDS_POSTPAINT);
1013 
1014 cleanup:
1015     /* cleanup, if we rendered offscreen */
1016     if (hdc != hdcDst) {
1017 	BitBlt(hdcDst, 0, 0, rcClient.right, rcClient.bottom, hdc, 0, 0, SRCCOPY);
1018 	SelectObject(hdc, hOldBmp);
1019 	DeleteObject(hOffScreenBmp);
1020 	DeleteObject(hdc);
1021     }
1022 }
1023 
1024 
1025 static void
1026 TRACKBAR_AlignBuddies (const TRACKBAR_INFO *infoPtr)
1027 {
1028     HWND hwndParent = GetParent (infoPtr->hwndSelf);
1029     RECT rcSelf, rcBuddy;
1030     INT x, y;
1031 
1032     GetWindowRect (infoPtr->hwndSelf, &rcSelf);
1033     MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcSelf, 2);
1034 
1035     /* align buddy left or above */
1036     if (infoPtr->hwndBuddyLA) {
1037 	GetWindowRect (infoPtr->hwndBuddyLA, &rcBuddy);
1038 	MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcBuddy, 2);
1039 
1040 	if (infoPtr->dwStyle & TBS_VERT) {
1041 	    x = (infoPtr->rcChannel.right + infoPtr->rcChannel.left) / 2 -
1042 		(rcBuddy.right - rcBuddy.left) / 2 + rcSelf.left;
1043 	    y = rcSelf.top - (rcBuddy.bottom - rcBuddy.top);
1044 	}
1045 	else {
1046 	    x = rcSelf.left - (rcBuddy.right - rcBuddy.left);
1047 	    y = (infoPtr->rcChannel.bottom + infoPtr->rcChannel.top) / 2 -
1048 		(rcBuddy.bottom - rcBuddy.top) / 2 + rcSelf.top;
1049 	}
1050 
1051 	SetWindowPos (infoPtr->hwndBuddyLA, 0, x, y, 0, 0,
1052                       SWP_NOZORDER | SWP_NOSIZE);
1053     }
1054 
1055 
1056     /* align buddy right or below */
1057     if (infoPtr->hwndBuddyRB) {
1058 	GetWindowRect (infoPtr->hwndBuddyRB, &rcBuddy);
1059 	MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcBuddy, 2);
1060 
1061 	if (infoPtr->dwStyle & TBS_VERT) {
1062 	    x = (infoPtr->rcChannel.right + infoPtr->rcChannel.left) / 2 -
1063 		(rcBuddy.right - rcBuddy.left) / 2 + rcSelf.left;
1064 	    y = rcSelf.bottom;
1065 	}
1066 	else {
1067 	    x = rcSelf.right;
1068 	    y = (infoPtr->rcChannel.bottom + infoPtr->rcChannel.top) / 2 -
1069 		(rcBuddy.bottom - rcBuddy.top) / 2 + rcSelf.top;
1070 	}
1071 	SetWindowPos (infoPtr->hwndBuddyRB, 0, x, y, 0, 0,
1072                       SWP_NOZORDER | SWP_NOSIZE);
1073     }
1074 }
1075 
1076 
1077 static LRESULT
1078 TRACKBAR_ClearSel (TRACKBAR_INFO *infoPtr, BOOL fRedraw)
1079 {
1080     infoPtr->lSelMin = 0;
1081     infoPtr->lSelMax = 0;
1082     infoPtr->flags |= TB_SELECTIONCHANGED;
1083 
1084     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1085 
1086     return 0;
1087 }
1088 
1089 
1090 static LRESULT
1091 TRACKBAR_ClearTics (TRACKBAR_INFO *infoPtr, BOOL fRedraw)
1092 {
1093     if (infoPtr->tics) {
1094         Free (infoPtr->tics);
1095         infoPtr->tics = NULL;
1096         infoPtr->uNumTics = 0;
1097     }
1098 
1099     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1100 
1101     return 0;
1102 }
1103 
1104 
1105 static inline LRESULT
1106 TRACKBAR_GetChannelRect (const TRACKBAR_INFO *infoPtr, LPRECT lprc)
1107 {
1108     if (lprc == NULL) return 0;
1109 
1110     lprc->left   = infoPtr->rcChannel.left;
1111     lprc->right  = infoPtr->rcChannel.right;
1112     lprc->bottom = infoPtr->rcChannel.bottom;
1113     lprc->top    = infoPtr->rcChannel.top;
1114 
1115     return 0;
1116 }
1117 
1118 
1119 static inline LONG
1120 TRACKBAR_GetNumTics (const TRACKBAR_INFO *infoPtr)
1121 {
1122     if (infoPtr->dwStyle & TBS_NOTICKS) return 0;
1123 
1124     return infoPtr->uNumTics + 2;
1125 }
1126 
1127 
1128 static int __cdecl comp_tics (const void *ap, const void *bp)
1129 {
1130     const DWORD a = *(const DWORD *)ap;
1131     const DWORD b = *(const DWORD *)bp;
1132 
1133     TRACE("(a=%d, b=%d)\n", a, b);
1134     if (a < b) return -1;
1135     if (a > b) return 1;
1136     return 0;
1137 }
1138 
1139 
1140 static inline LONG
1141 TRACKBAR_GetTic (const TRACKBAR_INFO *infoPtr, INT iTic)
1142 {
1143     if ((iTic < 0) || (iTic >= infoPtr->uNumTics) || !infoPtr->tics)
1144 	return -1;
1145 
1146     qsort(infoPtr->tics, infoPtr->uNumTics, sizeof(DWORD), comp_tics);
1147     return infoPtr->tics[iTic];
1148 }
1149 
1150 
1151 static inline LONG
1152 TRACKBAR_GetTicPos (const TRACKBAR_INFO *infoPtr, INT iTic)
1153 {
1154     LONG range, width, pos, tic;
1155     int offsetthumb;
1156 
1157     if ((iTic < 0) || (iTic >= infoPtr->uNumTics) || !infoPtr->tics)
1158 	return -1;
1159 
1160     tic   = TRACKBAR_GetTic (infoPtr, iTic);
1161     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
1162     if (range <= 0) range = 1;
1163     offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
1164     width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
1165     pos   = infoPtr->rcChannel.left + offsetthumb + (width * tic) / range;
1166 
1167     return pos;
1168 }
1169 
1170 
1171 static HWND
1172 TRACKBAR_SetBuddy (TRACKBAR_INFO *infoPtr, BOOL fLocation, HWND hwndBuddy)
1173 {
1174     HWND hwndTemp;
1175 
1176     if (fLocation) {
1177 	/* buddy is left or above */
1178 	hwndTemp = infoPtr->hwndBuddyLA;
1179 	infoPtr->hwndBuddyLA = hwndBuddy;
1180     }
1181     else {
1182         /* buddy is right or below */
1183         hwndTemp = infoPtr->hwndBuddyRB;
1184         infoPtr->hwndBuddyRB = hwndBuddy;
1185     }
1186 
1187     TRACKBAR_AlignBuddies (infoPtr);
1188 
1189     return hwndTemp;
1190 }
1191 
1192 
1193 static inline LONG
1194 TRACKBAR_SetLineSize (TRACKBAR_INFO *infoPtr, LONG lLineSize)
1195 {
1196     LONG lTemp = infoPtr->lLineSize;
1197 
1198     infoPtr->lLineSize = lLineSize;
1199 
1200     return lTemp;
1201 }
1202 
1203 static void TRACKBAR_UpdatePageSize(TRACKBAR_INFO *infoPtr)
1204 {
1205     if (infoPtr->flags & TB_USER_PAGE)
1206         return;
1207 
1208     infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
1209     if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
1210 }
1211 
1212 static inline LONG
1213 TRACKBAR_SetPageSize (TRACKBAR_INFO *infoPtr, LONG lPageSize)
1214 {
1215     LONG lTemp = infoPtr->lPageSize;
1216 
1217     if (lPageSize == -1)
1218     {
1219         infoPtr->flags &= ~TB_USER_PAGE;
1220         TRACKBAR_UpdatePageSize(infoPtr);
1221     }
1222     else
1223     {
1224         infoPtr->flags |= TB_USER_PAGE;
1225         infoPtr->lPageSize = lPageSize;
1226     }
1227 
1228     return lTemp;
1229 }
1230 
1231 
1232 static inline LRESULT
1233 TRACKBAR_SetPos (TRACKBAR_INFO *infoPtr, BOOL fPosition, LONG lPosition)
1234 {
1235     LONG oldPos = infoPtr->lPos;
1236     infoPtr->lPos = lPosition;
1237 
1238     if (infoPtr->lPos < infoPtr->lRangeMin)
1239 	infoPtr->lPos = infoPtr->lRangeMin;
1240 
1241     if (infoPtr->lPos > infoPtr->lRangeMax)
1242 	infoPtr->lPos = infoPtr->lRangeMax;
1243 
1244     if (fPosition && oldPos != lPosition)
1245     {
1246         TRACKBAR_UpdateThumb(infoPtr);
1247         TRACKBAR_InvalidateThumbMove(infoPtr, oldPos, lPosition);
1248     }
1249 
1250     return 0;
1251 }
1252 
1253 static inline LRESULT
1254 TRACKBAR_SetRange (TRACKBAR_INFO *infoPtr, BOOL redraw, LONG range)
1255 {
1256     BOOL changed = infoPtr->lRangeMin != (SHORT)LOWORD(range) ||
1257                    infoPtr->lRangeMax != (SHORT)HIWORD(range);
1258 
1259     infoPtr->lRangeMin = (SHORT)LOWORD(range);
1260     infoPtr->lRangeMax = (SHORT)HIWORD(range);
1261 
1262     /* clip position to new min/max limit */
1263     if (infoPtr->lPos < infoPtr->lRangeMin)
1264         infoPtr->lPos = infoPtr->lRangeMin;
1265 
1266     if (infoPtr->lPos > infoPtr->lRangeMax)
1267         infoPtr->lPos = infoPtr->lRangeMax;
1268 
1269     TRACKBAR_UpdatePageSize(infoPtr);
1270 
1271     if (changed) {
1272         if (infoPtr->dwStyle & TBS_AUTOTICKS)
1273             TRACKBAR_RecalculateTics (infoPtr);
1274         infoPtr->flags |= TB_THUMBPOSCHANGED;
1275     }
1276 
1277     if (redraw) TRACKBAR_InvalidateAll(infoPtr);
1278 
1279     return 0;
1280 }
1281 
1282 
1283 static inline LRESULT
1284 TRACKBAR_SetRangeMax (TRACKBAR_INFO *infoPtr, BOOL redraw, LONG lMax)
1285 {
1286     BOOL changed = infoPtr->lRangeMax != lMax;
1287     LONG rightmost = max(lMax, infoPtr->lRangeMin);
1288 
1289     infoPtr->lRangeMax = lMax;
1290     if (infoPtr->lPos > rightmost) {
1291         infoPtr->lPos = rightmost;
1292         infoPtr->flags |= TB_THUMBPOSCHANGED;
1293     }
1294 
1295     TRACKBAR_UpdatePageSize(infoPtr);
1296 
1297     if (changed && (infoPtr->dwStyle & TBS_AUTOTICKS))
1298         TRACKBAR_RecalculateTics (infoPtr);
1299 
1300     if (redraw) TRACKBAR_InvalidateAll(infoPtr);
1301 
1302     return 0;
1303 }
1304 
1305 
1306 static inline LRESULT
1307 TRACKBAR_SetRangeMin (TRACKBAR_INFO *infoPtr, BOOL redraw, LONG lMin)
1308 {
1309     BOOL changed = infoPtr->lRangeMin != lMin;
1310 
1311     infoPtr->lRangeMin = lMin;
1312     if (infoPtr->lPos < infoPtr->lRangeMin) {
1313         infoPtr->lPos = infoPtr->lRangeMin;
1314         infoPtr->flags |= TB_THUMBPOSCHANGED;
1315     }
1316 
1317     TRACKBAR_UpdatePageSize(infoPtr);
1318 
1319     if (changed && (infoPtr->dwStyle & TBS_AUTOTICKS))
1320         TRACKBAR_RecalculateTics (infoPtr);
1321 
1322     if (redraw) TRACKBAR_InvalidateAll(infoPtr);
1323 
1324     return 0;
1325 }
1326 
1327 
1328 static inline LRESULT
1329 TRACKBAR_SetSel (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lSel)
1330 {
1331     if (!(infoPtr->dwStyle & TBS_ENABLESELRANGE)){
1332         infoPtr->lSelMin = 0;
1333         infoPtr->lSelMax = 0;
1334         return 0;
1335     }
1336 
1337     infoPtr->lSelMin = (SHORT)LOWORD(lSel);
1338     infoPtr->lSelMax = (SHORT)HIWORD(lSel);
1339     infoPtr->flags |= TB_SELECTIONCHANGED;
1340 
1341     if (infoPtr->lSelMin < infoPtr->lRangeMin)
1342         infoPtr->lSelMin = infoPtr->lRangeMin;
1343     if (infoPtr->lSelMax > infoPtr->lRangeMax)
1344         infoPtr->lSelMax = infoPtr->lRangeMax;
1345 
1346     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1347 
1348     return 0;
1349 }
1350 
1351 
1352 static inline LRESULT
1353 TRACKBAR_SetSelEnd (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lEnd)
1354 {
1355     if (!(infoPtr->dwStyle & TBS_ENABLESELRANGE)){
1356         infoPtr->lSelMax = 0;
1357 	return 0;
1358     }
1359 
1360     infoPtr->lSelMax = lEnd;
1361     infoPtr->flags |= TB_SELECTIONCHANGED;
1362 
1363     if (infoPtr->lSelMax > infoPtr->lRangeMax)
1364         infoPtr->lSelMax = infoPtr->lRangeMax;
1365 
1366     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1367 
1368     return 0;
1369 }
1370 
1371 
1372 static inline LRESULT
1373 TRACKBAR_SetSelStart (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lStart)
1374 {
1375     if (!(infoPtr->dwStyle & TBS_ENABLESELRANGE)){
1376         infoPtr->lSelMin = 0;
1377 	return 0;
1378     }
1379 
1380     infoPtr->lSelMin = lStart;
1381     infoPtr->flags  |=TB_SELECTIONCHANGED;
1382 
1383     if (infoPtr->lSelMin < infoPtr->lRangeMin)
1384         infoPtr->lSelMin = infoPtr->lRangeMin;
1385 
1386     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1387 
1388     return 0;
1389 }
1390 
1391 
1392 static inline LRESULT
1393 TRACKBAR_SetThumbLength (TRACKBAR_INFO *infoPtr, UINT iLength)
1394 {
1395     if (infoPtr->dwStyle & TBS_FIXEDLENGTH) {
1396         /* We're not supposed to check if it's really changed or not,
1397            just repaint in any case. */
1398         infoPtr->uThumbLen = iLength;
1399 	infoPtr->flags |= TB_THUMBSIZECHANGED;
1400 	TRACKBAR_InvalidateAll(infoPtr);
1401     }
1402 
1403     return 0;
1404 }
1405 
1406 
1407 static inline LRESULT
1408 TRACKBAR_SetTic (TRACKBAR_INFO *infoPtr, LONG lPos)
1409 {
1410     if ((lPos < infoPtr->lRangeMin) || (lPos> infoPtr->lRangeMax))
1411         return FALSE;
1412 
1413     TRACE("lPos=%d\n", lPos);
1414 
1415     infoPtr->uNumTics++;
1416     infoPtr->tics=ReAlloc( infoPtr->tics,
1417                                     (infoPtr->uNumTics)*sizeof (DWORD));
1418     if (!infoPtr->tics) {
1419 	infoPtr->uNumTics = 0;
1420 	notify(infoPtr, NM_OUTOFMEMORY);
1421 	return FALSE;
1422     }
1423     infoPtr->tics[infoPtr->uNumTics-1] = lPos;
1424 
1425     TRACKBAR_InvalidateAll(infoPtr);
1426 
1427     return TRUE;
1428 }
1429 
1430 
1431 static inline LRESULT
1432 TRACKBAR_SetTicFreq (TRACKBAR_INFO *infoPtr, WORD wFreq)
1433 {
1434     if (infoPtr->dwStyle & TBS_AUTOTICKS) {
1435         infoPtr->uTicFreq = wFreq;
1436 	TRACKBAR_RecalculateTics (infoPtr);
1437 	TRACKBAR_InvalidateAll(infoPtr);
1438     }
1439 
1440     TRACKBAR_UpdateThumb (infoPtr);
1441     return 0;
1442 }
1443 
1444 
1445 static inline INT
1446 TRACKBAR_SetTipSide (TRACKBAR_INFO *infoPtr, INT fLocation)
1447 {
1448     INT fTemp = infoPtr->fLocation;
1449 
1450     infoPtr->fLocation = fLocation;
1451 
1452     return fTemp;
1453 }
1454 
1455 
1456 static inline LRESULT
1457 TRACKBAR_SetToolTips (TRACKBAR_INFO *infoPtr, HWND hwndTT)
1458 {
1459     infoPtr->hwndToolTip = hwndTT;
1460 
1461     return 0;
1462 }
1463 
1464 
1465 static inline BOOL
1466 TRACKBAR_SetUnicodeFormat (TRACKBAR_INFO *infoPtr, BOOL fUnicode)
1467 {
1468     BOOL bTemp = infoPtr->bUnicode;
1469 
1470     infoPtr->bUnicode = fUnicode;
1471 
1472     return bTemp;
1473 }
1474 
1475 static int get_scaled_metric(const TRACKBAR_INFO *infoPtr, int value)
1476 {
1477     return MulDiv(value, GetDpiForWindow(infoPtr->hwndSelf), 96);
1478 }
1479 
1480 static LRESULT
1481 TRACKBAR_InitializeThumb (TRACKBAR_INFO *infoPtr)
1482 {
1483     int client_size;
1484     RECT rect;
1485 
1486     infoPtr->uThumbLen = get_scaled_metric(infoPtr, infoPtr->dwStyle & TBS_ENABLESELRANGE ? 23 : 21);
1487 
1488     if (!(infoPtr->dwStyle & TBS_FIXEDLENGTH))
1489     {
1490         GetClientRect(infoPtr->hwndSelf, &rect);
1491         if (infoPtr->dwStyle & TBS_VERT)
1492             client_size = rect.right - rect.left;
1493         else
1494             client_size = rect.bottom - rect.top;
1495 
1496         if (client_size < infoPtr->uThumbLen)
1497             infoPtr->uThumbLen = client_size > get_scaled_metric(infoPtr, 9) ?
1498                 client_size - get_scaled_metric(infoPtr, 5) : get_scaled_metric(infoPtr, 4);
1499     }
1500 
1501     TRACKBAR_CalcChannel (infoPtr);
1502     TRACKBAR_UpdateThumb (infoPtr);
1503     infoPtr->flags &= ~TB_SELECTIONCHANGED;
1504 
1505     return 0;
1506 }
1507 
1508 
1509 static LRESULT
1510 TRACKBAR_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
1511 {
1512     TRACKBAR_INFO *infoPtr;
1513 
1514     infoPtr = Alloc (sizeof(TRACKBAR_INFO));
1515     if (!infoPtr) return -1;
1516     SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
1517 
1518     /* set default values */
1519     infoPtr->hwndSelf  = hwnd;
1520     infoPtr->dwStyle   = lpcs->style;
1521     infoPtr->lRangeMin = 0;
1522     infoPtr->lRangeMax = 100;
1523     infoPtr->lLineSize = 1;
1524     infoPtr->lPageSize = TB_DEFAULTPAGESIZE;
1525     infoPtr->lSelMin   = 0;
1526     infoPtr->lSelMax   = 0;
1527     infoPtr->lPos      = 0;
1528     infoPtr->fLocation = TBTS_TOP;
1529     infoPtr->uNumTics  = 0;    /* start and end tic are not included in count*/
1530     infoPtr->uTicFreq  = 1;
1531     infoPtr->tics      = NULL;
1532     infoPtr->hwndNotify= lpcs->hwndParent;
1533 
1534     TRACKBAR_InitializeThumb (infoPtr);
1535 
1536     /* Create tooltip control */
1537     if (infoPtr->dwStyle & TBS_TOOLTIPS) {
1538 
1539     	infoPtr->hwndToolTip =
1540             CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
1541                              CW_USEDEFAULT, CW_USEDEFAULT,
1542                              CW_USEDEFAULT, CW_USEDEFAULT,
1543                              hwnd, 0, 0, 0);
1544 
1545     	if (infoPtr->hwndToolTip) {
1546             TTTOOLINFOW ti;
1547             WCHAR wEmpty = 0;
1548             ZeroMemory (&ti, sizeof(ti));
1549             ti.cbSize   = sizeof(ti);
1550      	    ti.uFlags   = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
1551 	    ti.hwnd     = hwnd;
1552             ti.lpszText = &wEmpty;
1553 
1554             SendMessageW (infoPtr->hwndToolTip, TTM_ADDTOOLW, 0, (LPARAM)&ti);
1555 	 }
1556     }
1557 
1558     OpenThemeData (hwnd, themeClass);
1559 
1560     return 0;
1561 }
1562 
1563 
1564 static LRESULT
1565 TRACKBAR_Destroy (TRACKBAR_INFO *infoPtr)
1566 {
1567     /* delete tooltip control */
1568     if (infoPtr->hwndToolTip)
1569     	DestroyWindow (infoPtr->hwndToolTip);
1570 
1571     Free (infoPtr->tics);
1572     infoPtr->tics = NULL;
1573 
1574     SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
1575     CloseThemeData (GetWindowTheme (infoPtr->hwndSelf));
1576     Free (infoPtr);
1577 
1578     return 0;
1579 }
1580 
1581 
1582 static LRESULT
1583 TRACKBAR_KillFocus (TRACKBAR_INFO *infoPtr)
1584 {
1585     TRACE("\n");
1586     infoPtr->flags &= ~TB_IS_FOCUSED;
1587     TRACKBAR_InvalidateAll(infoPtr);
1588 
1589     return 0;
1590 }
1591 
1592 static LRESULT
1593 TRACKBAR_LButtonDown (TRACKBAR_INFO *infoPtr, INT x, INT y)
1594 {
1595     POINT clickPoint;
1596 
1597     clickPoint.x = x;
1598     clickPoint.y = y;
1599 
1600     SetFocus(infoPtr->hwndSelf);
1601 
1602     if (PtInRect(&infoPtr->rcThumb, clickPoint)) {
1603         infoPtr->flags |= TB_DRAG_MODE;
1604         SetCapture (infoPtr->hwndSelf);
1605 	TRACKBAR_UpdateToolTip (infoPtr);
1606 	TRACKBAR_ActivateToolTip (infoPtr, TRUE);
1607 	TRACKBAR_InvalidateThumb(infoPtr, infoPtr->lPos);
1608     } else {
1609 	LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
1610 	if (dir == 0) return 0;
1611 	infoPtr->flags |= (dir < 0) ? TB_AUTO_PAGE_LEFT : TB_AUTO_PAGE_RIGHT;
1612 	TRACKBAR_AutoPage (infoPtr, clickPoint);
1613         SetCapture (infoPtr->hwndSelf);
1614         SetTimer(infoPtr->hwndSelf, TB_REFRESH_TIMER, TB_REFRESH_DELAY, 0);
1615     }
1616 
1617     return 0;
1618 }
1619 
1620 
1621 static LRESULT
1622 TRACKBAR_LButtonUp (TRACKBAR_INFO *infoPtr)
1623 {
1624     if (infoPtr->flags & TB_DRAG_MODE) {
1625         notify_with_scroll (infoPtr, TB_THUMBPOSITION | (infoPtr->lPos<<16));
1626         notify_with_scroll (infoPtr, TB_ENDTRACK);
1627         infoPtr->flags &= ~TB_DRAG_MODE;
1628         ReleaseCapture ();
1629 	notify(infoPtr, NM_RELEASEDCAPTURE);
1630         TRACKBAR_ActivateToolTip(infoPtr, FALSE);
1631 	TRACKBAR_InvalidateThumb(infoPtr, infoPtr->lPos);
1632     }
1633     if (infoPtr->flags & TB_AUTO_PAGE) {
1634 	KillTimer (infoPtr->hwndSelf, TB_REFRESH_TIMER);
1635         infoPtr->flags &= ~TB_AUTO_PAGE;
1636         notify_with_scroll (infoPtr, TB_ENDTRACK);
1637         ReleaseCapture ();
1638 	notify(infoPtr, NM_RELEASEDCAPTURE);
1639     }
1640 
1641     return 0;
1642 }
1643 
1644 
1645 static LRESULT
1646 TRACKBAR_CaptureChanged (const TRACKBAR_INFO *infoPtr)
1647 {
1648     notify_with_scroll (infoPtr, TB_ENDTRACK);
1649     return 0;
1650 }
1651 
1652 
1653 static LRESULT
1654 TRACKBAR_Paint (TRACKBAR_INFO *infoPtr, HDC hdc)
1655 {
1656     if (hdc) {
1657 	TRACKBAR_Refresh(infoPtr, hdc);
1658     } else {
1659 	PAINTSTRUCT ps;
1660     	hdc = BeginPaint (infoPtr->hwndSelf, &ps);
1661     	TRACKBAR_Refresh (infoPtr, hdc);
1662     	EndPaint (infoPtr->hwndSelf, &ps);
1663     }
1664 
1665     return 0;
1666 }
1667 
1668 
1669 static LRESULT
1670 TRACKBAR_SetFocus (TRACKBAR_INFO *infoPtr)
1671 {
1672     TRACE("\n");
1673     infoPtr->flags |= TB_IS_FOCUSED;
1674     TRACKBAR_InvalidateAll(infoPtr);
1675 
1676     return 0;
1677 }
1678 
1679 
1680 static LRESULT
1681 TRACKBAR_Size (TRACKBAR_INFO *infoPtr)
1682 {
1683     if (infoPtr->dwStyle & TBS_FIXEDLENGTH)
1684     {
1685         TRACKBAR_CalcChannel(infoPtr);
1686         TRACKBAR_UpdateThumb(infoPtr);
1687     }
1688     else
1689         TRACKBAR_InitializeThumb(infoPtr);
1690     TRACKBAR_AlignBuddies (infoPtr);
1691     TRACKBAR_InvalidateAll(infoPtr);
1692 
1693     return 0;
1694 }
1695 
1696 static LRESULT
1697 TRACKBAR_StyleChanged (TRACKBAR_INFO *infoPtr, WPARAM wStyleType,
1698                        const STYLESTRUCT *lpss)
1699 {
1700     if (wStyleType != GWL_STYLE) return 0;
1701 
1702     infoPtr->dwStyle = lpss->styleNew;
1703 
1704     return 0;
1705 }
1706 
1707 static LRESULT
1708 TRACKBAR_Timer (TRACKBAR_INFO *infoPtr)
1709 {
1710     if (infoPtr->flags & TB_AUTO_PAGE) {
1711 	POINT pt;
1712 	if (GetCursorPos(&pt))
1713 	    if (ScreenToClient(infoPtr->hwndSelf, &pt))
1714 		TRACKBAR_AutoPage(infoPtr, pt);
1715     }
1716     return 0;
1717 }
1718 
1719 
1720 /* update theme after a WM_THEMECHANGED message */
1721 static LRESULT theme_changed (const TRACKBAR_INFO* infoPtr)
1722 {
1723     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
1724     CloseThemeData (theme);
1725     OpenThemeData (infoPtr->hwndSelf, themeClass);
1726     return 0;
1727 }
1728 
1729 
1730 static LRESULT
1731 TRACKBAR_MouseMove (TRACKBAR_INFO *infoPtr, INT x, INT y)
1732 {
1733     INT clickPlace = (infoPtr->dwStyle & TBS_VERT) ? y : x;
1734     LONG dragPos, oldPos = infoPtr->lPos;
1735 
1736     TRACE("(x=%d. y=%d)\n", x, y);
1737 
1738     if (infoPtr->flags & TB_AUTO_PAGE) {
1739 	POINT pt;
1740 	pt.x = x;
1741 	pt.y = y;
1742 	TRACKBAR_AutoPage (infoPtr, pt);
1743 	return TRUE;
1744     }
1745 
1746     if (!(infoPtr->flags & TB_DRAG_MODE))
1747     {
1748         if (GetWindowTheme (infoPtr->hwndSelf))
1749         {
1750             DWORD oldFlags = infoPtr->flags;
1751             POINT pt;
1752             pt.x = x;
1753             pt.y = y;
1754             if (PtInRect (&infoPtr->rcThumb, pt))
1755             {
1756                 TRACKMOUSEEVENT tme;
1757                 tme.cbSize = sizeof( tme );
1758                 tme.dwFlags = TME_LEAVE;
1759                 tme.hwndTrack = infoPtr->hwndSelf;
1760                 TrackMouseEvent( &tme );
1761                 infoPtr->flags |= TB_THUMB_HOT;
1762             }
1763             else
1764             {
1765                 TRACKMOUSEEVENT tme;
1766                 tme.cbSize = sizeof( tme );
1767                 tme.dwFlags = TME_CANCEL;
1768                 tme.hwndTrack = infoPtr->hwndSelf;
1769                 TrackMouseEvent( &tme );
1770                 infoPtr->flags &= ~TB_THUMB_HOT;
1771             }
1772             if (oldFlags != infoPtr->flags) InvalidateRect (infoPtr->hwndSelf, &infoPtr->rcThumb, FALSE);
1773         }
1774         return TRUE;
1775     }
1776 
1777     dragPos = TRACKBAR_ConvertPlaceToPosition (infoPtr, clickPlace);
1778 
1779     if (dragPos == oldPos) return TRUE;
1780 
1781     infoPtr->lPos = dragPos;
1782     TRACKBAR_UpdateThumb (infoPtr);
1783 
1784     notify_with_scroll (infoPtr, TB_THUMBTRACK | (infoPtr->lPos<<16));
1785 
1786     TRACKBAR_InvalidateThumbMove(infoPtr, oldPos, dragPos);
1787     UpdateWindow (infoPtr->hwndSelf);
1788 
1789     return TRUE;
1790 }
1791 
1792 static BOOL
1793 TRACKBAR_KeyDown (TRACKBAR_INFO *infoPtr, INT nVirtKey)
1794 {
1795     BOOL downIsLeft = infoPtr->dwStyle & TBS_DOWNISLEFT;
1796     BOOL vert = infoPtr->dwStyle & TBS_VERT;
1797     LONG pos = infoPtr->lPos;
1798 
1799     TRACE("%x\n", nVirtKey);
1800 
1801     switch (nVirtKey) {
1802     case VK_UP:
1803 	if (!vert && downIsLeft) TRACKBAR_LineDown(infoPtr);
1804         else TRACKBAR_LineUp(infoPtr);
1805         break;
1806     case VK_LEFT:
1807         if (vert && downIsLeft) TRACKBAR_LineDown(infoPtr);
1808         else TRACKBAR_LineUp(infoPtr);
1809         break;
1810     case VK_DOWN:
1811 	if (!vert && downIsLeft) TRACKBAR_LineUp(infoPtr);
1812         else TRACKBAR_LineDown(infoPtr);
1813         break;
1814     case VK_RIGHT:
1815 	if (vert && downIsLeft) TRACKBAR_LineUp(infoPtr);
1816         else TRACKBAR_LineDown(infoPtr);
1817         break;
1818     case VK_NEXT:
1819 	if (!vert && downIsLeft) TRACKBAR_PageUp(infoPtr);
1820         else TRACKBAR_PageDown(infoPtr);
1821         break;
1822     case VK_PRIOR:
1823 	if (!vert && downIsLeft) TRACKBAR_PageDown(infoPtr);
1824         else TRACKBAR_PageUp(infoPtr);
1825         break;
1826     case VK_HOME:
1827         if (infoPtr->lPos == infoPtr->lRangeMin) return FALSE;
1828         infoPtr->lPos = infoPtr->lRangeMin;
1829         notify_with_scroll (infoPtr, TB_TOP);
1830         break;
1831     case VK_END:
1832         if (infoPtr->lPos == infoPtr->lRangeMax) return FALSE;
1833         infoPtr->lPos = infoPtr->lRangeMax;
1834         notify_with_scroll (infoPtr, TB_BOTTOM);
1835         break;
1836     }
1837 
1838     if (pos != infoPtr->lPos) {
1839 	TRACKBAR_UpdateThumb (infoPtr);
1840 	TRACKBAR_InvalidateThumbMove (infoPtr, pos, infoPtr->lPos);
1841     }
1842 
1843     return TRUE;
1844 }
1845 
1846 
1847 static inline BOOL
1848 TRACKBAR_KeyUp (const TRACKBAR_INFO *infoPtr, INT nVirtKey)
1849 {
1850     switch (nVirtKey) {
1851     case VK_LEFT:
1852     case VK_UP:
1853     case VK_RIGHT:
1854     case VK_DOWN:
1855     case VK_NEXT:
1856     case VK_PRIOR:
1857     case VK_HOME:
1858     case VK_END:
1859         notify_with_scroll (infoPtr, TB_ENDTRACK);
1860     }
1861     return TRUE;
1862 }
1863 
1864 
1865 static LRESULT
1866 TRACKBAR_Enable (TRACKBAR_INFO *infoPtr, BOOL enable)
1867 {
1868     if (enable)
1869         infoPtr->dwStyle &= ~WS_DISABLED;
1870     else
1871         infoPtr->dwStyle |= WS_DISABLED;
1872 
1873     InvalidateRect(infoPtr->hwndSelf, &infoPtr->rcThumb, TRUE);
1874 
1875     return 1;
1876 }
1877 
1878 static LRESULT WINAPI
1879 TRACKBAR_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1880 {
1881     TRACKBAR_INFO *infoPtr = (TRACKBAR_INFO *)GetWindowLongPtrW (hwnd, 0);
1882 
1883     TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1884 
1885     if (!infoPtr && (uMsg != WM_CREATE))
1886         return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1887 
1888     switch (uMsg)
1889     {
1890     case TBM_CLEARSEL:
1891         return TRACKBAR_ClearSel (infoPtr, (BOOL)wParam);
1892 
1893     case TBM_CLEARTICS:
1894         return TRACKBAR_ClearTics (infoPtr, (BOOL)wParam);
1895 
1896     case TBM_GETBUDDY:
1897         return (LRESULT)(wParam ? infoPtr->hwndBuddyLA : infoPtr->hwndBuddyRB);
1898 
1899     case TBM_GETCHANNELRECT:
1900         return TRACKBAR_GetChannelRect (infoPtr, (LPRECT)lParam);
1901 
1902     case TBM_GETLINESIZE:
1903         return infoPtr->lLineSize;
1904 
1905     case TBM_GETNUMTICS:
1906         return TRACKBAR_GetNumTics (infoPtr);
1907 
1908     case TBM_GETPAGESIZE:
1909         return infoPtr->lPageSize;
1910 
1911     case TBM_GETPOS:
1912         return infoPtr->lPos;
1913 
1914     case TBM_GETPTICS:
1915         return (LRESULT)infoPtr->tics;
1916 
1917     case TBM_GETRANGEMAX:
1918         return infoPtr->lRangeMax;
1919 
1920     case TBM_GETRANGEMIN:
1921         return infoPtr->lRangeMin;
1922 
1923     case TBM_GETSELEND:
1924         return infoPtr->lSelMax;
1925 
1926     case TBM_GETSELSTART:
1927         return infoPtr->lSelMin;
1928 
1929     case TBM_GETTHUMBLENGTH:
1930         return infoPtr->uThumbLen;
1931 
1932     case TBM_GETTHUMBRECT:
1933 	return CopyRect((LPRECT)lParam, &infoPtr->rcThumb);
1934 
1935     case TBM_GETTIC:
1936         return TRACKBAR_GetTic (infoPtr, (INT)wParam);
1937 
1938     case TBM_GETTICPOS:
1939         return TRACKBAR_GetTicPos (infoPtr, (INT)wParam);
1940 
1941     case TBM_GETTOOLTIPS:
1942         return (LRESULT)infoPtr->hwndToolTip;
1943 
1944     case TBM_GETUNICODEFORMAT:
1945         return infoPtr->bUnicode;
1946 
1947     case TBM_SETBUDDY:
1948         return (LRESULT) TRACKBAR_SetBuddy(infoPtr, (BOOL)wParam, (HWND)lParam);
1949 
1950     case TBM_SETLINESIZE:
1951         return TRACKBAR_SetLineSize (infoPtr, (LONG)lParam);
1952 
1953     case TBM_SETPAGESIZE:
1954         return TRACKBAR_SetPageSize (infoPtr, (LONG)lParam);
1955 
1956     case TBM_SETPOS:
1957         return TRACKBAR_SetPos (infoPtr, (BOOL)wParam, (LONG)lParam);
1958 
1959     case TBM_SETRANGE:
1960         return TRACKBAR_SetRange (infoPtr, (BOOL)wParam, (LONG)lParam);
1961 
1962     case TBM_SETRANGEMAX:
1963         return TRACKBAR_SetRangeMax (infoPtr, (BOOL)wParam, (LONG)lParam);
1964 
1965     case TBM_SETRANGEMIN:
1966         return TRACKBAR_SetRangeMin (infoPtr, (BOOL)wParam, (LONG)lParam);
1967 
1968     case TBM_SETSEL:
1969         return TRACKBAR_SetSel (infoPtr, (BOOL)wParam, (LONG)lParam);
1970 
1971     case TBM_SETSELEND:
1972         return TRACKBAR_SetSelEnd (infoPtr, (BOOL)wParam, (LONG)lParam);
1973 
1974     case TBM_SETSELSTART:
1975         return TRACKBAR_SetSelStart (infoPtr, (BOOL)wParam, (LONG)lParam);
1976 
1977     case TBM_SETTHUMBLENGTH:
1978         return TRACKBAR_SetThumbLength (infoPtr, (UINT)wParam);
1979 
1980     case TBM_SETTIC:
1981         return TRACKBAR_SetTic (infoPtr, (LONG)lParam);
1982 
1983     case TBM_SETTICFREQ:
1984         return TRACKBAR_SetTicFreq (infoPtr, (WORD)wParam);
1985 
1986     case TBM_SETTIPSIDE:
1987         return TRACKBAR_SetTipSide (infoPtr, (INT)wParam);
1988 
1989     case TBM_SETTOOLTIPS:
1990         return TRACKBAR_SetToolTips (infoPtr, (HWND)wParam);
1991 
1992     case TBM_SETUNICODEFORMAT:
1993 	return TRACKBAR_SetUnicodeFormat (infoPtr, (BOOL)wParam);
1994 
1995 
1996     case WM_CAPTURECHANGED:
1997         if (hwnd == (HWND)lParam) return 0;
1998         return TRACKBAR_CaptureChanged (infoPtr);
1999 
2000     case WM_CREATE:
2001         return TRACKBAR_Create (hwnd, (LPCREATESTRUCTW)lParam);
2002 
2003     case WM_DESTROY:
2004         return TRACKBAR_Destroy (infoPtr);
2005 
2006     case WM_ENABLE:
2007         return TRACKBAR_Enable (infoPtr, (BOOL)wParam);
2008 
2009     case WM_ERASEBKGND:
2010 	return 0;
2011 
2012     case WM_GETDLGCODE:
2013         return DLGC_WANTARROWS;
2014 
2015     case WM_KEYDOWN:
2016         return TRACKBAR_KeyDown (infoPtr, (INT)wParam);
2017 
2018     case WM_KEYUP:
2019         return TRACKBAR_KeyUp (infoPtr, (INT)wParam);
2020 
2021     case WM_KILLFOCUS:
2022         return TRACKBAR_KillFocus (infoPtr);
2023 
2024     case WM_LBUTTONDOWN:
2025         return TRACKBAR_LButtonDown (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
2026 
2027     case WM_LBUTTONUP:
2028         return TRACKBAR_LButtonUp (infoPtr);
2029 
2030     case WM_MOUSELEAVE:
2031         infoPtr->flags &= ~TB_THUMB_HOT;
2032         InvalidateRect (infoPtr->hwndSelf, &infoPtr->rcThumb, FALSE);
2033         return 0;
2034 
2035     case WM_MOUSEMOVE:
2036         return TRACKBAR_MouseMove (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
2037 
2038     case WM_PRINTCLIENT:
2039     case WM_PAINT:
2040         return TRACKBAR_Paint (infoPtr, (HDC)wParam);
2041 
2042     case WM_SETFOCUS:
2043         return TRACKBAR_SetFocus (infoPtr);
2044 
2045     case WM_SIZE:
2046         return TRACKBAR_Size (infoPtr);
2047 
2048     case WM_STYLECHANGED:
2049         return TRACKBAR_StyleChanged (infoPtr, wParam, (LPSTYLESTRUCT)lParam);
2050 
2051     case WM_THEMECHANGED:
2052         return theme_changed (infoPtr);
2053 
2054     case WM_TIMER:
2055 	return TRACKBAR_Timer (infoPtr);
2056 
2057     case WM_WININICHANGE:
2058         return TRACKBAR_InitializeThumb (infoPtr);
2059 
2060     default:
2061         if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
2062             ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
2063         return DefWindowProcW (hwnd, uMsg, wParam, lParam);
2064     }
2065 }
2066 
2067 
2068 void TRACKBAR_Register (void)
2069 {
2070     WNDCLASSW wndClass;
2071 
2072     ZeroMemory (&wndClass, sizeof(WNDCLASSW));
2073     wndClass.style         = CS_GLOBALCLASS;
2074     wndClass.lpfnWndProc   = TRACKBAR_WindowProc;
2075     wndClass.cbClsExtra    = 0;
2076     wndClass.cbWndExtra    = sizeof(TRACKBAR_INFO *);
2077     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2078     wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
2079     wndClass.lpszClassName = TRACKBAR_CLASSW;
2080 
2081     RegisterClassW (&wndClass);
2082 }
2083 
2084 
2085 void TRACKBAR_Unregister (void)
2086 {
2087     UnregisterClassW (TRACKBAR_CLASSW, NULL);
2088 }
2089