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