~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/comctl32/trackbar.c

Version: ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Trackbar control
  3  *
  4  * Copyright 1998, 1999 Eric Kohl
  5  * Copyright 1998, 1999 Alex Priem
  6  * Copyright 2002 Dimitrie O. Paun
  7  *
  8  * This library is free software; you can redistribute it and/or
  9  * modify it under the terms of the GNU Lesser General Public
 10  * License as published by the Free Software Foundation; either
 11  * version 2.1 of the License, or (at your option) any later version.
 12  *
 13  * This library is distributed in the hope that it will be useful,
 14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16  * Lesser General Public License for more details.
 17  *
 18  * You should have received a copy of the GNU Lesser General Public
 19  * License along with this library; if not, write to the Free Software
 20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 21  *
 22  * NOTE
 23  * 
 24  * This code was audited for completeness against the documented features
 25  * of Comctl32.dll version 6.0 on Sep. 12, 2002, by Dimitrie O. Paun.
 26  * 
 27  * Unless otherwise noted, we believe this code to be complete, as per
 28  * the specification mentioned above.
 29  * If you discover missing features, or bugs, please note them below.
 30  * 
 31  */
 32 
 33 #include <stdarg.h>
 34 #include <stdio.h>
 35 #include <stdlib.h>
 36 #include <string.h>
 37 
 38 #include "windef.h"
 39 #include "winbase.h"
 40 #include "wingdi.h"
 41 #include "winuser.h"
 42 #include "winnls.h"
 43 #include "commctrl.h"
 44 #include "uxtheme.h"
 45 #include "tmschema.h"
 46 #include "wine/debug.h"
 47 
 48 #include "comctl32.h"
 49 
 50 WINE_DEFAULT_DEBUG_CHANNEL(trackbar);
 51 
 52 typedef struct
 53 {
 54     HWND hwndSelf;
 55     LONG lRangeMin;
 56     LONG lRangeMax;
 57     LONG lLineSize;
 58     LONG lPageSize;
 59     LONG lSelMin;
 60     LONG lSelMax;
 61     LONG lPos;
 62     UINT uThumbLen;
 63     UINT uNumTics;
 64     UINT uTicFreq;
 65     HWND hwndNotify;
 66     HWND hwndToolTip;
 67     HWND hwndBuddyLA;
 68     HWND hwndBuddyRB;
 69     INT  fLocation;
 70     INT  flags;
 71     BOOL bUnicode;
 72     BOOL bFocussed;
 73     RECT rcChannel;
 74     RECT rcSelection;
 75     RECT rcThumb;
 76     LPLONG tics;
 77 } TRACKBAR_INFO;
 78 
 79 #define TB_REFRESH_TIMER        1
 80 #define TB_REFRESH_DELAY        500
 81 
 82 #define TOOLTIP_OFFSET          2     /* distance from ctrl edge to tooltip */
 83 
 84 /* Used by TRACKBAR_Refresh to find out which parts of the control
 85    need to be recalculated */
 86 
 87 #define TB_THUMBPOSCHANGED      1
 88 #define TB_THUMBSIZECHANGED     2
 89 #define TB_THUMBCHANGED         (TB_THUMBPOSCHANGED | TB_THUMBSIZECHANGED)
 90 #define TB_SELECTIONCHANGED     4
 91 #define TB_DRAG_MODE            8     /* we're dragging the slider */
 92 #define TB_AUTO_PAGE_LEFT       16
 93 #define TB_AUTO_PAGE_RIGHT      32
 94 #define TB_AUTO_PAGE            (TB_AUTO_PAGE_LEFT | TB_AUTO_PAGE_RIGHT)
 95 #define TB_THUMB_HOT            64    /* mouse hovers above thumb */
 96 
 97 /* helper defines for TRACKBAR_DrawTic */
 98 #define TIC_EDGE                0x20
 99 #define TIC_SELECTIONMARKMAX    0x80
100 #define TIC_SELECTIONMARKMIN    0x100
101 #define TIC_SELECTIONMARK       (TIC_SELECTIONMARKMAX | TIC_SELECTIONMARKMIN)
102 
103 static const WCHAR themeClass[] = { 'T','r','a','c','k','b','a','r',0 };
104 
105 static inline int 
106 notify_customdraw (const TRACKBAR_INFO *infoPtr, NMCUSTOMDRAW *pnmcd, int stage)
107 {
108     pnmcd->dwDrawStage = stage;
109     return SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 
110                          pnmcd->hdr.idFrom, (LPARAM)pnmcd);
111 }
112 
113 static LRESULT notify_hdr (const TRACKBAR_INFO *infoPtr, INT code, LPNMHDR pnmh)
114 {
115     LRESULT result;
116     
117     TRACE("(code=%d)\n", code);
118 
119     pnmh->hwndFrom = infoPtr->hwndSelf;
120     pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
121     pnmh->code = code;
122     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
123 
124     TRACE("  <= %ld\n", result);
125 
126     return result;
127 }
128 
129 static inline int notify (const TRACKBAR_INFO *infoPtr, INT code)
130 {
131     NMHDR nmh;
132     return notify_hdr(infoPtr, code, &nmh);
133 }
134 
135 static BOOL
136 notify_with_scroll (const TRACKBAR_INFO *infoPtr, UINT code)
137 {
138     BOOL bVert = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT;
139 
140     TRACE("%x\n", code);
141 
142     return (BOOL) SendMessageW (infoPtr->hwndNotify,
143                                 bVert ? WM_VSCROLL : WM_HSCROLL,
144                                 (WPARAM)code, (LPARAM)infoPtr->hwndSelf);
145 }
146     
147 static void TRACKBAR_RecalculateTics (TRACKBAR_INFO *infoPtr)
148 {
149     int tic;
150     unsigned nrTics, i;
151 
152     if (infoPtr->uTicFreq && infoPtr->lRangeMax >= infoPtr->lRangeMin)
153         nrTics=(infoPtr->lRangeMax - infoPtr->lRangeMin)/infoPtr->uTicFreq;
154     else {
155         nrTics = 0;
156         Free (infoPtr->tics);
157         infoPtr->tics = NULL;
158         infoPtr->uNumTics = 0;
159         return;
160     }
161 
162     if (nrTics != infoPtr->uNumTics) {
163         infoPtr->tics=ReAlloc (infoPtr->tics,
164                                         (nrTics+1)*sizeof (DWORD));
165         if (!infoPtr->tics) {
166             infoPtr->uNumTics = 0;
167             notify(infoPtr, NM_OUTOFMEMORY);
168             return;
169         }
170         infoPtr->uNumTics = nrTics;
171     }
172 
173     tic = infoPtr->lRangeMin + infoPtr->uTicFreq;
174     for (i = 0; i < nrTics; i++, tic += infoPtr->uTicFreq)
175         infoPtr->tics[i] = tic;
176 }
177 
178 /* converts from physical (mouse) position to logical position
179    (in range of trackbar) */
180 
181 static inline LONG
182 TRACKBAR_ConvertPlaceToPosition (const TRACKBAR_INFO *infoPtr, int place, int vertical)
183 {
184     double range, width, pos, offsetthumb;
185 
186     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
187     if (vertical) {
188         offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
189         width = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - (offsetthumb * 2) - 1;
190         pos = (range*(place - infoPtr->rcChannel.top - offsetthumb)) / width;
191     } else {
192         offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
193         width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - (offsetthumb * 2) - 1;
194         pos = (range*(place - infoPtr->rcChannel.left - offsetthumb)) / width;
195     }
196     pos += infoPtr->lRangeMin;
197     if (pos > infoPtr->lRangeMax)
198         pos = infoPtr->lRangeMax;
199     else if (pos < infoPtr->lRangeMin)
200         pos = infoPtr->lRangeMin;
201 
202     TRACE("%.2f\n", pos);
203     return (LONG)(pos + 0.5);
204 }
205 
206 
207 /* return: 0> prev, 0 none, >0 next */
208 static LONG
209 TRACKBAR_GetAutoPageDirection (const TRACKBAR_INFO *infoPtr, POINT clickPoint)
210 {
211     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
212     RECT pageRect;
213 
214     if (dwStyle & TBS_VERT) {
215         pageRect.top = infoPtr->rcChannel.top;
216         pageRect.bottom = infoPtr->rcChannel.bottom;
217         pageRect.left = infoPtr->rcThumb.left;
218         pageRect.right = infoPtr->rcThumb.right;
219     } else {
220         pageRect.top = infoPtr->rcThumb.top;
221         pageRect.bottom = infoPtr->rcThumb.bottom;
222         pageRect.left = infoPtr->rcChannel.left;
223         pageRect.right = infoPtr->rcChannel.right;
224     }
225 
226 
227     if (PtInRect(&pageRect, clickPoint))
228     {
229         int clickPlace = (dwStyle & TBS_VERT) ? clickPoint.y : clickPoint.x;
230 
231         LONG clickPos = TRACKBAR_ConvertPlaceToPosition(infoPtr, clickPlace,
232                                                         dwStyle & TBS_VERT);
233         return clickPos - infoPtr->lPos;
234     }
235 
236     return 0;
237 }
238 
239 static inline void
240 TRACKBAR_PageDown (TRACKBAR_INFO *infoPtr)
241 {
242     if (infoPtr->lPos == infoPtr->lRangeMax) return;
243 
244     infoPtr->lPos += infoPtr->lPageSize;
245     if (infoPtr->lPos > infoPtr->lRangeMax)
246         infoPtr->lPos = infoPtr->lRangeMax;
247     notify_with_scroll (infoPtr, TB_PAGEDOWN);
248 }
249 
250 
251 static inline void
252 TRACKBAR_PageUp (TRACKBAR_INFO *infoPtr)
253 {
254     if (infoPtr->lPos == infoPtr->lRangeMin) return;
255 
256     infoPtr->lPos -= infoPtr->lPageSize;
257     if (infoPtr->lPos < infoPtr->lRangeMin)
258         infoPtr->lPos = infoPtr->lRangeMin;
259     notify_with_scroll (infoPtr, TB_PAGEUP);
260 }
261 
262 static inline void TRACKBAR_LineUp(TRACKBAR_INFO *infoPtr)
263 {
264     if (infoPtr->lPos == infoPtr->lRangeMin) return;
265     infoPtr->lPos -= infoPtr->lLineSize;
266     if (infoPtr->lPos < infoPtr->lRangeMin)
267         infoPtr->lPos = infoPtr->lRangeMin;
268     notify_with_scroll (infoPtr, TB_LINEUP);
269 }
270 
271 static inline void TRACKBAR_LineDown(TRACKBAR_INFO *infoPtr)
272 {
273     if (infoPtr->lPos == infoPtr->lRangeMax) return;
274     infoPtr->lPos += infoPtr->lLineSize;
275     if (infoPtr->lPos > infoPtr->lRangeMax)
276         infoPtr->lPos = infoPtr->lRangeMax;
277     notify_with_scroll (infoPtr, TB_LINEDOWN);
278 }
279 
280 static void
281 TRACKBAR_CalcChannel (TRACKBAR_INFO *infoPtr)
282 {
283     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
284     INT cyChannel, offsetthumb, offsetedge;
285     RECT lpRect, *channel = & infoPtr->rcChannel;
286 
287     GetClientRect (infoPtr->hwndSelf, &lpRect);
288 
289     offsetthumb = infoPtr->uThumbLen / 4;
290     offsetedge  = offsetthumb + 3;
291     cyChannel   = (dwStyle & TBS_ENABLESELRANGE) ? offsetthumb*3 : 4;
292     if (dwStyle & TBS_VERT) {
293         channel->top    = lpRect.top + offsetedge;
294         channel->bottom = lpRect.bottom - offsetedge;
295         if (dwStyle & TBS_ENABLESELRANGE)
296             channel->left = lpRect.left + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
297         else
298             channel->left = lpRect.left + (infoPtr->uThumbLen / 2) - 1;
299         if (dwStyle & TBS_BOTH) {
300             if (dwStyle & TBS_NOTICKS)
301                 channel->left += 1;
302             else
303                 channel->left += 9;
304         }
305         else if (dwStyle & TBS_TOP) {
306             if (dwStyle & TBS_NOTICKS)
307                 channel->left += 2;
308             else
309                 channel->left += 10;
310         }
311         channel->right = channel->left + cyChannel;
312     } else {
313         channel->left = lpRect.left + offsetedge;
314         channel->right = lpRect.right - offsetedge;
315         if (dwStyle & TBS_ENABLESELRANGE)
316             channel->top = lpRect.top + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
317         else
318             channel->top = lpRect.top + (infoPtr->uThumbLen / 2) - 1;
319         if (dwStyle & TBS_BOTH) {
320             if (dwStyle & TBS_NOTICKS)
321                 channel->top += 1;
322             else
323                 channel->top += 9;
324         }
325         else if (dwStyle & TBS_TOP) {
326             if (dwStyle & TBS_NOTICKS)
327                 channel->top += 2;
328             else
329                 channel->top += 10;
330         }
331         channel->bottom   = channel->top + cyChannel;
332     }
333 }
334 
335 static void
336 TRACKBAR_CalcThumb (const TRACKBAR_INFO *infoPtr, LONG lPos, RECT *thumb)
337 {
338     int range, width, height, thumbwidth;
339     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
340     RECT lpRect;
341 
342     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
343     thumbwidth = (infoPtr->uThumbLen / 2) | 1;
344 
345     if (!range) range = 1;
346 
347     GetClientRect(infoPtr->hwndSelf, &lpRect);
348     if (dwStyle & TBS_VERT)
349     {
350         height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - thumbwidth;
351 
352         if ((dwStyle & (TBS_BOTH | TBS_LEFT)) && !(dwStyle & TBS_NOTICKS))
353             thumb->left = 10;
354         else
355             thumb->left = 2;
356         thumb->right = thumb->left + infoPtr->uThumbLen;
357         thumb->top = infoPtr->rcChannel.top +
358                      (height*(lPos - infoPtr->lRangeMin))/range;
359         thumb->bottom = thumb->top + thumbwidth;
360     }
361     else
362     {
363         width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - thumbwidth;
364 
365         thumb->left = infoPtr->rcChannel.left +
366                       (width*(lPos - infoPtr->lRangeMin))/range;
367         thumb->right = thumb->left + thumbwidth;
368         if ((dwStyle & (TBS_BOTH | TBS_TOP)) && !(dwStyle & TBS_NOTICKS))
369             thumb->top = 10;
370         else
371             thumb->top = 2;
372         thumb->bottom = thumb->top + infoPtr->uThumbLen;
373     }
374 }
375 
376 static inline void
377 TRACKBAR_UpdateThumb (TRACKBAR_INFO *infoPtr)
378 {
379     TRACKBAR_CalcThumb(infoPtr, infoPtr->lPos, &infoPtr->rcThumb);
380 }
381 
382 static inline void
383 TRACKBAR_InvalidateAll (const TRACKBAR_INFO *infoPtr)
384 {
385     InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
386 }
387 
388 static void
389 TRACKBAR_InvalidateThumb (const TRACKBAR_INFO *infoPtr, LONG thumbPos)
390 {
391     RECT rcThumb;
392 
393     TRACKBAR_CalcThumb(infoPtr, thumbPos, &rcThumb);
394     InflateRect(&rcThumb, 1, 1);
395     InvalidateRect(infoPtr->hwndSelf, &rcThumb, FALSE);
396 }
397 
398 static inline void
399 TRACKBAR_InvalidateThumbMove (const TRACKBAR_INFO *infoPtr, LONG oldPos, LONG newPos)
400 {
401     TRACKBAR_InvalidateThumb (infoPtr, oldPos);
402     if (newPos != oldPos)
403         TRACKBAR_InvalidateThumb (infoPtr, newPos);
404 }
405 
406 static inline BOOL
407 TRACKBAR_HasSelection (const TRACKBAR_INFO *infoPtr)
408 {
409     return infoPtr->lSelMin != infoPtr->lSelMax;
410 }
411 
412 static void
413 TRACKBAR_CalcSelection (TRACKBAR_INFO *infoPtr)
414 {
415     RECT *selection = &infoPtr->rcSelection;
416     int range = infoPtr->lRangeMax - infoPtr->lRangeMin;
417     int offsetthumb, height, width;
418 
419     if (range <= 0) {
420         SetRectEmpty (selection);
421     } else {
422         if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT) {
423             offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
424             height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - offsetthumb*2;
425             selection->top    = infoPtr->rcChannel.top + offsetthumb +
426                 (height*infoPtr->lSelMin)/range;
427             selection->bottom = infoPtr->rcChannel.top + offsetthumb +
428                 (height*infoPtr->lSelMax)/range;
429             selection->left   = infoPtr->rcChannel.left + 3;
430             selection->right  = infoPtr->rcChannel.right - 3;
431         } else {
432             offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
433             width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
434             selection->left   = infoPtr->rcChannel.left + offsetthumb +
435                 (width*infoPtr->lSelMin)/range;
436             selection->right  = infoPtr->rcChannel.left + offsetthumb +
437                 (width*infoPtr->lSelMax)/range;
438             selection->top    = infoPtr->rcChannel.top + 3;
439             selection->bottom = infoPtr->rcChannel.bottom - 3;
440         }
441     }
442 
443     TRACE("selection[%s]\n", wine_dbgstr_rect(selection));
444 }
445 
446 static BOOL
447 TRACKBAR_AutoPage (TRACKBAR_INFO *infoPtr, POINT clickPoint)
448 {
449     LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
450     LONG prevPos = infoPtr->lPos;
451 
452     TRACE("x=%d, y=%d, dir=%d\n", clickPoint.x, clickPoint.y, dir);
453 
454     if (dir > 0 && (infoPtr->flags & TB_AUTO_PAGE_RIGHT))
455         TRACKBAR_PageDown(infoPtr);
456     else if (dir < 0 && (infoPtr->flags & TB_AUTO_PAGE_LEFT))
457         TRACKBAR_PageUp(infoPtr);
458     else return FALSE;
459 
460     infoPtr->flags |= TB_THUMBPOSCHANGED;
461     TRACKBAR_InvalidateThumbMove (infoPtr, prevPos, infoPtr->lPos);
462 
463     return TRUE;
464 }
465 
466 /* Trackbar drawing code. I like my spaghetti done milanese.  */
467 
468 static void
469 TRACKBAR_DrawChannel (const TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
470 {
471     RECT rcChannel = infoPtr->rcChannel;
472     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
473 
474     if (theme)
475     {
476         DrawThemeBackground (theme, hdc, 
477             (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT) ? 
478                 TKP_TRACKVERT : TKP_TRACK, TKS_NORMAL, &rcChannel, 0);
479     }
480     else
481     {
482         DrawEdge (hdc, &rcChannel, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
483         if (dwStyle & TBS_ENABLESELRANGE) {              /* fill the channel */
484             FillRect (hdc, &rcChannel, GetStockObject(WHITE_BRUSH));
485             if (TRACKBAR_HasSelection(infoPtr))
486                 FillRect (hdc, &infoPtr->rcSelection, GetSysColorBrush(COLOR_HIGHLIGHT));
487         }
488     }
489 }
490 
491 static void
492 TRACKBAR_DrawOneTic (const TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
493 {
494     int x, y, ox, oy, range, side, indent = 0, len = 3;
495     int offsetthumb;
496     RECT rcTics;
497 
498     if (flags & TBS_VERT) {
499         offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
500         rcTics.left = infoPtr->rcThumb.left - 2;
501         rcTics.right = infoPtr->rcThumb.right + 2;
502         rcTics.top    = infoPtr->rcChannel.top + offsetthumb + 1;
503         rcTics.bottom = infoPtr->rcChannel.bottom - offsetthumb;
504     } else {
505         offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
506         rcTics.left   = infoPtr->rcChannel.left + offsetthumb + 1;
507         rcTics.right  = infoPtr->rcChannel.right - offsetthumb;
508         rcTics.top = infoPtr->rcThumb.top - 2;
509         rcTics.bottom = infoPtr->rcThumb.bottom + 2;
510     }
511 
512     if (flags & (TBS_TOP | TBS_LEFT)) {
513         x = rcTics.left;
514         y = rcTics.top;
515         side = -1;
516     } else {
517         x = rcTics.right;
518         y = rcTics.bottom;
519         side = 1;
520     }
521 
522     range = infoPtr->lRangeMax - infoPtr->lRangeMin;
523     if (range <= 0)
524       range = 1; /* to avoid division by zero */
525 
526     if (flags & TIC_SELECTIONMARK) {
527         indent = (flags & TIC_SELECTIONMARKMIN) ? -1 : 1;
528     } else if (flags & TIC_EDGE) {
529         len++;
530     }
531 
532     if (flags & TBS_VERT) {
533         int height = rcTics.bottom - rcTics.top;
534         y = rcTics.top + (height*(ticPos - infoPtr->lRangeMin))/range;
535     } else {
536         int width = rcTics.right - rcTics.left;
537         x = rcTics.left + (width*(ticPos - infoPtr->lRangeMin))/range;
538     }
539 
540     ox = x;
541     oy = y;
542     MoveToEx(hdc, x, y, 0);
543     if (flags & TBS_VERT) x += len * side;
544     else y += len * side;
545     LineTo(hdc, x, y);
546             
547     if (flags & TIC_SELECTIONMARK) {
548         if (flags & TBS_VERT) {
549             x -= side;
550         } else {
551             y -= side;
552         }
553         MoveToEx(hdc, x, y, 0);
554         if (flags & TBS_VERT) {
555             y += 2 * indent;
556         } else {
557             x += 2 * indent;
558         }
559         
560         LineTo(hdc, x, y);
561         LineTo(hdc, ox, oy);
562     }
563 }
564 
565 
566 static inline void
567 TRACKBAR_DrawTic (const TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
568 {
569     if ((flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
570         TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags | TBS_LEFT);
571 
572     if (!(flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
573         TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags & ~TBS_LEFT);
574 }
575 
576 static void
577 TRACKBAR_DrawTics (const TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
578 {
579     unsigned int i;
580     int ticFlags = dwStyle & 0x0f;
581     LOGPEN ticPen = { PS_SOLID, {1, 0}, GetSysColor (COLOR_3DDKSHADOW) };
582     HPEN hOldPen, hTicPen;
583     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
584     
585     if (theme)
586     {
587         int part = (dwStyle & TBS_VERT) ? TKP_TICSVERT : TKP_TICS;
588         GetThemeColor (theme, part, TSS_NORMAL, TMT_COLOR, &ticPen.lopnColor);
589     }
590     /* create the pen to draw the tics with */
591     hTicPen = CreatePenIndirect(&ticPen);
592     hOldPen = hTicPen ? SelectObject(hdc, hTicPen) : 0;
593 
594     /* actually draw the tics */
595     for (i=0; i<infoPtr->uNumTics; i++)
596         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->tics[i], ticFlags);
597 
598     TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMin, ticFlags | TIC_EDGE);
599     TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMax, ticFlags | TIC_EDGE);
600 
601     if ((dwStyle & TBS_ENABLESELRANGE) && TRACKBAR_HasSelection(infoPtr)) {
602         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMin,
603                           ticFlags | TIC_SELECTIONMARKMIN);
604         TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMax,
605                           ticFlags | TIC_SELECTIONMARKMAX);
606     }
607     
608     /* clean up the pen, if we created one */
609     if (hTicPen) {
610         SelectObject(hdc, hOldPen);
611         DeleteObject(hTicPen);
612     }
613 }
614 
615 static void
616 TRACKBAR_DrawThumb (const TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
617 {
618     HBRUSH oldbr;
619     HPEN  oldpen;
620     RECT thumb = infoPtr->rcThumb;
621     int BlackUntil = 3;
622     int PointCount = 6;
623     POINT points[6];
624     int fillClr;
625     int PointDepth;
626     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
627     
628     if (theme)
629     {
630         int partId;
631         int stateId;
632         if (dwStyle & TBS_BOTH)
633             partId = (dwStyle & TBS_VERT) ? TKP_THUMBVERT : TKP_THUMB;
634         else if (dwStyle & TBS_LEFT)
635             partId = (dwStyle & TBS_VERT) ? TKP_THUMBLEFT : TKP_THUMBTOP;
636         else
637             partId = (dwStyle & TBS_VERT) ? TKP_THUMBRIGHT : TKP_THUMBBOTTOM;
638             
639         if (dwStyle & WS_DISABLED)
640             stateId = TUS_DISABLED;
641         else if (infoPtr->flags & TB_DRAG_MODE)
642             stateId = TUS_PRESSED;
643         else if (infoPtr->flags & TB_THUMB_HOT)
644             stateId = TUS_HOT;
645         else
646             stateId = TUS_NORMAL;
647         
648         DrawThemeBackground (theme, hdc, partId, stateId, &thumb, 0);
649         
650         return;
651     }
652 
653     fillClr = infoPtr->flags & TB_DRAG_MODE ? COLOR_BTNHILIGHT : COLOR_BTNFACE;
654     oldbr = SelectObject (hdc, GetSysColorBrush(fillClr));
655     SetPolyFillMode (hdc, WINDING);
656 
657     if (dwStyle & TBS_BOTH)
658     {
659        points[0].x=thumb.right;
660        points[0].y=thumb.top;
661        points[1].x=thumb.right;
662        points[1].y=thumb.bottom;
663        points[2].x=thumb.left;
664        points[2].y=thumb.bottom;
665        points[3].x=thumb.left;
666        points[3].y=thumb.top;
667        points[4].x=points[0].x;
668        points[4].y=points[0].y;
669        PointCount = 5;
670        BlackUntil = 3;
671     }
672     else
673     {
674         if (dwStyle & TBS_VERT)
675         {
676           PointDepth = (thumb.bottom - thumb.top) / 2;
677           if (dwStyle & TBS_LEFT)
678           {
679             points[0].x=thumb.right;
680             points[0].y=thumb.top;
681             points[1].x=thumb.right;
682             points[1].y=thumb.bottom;
683             points[2].x=thumb.left + PointDepth;
684             points[2].y=thumb.bottom;
685             points[3].x=thumb.left;
686             points[3].y=(thumb.bottom - thumb.top) / 2 + thumb.top + 1;
687             points[4].x=thumb.left + PointDepth;
688             points[4].y=thumb.top;
689             points[5].x=points[0].x;
690             points[5].y=points[0].y;
691             BlackUntil = 4;
692           }
693           else
694           {
695             points[0].x=thumb.right;
696             points[0].y=(thumb.bottom - thumb.top) / 2 + thumb.top + 1;
697             points[1].x=thumb.right - PointDepth;
698             points[1].y=thumb.bottom;
699             points[2].x=thumb.left;
700             points[2].y=thumb.bottom;
701             points[3].x=thumb.left;
702             points[3].y=thumb.top;
703             points[4].x=thumb.right - PointDepth;
704             points[4].y=thumb.top;
705             points[5].x=points[0].x;
706             points[5].y=points[0].y;
707           }
708         }
709         else
710         {
711           PointDepth = (thumb.right - thumb.left) / 2;
712           if (dwStyle & TBS_TOP)
713           {
714             points[0].x=(thumb.right - thumb.left) / 2 + thumb.left + 1;
715             points[0].y=thumb.top;
716             points[1].x=thumb.right;
717             points[1].y=thumb.top + PointDepth;
718             points[2].x=thumb.right;
719             points[2].y=thumb.bottom;
720             points[3].x=thumb.left;
721             points[3].y=thumb.bottom;
722             points[4].x=thumb.left;
723             points[4].y=thumb.top + PointDepth;
724             points[5].x=points[0].x;
725             points[5].y=points[0].y;
726             BlackUntil = 4;
727           }
728           else
729           {
730             points[0].x=thumb.right;
731             points[0].y=thumb.top;
732             points[1].x=thumb.right;
733             points[1].y=thumb.bottom - PointDepth;
734             points[2].x=(thumb.right - thumb.left) / 2 + thumb.left + 1;
735             points[2].y=thumb.bottom;
736             points[3].x=thumb.left;
737             points[3].y=thumb.bottom - PointDepth;
738             points[4].x=thumb.left;
739             points[4].y=thumb.top;
740             points[5].x=points[0].x;
741             points[5].y=points[0].y;
742           }
743         }
744 
745     }
746 
747     /* Draw the thumb now */
748     Polygon (hdc, points, PointCount);
749     oldpen = SelectObject(hdc, GetStockObject(BLACK_PEN));
750     Polyline(hdc,points, BlackUntil);
751     SelectObject(hdc, GetStockObject(WHITE_PEN));
752     Polyline(hdc, &points[BlackUntil-1], PointCount+1-BlackUntil);
753     SelectObject(hdc, oldpen);
754     SelectObject(hdc, oldbr);
755 }
756 
757 
758 static inline void
759 TRACKBAR_ActivateToolTip (const TRACKBAR_INFO *infoPtr, BOOL fShow)
760 {
761     TTTOOLINFOW ti;
762 
763     if (!infoPtr->hwndToolTip) return;
764 
765     ZeroMemory(&ti, sizeof(ti));
766     ti.cbSize = sizeof(ti);
767     ti.hwnd   = infoPtr->hwndSelf;
768 
769     SendMessageW (infoPtr->hwndToolTip, TTM_TRACKACTIVATE, fShow, (LPARAM)&ti);
770 }
771 
772 
773 static void
774 TRACKBAR_UpdateToolTip (const TRACKBAR_INFO *infoPtr)
775 {
776     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
777     WCHAR buf[80];
778     static const WCHAR fmt[] = { '%', 'l', 'd', 0 };
779     TTTOOLINFOW ti;
780     POINT pt;
781     RECT rcClient;
782     LRESULT size;
783 
784     if (!infoPtr->hwndToolTip) return;
785 
786     ZeroMemory(&ti, sizeof(ti));
787     ti.cbSize = sizeof(ti);
788     ti.hwnd   = infoPtr->hwndSelf;
789     ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
790 
791     wsprintfW (buf, fmt, infoPtr->lPos);
792     ti.lpszText = buf;
793     SendMessageW (infoPtr->hwndToolTip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti);
794 
795     GetClientRect (infoPtr->hwndSelf, &rcClient);
796     size = SendMessageW (infoPtr->hwndToolTip, TTM_GETBUBBLESIZE, 0, (LPARAM)&ti);
797     if (dwStyle & TBS_VERT) {
798         if (infoPtr->fLocation == TBTS_LEFT)
799             pt.x = 0 - LOWORD(size) - TOOLTIP_OFFSET;
800         else
801             pt.x = rcClient.right + TOOLTIP_OFFSET;
802         pt.y = (infoPtr->rcThumb.top + infoPtr->rcThumb.bottom - HIWORD(size))/2;
803     } else {
804         if (infoPtr->fLocation == TBTS_TOP)
805             pt.y = 0 - HIWORD(size) - TOOLTIP_OFFSET;
806         else
807             pt.y = rcClient.bottom + TOOLTIP_OFFSET;
808         pt.x = (infoPtr->rcThumb.left + infoPtr->rcThumb.right - LOWORD(size))/2;
809     }
810     ClientToScreen(infoPtr->hwndSelf, &pt);
811 
812     SendMessageW (infoPtr->hwndToolTip, TTM_TRACKPOSITION,
813                   0, MAKELPARAM(pt.x, pt.y));
814 }
815 
816 
817 static void
818 TRACKBAR_Refresh (TRACKBAR_INFO *infoPtr, HDC hdcDst)
819 {
820     DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
821     RECT rcClient;
822     HDC hdc;
823     HBITMAP hOldBmp = 0, hOffScreenBmp = 0;
824     NMCUSTOMDRAW nmcd;
825     int gcdrf, icdrf;
826 
827     if (infoPtr->flags & TB_THUMBCHANGED) {
828         TRACKBAR_UpdateThumb (infoPtr);
829         if (infoPtr->flags & TB_THUMBSIZECHANGED)
830             TRACKBAR_CalcChannel (infoPtr);
831     }
832     if (infoPtr->flags & TB_SELECTIONCHANGED)
833         TRACKBAR_CalcSelection (infoPtr);
834 
835     if (infoPtr->flags & TB_DRAG_MODE)
836         TRACKBAR_UpdateToolTip (infoPtr);
837 
838     infoPtr->flags &= ~ (TB_THUMBCHANGED | TB_SELECTIONCHANGED);
839 
840     GetClientRect (infoPtr->hwndSelf, &rcClient);
841     
842     /* try to render offscreen, if we fail, carrry onscreen */
843     hdc = CreateCompatibleDC(hdcDst);
844     if (hdc) {
845         hOffScreenBmp = CreateCompatibleBitmap(hdcDst, rcClient.right, rcClient.bottom);
846         if (hOffScreenBmp) {
847             hOldBmp = SelectObject(hdc, hOffScreenBmp);
848         } else {
849             DeleteObject(hdc);
850             hdc = hdcDst;
851         }
852     } else {
853         hdc = hdcDst;
854     }
855 
856     ZeroMemory(&nmcd, sizeof(nmcd));
857     nmcd.hdr.hwndFrom = infoPtr->hwndSelf;
858     nmcd.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
859     nmcd.hdr.code = NM_CUSTOMDRAW;
860     nmcd.hdc = hdc;
861 
862     /* start the paint cycle */
863     nmcd.rc = rcClient;
864     gcdrf = notify_customdraw(infoPtr, &nmcd, CDDS_PREPAINT);
865     if (gcdrf & CDRF_SKIPDEFAULT) goto cleanup;
866     
867     /* Erase background */
868     if (gcdrf == CDRF_DODEFAULT ||
869         notify_customdraw(infoPtr, &nmcd, CDDS_PREERASE) != CDRF_SKIPDEFAULT) {
870         if (GetWindowTheme (infoPtr->hwndSelf)) {
871             DrawThemeParentBackground (infoPtr->hwndSelf, hdc, 0);
872         }
873         else
874             FillRect (hdc, &rcClient, GetSysColorBrush(COLOR_BTNFACE));
875         if (gcdrf != CDRF_DODEFAULT)
876             notify_customdraw(infoPtr, &nmcd, CDDS_POSTERASE);
877     }
878     
879     /* draw channel */
880     if (gcdrf & CDRF_NOTIFYITEMDRAW) {
881         nmcd.dwItemSpec = TBCD_CHANNEL;
882         nmcd.uItemState = CDIS_DEFAULT;
883         nmcd.rc = infoPtr->rcChannel;
884         icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
885     } else icdrf = CDRF_DODEFAULT;
886     if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
887         TRACKBAR_DrawChannel (infoPtr, hdc, dwStyle);
888         if (icdrf & CDRF_NOTIFYPOSTPAINT)
889             notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
890     }
891 
892 
893     /* draw tics */
894     if (!(dwStyle & TBS_NOTICKS)) {
895         if (gcdrf & CDRF_NOTIFYITEMDRAW) {
896             nmcd.dwItemSpec = TBCD_TICS;
897             nmcd.uItemState = CDIS_DEFAULT;
898             nmcd.rc = rcClient;
899             icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
900         } else icdrf = CDRF_DODEFAULT;
901         if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
902             TRACKBAR_DrawTics (infoPtr, hdc, dwStyle);
903             if (icdrf & CDRF_NOTIFYPOSTPAINT)
904                 notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
905         }
906     }
907     
908     /* draw thumb */
909     if (!(dwStyle & TBS_NOTHUMB)) {
910         if (gcdrf & CDRF_NOTIFYITEMDRAW) {
911             nmcd.dwItemSpec = TBCD_THUMB;
912             nmcd.uItemState = infoPtr->flags & TB_DRAG_MODE ? CDIS_HOT : CDIS_DEFAULT;
913             nmcd.rc = infoPtr->rcThumb;
914             icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
915         } else icdrf = CDRF_DODEFAULT;
916         if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
917             TRACKBAR_DrawThumb(infoPtr, hdc, dwStyle);
918             if (icdrf & CDRF_NOTIFYPOSTPAINT)
919                 notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
920         }
921     }
922 
923     /* draw focus rectangle */
924     if (infoPtr->bFocussed) {
925         DrawFocusRect(hdc, &rcClient);
926     }
927 
928     /* finish up the painting */
929     if (gcdrf & CDRF_NOTIFYPOSTPAINT)
930         notify_customdraw(infoPtr, &nmcd, CDDS_POSTPAINT);
931     
932 cleanup:
933     /* cleanup, if we rendered offscreen */
934     if (hdc != hdcDst) {
935         BitBlt(hdcDst, 0, 0, rcClient.right, rcClient.bottom, hdc, 0, 0, SRCCOPY);
936         SelectObject(hdc, hOldBmp);
937         DeleteObject(hOffScreenBmp);
938         DeleteObject(hdc);
939     }
940 }
941 
942 
943 static void