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