Removed the A/W constants for builtin cursors, icons and resource
[wine] / dlls / comctl32 / trackbar.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * NOTE
23  * 
24  * This code was audited for completeness against the documented features
25  * of Comctl32.dll version 6.0 on Sep. 12, 2002, by Dimitrie O. Paun.
26  * 
27  * Unless otherwise noted, we believe this code to be complete, as per
28  * the specification mentioned above.
29  * If you discover missing features, or bugs, please note them below.
30  * 
31  */
32
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "windef.h"
39 #include "winbase.h"
40 #include "wingdi.h"
41 #include "winuser.h"
42 #include "winnls.h"
43 #include "commctrl.h"
44 #include "wine/debug.h"
45
46 #include "comctl32.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(trackbar);
49
50 typedef struct
51 {
52     HWND hwndSelf;
53     LONG lRangeMin;
54     LONG lRangeMax;
55     LONG lLineSize;
56     LONG lPageSize;
57     LONG lSelMin;
58     LONG lSelMax;
59     LONG lPos;
60     UINT uThumbLen;
61     UINT uNumTics;
62     UINT uTicFreq;
63     HWND hwndNotify;
64     HWND hwndToolTip;
65     HWND hwndBuddyLA;
66     HWND hwndBuddyRB;
67     INT  fLocation;
68     INT  flags;
69     BOOL bUnicode;
70     BOOL bFocussed;
71     RECT rcChannel;
72     RECT rcSelection;
73     RECT rcThumb;
74     LPLONG tics;
75 } TRACKBAR_INFO;
76
77 #define TB_REFRESH_TIMER        1
78 #define TB_REFRESH_DELAY        500
79
80 #define TOOLTIP_OFFSET          2     /* distance from ctrl edge to tooltip */
81
82 /* Used by TRACKBAR_Refresh to find out which parts of the control
83    need to be recalculated */
84
85 #define TB_THUMBPOSCHANGED      1
86 #define TB_THUMBSIZECHANGED     2
87 #define TB_THUMBCHANGED         (TB_THUMBPOSCHANGED | TB_THUMBSIZECHANGED)
88 #define TB_SELECTIONCHANGED     4
89 #define TB_DRAG_MODE            8     /* we're dragging the slider */
90 #define TB_AUTO_PAGE_LEFT       16
91 #define TB_AUTO_PAGE_RIGHT      32
92 #define TB_AUTO_PAGE            (TB_AUTO_PAGE_LEFT | TB_AUTO_PAGE_RIGHT)
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 BOOL TRACKBAR_SendNotify (TRACKBAR_INFO *infoPtr, UINT code);
101
102 static inline int 
103 notify_customdraw(NMCUSTOMDRAW *pnmcd, int stage)
104 {
105     pnmcd->dwDrawStage = stage;
106     return SendMessageW (GetParent(pnmcd->hdr.hwndFrom), WM_NOTIFY, 
107                          pnmcd->hdr.idFrom, (LPARAM)pnmcd);
108 }
109
110 static void TRACKBAR_RecalculateTics (TRACKBAR_INFO *infoPtr)
111 {
112     int i, tic, nrTics;
113
114     if (infoPtr->uTicFreq && infoPtr->lRangeMax >= infoPtr->lRangeMin)
115         nrTics=(infoPtr->lRangeMax - infoPtr->lRangeMin)/infoPtr->uTicFreq;
116     else {
117         nrTics = 0;
118         COMCTL32_Free (infoPtr->tics);
119         infoPtr->tics = NULL;
120         infoPtr->uNumTics = 0;
121         return;
122     }
123
124     if (nrTics != infoPtr->uNumTics) {
125         infoPtr->tics=COMCTL32_ReAlloc (infoPtr->tics,
126                                         (nrTics+1)*sizeof (DWORD));
127         if (!infoPtr->tics) {
128             infoPtr->uNumTics = 0;
129             TRACKBAR_SendNotify(infoPtr, NM_OUTOFMEMORY);
130             return;
131         }
132         infoPtr->uNumTics = nrTics;
133     }
134
135     tic = infoPtr->lRangeMin + infoPtr->uTicFreq;
136     for (i = 0; i < nrTics; i++, tic += infoPtr->uTicFreq)
137         infoPtr->tics[i] = tic;
138 }
139
140 /* converts from physical (mouse) position to logical position
141    (in range of trackbar) */
142
143 static inline LONG
144 TRACKBAR_ConvertPlaceToPosition (TRACKBAR_INFO *infoPtr, int place,
145                                  int vertical)
146 {
147     double range, width, pos, offsetthumb;
148
149     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
150     if (vertical) {
151         offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
152         width = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - (offsetthumb * 2) - 1;
153         pos = (range*(place - infoPtr->rcChannel.top - offsetthumb)) / width;
154     } else {
155         offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
156         width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - (offsetthumb * 2) - 1;
157         pos = (range*(place - infoPtr->rcChannel.left - offsetthumb)) / width;
158     }
159     pos += infoPtr->lRangeMin;
160     if (pos > infoPtr->lRangeMax)
161         pos = infoPtr->lRangeMax;
162     else if (pos < infoPtr->lRangeMin)
163         pos = infoPtr->lRangeMin;
164
165     TRACE("%.2f\n", pos);
166     return (LONG)(pos + 0.5);
167 }
168
169
170 /* return: 0> prev, 0 none, >0 next */
171 static LONG
172 TRACKBAR_GetAutoPageDirection (TRACKBAR_INFO *infoPtr, POINT clickPoint)
173 {
174     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
175     RECT pageRect;
176
177     if (dwStyle & TBS_VERT) {
178         pageRect.top = infoPtr->rcChannel.top;
179         pageRect.bottom = infoPtr->rcChannel.bottom;
180         pageRect.left = infoPtr->rcThumb.left;
181         pageRect.right = infoPtr->rcThumb.right;
182     } else {
183         pageRect.top = infoPtr->rcThumb.top;
184         pageRect.bottom = infoPtr->rcThumb.bottom;
185         pageRect.left = infoPtr->rcChannel.left;
186         pageRect.right = infoPtr->rcChannel.right;
187     }
188
189
190     if (PtInRect(&pageRect, clickPoint))
191     {
192         int clickPlace = (dwStyle & TBS_VERT) ? clickPoint.y : clickPoint.x;
193
194         LONG clickPos = TRACKBAR_ConvertPlaceToPosition(infoPtr, clickPlace,
195                                                         dwStyle & TBS_VERT);
196         return clickPos - infoPtr->lPos;
197     }
198
199     return 0;
200 }
201
202 static void inline
203 TRACKBAR_PageUp (TRACKBAR_INFO *infoPtr)
204 {
205     if (infoPtr->lPos == infoPtr->lRangeMax) return;
206
207     infoPtr->lPos += infoPtr->lPageSize;
208     if (infoPtr->lPos > infoPtr->lRangeMax)
209         infoPtr->lPos = infoPtr->lRangeMax;
210     TRACKBAR_SendNotify (infoPtr, TB_PAGEUP);
211 }
212
213
214 static void inline
215 TRACKBAR_PageDown (TRACKBAR_INFO *infoPtr)
216 {
217     if (infoPtr->lPos == infoPtr->lRangeMin) return;
218
219     infoPtr->lPos -= infoPtr->lPageSize;
220     if (infoPtr->lPos < infoPtr->lRangeMin)
221         infoPtr->lPos = infoPtr->lRangeMin;
222     TRACKBAR_SendNotify (infoPtr, TB_PAGEDOWN);
223 }
224
225 static void
226 TRACKBAR_CalcChannel (TRACKBAR_INFO *infoPtr)
227 {
228     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
229     INT cyChannel, offsetthumb, offsetedge;
230     RECT lpRect, *channel = & infoPtr->rcChannel;
231
232     GetClientRect (infoPtr->hwndSelf, &lpRect);
233
234     offsetthumb = infoPtr->uThumbLen / 4;
235     offsetedge  = offsetthumb + 3;
236     cyChannel   = (dwStyle & TBS_ENABLESELRANGE) ? offsetthumb*3 : 4;
237     if (dwStyle & TBS_VERT) {
238         channel->top    = lpRect.top + offsetedge;
239         channel->bottom = lpRect.bottom - offsetedge;
240         if (dwStyle & TBS_ENABLESELRANGE)
241             channel->left = lpRect.left + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
242         else
243             channel->left = lpRect.left + (infoPtr->uThumbLen / 2) - 1;
244         if (dwStyle & TBS_BOTH) {
245             if (dwStyle & TBS_NOTICKS)
246                 channel->left += 1;
247             else
248                 channel->left += 9;
249         }
250         else if (dwStyle & TBS_TOP) {
251             if (dwStyle & TBS_NOTICKS)
252                 channel->left += 2;
253             else
254                 channel->left += 10;
255         }
256         channel->right = channel->left + cyChannel;
257     } else {
258         channel->left = lpRect.left + offsetedge;
259         channel->right = lpRect.right - offsetedge;
260         if (dwStyle & TBS_ENABLESELRANGE)
261             channel->top = lpRect.top + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
262         else
263             channel->top = lpRect.top + (infoPtr->uThumbLen / 2) - 1;
264         if (dwStyle & TBS_BOTH) {
265             if (dwStyle & TBS_NOTICKS)
266                 channel->top += 1;
267             else
268                 channel->top += 9;
269         }
270         else if (dwStyle & TBS_TOP) {
271             if (dwStyle & TBS_NOTICKS)
272                 channel->top += 2;
273             else
274                 channel->top += 10;
275         }
276         channel->bottom   = channel->top + cyChannel;
277     }
278 }
279
280 static void
281 TRACKBAR_CalcThumb (TRACKBAR_INFO *infoPtr, LONG lPos, RECT *thumb)
282 {
283     int range, width, height, thumbwidth;
284     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
285     RECT lpRect;
286
287     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
288     thumbwidth = (infoPtr->uThumbLen / 2) | 1;
289
290     if (!range) range = 1;
291
292     GetClientRect(infoPtr->hwndSelf, &lpRect);
293     if (dwStyle & TBS_VERT)
294     {
295         height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - thumbwidth;
296
297         if ((dwStyle & (TBS_BOTH | TBS_LEFT)) && !(dwStyle & TBS_NOTICKS))
298             thumb->left = 10;
299         else
300             thumb->left = 2;
301         thumb->right = thumb->left + infoPtr->uThumbLen;
302         thumb->top = infoPtr->rcChannel.top +
303                      (height*(lPos - infoPtr->lRangeMin))/range;
304         thumb->bottom = thumb->top + thumbwidth;
305     }
306     else
307     {
308         width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - thumbwidth;
309
310         thumb->left = infoPtr->rcChannel.left +
311                       (width*(lPos - infoPtr->lRangeMin))/range;
312         thumb->right = thumb->left + thumbwidth;
313         if ((dwStyle & (TBS_BOTH | TBS_TOP)) && !(dwStyle & TBS_NOTICKS))
314             thumb->top = 10;
315         else
316             thumb->top = 2;
317         thumb->bottom = thumb->top + infoPtr->uThumbLen;
318     }
319 }
320
321 inline static void
322 TRACKBAR_UpdateThumb (TRACKBAR_INFO *infoPtr)
323 {
324     TRACKBAR_CalcThumb(infoPtr, infoPtr->lPos, &infoPtr->rcThumb);
325 }
326
327 static inline void
328 TRACKBAR_InvalidateAll(TRACKBAR_INFO * infoPtr)
329 {
330     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
331 }
332
333 static void
334 TRACKBAR_InvalidateThumb (TRACKBAR_INFO *infoPtr, LONG thumbPos)
335 {
336     RECT rcThumb;
337
338     TRACKBAR_CalcThumb(infoPtr, thumbPos, &rcThumb);
339     InflateRect(&rcThumb, 1, 1);
340     InvalidateRect(infoPtr->hwndSelf, &rcThumb, FALSE);
341 }
342
343 static inline void
344 TRACKBAR_InvalidateThumbMove (TRACKBAR_INFO *infoPtr, LONG oldPos, LONG newPos)
345 {
346     TRACKBAR_InvalidateThumb (infoPtr, oldPos);
347     if (newPos != oldPos)
348         TRACKBAR_InvalidateThumb (infoPtr, newPos);
349 }
350
351 static BOOL inline
352 TRACKBAR_HasSelection (TRACKBAR_INFO *infoPtr)
353 {
354     return infoPtr->lSelMin != infoPtr->lSelMax;
355 }
356
357 static void
358 TRACKBAR_CalcSelection (TRACKBAR_INFO *infoPtr)
359 {
360     RECT *selection = &infoPtr->rcSelection;
361     int range = infoPtr->lRangeMax - infoPtr->lRangeMin;
362     int offsetthumb, height, width;
363
364     if (range <= 0) {
365         SetRectEmpty (selection);
366     } else {
367         if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT) {
368             offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
369             height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - offsetthumb*2;
370             selection->top    = infoPtr->rcChannel.top + offsetthumb +
371                 (height*infoPtr->lSelMin)/range;
372             selection->bottom = infoPtr->rcChannel.top + offsetthumb +
373                 (height*infoPtr->lSelMax)/range;
374             selection->left   = infoPtr->rcChannel.left + 3;
375             selection->right  = infoPtr->rcChannel.right - 3;
376         } else {
377             offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
378             width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
379             selection->left   = infoPtr->rcChannel.left + offsetthumb +
380                 (width*infoPtr->lSelMin)/range;
381             selection->right  = infoPtr->rcChannel.left + offsetthumb +
382                 (width*infoPtr->lSelMax)/range;
383             selection->top    = infoPtr->rcChannel.top + 3;
384             selection->bottom = infoPtr->rcChannel.bottom - 3;
385         }
386     }
387
388     TRACE("selection[left=%ld, top=%ld, right=%ld, bottom=%ld]\n",
389            selection->left, selection->top, selection->right, selection->bottom);
390 }
391
392 static BOOL
393 TRACKBAR_AutoPage (TRACKBAR_INFO *infoPtr, POINT clickPoint)
394 {
395     LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
396     LONG prevPos = infoPtr->lPos;
397
398     TRACE("x=%ld, y=%ld, dir=%ld\n", clickPoint.x, clickPoint.y, dir);
399
400     if (dir > 0 && (infoPtr->flags & TB_AUTO_PAGE_RIGHT))
401         TRACKBAR_PageUp(infoPtr);
402     else if (dir < 0 && (infoPtr->flags & TB_AUTO_PAGE_LEFT))
403         TRACKBAR_PageDown(infoPtr);
404     else return FALSE;
405
406     infoPtr->flags |= TB_THUMBPOSCHANGED;
407     TRACKBAR_InvalidateThumbMove (infoPtr, prevPos, infoPtr->lPos);
408
409     return TRUE;
410 }
411
412 /* Trackbar drawing code. I like my spaghetti done milanese.  */
413
414 static void
415 TRACKBAR_DrawChannel (TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
416 {
417     RECT rcChannel = infoPtr->rcChannel;
418
419     DrawEdge (hdc, &rcChannel, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
420     if (dwStyle & TBS_ENABLESELRANGE) {          /* fill the channel */
421         FillRect (hdc, &rcChannel, GetStockObject(WHITE_BRUSH));
422         if (TRACKBAR_HasSelection(infoPtr))
423             FillRect (hdc, &infoPtr->rcSelection, GetSysColorBrush(COLOR_HIGHLIGHT));
424     }
425 }
426
427 static void
428 TRACKBAR_DrawOneTic (TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
429 {
430     int x, y, ox, oy, range, side, indent = 0, len = 3;
431     int offsetthumb;
432     RECT rcTics;
433
434     if (flags & TBS_VERT) {
435         offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
436         rcTics.left = infoPtr->rcThumb.left - 2;
437         rcTics.right = infoPtr->rcThumb.right + 2;
438         rcTics.top    = infoPtr->rcChannel.top + offsetthumb + 1;
439         rcTics.bottom = infoPtr->rcChannel.bottom - offsetthumb;
440     } else {
441         offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
442         rcTics.left   = infoPtr->rcChannel.left + offsetthumb + 1;
443         rcTics.right  = infoPtr->rcChannel.right - offsetthumb;
444         rcTics.top = infoPtr->rcThumb.top - 2;
445         rcTics.bottom = infoPtr->rcThumb.bottom + 2;
446     }
447
448     if (flags & (TBS_TOP | TBS_LEFT)) {
449         x = rcTics.left;
450         y = rcTics.top;
451         side = -1;
452     } else {
453         x = rcTics.right;
454         y = rcTics.bottom;
455         side = 1;
456     }
457
458     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
459     if (range <= 0)
460       range = 1; /* to avoid division by zero */
461
462     if (flags & TIC_SELECTIONMARK) {
463         indent = (flags & TIC_SELECTIONMARKMIN) ? -1 : 1;
464     } else if (flags & TIC_EDGE) {
465         len++;
466     }
467
468     if (flags & TBS_VERT) {
469         int height = rcTics.bottom - rcTics.top;
470         y = rcTics.top + (height*(ticPos - infoPtr->lRangeMin))/range;
471     } else {
472         int width = rcTics.right - rcTics.left;
473         x = rcTics.left + (width*(ticPos - infoPtr->lRangeMin))/range;
474     }
475
476     ox = x;
477     oy = y;
478     MoveToEx(hdc, x, y, 0);
479     if (flags & TBS_VERT) x += len * side;
480     else y += len * side;
481     LineTo(hdc, x, y);
482             
483     if (flags & TIC_SELECTIONMARK) {
484         if (flags & TBS_VERT) {
485             x -= side;
486         } else {
487             y -= side;
488         }
489         MoveToEx(hdc, x, y, 0);
490         if (flags & TBS_VERT) {
491             y += 2 * indent;
492         } else {
493             x += 2 * indent;
494         }
495         
496         LineTo(hdc, x, y);
497         LineTo(hdc, ox, oy);
498     }
499 }
500
501
502 static inline void
503 TRACKBAR_DrawTic (TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
504 {
505     if ((flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
506         TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags | TBS_LEFT);
507
508     if (!(flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
509         TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags & ~TBS_LEFT);
510 }
511
512 static void
513 TRACKBAR_DrawTics (TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
514 {
515     int i, ticFlags = dwStyle & 0x0f;
516     LOGPEN ticPen = { PS_SOLID, {1, 0}, GetSysColor (COLOR_3DDKSHADOW) };
517     HPEN hOldPen, hTicPen;
518     
519     /* create the pen to draw the tics with */   
520     hTicPen = CreatePenIndirect(&ticPen);
521     hOldPen = hTicPen ? SelectObject(hdc, hTicPen) : 0;
522
523     /* actually draw the tics */
524     for (i=0; i<infoPtr->uNumTics; i++)
525         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->tics[i], ticFlags);
526
527     TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMin, ticFlags | TIC_EDGE);
528     TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMax, ticFlags | TIC_EDGE);
529
530     if ((dwStyle & TBS_ENABLESELRANGE) && TRACKBAR_HasSelection(infoPtr)) {
531         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMin,
532                           ticFlags | TIC_SELECTIONMARKMIN);
533         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMax,
534                           ticFlags | TIC_SELECTIONMARKMAX);
535     }
536     
537     /* clean up the pen, if we created one */
538     if (hTicPen) {
539         SelectObject(hdc, hOldPen);
540         DeleteObject(hTicPen);
541     }
542 }
543
544 static void
545 TRACKBAR_DrawThumb(TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
546 {
547     HBRUSH oldbr;
548     HPEN  oldpen;
549     RECT thumb = infoPtr->rcThumb;
550     int BlackUntil = 3;
551     int PointCount = 6;
552     POINT points[6];
553     int fillClr;
554     int PointDepth;
555
556     fillClr = infoPtr->flags & TB_DRAG_MODE ? COLOR_BTNHILIGHT : COLOR_BTNFACE;
557     oldbr = SelectObject (hdc, GetSysColorBrush(fillClr));
558     SetPolyFillMode (hdc, WINDING);
559
560     if (dwStyle & TBS_BOTH)
561     {
562        points[0].x=thumb.right;
563        points[0].y=thumb.top;
564        points[1].x=thumb.right;
565        points[1].y=thumb.bottom;
566        points[2].x=thumb.left;
567        points[2].y=thumb.bottom;
568        points[3].x=thumb.left;
569        points[3].y=thumb.top;
570        points[4].x=points[0].x;
571        points[4].y=points[0].y;
572        PointCount = 5;
573        BlackUntil = 3;
574     }
575     else
576     {
577         if (dwStyle & TBS_VERT)
578         {
579           PointDepth = (thumb.bottom - thumb.top) / 2;
580           if (dwStyle & TBS_LEFT)
581           {
582             points[0].x=thumb.right;
583             points[0].y=thumb.top;
584             points[1].x=thumb.right;
585             points[1].y=thumb.bottom;
586             points[2].x=thumb.left + PointDepth;
587             points[2].y=thumb.bottom;
588             points[3].x=thumb.left;
589             points[3].y=(thumb.bottom - thumb.top) / 2 + thumb.top + 1;
590             points[4].x=thumb.left + PointDepth;
591             points[4].y=thumb.top;
592             points[5].x=points[0].x;
593             points[5].y=points[0].y;
594             BlackUntil = 4;
595           }
596           else
597           {
598             points[0].x=thumb.right;
599             points[0].y=(thumb.bottom - thumb.top) / 2 + thumb.top + 1;
600             points[1].x=thumb.right - PointDepth;
601             points[1].y=thumb.bottom;
602             points[2].x=thumb.left;
603             points[2].y=thumb.bottom;
604             points[3].x=thumb.left;
605             points[3].y=thumb.top;
606             points[4].x=thumb.right - PointDepth;
607             points[4].y=thumb.top;
608             points[5].x=points[0].x;
609             points[5].y=points[0].y;
610           }
611         }
612         else
613         {
614           PointDepth = (thumb.right - thumb.left) / 2;
615           if (dwStyle & TBS_TOP)
616           {
617             points[0].x=(thumb.right - thumb.left) / 2 + thumb.left + 1;
618             points[0].y=thumb.top;
619             points[1].x=thumb.right;
620             points[1].y=thumb.top + PointDepth;
621             points[2].x=thumb.right;
622             points[2].y=thumb.bottom;
623             points[3].x=thumb.left;
624             points[3].y=thumb.bottom;
625             points[4].x=thumb.left;
626             points[4].y=thumb.top + PointDepth;
627             points[5].x=points[0].x;
628             points[5].y=points[0].y;
629             BlackUntil = 4;
630           }
631           else
632           {
633             points[0].x=thumb.right;
634             points[0].y=thumb.top;
635             points[1].x=thumb.right;
636             points[1].y=thumb.bottom - PointDepth;
637             points[2].x=(thumb.right - thumb.left) / 2 + thumb.left + 1;
638             points[2].y=thumb.bottom;
639             points[3].x=thumb.left;
640             points[3].y=thumb.bottom - PointDepth;
641             points[4].x=thumb.left;
642             points[4].y=thumb.top;
643             points[5].x=points[0].x;
644             points[5].y=points[0].y;
645           }
646         }
647
648     }
649
650     /* Draw the thumb now */
651     Polygon (hdc, points, PointCount);
652     oldpen = SelectObject(hdc, GetStockObject(BLACK_PEN));
653     Polyline(hdc,points, BlackUntil);
654     SelectObject(hdc, GetStockObject(WHITE_PEN));
655     Polyline(hdc, &points[BlackUntil-1], PointCount+1-BlackUntil);
656     SelectObject(hdc, oldpen);
657     SelectObject(hdc, oldbr);
658 }
659
660
661 static void inline
662 TRACKBAR_ActivateToolTip (TRACKBAR_INFO *infoPtr, BOOL fShow)
663 {
664     TTTOOLINFOW ti;
665
666     if (!infoPtr->hwndToolTip) return;
667
668     ZeroMemory(&ti, sizeof(ti));
669     ti.cbSize = sizeof(ti);
670     ti.hwnd   = infoPtr->hwndSelf;
671
672     SendMessageW (infoPtr->hwndToolTip, TTM_TRACKACTIVATE, fShow, (LPARAM)&ti);
673 }
674
675
676 static void
677 TRACKBAR_UpdateToolTip (TRACKBAR_INFO *infoPtr)
678 {
679     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
680     WCHAR buf[80], fmt[] = { '%', 'l', 'd', 0 };
681     TTTOOLINFOW ti;
682     POINT pt;
683     RECT rcClient;
684     LRESULT size;
685
686     if (!infoPtr->hwndToolTip) return;
687
688     ZeroMemory(&ti, sizeof(ti));
689     ti.cbSize = sizeof(ti);
690     ti.hwnd   = infoPtr->hwndSelf;
691     ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
692
693     wsprintfW (buf, fmt, infoPtr->lPos);
694     ti.lpszText = buf;
695     SendMessageW (infoPtr->hwndToolTip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti);
696
697     GetClientRect (infoPtr->hwndSelf, &rcClient);
698     size = SendMessageW (infoPtr->hwndToolTip, TTM_GETBUBBLESIZE, 0, (LPARAM)&ti);
699     if (dwStyle & TBS_VERT) {
700         if (infoPtr->fLocation == TBTS_LEFT)
701             pt.x = 0 - LOWORD(size) - TOOLTIP_OFFSET;
702         else
703             pt.x = rcClient.right + TOOLTIP_OFFSET;
704         pt.y = (infoPtr->rcThumb.top + infoPtr->rcThumb.bottom - HIWORD(size))/2;
705     } else {
706         if (infoPtr->fLocation == TBTS_TOP)
707             pt.y = 0 - HIWORD(size) - TOOLTIP_OFFSET;
708         else
709             pt.y = rcClient.bottom + TOOLTIP_OFFSET;
710         pt.x = (infoPtr->rcThumb.left + infoPtr->rcThumb.right - LOWORD(size))/2;
711     }
712     ClientToScreen(infoPtr->hwndSelf, &pt);
713
714     SendMessageW (infoPtr->hwndToolTip, TTM_TRACKPOSITION,
715                   0, (LPARAM)MAKELPARAM(pt.x, pt.y));
716 }
717
718
719 static void
720 TRACKBAR_Refresh (TRACKBAR_INFO *infoPtr, HDC hdcDst)
721 {
722     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
723     RECT rcClient;
724     HDC hdc;
725     HBITMAP hOldBmp = 0, hOffScreenBmp = 0;
726     NMCUSTOMDRAW nmcd;
727     int gcdrf, icdrf;
728
729     if (infoPtr->flags & TB_THUMBCHANGED) {
730         TRACKBAR_UpdateThumb (infoPtr);
731         if (infoPtr->flags & TB_THUMBSIZECHANGED)
732             TRACKBAR_CalcChannel (infoPtr);
733     }
734     if (infoPtr->flags & TB_SELECTIONCHANGED)
735         TRACKBAR_CalcSelection (infoPtr);
736
737     if (infoPtr->flags & TB_DRAG_MODE)
738         TRACKBAR_UpdateToolTip (infoPtr);
739
740     infoPtr->flags &= ~ (TB_THUMBCHANGED | TB_SELECTIONCHANGED);
741
742     GetClientRect (infoPtr->hwndSelf, &rcClient);
743     
744     /* try to render offscreen, if we fail, carrry onscreen */
745     hdc = CreateCompatibleDC(hdcDst);
746     if (hdc) {
747         hOffScreenBmp = CreateCompatibleBitmap(hdcDst, rcClient.right, rcClient.bottom);
748         if (hOffScreenBmp) {
749             hOldBmp = SelectObject(hdc, hOffScreenBmp);
750         } else {
751             DeleteObject(hdc);
752             hdc = hdcDst;
753         }
754     } else {
755         hdc = hdcDst;
756     }
757
758     ZeroMemory(&nmcd, sizeof(nmcd));
759     nmcd.hdr.hwndFrom = infoPtr->hwndSelf;
760     nmcd.hdr.idFrom = GetWindowLongW (infoPtr->hwndSelf, GWL_ID);
761     nmcd.hdr.code = NM_CUSTOMDRAW;
762     nmcd.hdc = hdc;
763
764     /* start the paint cycle */
765     nmcd.rc = rcClient;
766     gcdrf = notify_customdraw(&nmcd, CDDS_PREPAINT);
767     if (gcdrf & CDRF_SKIPDEFAULT) goto cleanup;
768     
769     /* Erase backbround */
770     if (gcdrf == CDRF_DODEFAULT ||
771         notify_customdraw(&nmcd, CDDS_PREERASE) != CDRF_SKIPDEFAULT) {
772         FillRect (hdc, &rcClient, GetSysColorBrush(COLOR_BTNFACE));
773         if (gcdrf != CDRF_DODEFAULT)
774             notify_customdraw(&nmcd, CDDS_POSTERASE);
775     }
776     
777     /* draw channel */
778     if (gcdrf & CDRF_NOTIFYITEMDRAW) {
779         nmcd.dwItemSpec = TBCD_CHANNEL;
780         nmcd.uItemState = CDIS_DEFAULT;
781         nmcd.rc = infoPtr->rcChannel;
782         icdrf = notify_customdraw(&nmcd, CDDS_ITEMPREPAINT);
783     } else icdrf = CDRF_DODEFAULT;
784     if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
785         TRACKBAR_DrawChannel (infoPtr, hdc, dwStyle);
786         if (icdrf & CDRF_NOTIFYPOSTPAINT)
787             notify_customdraw(&nmcd, CDDS_ITEMPOSTPAINT);
788     }
789
790
791     /* draw tics */
792     if (!(dwStyle & TBS_NOTICKS)) {
793         if (gcdrf & CDRF_NOTIFYITEMDRAW) {
794             nmcd.dwItemSpec = TBCD_TICS;
795             nmcd.uItemState = CDIS_DEFAULT;
796             nmcd.rc = rcClient;
797             icdrf = notify_customdraw(&nmcd, CDDS_ITEMPREPAINT);
798         } else icdrf = CDRF_DODEFAULT;
799         if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
800             TRACKBAR_DrawTics (infoPtr, hdc, dwStyle);
801             if (icdrf & CDRF_NOTIFYPOSTPAINT)
802                 notify_customdraw(&nmcd, CDDS_ITEMPOSTPAINT);
803         }
804     }
805     
806     /* draw thumb */
807     if (!(dwStyle & TBS_NOTHUMB)) {
808         if (gcdrf & CDRF_NOTIFYITEMDRAW) {
809             nmcd.dwItemSpec = TBCD_THUMB;
810             nmcd.uItemState = infoPtr->flags & TB_DRAG_MODE ? CDIS_HOT : CDIS_DEFAULT;
811             nmcd.rc = infoPtr->rcThumb;
812             icdrf = notify_customdraw(&nmcd, CDDS_ITEMPREPAINT);
813         } else icdrf = CDRF_DODEFAULT;
814         if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
815             TRACKBAR_DrawThumb(infoPtr, hdc, dwStyle);
816             if (icdrf & CDRF_NOTIFYPOSTPAINT)
817                 notify_customdraw(&nmcd, CDDS_ITEMPOSTPAINT);
818         }
819     }
820
821     /* draw focus rectangle */
822     if (infoPtr->bFocussed) {
823         DrawFocusRect(hdc, &rcClient);
824     }
825
826     /* finish up the painting */
827     if (gcdrf & CDRF_NOTIFYPOSTPAINT)
828         notify_customdraw(&nmcd, CDDS_POSTPAINT);
829     
830 cleanup:
831     /* cleanup, if we rendered offscreen */
832     if (hdc != hdcDst) {
833         BitBlt(hdcDst, 0, 0, rcClient.right, rcClient.bottom, hdc, 0, 0, SRCCOPY);
834         SelectObject(hdc, hOldBmp);
835         DeleteObject(hOffScreenBmp);
836         DeleteObject(hdc);
837     }
838 }
839
840
841 static void
842 TRACKBAR_AlignBuddies (TRACKBAR_INFO *infoPtr)
843 {
844     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
845     HWND hwndParent = GetParent (infoPtr->hwndSelf);
846     RECT rcSelf, rcBuddy;
847     INT x, y;
848
849     GetWindowRect (infoPtr->hwndSelf, &rcSelf);
850     MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcSelf, 2);
851
852     /* align buddy left or above */
853     if (infoPtr->hwndBuddyLA) {
854         GetWindowRect (infoPtr->hwndBuddyLA, &rcBuddy);
855         MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcBuddy, 2);
856
857         if (dwStyle & TBS_VERT) {
858             x = (infoPtr->rcChannel.right + infoPtr->rcChannel.left) / 2 -
859                 (rcBuddy.right - rcBuddy.left) / 2 + rcSelf.left;
860             y = rcSelf.top - (rcBuddy.bottom - rcBuddy.top);
861         }
862         else {
863             x = rcSelf.left - (rcBuddy.right - rcBuddy.left);
864             y = (infoPtr->rcChannel.bottom + infoPtr->rcChannel.top) / 2 -
865                 (rcBuddy.bottom - rcBuddy.top) / 2 + rcSelf.top;
866         }
867
868         SetWindowPos (infoPtr->hwndBuddyLA, 0, x, y, 0, 0,
869                       SWP_NOZORDER | SWP_NOSIZE);
870     }
871
872
873     /* align buddy right or below */
874     if (infoPtr->hwndBuddyRB) {
875         GetWindowRect (infoPtr->hwndBuddyRB, &rcBuddy);
876         MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcBuddy, 2);
877
878         if (dwStyle & TBS_VERT) {
879             x = (infoPtr->rcChannel.right + infoPtr->rcChannel.left) / 2 -
880                 (rcBuddy.right - rcBuddy.left) / 2 + rcSelf.left;
881             y = rcSelf.bottom;
882         }
883         else {
884             x = rcSelf.right;
885             y = (infoPtr->rcChannel.bottom + infoPtr->rcChannel.top) / 2 -
886                 (rcBuddy.bottom - rcBuddy.top) / 2 + rcSelf.top;
887         }
888         SetWindowPos (infoPtr->hwndBuddyRB, 0, x, y, 0, 0,
889                       SWP_NOZORDER | SWP_NOSIZE);
890     }
891 }
892
893
894 static LRESULT
895 TRACKBAR_ClearSel (TRACKBAR_INFO *infoPtr, BOOL fRedraw)
896 {
897     infoPtr->lSelMin = 0;
898     infoPtr->lSelMax = 0;
899     infoPtr->flags |= TB_SELECTIONCHANGED;
900
901     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
902
903     return 0;
904 }
905
906
907 static LRESULT
908 TRACKBAR_ClearTics (TRACKBAR_INFO *infoPtr, BOOL fRedraw)
909 {
910     if (infoPtr->tics) {
911         COMCTL32_Free (infoPtr->tics);
912         infoPtr->tics = NULL;
913         infoPtr->uNumTics = 0;
914     }
915
916     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
917
918     return 0;
919 }
920
921
922 static LRESULT inline
923 TRACKBAR_GetChannelRect (TRACKBAR_INFO *infoPtr, LPRECT lprc)
924 {
925     if (lprc == NULL) return 0;
926
927     lprc->left   = infoPtr->rcChannel.left;
928     lprc->right  = infoPtr->rcChannel.right;
929     lprc->bottom = infoPtr->rcChannel.bottom;
930     lprc->top    = infoPtr->rcChannel.top;
931
932     return 0;
933 }
934
935
936 static LONG inline
937 TRACKBAR_GetNumTics (TRACKBAR_INFO *infoPtr)
938 {
939     if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_NOTICKS)
940         return 0;
941
942     return infoPtr->uNumTics + 2;
943 }
944
945
946 static int comp_tics(const void *ap, const void *bp)
947 {
948     DWORD a = *((DWORD *)ap);
949     DWORD b = *((DWORD *)bp);
950
951     TRACE("(a=%ld, b=%ld)\n", a, b);
952     if (a < b) return -1;
953     if (a > b) return 1;
954     return 0;
955 }
956
957
958 static LONG inline
959 TRACKBAR_GetTic (TRACKBAR_INFO *infoPtr, INT iTic)
960 {
961     if ((iTic < 0) || (iTic >= infoPtr->uNumTics) || !infoPtr->tics)
962         return -1;
963
964     qsort(infoPtr->tics, infoPtr->uNumTics, sizeof(DWORD), comp_tics);
965     return infoPtr->tics[iTic];
966 }
967
968
969 static LONG inline
970 TRACKBAR_GetTicPos (TRACKBAR_INFO *infoPtr, INT iTic)
971 {
972     LONG range, width, pos, tic;
973     int offsetthumb;
974
975     if ((iTic < 0) || (iTic >= infoPtr->uNumTics) || !infoPtr->tics)
976         return -1;
977
978     tic   = TRACKBAR_GetTic (infoPtr, iTic);
979     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
980     if (range <= 0) range = 1;
981     offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
982     width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
983     pos   = infoPtr->rcChannel.left + offsetthumb + (width * tic) / range;
984
985     return pos;
986 }
987
988
989 static HWND
990 TRACKBAR_SetBuddy (TRACKBAR_INFO *infoPtr, BOOL fLocation, HWND hwndBuddy)
991 {
992     HWND hwndTemp;
993
994     if (fLocation) {
995         /* buddy is left or above */
996         hwndTemp = infoPtr->hwndBuddyLA;
997         infoPtr->hwndBuddyLA = hwndBuddy;
998     }
999     else {
1000         /* buddy is right or below */
1001         hwndTemp = infoPtr->hwndBuddyRB;
1002         infoPtr->hwndBuddyRB = hwndBuddy;
1003     }
1004
1005     TRACKBAR_AlignBuddies (infoPtr);
1006
1007     return hwndTemp;
1008 }
1009
1010
1011 static LONG inline
1012 TRACKBAR_SetLineSize (TRACKBAR_INFO *infoPtr, LONG lLineSize)
1013 {
1014     LONG lTemp = infoPtr->lLineSize;
1015
1016     infoPtr->lLineSize = lLineSize;
1017
1018     return lTemp;
1019 }
1020
1021
1022 static LONG inline
1023 TRACKBAR_SetPageSize (TRACKBAR_INFO *infoPtr, LONG lPageSize)
1024 {
1025     LONG lTemp = infoPtr->lPageSize;
1026
1027     infoPtr->lPageSize = lPageSize;
1028
1029     return lTemp;
1030 }
1031
1032
1033 static LRESULT inline
1034 TRACKBAR_SetPos (TRACKBAR_INFO *infoPtr, BOOL fPosition, LONG lPosition)
1035 {
1036     LONG oldPos = infoPtr->lPos;
1037     infoPtr->lPos = lPosition;
1038
1039     if (infoPtr->lPos < infoPtr->lRangeMin)
1040         infoPtr->lPos = infoPtr->lRangeMin;
1041
1042     if (infoPtr->lPos > infoPtr->lRangeMax)
1043         infoPtr->lPos = infoPtr->lRangeMax;
1044     infoPtr->flags |= TB_THUMBPOSCHANGED;
1045
1046     if (fPosition) TRACKBAR_InvalidateThumbMove(infoPtr, oldPos, lPosition);
1047
1048     return 0;
1049 }
1050
1051
1052 static LRESULT inline
1053 TRACKBAR_SetRange (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lRange)
1054 {
1055     infoPtr->lRangeMin = (SHORT)LOWORD(lRange);
1056     infoPtr->lRangeMax = (SHORT)HIWORD(lRange);
1057
1058     if (infoPtr->lPos < infoPtr->lRangeMin) {
1059         infoPtr->lPos = infoPtr->lRangeMin;
1060         infoPtr->flags |= TB_THUMBPOSCHANGED;
1061     }
1062
1063     if (infoPtr->lPos > infoPtr->lRangeMax) {
1064         infoPtr->lPos = infoPtr->lRangeMax;
1065         infoPtr->flags |= TB_THUMBPOSCHANGED;
1066     }
1067
1068     infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
1069     if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
1070
1071     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1072
1073     return 0;
1074 }
1075
1076
1077 static LRESULT inline
1078 TRACKBAR_SetRangeMax (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lMax)
1079 {
1080     infoPtr->lRangeMax = lMax;
1081     if (infoPtr->lPos > infoPtr->lRangeMax) {
1082         infoPtr->lPos = infoPtr->lRangeMax;
1083         infoPtr->flags |= TB_THUMBPOSCHANGED;
1084     }
1085
1086     infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
1087     if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
1088
1089     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1090
1091     return 0;
1092 }
1093
1094
1095 static LRESULT inline
1096 TRACKBAR_SetRangeMin (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lMin)
1097 {
1098     infoPtr->lRangeMin = lMin;
1099     if (infoPtr->lPos < infoPtr->lRangeMin) {
1100         infoPtr->lPos = infoPtr->lRangeMin;
1101         infoPtr->flags |= TB_THUMBPOSCHANGED;
1102     }
1103
1104     infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
1105     if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
1106
1107     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1108
1109     return 0;
1110 }
1111
1112
1113 static LRESULT inline
1114 TRACKBAR_SetSel (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lSel)
1115 {
1116     if (!GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_ENABLESELRANGE)
1117         return 0;
1118
1119     infoPtr->lSelMin = (SHORT)LOWORD(lSel);
1120     infoPtr->lSelMax = (SHORT)HIWORD(lSel);
1121     infoPtr->flags |= TB_SELECTIONCHANGED;
1122
1123     if (infoPtr->lSelMin < infoPtr->lRangeMin)
1124         infoPtr->lSelMin = infoPtr->lRangeMin;
1125     if (infoPtr->lSelMax > infoPtr->lRangeMax)
1126         infoPtr->lSelMax = infoPtr->lRangeMax;
1127
1128     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1129
1130     return 0;
1131 }
1132
1133
1134 static LRESULT inline
1135 TRACKBAR_SetSelEnd (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lEnd)
1136 {
1137     if (!GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_ENABLESELRANGE)
1138         return 0;
1139
1140     infoPtr->lSelMax = lEnd;
1141     infoPtr->flags |= TB_SELECTIONCHANGED;
1142
1143     if (infoPtr->lSelMax > infoPtr->lRangeMax)
1144         infoPtr->lSelMax = infoPtr->lRangeMax;
1145
1146     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1147
1148     return 0;
1149 }
1150
1151
1152 static LRESULT inline
1153 TRACKBAR_SetSelStart (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lStart)
1154 {
1155     if (!GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_ENABLESELRANGE)
1156         return 0;
1157
1158     infoPtr->lSelMin = lStart;
1159     infoPtr->flags  |=TB_SELECTIONCHANGED;
1160
1161     if (infoPtr->lSelMin < infoPtr->lRangeMin)
1162         infoPtr->lSelMin = infoPtr->lRangeMin;
1163
1164     if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1165
1166     return 0;
1167 }
1168
1169
1170 static LRESULT inline
1171 TRACKBAR_SetThumbLength (TRACKBAR_INFO *infoPtr, UINT iLength)
1172 {
1173     if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_FIXEDLENGTH) {
1174         infoPtr->uThumbLen = iLength;
1175         infoPtr->flags |= TB_THUMBSIZECHANGED;
1176         InvalidateRect (infoPtr->hwndSelf, &infoPtr->rcThumb, FALSE);
1177     }
1178
1179     return 0;
1180 }
1181
1182
1183 static LRESULT inline
1184 TRACKBAR_SetTic (TRACKBAR_INFO *infoPtr, LONG lPos)
1185 {
1186     if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_AUTOTICKS)
1187         return FALSE;
1188
1189     if ((lPos < infoPtr->lRangeMin) || (lPos> infoPtr->lRangeMax))
1190         return FALSE;
1191
1192     TRACE("lPos=%ld\n", lPos);
1193
1194     infoPtr->uNumTics++;
1195     infoPtr->tics=COMCTL32_ReAlloc( infoPtr->tics,
1196                                     (infoPtr->uNumTics)*sizeof (DWORD));
1197     if (!infoPtr->tics) {
1198         infoPtr->uNumTics = 0;
1199         TRACKBAR_SendNotify(infoPtr, NM_OUTOFMEMORY);
1200         return FALSE;
1201     }
1202     infoPtr->tics[infoPtr->uNumTics-1] = lPos;
1203
1204     TRACKBAR_InvalidateAll(infoPtr);
1205
1206     return TRUE;
1207 }
1208
1209
1210 static LRESULT inline
1211 TRACKBAR_SetTicFreq (TRACKBAR_INFO *infoPtr, WORD wFreq)
1212 {
1213     if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_AUTOTICKS) {
1214         infoPtr->uTicFreq = wFreq;
1215         TRACKBAR_RecalculateTics (infoPtr);
1216         TRACKBAR_InvalidateAll(infoPtr);
1217     }
1218
1219     return 0;
1220 }
1221
1222
1223 static INT inline
1224 TRACKBAR_SetTipSide (TRACKBAR_INFO *infoPtr, INT fLocation)
1225 {
1226     INT fTemp = infoPtr->fLocation;
1227
1228     infoPtr->fLocation = fLocation;
1229
1230     return fTemp;
1231 }
1232
1233
1234 static LRESULT inline
1235 TRACKBAR_SetToolTips (TRACKBAR_INFO *infoPtr, HWND hwndTT)
1236 {
1237     infoPtr->hwndToolTip = hwndTT;
1238
1239     return 0;
1240 }
1241
1242
1243 static BOOL inline
1244 TRACKBAR_SetUnicodeFormat (TRACKBAR_INFO *infoPtr, BOOL fUnicode)
1245 {
1246     BOOL bTemp = infoPtr->bUnicode;
1247
1248     infoPtr->bUnicode = fUnicode;
1249
1250     return bTemp;
1251 }
1252
1253
1254 static LRESULT
1255 TRACKBAR_InitializeThumb (TRACKBAR_INFO *infoPtr)
1256 {
1257     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
1258     RECT rect;
1259     int clientWidth, clientMetric;
1260
1261     /* initial thumb length */
1262     clientMetric = (dwStyle & TBS_ENABLESELRANGE) ? 23 : 21;
1263     GetClientRect(infoPtr->hwndSelf,&rect);
1264     if (dwStyle & TBS_VERT) {
1265         clientWidth = rect.right - rect.left;
1266     } else {
1267         clientWidth = rect.bottom - rect.top;
1268     }
1269     if (clientWidth >= clientMetric)
1270         infoPtr->uThumbLen = clientMetric;
1271     else
1272         infoPtr->uThumbLen = clientWidth > 9 ? clientWidth - 6 : 4;
1273
1274     TRACKBAR_CalcChannel (infoPtr);
1275     TRACKBAR_UpdateThumb (infoPtr);
1276     infoPtr->flags &= ~TB_SELECTIONCHANGED;
1277
1278     return 0;
1279 }
1280
1281
1282 static LRESULT
1283 TRACKBAR_Create (HWND hwnd, LPCREATESTRUCTW lpcs)
1284 {
1285     TRACKBAR_INFO *infoPtr;
1286     DWORD oldStyle, newStyle;
1287
1288     infoPtr = (TRACKBAR_INFO *)COMCTL32_Alloc (sizeof(TRACKBAR_INFO));
1289     if (!infoPtr) return -1;
1290     SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
1291
1292     /* set default values */
1293     infoPtr->hwndSelf  = hwnd;
1294     infoPtr->lRangeMin = 0;
1295     infoPtr->lRangeMax = 100;
1296     infoPtr->lLineSize = 1;
1297     infoPtr->lPageSize = 20;
1298     infoPtr->lSelMin   = 0;
1299     infoPtr->lSelMax   = 0;
1300     infoPtr->lPos      = 0;
1301     infoPtr->fLocation = -1;
1302     infoPtr->uNumTics  = 0;    /* start and end tic are not included in count*/
1303     infoPtr->uTicFreq  = 1;
1304     infoPtr->tics      = NULL;
1305     infoPtr->hwndNotify= GetParent (hwnd);
1306
1307     TRACKBAR_InitializeThumb (infoPtr);
1308
1309     oldStyle = newStyle = GetWindowLongW (hwnd, GWL_STYLE);
1310     if (oldStyle & TBS_VERT) {
1311         if (! (oldStyle & (TBS_LEFT | TBS_RIGHT | TBS_BOTH)) )
1312             newStyle |= TBS_RIGHT;
1313     } else {
1314         if (! (oldStyle & (TBS_TOP | TBS_BOTTOM | TBS_BOTH)) )
1315             newStyle |= TBS_BOTTOM;
1316     }
1317     if (newStyle != oldStyle)
1318         SetWindowLongW (hwnd, GWL_STYLE, newStyle);
1319
1320     /* Create tooltip control */
1321     if (newStyle & TBS_TOOLTIPS) {
1322
1323         infoPtr->hwndToolTip =
1324             CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, 0,
1325                              CW_USEDEFAULT, CW_USEDEFAULT,
1326                              CW_USEDEFAULT, CW_USEDEFAULT,
1327                              hwnd, 0, 0, 0);
1328
1329         if (infoPtr->hwndToolTip) {
1330             TTTOOLINFOW ti;         
1331             ZeroMemory (&ti, sizeof(ti));
1332             ti.cbSize   = sizeof(ti);
1333             ti.uFlags   = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
1334             ti.hwnd     = hwnd;
1335
1336             SendMessageW (infoPtr->hwndToolTip, TTM_ADDTOOLW, 0, (LPARAM)&ti);
1337          }
1338     }
1339
1340     return 0;
1341 }
1342
1343
1344 static LRESULT
1345 TRACKBAR_Destroy (TRACKBAR_INFO *infoPtr)
1346 {
1347     /* delete tooltip control */
1348     if (infoPtr->hwndToolTip)
1349         DestroyWindow (infoPtr->hwndToolTip);
1350
1351     COMCTL32_Free (infoPtr);
1352     SetWindowLongW (infoPtr->hwndSelf, 0, 0);
1353     return 0;
1354 }
1355
1356
1357 static LRESULT
1358 TRACKBAR_KillFocus (TRACKBAR_INFO *infoPtr, HWND hwndGetFocus)
1359 {
1360     TRACE("\n");
1361     infoPtr->bFocussed = FALSE;
1362     TRACKBAR_InvalidateAll(infoPtr);
1363
1364     return 0;
1365 }
1366
1367 static LRESULT
1368 TRACKBAR_LButtonDown (TRACKBAR_INFO *infoPtr, DWORD fwKeys, POINTS pts)
1369 {
1370     POINT clickPoint = { pts.x, pts.y };
1371
1372     SetFocus(infoPtr->hwndSelf);
1373
1374     if (PtInRect(&infoPtr->rcThumb, clickPoint)) {
1375         infoPtr->flags |= TB_DRAG_MODE;
1376         SetCapture (infoPtr->hwndSelf);
1377         TRACKBAR_UpdateToolTip (infoPtr);
1378         TRACKBAR_ActivateToolTip (infoPtr, TRUE);
1379         TRACKBAR_InvalidateThumb(infoPtr, infoPtr->lPos);
1380     } else {
1381         LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
1382         if (dir == 0) return 0;
1383         infoPtr->flags |= (dir < 0) ? TB_AUTO_PAGE_LEFT : TB_AUTO_PAGE_RIGHT;
1384         TRACKBAR_AutoPage (infoPtr, clickPoint);
1385         SetCapture (infoPtr->hwndSelf);
1386         SetTimer(infoPtr->hwndSelf, TB_REFRESH_TIMER, TB_REFRESH_DELAY, 0);
1387     }
1388
1389     return 0;
1390 }
1391
1392
1393 static LRESULT
1394 TRACKBAR_LButtonUp (TRACKBAR_INFO *infoPtr, DWORD fwKeys, POINTS pts)
1395 {
1396     if (infoPtr->flags & TB_DRAG_MODE) {
1397         TRACKBAR_SendNotify (infoPtr, TB_ENDTRACK);
1398         infoPtr->flags &= ~TB_DRAG_MODE;
1399         ReleaseCapture ();
1400         TRACKBAR_SendNotify(infoPtr, NM_RELEASEDCAPTURE);
1401         TRACKBAR_ActivateToolTip(infoPtr, FALSE);
1402         TRACKBAR_InvalidateThumb(infoPtr, infoPtr->lPos);
1403     }
1404     if (infoPtr->flags & TB_AUTO_PAGE) {
1405         KillTimer (infoPtr->hwndSelf, TB_REFRESH_TIMER);
1406         infoPtr->flags &= ~TB_AUTO_PAGE;
1407         ReleaseCapture ();
1408         TRACKBAR_SendNotify(infoPtr, NM_RELEASEDCAPTURE);
1409     }
1410
1411     return 0;
1412 }
1413
1414
1415 static LRESULT
1416 TRACKBAR_CaptureChanged (TRACKBAR_INFO *infoPtr)
1417 {
1418     TRACKBAR_SendNotify (infoPtr, TB_ENDTRACK);
1419     return 0;
1420 }
1421
1422
1423 static LRESULT
1424 TRACKBAR_Paint (TRACKBAR_INFO *infoPtr, HDC hdc)
1425 {
1426     if (hdc) {
1427         TRACKBAR_Refresh(infoPtr, hdc);
1428     } else {
1429         PAINTSTRUCT ps;
1430         hdc = BeginPaint (infoPtr->hwndSelf, &ps);
1431         TRACKBAR_Refresh (infoPtr, hdc);
1432         EndPaint (infoPtr->hwndSelf, &ps);
1433     }
1434
1435     return 0;
1436 }
1437
1438
1439 static LRESULT
1440 TRACKBAR_SetFocus (TRACKBAR_INFO *infoPtr, HWND hwndLoseFocus)
1441 {
1442     TRACE("\n");
1443     infoPtr->bFocussed = TRUE;
1444     TRACKBAR_InvalidateAll(infoPtr);
1445
1446     return 0;
1447 }
1448
1449
1450 static LRESULT
1451 TRACKBAR_Size (TRACKBAR_INFO *infoPtr, DWORD fwSizeType, INT nWidth, INT nHeight)
1452 {
1453     TRACKBAR_CalcChannel (infoPtr);
1454     TRACKBAR_AlignBuddies (infoPtr);
1455
1456     return 0;
1457 }
1458
1459
1460 static LRESULT
1461 TRACKBAR_Timer (TRACKBAR_INFO *infoPtr, INT wTimerID, TIMERPROC *tmrpc)
1462 {
1463     if (infoPtr->flags & TB_AUTO_PAGE) {
1464         POINT pt;
1465         if (GetCursorPos(&pt))
1466             if (ScreenToClient(infoPtr->hwndSelf, &pt))
1467                 TRACKBAR_AutoPage(infoPtr, pt);
1468     }
1469     return 0;
1470 }
1471
1472
1473 static BOOL
1474 TRACKBAR_SendNotify (TRACKBAR_INFO *infoPtr, UINT code)
1475 {
1476     BOOL bVert = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT;
1477
1478     TRACE("%x\n", code);
1479
1480     return (BOOL) SendMessageW (GetParent (infoPtr->hwndSelf),
1481                                 bVert ? WM_VSCROLL : WM_HSCROLL,
1482                                 (WPARAM)code, (LPARAM)infoPtr->hwndSelf);
1483 }
1484
1485
1486 static LRESULT
1487 TRACKBAR_MouseMove (TRACKBAR_INFO *infoPtr, DWORD fwKeys, POINTS pts)
1488 {
1489     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
1490     INT clickPlace = (dwStyle & TBS_VERT) ? pts.y : pts.x;
1491     DOUBLE dragPos, oldPos = infoPtr->lPos;
1492
1493     TRACE("(x=%d. y=%d)\n", pts.x, pts.y);
1494
1495     if (infoPtr->flags & TB_AUTO_PAGE) {
1496         POINT pt;
1497         POINTSTOPOINT(pt, pts);
1498         TRACKBAR_AutoPage (infoPtr, pt);
1499         return TRUE;
1500     }
1501
1502     if (!(infoPtr->flags & TB_DRAG_MODE)) return TRUE;
1503
1504     dragPos = TRACKBAR_ConvertPlaceToPosition (infoPtr, clickPlace,
1505                                                dwStyle & TBS_VERT);
1506     if (dragPos > ((INT)dragPos) + 0.5) dragPos++;
1507
1508     if (dragPos == oldPos) return TRUE;
1509
1510     infoPtr->lPos = dragPos;
1511
1512     infoPtr->flags |= TB_THUMBPOSCHANGED;
1513     TRACKBAR_SendNotify (infoPtr, TB_THUMBTRACK | (infoPtr->lPos<<16));
1514
1515
1516     TRACKBAR_InvalidateThumbMove(infoPtr, oldPos, dragPos);
1517     UpdateWindow (infoPtr->hwndSelf);
1518
1519     return TRUE;
1520 }
1521
1522
1523 static BOOL
1524 TRACKBAR_KeyDown (TRACKBAR_INFO *infoPtr, INT nVirtKey, DWORD lKeyData)
1525 {
1526     BOOL downIsLeft = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_DOWNISLEFT;
1527     LONG pos = infoPtr->lPos;
1528
1529     TRACE("%x\n", nVirtKey);
1530
1531     switch (nVirtKey) {
1532     case VK_UP:
1533         if (downIsLeft) goto step_right;
1534     case VK_LEFT:
1535     step_left:
1536         if (infoPtr->lPos == infoPtr->lRangeMin) return FALSE;
1537         infoPtr->lPos -= infoPtr->lLineSize;
1538         if (infoPtr->lPos < infoPtr->lRangeMin)
1539             infoPtr->lPos = infoPtr->lRangeMin;
1540         TRACKBAR_SendNotify (infoPtr, TB_LINEUP);
1541         break;
1542     case VK_DOWN:
1543         if (downIsLeft) goto step_left;
1544     case VK_RIGHT:
1545     step_right:
1546         if (infoPtr->lPos == infoPtr->lRangeMax) return FALSE;
1547         infoPtr->lPos += infoPtr->lLineSize;
1548         if (infoPtr->lPos > infoPtr->lRangeMax)
1549             infoPtr->lPos = infoPtr->lRangeMax;
1550         TRACKBAR_SendNotify (infoPtr, TB_LINEDOWN);
1551         break;
1552     case VK_NEXT:
1553         if (downIsLeft) goto page_left;
1554     page_right:
1555         TRACKBAR_PageUp(infoPtr);
1556         break;
1557     case VK_PRIOR:
1558         if (downIsLeft) goto page_right;
1559     page_left:
1560         TRACKBAR_PageDown(infoPtr);
1561         break;
1562     case VK_HOME:
1563         if (infoPtr->lPos == infoPtr->lRangeMin) return FALSE;
1564         infoPtr->lPos = infoPtr->lRangeMin;
1565         TRACKBAR_SendNotify (infoPtr, TB_TOP);
1566         break;
1567     case VK_END:
1568         if (infoPtr->lPos == infoPtr->lRangeMax) return FALSE;
1569         infoPtr->lPos = infoPtr->lRangeMax;
1570         TRACKBAR_SendNotify (infoPtr, TB_BOTTOM);
1571         break;
1572     }
1573
1574     if (pos != infoPtr->lPos) {
1575         infoPtr->flags |=TB_THUMBPOSCHANGED;
1576         TRACKBAR_InvalidateThumbMove (infoPtr, pos, infoPtr->lPos);
1577     }
1578
1579     return TRUE;
1580 }
1581
1582
1583 static BOOL inline
1584 TRACKBAR_KeyUp (TRACKBAR_INFO *infoPtr, INT nVirtKey, DWORD lKeyData)
1585 {
1586     switch (nVirtKey) {
1587     case VK_LEFT:
1588     case VK_UP:
1589     case VK_RIGHT:
1590     case VK_DOWN:
1591     case VK_NEXT:
1592     case VK_PRIOR:
1593     case VK_HOME:
1594     case VK_END:
1595         TRACKBAR_SendNotify (infoPtr, TB_ENDTRACK);
1596     }
1597     return TRUE;
1598 }
1599
1600
1601 static LRESULT WINAPI
1602 TRACKBAR_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1603 {
1604     TRACKBAR_INFO *infoPtr = (TRACKBAR_INFO *)GetWindowLongW (hwnd, 0);
1605
1606     TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1607
1608     if (!infoPtr && (uMsg != WM_CREATE))
1609         return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1610
1611     switch (uMsg)
1612     {
1613     case TBM_CLEARSEL:
1614         return TRACKBAR_ClearSel (infoPtr, (BOOL)wParam);
1615
1616     case TBM_CLEARTICS:
1617         return TRACKBAR_ClearTics (infoPtr, (BOOL)wParam);
1618
1619     case TBM_GETBUDDY:
1620         return (LRESULT)(wParam ? infoPtr->hwndBuddyLA : infoPtr->hwndBuddyRB);
1621
1622     case TBM_GETCHANNELRECT:
1623         return TRACKBAR_GetChannelRect (infoPtr, (LPRECT)lParam);
1624
1625     case TBM_GETLINESIZE:
1626         return infoPtr->lLineSize;
1627
1628     case TBM_GETNUMTICS:
1629         return TRACKBAR_GetNumTics (infoPtr);
1630
1631     case TBM_GETPAGESIZE:
1632         return infoPtr->lPageSize;
1633
1634     case TBM_GETPOS:
1635         return infoPtr->lPos;
1636
1637     case TBM_GETPTICS:
1638         return (LRESULT)infoPtr->tics;
1639
1640     case TBM_GETRANGEMAX:
1641         return infoPtr->lRangeMax;
1642
1643     case TBM_GETRANGEMIN:
1644         return infoPtr->lRangeMin;
1645
1646     case TBM_GETSELEND:
1647         return infoPtr->lSelMax;
1648
1649     case TBM_GETSELSTART:
1650         return infoPtr->lSelMin;
1651
1652     case TBM_GETTHUMBLENGTH:
1653         return infoPtr->uThumbLen;
1654
1655     case TBM_GETTHUMBRECT:
1656         return CopyRect((LPRECT)lParam, &infoPtr->rcThumb);
1657
1658     case TBM_GETTIC:
1659         return TRACKBAR_GetTic (infoPtr, (INT)wParam);
1660
1661     case TBM_GETTICPOS:
1662         return TRACKBAR_GetTicPos (infoPtr, (INT)wParam);
1663
1664     case TBM_GETTOOLTIPS:
1665         return (LRESULT)infoPtr->hwndToolTip;
1666
1667     case TBM_GETUNICODEFORMAT:
1668         return infoPtr->bUnicode;
1669
1670     case TBM_SETBUDDY:
1671         return (LRESULT) TRACKBAR_SetBuddy(infoPtr, (BOOL)wParam, (HWND)lParam);
1672
1673     case TBM_SETLINESIZE:
1674         return TRACKBAR_SetLineSize (infoPtr, (LONG)lParam);
1675
1676     case TBM_SETPAGESIZE:
1677         return TRACKBAR_SetPageSize (infoPtr, (LONG)lParam);
1678
1679     case TBM_SETPOS:
1680         return TRACKBAR_SetPos (infoPtr, (BOOL)wParam, (LONG)lParam);
1681
1682     case TBM_SETRANGE:
1683         return TRACKBAR_SetRange (infoPtr, (BOOL)wParam, (LONG)lParam);
1684
1685     case TBM_SETRANGEMAX:
1686         return TRACKBAR_SetRangeMax (infoPtr, (BOOL)wParam, (LONG)lParam);
1687
1688     case TBM_SETRANGEMIN:
1689         return TRACKBAR_SetRangeMin (infoPtr, (BOOL)wParam, (LONG)lParam);
1690
1691     case TBM_SETSEL:
1692         return TRACKBAR_SetSel (infoPtr, (BOOL)wParam, (LONG)lParam);
1693
1694     case TBM_SETSELEND:
1695         return TRACKBAR_SetSelEnd (infoPtr, (BOOL)wParam, (LONG)lParam);
1696
1697     case TBM_SETSELSTART:
1698         return TRACKBAR_SetSelStart (infoPtr, (BOOL)wParam, (LONG)lParam);
1699
1700     case TBM_SETTHUMBLENGTH:
1701         return TRACKBAR_SetThumbLength (infoPtr, (UINT)wParam);
1702
1703     case TBM_SETTIC:
1704         return TRACKBAR_SetTic (infoPtr, (LONG)lParam);
1705
1706     case TBM_SETTICFREQ:
1707         return TRACKBAR_SetTicFreq (infoPtr, (WORD)wParam);
1708
1709     case TBM_SETTIPSIDE:
1710         return TRACKBAR_SetTipSide (infoPtr, (INT)wParam);
1711
1712     case TBM_SETTOOLTIPS:
1713         return TRACKBAR_SetToolTips (infoPtr, (HWND)wParam);
1714
1715     case TBM_SETUNICODEFORMAT:
1716         return TRACKBAR_SetUnicodeFormat (infoPtr, (BOOL)wParam);
1717
1718
1719     case WM_CAPTURECHANGED:
1720         return TRACKBAR_CaptureChanged (infoPtr);
1721
1722     case WM_CREATE:
1723         return TRACKBAR_Create (hwnd, (LPCREATESTRUCTW)lParam);
1724
1725     case WM_DESTROY:
1726         return TRACKBAR_Destroy (infoPtr);
1727
1728 /*      case WM_ENABLE: */
1729
1730     case WM_ERASEBKGND:
1731         return 0;
1732
1733     case WM_GETDLGCODE:
1734         return DLGC_WANTARROWS;
1735
1736     case WM_KEYDOWN:
1737         return TRACKBAR_KeyDown (infoPtr, (INT)wParam, (DWORD)lParam);
1738
1739     case WM_KEYUP:
1740         return TRACKBAR_KeyUp (infoPtr, (INT)wParam, (DWORD)lParam);
1741
1742     case WM_KILLFOCUS:
1743         return TRACKBAR_KillFocus (infoPtr, (HWND)wParam);
1744
1745     case WM_LBUTTONDOWN:
1746         return TRACKBAR_LButtonDown (infoPtr, wParam, MAKEPOINTS(lParam));
1747
1748     case WM_LBUTTONUP:
1749         return TRACKBAR_LButtonUp (infoPtr, wParam, MAKEPOINTS(lParam));
1750
1751     case WM_MOUSEMOVE:
1752         return TRACKBAR_MouseMove (infoPtr, wParam, MAKEPOINTS(lParam));
1753
1754     case WM_PAINT:
1755         return TRACKBAR_Paint (infoPtr, (HDC)wParam);
1756
1757     case WM_SETFOCUS:
1758         return TRACKBAR_SetFocus (infoPtr, (HWND)wParam);
1759
1760     case WM_SIZE:
1761         return TRACKBAR_Size (infoPtr, wParam, LOWORD(lParam), HIWORD(lParam));
1762
1763     case WM_TIMER:
1764         return TRACKBAR_Timer (infoPtr, (INT)wParam, (TIMERPROC *)lParam);
1765
1766     case WM_WININICHANGE:
1767         return TRACKBAR_InitializeThumb (infoPtr);
1768
1769     default:
1770         if ((uMsg >= WM_USER) && (uMsg < WM_APP))
1771             ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
1772         return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1773     }
1774     return 0;
1775 }
1776
1777
1778 void TRACKBAR_Register (void)
1779 {
1780     WNDCLASSW wndClass;
1781
1782     ZeroMemory (&wndClass, sizeof(WNDCLASSW));
1783     wndClass.style         = CS_GLOBALCLASS;
1784     wndClass.lpfnWndProc   = (WNDPROC)TRACKBAR_WindowProc;
1785     wndClass.cbClsExtra    = 0;
1786     wndClass.cbWndExtra    = sizeof(TRACKBAR_INFO *);
1787     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1788     wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1789     wndClass.lpszClassName = TRACKBAR_CLASSW;
1790
1791     RegisterClassW (&wndClass);
1792 }
1793
1794
1795 void TRACKBAR_Unregister (void)
1796 {
1797     UnregisterClassW (TRACKBAR_CLASSW, NULL);
1798 }