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