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