Add cyrillic glyphs to Wine System.
[wine/dcerpc.git] / dlls / comctl32 / trackbar.c
blob2114c593ef64db0f5c90b3bd71257062f6a1fd62
1 /*
2 * Trackbar control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1998, 1999 Alex Priem
6 * Copyright 2002 Dimitrie O. Paun
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.
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.
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
22 * NOTE
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.
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.
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
38 #include "windef.h"
39 #include "winbase.h"
40 #include "wingdi.h"
41 #include "winuser.h"
42 #include "winnls.h"
43 #include "commctrl.h"
44 #include "wine/debug.h"
46 #include "comctl32.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(trackbar);
50 typedef struct
52 HWND hwndSelf;
53 LONG lRangeMin;
54 LONG lRangeMax;
55 LONG lLineSize;
56 LONG lPageSize;
57 LONG lSelMin;
58 LONG lSelMax;
59 LONG lPos;
60 UINT uThumbLen;
61 UINT uNumTics;
62 UINT uTicFreq;
63 HWND hwndNotify;
64 HWND hwndToolTip;
65 HWND hwndBuddyLA;
66 HWND hwndBuddyRB;
67 INT fLocation;
68 INT flags;
69 BOOL bUnicode;
70 BOOL bFocussed;
71 RECT rcChannel;
72 RECT rcSelection;
73 RECT rcThumb;
74 LPLONG tics;
75 } TRACKBAR_INFO;
77 #define TB_REFRESH_TIMER 1
78 #define TB_REFRESH_DELAY 500
80 #define TOOLTIP_OFFSET 2 /* distance from ctrl edge to tooltip */
82 /* Used by TRACKBAR_Refresh to find out which parts of the control
83 need to be recalculated */
85 #define TB_THUMBPOSCHANGED 1
86 #define TB_THUMBSIZECHANGED 2
87 #define TB_THUMBCHANGED (TB_THUMBPOSCHANGED | TB_THUMBSIZECHANGED)
88 #define TB_SELECTIONCHANGED 4
89 #define TB_DRAG_MODE 8 /* we're dragging the slider */
90 #define TB_AUTO_PAGE_LEFT 16
91 #define TB_AUTO_PAGE_RIGHT 32
92 #define TB_AUTO_PAGE (TB_AUTO_PAGE_LEFT | TB_AUTO_PAGE_RIGHT)
94 /* helper defines for TRACKBAR_DrawTic */
95 #define TIC_EDGE 0x20
96 #define TIC_SELECTIONMARKMAX 0x80
97 #define TIC_SELECTIONMARKMIN 0x100
98 #define TIC_SELECTIONMARK (TIC_SELECTIONMARKMAX | TIC_SELECTIONMARKMIN)
100 static inline int
101 notify_customdraw(TRACKBAR_INFO *infoPtr, NMCUSTOMDRAW *pnmcd, int stage)
103 pnmcd->dwDrawStage = stage;
104 return SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
105 pnmcd->hdr.idFrom, (LPARAM)pnmcd);
108 static LRESULT notify_hdr(TRACKBAR_INFO *infoPtr, INT code, LPNMHDR pnmh)
110 LRESULT result;
112 TRACE("(code=%d)\n", code);
114 pnmh->hwndFrom = infoPtr->hwndSelf;
115 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
116 pnmh->code = code;
117 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
118 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
120 TRACE(" <= %ld\n", result);
122 return result;
125 static inline int notify(TRACKBAR_INFO *infoPtr, INT code)
127 NMHDR nmh;
128 return notify_hdr(infoPtr, code, &nmh);
131 static BOOL
132 notify_with_scroll (TRACKBAR_INFO *infoPtr, UINT code)
134 BOOL bVert = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT;
136 TRACE("%x\n", code);
138 return (BOOL) SendMessageW (infoPtr->hwndNotify,
139 bVert ? WM_VSCROLL : WM_HSCROLL,
140 (WPARAM)code, (LPARAM)infoPtr->hwndSelf);
143 static void TRACKBAR_RecalculateTics (TRACKBAR_INFO *infoPtr)
145 int i, tic, nrTics;
147 if (infoPtr->uTicFreq && infoPtr->lRangeMax >= infoPtr->lRangeMin)
148 nrTics=(infoPtr->lRangeMax - infoPtr->lRangeMin)/infoPtr->uTicFreq;
149 else {
150 nrTics = 0;
151 Free (infoPtr->tics);
152 infoPtr->tics = NULL;
153 infoPtr->uNumTics = 0;
154 return;
157 if (nrTics != infoPtr->uNumTics) {
158 infoPtr->tics=ReAlloc (infoPtr->tics,
159 (nrTics+1)*sizeof (DWORD));
160 if (!infoPtr->tics) {
161 infoPtr->uNumTics = 0;
162 notify(infoPtr, NM_OUTOFMEMORY);
163 return;
165 infoPtr->uNumTics = nrTics;
168 tic = infoPtr->lRangeMin + infoPtr->uTicFreq;
169 for (i = 0; i < nrTics; i++, tic += infoPtr->uTicFreq)
170 infoPtr->tics[i] = tic;
173 /* converts from physical (mouse) position to logical position
174 (in range of trackbar) */
176 static inline LONG
177 TRACKBAR_ConvertPlaceToPosition (TRACKBAR_INFO *infoPtr, int place,
178 int vertical)
180 double range, width, pos, offsetthumb;
182 range = infoPtr->lRangeMax - infoPtr->lRangeMin;
183 if (vertical) {
184 offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
185 width = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - (offsetthumb * 2) - 1;
186 pos = (range*(place - infoPtr->rcChannel.top - offsetthumb)) / width;
187 } else {
188 offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
189 width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - (offsetthumb * 2) - 1;
190 pos = (range*(place - infoPtr->rcChannel.left - offsetthumb)) / width;
192 pos += infoPtr->lRangeMin;
193 if (pos > infoPtr->lRangeMax)
194 pos = infoPtr->lRangeMax;
195 else if (pos < infoPtr->lRangeMin)
196 pos = infoPtr->lRangeMin;
198 TRACE("%.2f\n", pos);
199 return (LONG)(pos + 0.5);
203 /* return: 0> prev, 0 none, >0 next */
204 static LONG
205 TRACKBAR_GetAutoPageDirection (TRACKBAR_INFO *infoPtr, POINT clickPoint)
207 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
208 RECT pageRect;
210 if (dwStyle & TBS_VERT) {
211 pageRect.top = infoPtr->rcChannel.top;
212 pageRect.bottom = infoPtr->rcChannel.bottom;
213 pageRect.left = infoPtr->rcThumb.left;
214 pageRect.right = infoPtr->rcThumb.right;
215 } else {
216 pageRect.top = infoPtr->rcThumb.top;
217 pageRect.bottom = infoPtr->rcThumb.bottom;
218 pageRect.left = infoPtr->rcChannel.left;
219 pageRect.right = infoPtr->rcChannel.right;
223 if (PtInRect(&pageRect, clickPoint))
225 int clickPlace = (dwStyle & TBS_VERT) ? clickPoint.y : clickPoint.x;
227 LONG clickPos = TRACKBAR_ConvertPlaceToPosition(infoPtr, clickPlace,
228 dwStyle & TBS_VERT);
229 return clickPos - infoPtr->lPos;
232 return 0;
235 static void inline
236 TRACKBAR_PageDown (TRACKBAR_INFO *infoPtr)
238 if (infoPtr->lPos == infoPtr->lRangeMax) return;
240 infoPtr->lPos += infoPtr->lPageSize;
241 if (infoPtr->lPos > infoPtr->lRangeMax)
242 infoPtr->lPos = infoPtr->lRangeMax;
243 notify_with_scroll (infoPtr, TB_PAGEDOWN);
247 static void inline
248 TRACKBAR_PageUp (TRACKBAR_INFO *infoPtr)
250 if (infoPtr->lPos == infoPtr->lRangeMin) return;
252 infoPtr->lPos -= infoPtr->lPageSize;
253 if (infoPtr->lPos < infoPtr->lRangeMin)
254 infoPtr->lPos = infoPtr->lRangeMin;
255 notify_with_scroll (infoPtr, TB_PAGEUP);
258 static void inline TRACKBAR_LineUp(TRACKBAR_INFO *infoPtr)
260 if (infoPtr->lPos == infoPtr->lRangeMin) return;
261 infoPtr->lPos -= infoPtr->lLineSize;
262 if (infoPtr->lPos < infoPtr->lRangeMin)
263 infoPtr->lPos = infoPtr->lRangeMin;
264 notify_with_scroll (infoPtr, TB_LINEUP);
267 static void inline TRACKBAR_LineDown(TRACKBAR_INFO *infoPtr)
269 if (infoPtr->lPos == infoPtr->lRangeMax) return;
270 infoPtr->lPos += infoPtr->lLineSize;
271 if (infoPtr->lPos > infoPtr->lRangeMax)
272 infoPtr->lPos = infoPtr->lRangeMax;
273 notify_with_scroll (infoPtr, TB_LINEDOWN);
276 static void
277 TRACKBAR_CalcChannel (TRACKBAR_INFO *infoPtr)
279 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
280 INT cyChannel, offsetthumb, offsetedge;
281 RECT lpRect, *channel = & infoPtr->rcChannel;
283 GetClientRect (infoPtr->hwndSelf, &lpRect);
285 offsetthumb = infoPtr->uThumbLen / 4;
286 offsetedge = offsetthumb + 3;
287 cyChannel = (dwStyle & TBS_ENABLESELRANGE) ? offsetthumb*3 : 4;
288 if (dwStyle & TBS_VERT) {
289 channel->top = lpRect.top + offsetedge;
290 channel->bottom = lpRect.bottom - offsetedge;
291 if (dwStyle & TBS_ENABLESELRANGE)
292 channel->left = lpRect.left + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
293 else
294 channel->left = lpRect.left + (infoPtr->uThumbLen / 2) - 1;
295 if (dwStyle & TBS_BOTH) {
296 if (dwStyle & TBS_NOTICKS)
297 channel->left += 1;
298 else
299 channel->left += 9;
301 else if (dwStyle & TBS_TOP) {
302 if (dwStyle & TBS_NOTICKS)
303 channel->left += 2;
304 else
305 channel->left += 10;
307 channel->right = channel->left + cyChannel;
308 } else {
309 channel->left = lpRect.left + offsetedge;
310 channel->right = lpRect.right - offsetedge;
311 if (dwStyle & TBS_ENABLESELRANGE)
312 channel->top = lpRect.top + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
313 else
314 channel->top = lpRect.top + (infoPtr->uThumbLen / 2) - 1;
315 if (dwStyle & TBS_BOTH) {
316 if (dwStyle & TBS_NOTICKS)
317 channel->top += 1;
318 else
319 channel->top += 9;
321 else if (dwStyle & TBS_TOP) {
322 if (dwStyle & TBS_NOTICKS)
323 channel->top += 2;
324 else
325 channel->top += 10;
327 channel->bottom = channel->top + cyChannel;
331 static void
332 TRACKBAR_CalcThumb (TRACKBAR_INFO *infoPtr, LONG lPos, RECT *thumb)
334 int range, width, height, thumbwidth;
335 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
336 RECT lpRect;
338 range = infoPtr->lRangeMax - infoPtr->lRangeMin;
339 thumbwidth = (infoPtr->uThumbLen / 2) | 1;
341 if (!range) range = 1;
343 GetClientRect(infoPtr->hwndSelf, &lpRect);
344 if (dwStyle & TBS_VERT)
346 height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - thumbwidth;
348 if ((dwStyle & (TBS_BOTH | TBS_LEFT)) && !(dwStyle & TBS_NOTICKS))
349 thumb->left = 10;
350 else
351 thumb->left = 2;
352 thumb->right = thumb->left + infoPtr->uThumbLen;
353 thumb->top = infoPtr->rcChannel.top +
354 (height*(lPos - infoPtr->lRangeMin))/range;
355 thumb->bottom = thumb->top + thumbwidth;
357 else
359 width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - thumbwidth;
361 thumb->left = infoPtr->rcChannel.left +
362 (width*(lPos - infoPtr->lRangeMin))/range;
363 thumb->right = thumb->left + thumbwidth;
364 if ((dwStyle & (TBS_BOTH | TBS_TOP)) && !(dwStyle & TBS_NOTICKS))
365 thumb->top = 10;
366 else
367 thumb->top = 2;
368 thumb->bottom = thumb->top + infoPtr->uThumbLen;
372 inline static void
373 TRACKBAR_UpdateThumb (TRACKBAR_INFO *infoPtr)
375 TRACKBAR_CalcThumb(infoPtr, infoPtr->lPos, &infoPtr->rcThumb);
378 static inline void
379 TRACKBAR_InvalidateAll(TRACKBAR_INFO * infoPtr)
381 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
384 static void
385 TRACKBAR_InvalidateThumb (TRACKBAR_INFO *infoPtr, LONG thumbPos)
387 RECT rcThumb;
389 TRACKBAR_CalcThumb(infoPtr, thumbPos, &rcThumb);
390 InflateRect(&rcThumb, 1, 1);
391 InvalidateRect(infoPtr->hwndSelf, &rcThumb, FALSE);
394 static inline void
395 TRACKBAR_InvalidateThumbMove (TRACKBAR_INFO *infoPtr, LONG oldPos, LONG newPos)
397 TRACKBAR_InvalidateThumb (infoPtr, oldPos);
398 if (newPos != oldPos)
399 TRACKBAR_InvalidateThumb (infoPtr, newPos);
402 static BOOL inline
403 TRACKBAR_HasSelection (TRACKBAR_INFO *infoPtr)
405 return infoPtr->lSelMin != infoPtr->lSelMax;
408 static void
409 TRACKBAR_CalcSelection (TRACKBAR_INFO *infoPtr)
411 RECT *selection = &infoPtr->rcSelection;
412 int range = infoPtr->lRangeMax - infoPtr->lRangeMin;
413 int offsetthumb, height, width;
415 if (range <= 0) {
416 SetRectEmpty (selection);
417 } else {
418 if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT) {
419 offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
420 height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - offsetthumb*2;
421 selection->top = infoPtr->rcChannel.top + offsetthumb +
422 (height*infoPtr->lSelMin)/range;
423 selection->bottom = infoPtr->rcChannel.top + offsetthumb +
424 (height*infoPtr->lSelMax)/range;
425 selection->left = infoPtr->rcChannel.left + 3;
426 selection->right = infoPtr->rcChannel.right - 3;
427 } else {
428 offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
429 width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
430 selection->left = infoPtr->rcChannel.left + offsetthumb +
431 (width*infoPtr->lSelMin)/range;
432 selection->right = infoPtr->rcChannel.left + offsetthumb +
433 (width*infoPtr->lSelMax)/range;
434 selection->top = infoPtr->rcChannel.top + 3;
435 selection->bottom = infoPtr->rcChannel.bottom - 3;
439 TRACE("selection[left=%ld, top=%ld, right=%ld, bottom=%ld]\n",
440 selection->left, selection->top, selection->right, selection->bottom);
443 static BOOL
444 TRACKBAR_AutoPage (TRACKBAR_INFO *infoPtr, POINT clickPoint)
446 LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
447 LONG prevPos = infoPtr->lPos;
449 TRACE("x=%ld, y=%ld, dir=%ld\n", clickPoint.x, clickPoint.y, dir);
451 if (dir > 0 && (infoPtr->flags & TB_AUTO_PAGE_RIGHT))
452 TRACKBAR_PageDown(infoPtr);
453 else if (dir < 0 && (infoPtr->flags & TB_AUTO_PAGE_LEFT))
454 TRACKBAR_PageUp(infoPtr);
455 else return FALSE;
457 infoPtr->flags |= TB_THUMBPOSCHANGED;
458 TRACKBAR_InvalidateThumbMove (infoPtr, prevPos, infoPtr->lPos);
460 return TRUE;
463 /* Trackbar drawing code. I like my spaghetti done milanese. */
465 static void
466 TRACKBAR_DrawChannel (TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
468 RECT rcChannel = infoPtr->rcChannel;
470 DrawEdge (hdc, &rcChannel, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
471 if (dwStyle & TBS_ENABLESELRANGE) { /* fill the channel */
472 FillRect (hdc, &rcChannel, GetStockObject(WHITE_BRUSH));
473 if (TRACKBAR_HasSelection(infoPtr))
474 FillRect (hdc, &infoPtr->rcSelection, GetSysColorBrush(COLOR_HIGHLIGHT));
478 static void
479 TRACKBAR_DrawOneTic (TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
481 int x, y, ox, oy, range, side, indent = 0, len = 3;
482 int offsetthumb;
483 RECT rcTics;
485 if (flags & TBS_VERT) {
486 offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
487 rcTics.left = infoPtr->rcThumb.left - 2;
488 rcTics.right = infoPtr->rcThumb.right + 2;
489 rcTics.top = infoPtr->rcChannel.top + offsetthumb + 1;
490 rcTics.bottom = infoPtr->rcChannel.bottom - offsetthumb;
491 } else {
492 offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
493 rcTics.left = infoPtr->rcChannel.left + offsetthumb + 1;
494 rcTics.right = infoPtr->rcChannel.right - offsetthumb;
495 rcTics.top = infoPtr->rcThumb.top - 2;
496 rcTics.bottom = infoPtr->rcThumb.bottom + 2;
499 if (flags & (TBS_TOP | TBS_LEFT)) {
500 x = rcTics.left;
501 y = rcTics.top;
502 side = -1;
503 } else {
504 x = rcTics.right;
505 y = rcTics.bottom;
506 side = 1;
509 range = infoPtr->lRangeMax - infoPtr->lRangeMin;
510 if (range <= 0)
511 range = 1; /* to avoid division by zero */
513 if (flags & TIC_SELECTIONMARK) {
514 indent = (flags & TIC_SELECTIONMARKMIN) ? -1 : 1;
515 } else if (flags & TIC_EDGE) {
516 len++;
519 if (flags & TBS_VERT) {
520 int height = rcTics.bottom - rcTics.top;
521 y = rcTics.top + (height*(ticPos - infoPtr->lRangeMin))/range;
522 } else {
523 int width = rcTics.right - rcTics.left;
524 x = rcTics.left + (width*(ticPos - infoPtr->lRangeMin))/range;
527 ox = x;
528 oy = y;
529 MoveToEx(hdc, x, y, 0);
530 if (flags & TBS_VERT) x += len * side;
531 else y += len * side;
532 LineTo(hdc, x, y);
534 if (flags & TIC_SELECTIONMARK) {
535 if (flags & TBS_VERT) {
536 x -= side;
537 } else {
538 y -= side;
540 MoveToEx(hdc, x, y, 0);
541 if (flags & TBS_VERT) {
542 y += 2 * indent;
543 } else {
544 x += 2 * indent;
547 LineTo(hdc, x, y);
548 LineTo(hdc, ox, oy);
553 static inline void
554 TRACKBAR_DrawTic (TRACKBAR_INFO *infoPtr, HDC hdc, LONG ticPos, int flags)
556 if ((flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
557 TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags | TBS_LEFT);
559 if (!(flags & (TBS_LEFT | TBS_TOP)) || (flags & TBS_BOTH))
560 TRACKBAR_DrawOneTic (infoPtr, hdc, ticPos, flags & ~TBS_LEFT);
563 static void
564 TRACKBAR_DrawTics (TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
566 int i, ticFlags = dwStyle & 0x0f;
567 LOGPEN ticPen = { PS_SOLID, {1, 0}, GetSysColor (COLOR_3DDKSHADOW) };
568 HPEN hOldPen, hTicPen;
570 /* create the pen to draw the tics with */
571 hTicPen = CreatePenIndirect(&ticPen);
572 hOldPen = hTicPen ? SelectObject(hdc, hTicPen) : 0;
574 /* actually draw the tics */
575 for (i=0; i<infoPtr->uNumTics; i++)
576 TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->tics[i], ticFlags);
578 TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMin, ticFlags | TIC_EDGE);
579 TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lRangeMax, ticFlags | TIC_EDGE);
581 if ((dwStyle & TBS_ENABLESELRANGE) && TRACKBAR_HasSelection(infoPtr)) {
582 TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMin,
583 ticFlags | TIC_SELECTIONMARKMIN);
584 TRACKBAR_DrawTic (infoPtr, hdc, infoPtr->lSelMax,
585 ticFlags | TIC_SELECTIONMARKMAX);
588 /* clean up the pen, if we created one */
589 if (hTicPen) {
590 SelectObject(hdc, hOldPen);
591 DeleteObject(hTicPen);
595 static void
596 TRACKBAR_DrawThumb(TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
598 HBRUSH oldbr;
599 HPEN oldpen;
600 RECT thumb = infoPtr->rcThumb;
601 int BlackUntil = 3;
602 int PointCount = 6;
603 POINT points[6];
604 int fillClr;
605 int PointDepth;
607 fillClr = infoPtr->flags & TB_DRAG_MODE ? COLOR_BTNHILIGHT : COLOR_BTNFACE;
608 oldbr = SelectObject (hdc, GetSysColorBrush(fillClr));
609 SetPolyFillMode (hdc, WINDING);
611 if (dwStyle & TBS_BOTH)
613 points[0].x=thumb.right;
614 points[0].y=thumb.top;
615 points[1].x=thumb.right;
616 points[1].y=thumb.bottom;
617 points[2].x=thumb.left;
618 points[2].y=thumb.bottom;
619 points[3].x=thumb.left;
620 points[3].y=thumb.top;
621 points[4].x=points[0].x;
622 points[4].y=points[0].y;
623 PointCount = 5;
624 BlackUntil = 3;
626 else
628 if (dwStyle & TBS_VERT)
630 PointDepth = (thumb.bottom - thumb.top) / 2;
631 if (dwStyle & TBS_LEFT)
633 points[0].x=thumb.right;
634 points[0].y=thumb.top;
635 points[1].x=thumb.right;
636 points[1].y=thumb.bottom;
637 points[2].x=thumb.left + PointDepth;
638 points[2].y=thumb.bottom;
639 points[3].x=thumb.left;
640 points[3].y=(thumb.bottom - thumb.top) / 2 + thumb.top + 1;
641 points[4].x=thumb.left + PointDepth;
642 points[4].y=thumb.top;
643 points[5].x=points[0].x;
644 points[5].y=points[0].y;
645 BlackUntil = 4;
647 else
649 points[0].x=thumb.right;
650 points[0].y=(thumb.bottom - thumb.top) / 2 + thumb.top + 1;
651 points[1].x=thumb.right - PointDepth;
652 points[1].y=thumb.bottom;
653 points[2].x=thumb.left;
654 points[2].y=thumb.bottom;
655 points[3].x=thumb.left;
656 points[3].y=thumb.top;
657 points[4].x=thumb.right - PointDepth;
658 points[4].y=thumb.top;
659 points[5].x=points[0].x;
660 points[5].y=points[0].y;
663 else
665 PointDepth = (thumb.right - thumb.left) / 2;
666 if (dwStyle & TBS_TOP)
668 points[0].x=(thumb.right - thumb.left) / 2 + thumb.left + 1;
669 points[0].y=thumb.top;
670 points[1].x=thumb.right;
671 points[1].y=thumb.top + PointDepth;
672 points[2].x=thumb.right;
673 points[2].y=thumb.bottom;
674 points[3].x=thumb.left;
675 points[3].y=thumb.bottom;
676 points[4].x=thumb.left;
677 points[4].y=thumb.top + PointDepth;
678 points[5].x=points[0].x;
679 points[5].y=points[0].y;
680 BlackUntil = 4;
682 else
684 points[0].x=thumb.right;
685 points[0].y=thumb.top;
686 points[1].x=thumb.right;
687 points[1].y=thumb.bottom - PointDepth;
688 points[2].x=(thumb.right - thumb.left) / 2 + thumb.left + 1;
689 points[2].y=thumb.bottom;
690 points[3].x=thumb.left;
691 points[3].y=thumb.bottom - PointDepth;
692 points[4].x=thumb.left;
693 points[4].y=thumb.top;
694 points[5].x=points[0].x;
695 points[5].y=points[0].y;
701 /* Draw the thumb now */
702 Polygon (hdc, points, PointCount);
703 oldpen = SelectObject(hdc, GetStockObject(BLACK_PEN));
704 Polyline(hdc,points, BlackUntil);
705 SelectObject(hdc, GetStockObject(WHITE_PEN));
706 Polyline(hdc, &points[BlackUntil-1], PointCount+1-BlackUntil);
707 SelectObject(hdc, oldpen);
708 SelectObject(hdc, oldbr);
712 static void inline
713 TRACKBAR_ActivateToolTip (TRACKBAR_INFO *infoPtr, BOOL fShow)
715 TTTOOLINFOW ti;
717 if (!infoPtr->hwndToolTip) return;
719 ZeroMemory(&ti, sizeof(ti));
720 ti.cbSize = sizeof(ti);
721 ti.hwnd = infoPtr->hwndSelf;
723 SendMessageW (infoPtr->hwndToolTip, TTM_TRACKACTIVATE, fShow, (LPARAM)&ti);
727 static void
728 TRACKBAR_UpdateToolTip (TRACKBAR_INFO *infoPtr)
730 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
731 WCHAR buf[80];
732 static const WCHAR fmt[] = { '%', 'l', 'd', 0 };
733 TTTOOLINFOW ti;
734 POINT pt;
735 RECT rcClient;
736 LRESULT size;
738 if (!infoPtr->hwndToolTip) return;
740 ZeroMemory(&ti, sizeof(ti));
741 ti.cbSize = sizeof(ti);
742 ti.hwnd = infoPtr->hwndSelf;
743 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
745 wsprintfW (buf, fmt, infoPtr->lPos);
746 ti.lpszText = buf;
747 SendMessageW (infoPtr->hwndToolTip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti);
749 GetClientRect (infoPtr->hwndSelf, &rcClient);
750 size = SendMessageW (infoPtr->hwndToolTip, TTM_GETBUBBLESIZE, 0, (LPARAM)&ti);
751 if (dwStyle & TBS_VERT) {
752 if (infoPtr->fLocation == TBTS_LEFT)
753 pt.x = 0 - LOWORD(size) - TOOLTIP_OFFSET;
754 else
755 pt.x = rcClient.right + TOOLTIP_OFFSET;
756 pt.y = (infoPtr->rcThumb.top + infoPtr->rcThumb.bottom - HIWORD(size))/2;
757 } else {
758 if (infoPtr->fLocation == TBTS_TOP)
759 pt.y = 0 - HIWORD(size) - TOOLTIP_OFFSET;
760 else
761 pt.y = rcClient.bottom + TOOLTIP_OFFSET;
762 pt.x = (infoPtr->rcThumb.left + infoPtr->rcThumb.right - LOWORD(size))/2;
764 ClientToScreen(infoPtr->hwndSelf, &pt);
766 SendMessageW (infoPtr->hwndToolTip, TTM_TRACKPOSITION,
767 0, (LPARAM)MAKELPARAM(pt.x, pt.y));
771 static void
772 TRACKBAR_Refresh (TRACKBAR_INFO *infoPtr, HDC hdcDst)
774 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
775 RECT rcClient;
776 HDC hdc;
777 HBITMAP hOldBmp = 0, hOffScreenBmp = 0;
778 NMCUSTOMDRAW nmcd;
779 int gcdrf, icdrf;
781 if (infoPtr->flags & TB_THUMBCHANGED) {
782 TRACKBAR_UpdateThumb (infoPtr);
783 if (infoPtr->flags & TB_THUMBSIZECHANGED)
784 TRACKBAR_CalcChannel (infoPtr);
786 if (infoPtr->flags & TB_SELECTIONCHANGED)
787 TRACKBAR_CalcSelection (infoPtr);
789 if (infoPtr->flags & TB_DRAG_MODE)
790 TRACKBAR_UpdateToolTip (infoPtr);
792 infoPtr->flags &= ~ (TB_THUMBCHANGED | TB_SELECTIONCHANGED);
794 GetClientRect (infoPtr->hwndSelf, &rcClient);
796 /* try to render offscreen, if we fail, carrry onscreen */
797 hdc = CreateCompatibleDC(hdcDst);
798 if (hdc) {
799 hOffScreenBmp = CreateCompatibleBitmap(hdcDst, rcClient.right, rcClient.bottom);
800 if (hOffScreenBmp) {
801 hOldBmp = SelectObject(hdc, hOffScreenBmp);
802 } else {
803 DeleteObject(hdc);
804 hdc = hdcDst;
806 } else {
807 hdc = hdcDst;
810 ZeroMemory(&nmcd, sizeof(nmcd));
811 nmcd.hdr.hwndFrom = infoPtr->hwndSelf;
812 nmcd.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
813 nmcd.hdr.code = NM_CUSTOMDRAW;
814 nmcd.hdc = hdc;
816 /* start the paint cycle */
817 nmcd.rc = rcClient;
818 gcdrf = notify_customdraw(infoPtr, &nmcd, CDDS_PREPAINT);
819 if (gcdrf & CDRF_SKIPDEFAULT) goto cleanup;
821 /* Erase backbround */
822 if (gcdrf == CDRF_DODEFAULT ||
823 notify_customdraw(infoPtr, &nmcd, CDDS_PREERASE) != CDRF_SKIPDEFAULT) {
824 FillRect (hdc, &rcClient, GetSysColorBrush(COLOR_BTNFACE));
825 if (gcdrf != CDRF_DODEFAULT)
826 notify_customdraw(infoPtr, &nmcd, CDDS_POSTERASE);
829 /* draw channel */
830 if (gcdrf & CDRF_NOTIFYITEMDRAW) {
831 nmcd.dwItemSpec = TBCD_CHANNEL;
832 nmcd.uItemState = CDIS_DEFAULT;
833 nmcd.rc = infoPtr->rcChannel;
834 icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
835 } else icdrf = CDRF_DODEFAULT;
836 if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
837 TRACKBAR_DrawChannel (infoPtr, hdc, dwStyle);
838 if (icdrf & CDRF_NOTIFYPOSTPAINT)
839 notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
843 /* draw tics */
844 if (!(dwStyle & TBS_NOTICKS)) {
845 if (gcdrf & CDRF_NOTIFYITEMDRAW) {
846 nmcd.dwItemSpec = TBCD_TICS;
847 nmcd.uItemState = CDIS_DEFAULT;
848 nmcd.rc = rcClient;
849 icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
850 } else icdrf = CDRF_DODEFAULT;
851 if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
852 TRACKBAR_DrawTics (infoPtr, hdc, dwStyle);
853 if (icdrf & CDRF_NOTIFYPOSTPAINT)
854 notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
858 /* draw thumb */
859 if (!(dwStyle & TBS_NOTHUMB)) {
860 if (gcdrf & CDRF_NOTIFYITEMDRAW) {
861 nmcd.dwItemSpec = TBCD_THUMB;
862 nmcd.uItemState = infoPtr->flags & TB_DRAG_MODE ? CDIS_HOT : CDIS_DEFAULT;
863 nmcd.rc = infoPtr->rcThumb;
864 icdrf = notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPREPAINT);
865 } else icdrf = CDRF_DODEFAULT;
866 if ( !(icdrf & CDRF_SKIPDEFAULT) ) {
867 TRACKBAR_DrawThumb(infoPtr, hdc, dwStyle);
868 if (icdrf & CDRF_NOTIFYPOSTPAINT)
869 notify_customdraw(infoPtr, &nmcd, CDDS_ITEMPOSTPAINT);
873 /* draw focus rectangle */
874 if (infoPtr->bFocussed) {
875 DrawFocusRect(hdc, &rcClient);
878 /* finish up the painting */
879 if (gcdrf & CDRF_NOTIFYPOSTPAINT)
880 notify_customdraw(infoPtr, &nmcd, CDDS_POSTPAINT);
882 cleanup:
883 /* cleanup, if we rendered offscreen */
884 if (hdc != hdcDst) {
885 BitBlt(hdcDst, 0, 0, rcClient.right, rcClient.bottom, hdc, 0, 0, SRCCOPY);
886 SelectObject(hdc, hOldBmp);
887 DeleteObject(hOffScreenBmp);
888 DeleteObject(hdc);
893 static void
894 TRACKBAR_AlignBuddies (TRACKBAR_INFO *infoPtr)
896 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
897 HWND hwndParent = GetParent (infoPtr->hwndSelf);
898 RECT rcSelf, rcBuddy;
899 INT x, y;
901 GetWindowRect (infoPtr->hwndSelf, &rcSelf);
902 MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcSelf, 2);
904 /* align buddy left or above */
905 if (infoPtr->hwndBuddyLA) {
906 GetWindowRect (infoPtr->hwndBuddyLA, &rcBuddy);
907 MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcBuddy, 2);
909 if (dwStyle & TBS_VERT) {
910 x = (infoPtr->rcChannel.right + infoPtr->rcChannel.left) / 2 -
911 (rcBuddy.right - rcBuddy.left) / 2 + rcSelf.left;
912 y = rcSelf.top - (rcBuddy.bottom - rcBuddy.top);
914 else {
915 x = rcSelf.left - (rcBuddy.right - rcBuddy.left);
916 y = (infoPtr->rcChannel.bottom + infoPtr->rcChannel.top) / 2 -
917 (rcBuddy.bottom - rcBuddy.top) / 2 + rcSelf.top;
920 SetWindowPos (infoPtr->hwndBuddyLA, 0, x, y, 0, 0,
921 SWP_NOZORDER | SWP_NOSIZE);
925 /* align buddy right or below */
926 if (infoPtr->hwndBuddyRB) {
927 GetWindowRect (infoPtr->hwndBuddyRB, &rcBuddy);
928 MapWindowPoints (HWND_DESKTOP, hwndParent, (LPPOINT)&rcBuddy, 2);
930 if (dwStyle & TBS_VERT) {
931 x = (infoPtr->rcChannel.right + infoPtr->rcChannel.left) / 2 -
932 (rcBuddy.right - rcBuddy.left) / 2 + rcSelf.left;
933 y = rcSelf.bottom;
935 else {
936 x = rcSelf.right;
937 y = (infoPtr->rcChannel.bottom + infoPtr->rcChannel.top) / 2 -
938 (rcBuddy.bottom - rcBuddy.top) / 2 + rcSelf.top;
940 SetWindowPos (infoPtr->hwndBuddyRB, 0, x, y, 0, 0,
941 SWP_NOZORDER | SWP_NOSIZE);
946 static LRESULT
947 TRACKBAR_ClearSel (TRACKBAR_INFO *infoPtr, BOOL fRedraw)
949 infoPtr->lSelMin = 0;
950 infoPtr->lSelMax = 0;
951 infoPtr->flags |= TB_SELECTIONCHANGED;
953 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
955 return 0;
959 static LRESULT
960 TRACKBAR_ClearTics (TRACKBAR_INFO *infoPtr, BOOL fRedraw)
962 if (infoPtr->tics) {
963 Free (infoPtr->tics);
964 infoPtr->tics = NULL;
965 infoPtr->uNumTics = 0;
968 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
970 return 0;
974 static LRESULT inline
975 TRACKBAR_GetChannelRect (TRACKBAR_INFO *infoPtr, LPRECT lprc)
977 if (lprc == NULL) return 0;
979 lprc->left = infoPtr->rcChannel.left;
980 lprc->right = infoPtr->rcChannel.right;
981 lprc->bottom = infoPtr->rcChannel.bottom;
982 lprc->top = infoPtr->rcChannel.top;
984 return 0;
988 static LONG inline
989 TRACKBAR_GetNumTics (TRACKBAR_INFO *infoPtr)
991 if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_NOTICKS)
992 return 0;
994 return infoPtr->uNumTics + 2;
998 static int comp_tics(const void *ap, const void *bp)
1000 DWORD a = *((DWORD *)ap);
1001 DWORD b = *((DWORD *)bp);
1003 TRACE("(a=%ld, b=%ld)\n", a, b);
1004 if (a < b) return -1;
1005 if (a > b) return 1;
1006 return 0;
1010 static LONG inline
1011 TRACKBAR_GetTic (TRACKBAR_INFO *infoPtr, INT iTic)
1013 if ((iTic < 0) || (iTic >= infoPtr->uNumTics) || !infoPtr->tics)
1014 return -1;
1016 qsort(infoPtr->tics, infoPtr->uNumTics, sizeof(DWORD), comp_tics);
1017 return infoPtr->tics[iTic];
1021 static LONG inline
1022 TRACKBAR_GetTicPos (TRACKBAR_INFO *infoPtr, INT iTic)
1024 LONG range, width, pos, tic;
1025 int offsetthumb;
1027 if ((iTic < 0) || (iTic >= infoPtr->uNumTics) || !infoPtr->tics)
1028 return -1;
1030 tic = TRACKBAR_GetTic (infoPtr, iTic);
1031 range = infoPtr->lRangeMax - infoPtr->lRangeMin;
1032 if (range <= 0) range = 1;
1033 offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
1034 width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
1035 pos = infoPtr->rcChannel.left + offsetthumb + (width * tic) / range;
1037 return pos;
1041 static HWND
1042 TRACKBAR_SetBuddy (TRACKBAR_INFO *infoPtr, BOOL fLocation, HWND hwndBuddy)
1044 HWND hwndTemp;
1046 if (fLocation) {
1047 /* buddy is left or above */
1048 hwndTemp = infoPtr->hwndBuddyLA;
1049 infoPtr->hwndBuddyLA = hwndBuddy;
1051 else {
1052 /* buddy is right or below */
1053 hwndTemp = infoPtr->hwndBuddyRB;
1054 infoPtr->hwndBuddyRB = hwndBuddy;
1057 TRACKBAR_AlignBuddies (infoPtr);
1059 return hwndTemp;
1063 static LONG inline
1064 TRACKBAR_SetLineSize (TRACKBAR_INFO *infoPtr, LONG lLineSize)
1066 LONG lTemp = infoPtr->lLineSize;
1068 infoPtr->lLineSize = lLineSize;
1070 return lTemp;
1074 static LONG inline
1075 TRACKBAR_SetPageSize (TRACKBAR_INFO *infoPtr, LONG lPageSize)
1077 LONG lTemp = infoPtr->lPageSize;
1079 infoPtr->lPageSize = lPageSize;
1081 return lTemp;
1085 static LRESULT inline
1086 TRACKBAR_SetPos (TRACKBAR_INFO *infoPtr, BOOL fPosition, LONG lPosition)
1088 LONG oldPos = infoPtr->lPos;
1089 infoPtr->lPos = lPosition;
1091 if (infoPtr->lPos < infoPtr->lRangeMin)
1092 infoPtr->lPos = infoPtr->lRangeMin;
1094 if (infoPtr->lPos > infoPtr->lRangeMax)
1095 infoPtr->lPos = infoPtr->lRangeMax;
1096 infoPtr->flags |= TB_THUMBPOSCHANGED;
1098 if (fPosition) TRACKBAR_InvalidateThumbMove(infoPtr, oldPos, lPosition);
1100 return 0;
1104 static LRESULT inline
1105 TRACKBAR_SetRange (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lRange)
1107 infoPtr->lRangeMin = (SHORT)LOWORD(lRange);
1108 infoPtr->lRangeMax = (SHORT)HIWORD(lRange);
1110 if (infoPtr->lPos < infoPtr->lRangeMin) {
1111 infoPtr->lPos = infoPtr->lRangeMin;
1112 infoPtr->flags |= TB_THUMBPOSCHANGED;
1115 if (infoPtr->lPos > infoPtr->lRangeMax) {
1116 infoPtr->lPos = infoPtr->lRangeMax;
1117 infoPtr->flags |= TB_THUMBPOSCHANGED;
1120 infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
1121 if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
1123 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1125 return 0;
1129 static LRESULT inline
1130 TRACKBAR_SetRangeMax (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lMax)
1132 infoPtr->lRangeMax = lMax;
1133 if (infoPtr->lPos > infoPtr->lRangeMax) {
1134 infoPtr->lPos = infoPtr->lRangeMax;
1135 infoPtr->flags |= TB_THUMBPOSCHANGED;
1138 infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
1139 if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
1141 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1143 return 0;
1147 static LRESULT inline
1148 TRACKBAR_SetRangeMin (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lMin)
1150 infoPtr->lRangeMin = lMin;
1151 if (infoPtr->lPos < infoPtr->lRangeMin) {
1152 infoPtr->lPos = infoPtr->lRangeMin;
1153 infoPtr->flags |= TB_THUMBPOSCHANGED;
1156 infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
1157 if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
1159 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1161 return 0;
1165 static LRESULT inline
1166 TRACKBAR_SetSel (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lSel)
1168 if (!GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_ENABLESELRANGE)
1169 return 0;
1171 infoPtr->lSelMin = (SHORT)LOWORD(lSel);
1172 infoPtr->lSelMax = (SHORT)HIWORD(lSel);
1173 infoPtr->flags |= TB_SELECTIONCHANGED;
1175 if (infoPtr->lSelMin < infoPtr->lRangeMin)
1176 infoPtr->lSelMin = infoPtr->lRangeMin;
1177 if (infoPtr->lSelMax > infoPtr->lRangeMax)
1178 infoPtr->lSelMax = infoPtr->lRangeMax;
1180 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1182 return 0;
1186 static LRESULT inline
1187 TRACKBAR_SetSelEnd (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lEnd)
1189 if (!GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_ENABLESELRANGE)
1190 return 0;
1192 infoPtr->lSelMax = lEnd;
1193 infoPtr->flags |= TB_SELECTIONCHANGED;
1195 if (infoPtr->lSelMax > infoPtr->lRangeMax)
1196 infoPtr->lSelMax = infoPtr->lRangeMax;
1198 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1200 return 0;
1204 static LRESULT inline
1205 TRACKBAR_SetSelStart (TRACKBAR_INFO *infoPtr, BOOL fRedraw, LONG lStart)
1207 if (!GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_ENABLESELRANGE)
1208 return 0;
1210 infoPtr->lSelMin = lStart;
1211 infoPtr->flags |=TB_SELECTIONCHANGED;
1213 if (infoPtr->lSelMin < infoPtr->lRangeMin)
1214 infoPtr->lSelMin = infoPtr->lRangeMin;
1216 if (fRedraw) TRACKBAR_InvalidateAll(infoPtr);
1218 return 0;
1222 static LRESULT inline
1223 TRACKBAR_SetThumbLength (TRACKBAR_INFO *infoPtr, UINT iLength)
1225 if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_FIXEDLENGTH) {
1226 infoPtr->uThumbLen = iLength;
1227 infoPtr->flags |= TB_THUMBSIZECHANGED;
1228 InvalidateRect (infoPtr->hwndSelf, &infoPtr->rcThumb, FALSE);
1231 return 0;
1235 static LRESULT inline
1236 TRACKBAR_SetTic (TRACKBAR_INFO *infoPtr, LONG lPos)
1238 if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_AUTOTICKS)
1239 return FALSE;
1241 if ((lPos < infoPtr->lRangeMin) || (lPos> infoPtr->lRangeMax))
1242 return FALSE;
1244 TRACE("lPos=%ld\n", lPos);
1246 infoPtr->uNumTics++;
1247 infoPtr->tics=ReAlloc( infoPtr->tics,
1248 (infoPtr->uNumTics)*sizeof (DWORD));
1249 if (!infoPtr->tics) {
1250 infoPtr->uNumTics = 0;
1251 notify(infoPtr, NM_OUTOFMEMORY);
1252 return FALSE;
1254 infoPtr->tics[infoPtr->uNumTics-1] = lPos;
1256 TRACKBAR_InvalidateAll(infoPtr);
1258 return TRUE;
1262 static LRESULT inline
1263 TRACKBAR_SetTicFreq (TRACKBAR_INFO *infoPtr, WORD wFreq)
1265 if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_AUTOTICKS) {
1266 infoPtr->uTicFreq = wFreq;
1267 TRACKBAR_RecalculateTics (infoPtr);
1268 TRACKBAR_InvalidateAll(infoPtr);
1271 return 0;
1275 static INT inline
1276 TRACKBAR_SetTipSide (TRACKBAR_INFO *infoPtr, INT fLocation)
1278 INT fTemp = infoPtr->fLocation;
1280 infoPtr->fLocation = fLocation;
1282 return fTemp;
1286 static LRESULT inline
1287 TRACKBAR_SetToolTips (TRACKBAR_INFO *infoPtr, HWND hwndTT)
1289 infoPtr->hwndToolTip = hwndTT;
1291 return 0;
1295 static BOOL inline
1296 TRACKBAR_SetUnicodeFormat (TRACKBAR_INFO *infoPtr, BOOL fUnicode)
1298 BOOL bTemp = infoPtr->bUnicode;
1300 infoPtr->bUnicode = fUnicode;
1302 return bTemp;
1306 static LRESULT
1307 TRACKBAR_InitializeThumb (TRACKBAR_INFO *infoPtr)
1309 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
1310 RECT rect;
1311 int clientWidth, clientMetric;
1313 /* initial thumb length */
1314 clientMetric = (dwStyle & TBS_ENABLESELRANGE) ? 23 : 21;
1315 GetClientRect(infoPtr->hwndSelf,&rect);
1316 if (dwStyle & TBS_VERT) {
1317 clientWidth = rect.right - rect.left;
1318 } else {
1319 clientWidth = rect.bottom - rect.top;
1321 if (clientWidth >= clientMetric)
1322 infoPtr->uThumbLen = clientMetric;
1323 else
1324 infoPtr->uThumbLen = clientWidth > 9 ? clientWidth - 6 : 4;
1326 TRACKBAR_CalcChannel (infoPtr);
1327 TRACKBAR_UpdateThumb (infoPtr);
1328 infoPtr->flags &= ~TB_SELECTIONCHANGED;
1330 return 0;
1334 static LRESULT
1335 TRACKBAR_Create (HWND hwnd, LPCREATESTRUCTW lpcs)
1337 TRACKBAR_INFO *infoPtr;
1338 DWORD oldStyle, newStyle;
1340 infoPtr = (TRACKBAR_INFO *)Alloc (sizeof(TRACKBAR_INFO));
1341 if (!infoPtr) return -1;
1342 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
1344 /* set default values */
1345 infoPtr->hwndSelf = hwnd;
1346 infoPtr->lRangeMin = 0;
1347 infoPtr->lRangeMax = 100;
1348 infoPtr->lLineSize = 1;
1349 infoPtr->lPageSize = 20;
1350 infoPtr->lSelMin = 0;
1351 infoPtr->lSelMax = 0;
1352 infoPtr->lPos = 0;
1353 infoPtr->fLocation = -1;
1354 infoPtr->uNumTics = 0; /* start and end tic are not included in count*/
1355 infoPtr->uTicFreq = 1;
1356 infoPtr->tics = NULL;
1357 infoPtr->hwndNotify= lpcs->hwndParent;
1359 TRACKBAR_InitializeThumb (infoPtr);
1361 oldStyle = newStyle = GetWindowLongW (hwnd, GWL_STYLE);
1362 if (oldStyle & TBS_VERT) {
1363 if (! (oldStyle & (TBS_LEFT | TBS_RIGHT | TBS_BOTH)) )
1364 newStyle |= TBS_RIGHT;
1365 } else {
1366 if (! (oldStyle & (TBS_TOP | TBS_BOTTOM | TBS_BOTH)) )
1367 newStyle |= TBS_BOTTOM;
1369 if (newStyle != oldStyle)
1370 SetWindowLongW (hwnd, GWL_STYLE, newStyle);
1372 /* Create tooltip control */
1373 if (newStyle & TBS_TOOLTIPS) {
1375 infoPtr->hwndToolTip =
1376 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, 0,
1377 CW_USEDEFAULT, CW_USEDEFAULT,
1378 CW_USEDEFAULT, CW_USEDEFAULT,
1379 hwnd, 0, 0, 0);
1381 if (infoPtr->hwndToolTip) {
1382 TTTOOLINFOW ti;
1383 ZeroMemory (&ti, sizeof(ti));
1384 ti.cbSize = sizeof(ti);
1385 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
1386 ti.hwnd = hwnd;
1388 SendMessageW (infoPtr->hwndToolTip, TTM_ADDTOOLW, 0, (LPARAM)&ti);
1392 return 0;
1396 static LRESULT
1397 TRACKBAR_Destroy (TRACKBAR_INFO *infoPtr)
1399 /* delete tooltip control */
1400 if (infoPtr->hwndToolTip)
1401 DestroyWindow (infoPtr->hwndToolTip);
1403 Free (infoPtr);
1404 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
1405 return 0;
1409 static LRESULT
1410 TRACKBAR_KillFocus (TRACKBAR_INFO *infoPtr, HWND hwndGetFocus)
1412 TRACE("\n");
1413 infoPtr->bFocussed = FALSE;
1414 TRACKBAR_InvalidateAll(infoPtr);
1416 return 0;
1419 static LRESULT
1420 TRACKBAR_LButtonDown (TRACKBAR_INFO *infoPtr, DWORD fwKeys, POINTS pts)
1422 POINT clickPoint = { pts.x, pts.y };
1424 SetFocus(infoPtr->hwndSelf);
1426 if (PtInRect(&infoPtr->rcThumb, clickPoint)) {
1427 infoPtr->flags |= TB_DRAG_MODE;
1428 SetCapture (infoPtr->hwndSelf);
1429 TRACKBAR_UpdateToolTip (infoPtr);
1430 TRACKBAR_ActivateToolTip (infoPtr, TRUE);
1431 TRACKBAR_InvalidateThumb(infoPtr, infoPtr->lPos);
1432 } else {
1433 LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
1434 if (dir == 0) return 0;
1435 infoPtr->flags |= (dir < 0) ? TB_AUTO_PAGE_LEFT : TB_AUTO_PAGE_RIGHT;
1436 TRACKBAR_AutoPage (infoPtr, clickPoint);
1437 SetCapture (infoPtr->hwndSelf);
1438 SetTimer(infoPtr->hwndSelf, TB_REFRESH_TIMER, TB_REFRESH_DELAY, 0);
1441 return 0;
1445 static LRESULT
1446 TRACKBAR_LButtonUp (TRACKBAR_INFO *infoPtr, DWORD fwKeys, POINTS pts)
1448 if (infoPtr->flags & TB_DRAG_MODE) {
1449 notify_with_scroll (infoPtr, TB_THUMBPOSITION | (infoPtr->lPos<<16));
1450 notify_with_scroll (infoPtr, TB_ENDTRACK);
1451 infoPtr->flags &= ~TB_DRAG_MODE;
1452 ReleaseCapture ();
1453 notify(infoPtr, NM_RELEASEDCAPTURE);
1454 TRACKBAR_ActivateToolTip(infoPtr, FALSE);
1455 TRACKBAR_InvalidateThumb(infoPtr, infoPtr->lPos);
1457 if (infoPtr->flags & TB_AUTO_PAGE) {
1458 KillTimer (infoPtr->hwndSelf, TB_REFRESH_TIMER);
1459 infoPtr->flags &= ~TB_AUTO_PAGE;
1460 notify_with_scroll (infoPtr, TB_ENDTRACK);
1461 ReleaseCapture ();
1462 notify(infoPtr, NM_RELEASEDCAPTURE);
1465 return 0;
1469 static LRESULT
1470 TRACKBAR_CaptureChanged (TRACKBAR_INFO *infoPtr)
1472 notify_with_scroll (infoPtr, TB_ENDTRACK);
1473 return 0;
1477 static LRESULT
1478 TRACKBAR_Paint (TRACKBAR_INFO *infoPtr, HDC hdc)
1480 if (hdc) {
1481 TRACKBAR_Refresh(infoPtr, hdc);
1482 } else {
1483 PAINTSTRUCT ps;
1484 hdc = BeginPaint (infoPtr->hwndSelf, &ps);
1485 TRACKBAR_Refresh (infoPtr, hdc);
1486 EndPaint (infoPtr->hwndSelf, &ps);
1489 return 0;
1493 static LRESULT
1494 TRACKBAR_SetFocus (TRACKBAR_INFO *infoPtr, HWND hwndLoseFocus)
1496 TRACE("\n");
1497 infoPtr->bFocussed = TRUE;
1498 TRACKBAR_InvalidateAll(infoPtr);
1500 return 0;
1504 static LRESULT
1505 TRACKBAR_Size (TRACKBAR_INFO *infoPtr, DWORD fwSizeType, INT nWidth, INT nHeight)
1507 TRACKBAR_InitializeThumb (infoPtr);
1508 TRACKBAR_AlignBuddies (infoPtr);
1510 return 0;
1514 static LRESULT
1515 TRACKBAR_Timer (TRACKBAR_INFO *infoPtr, INT wTimerID, TIMERPROC *tmrpc)
1517 if (infoPtr->flags & TB_AUTO_PAGE) {
1518 POINT pt;
1519 if (GetCursorPos(&pt))
1520 if (ScreenToClient(infoPtr->hwndSelf, &pt))
1521 TRACKBAR_AutoPage(infoPtr, pt);
1523 return 0;
1527 static LRESULT
1528 TRACKBAR_MouseMove (TRACKBAR_INFO *infoPtr, DWORD fwKeys, POINTS pts)
1530 DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
1531 INT clickPlace = (dwStyle & TBS_VERT) ? pts.y : pts.x;
1532 LONG dragPos, oldPos = infoPtr->lPos;
1534 TRACE("(x=%d. y=%d)\n", pts.x, pts.y);
1536 if (infoPtr->flags & TB_AUTO_PAGE) {
1537 POINT pt;
1538 POINTSTOPOINT(pt, pts);
1539 TRACKBAR_AutoPage (infoPtr, pt);
1540 return TRUE;
1543 if (!(infoPtr->flags & TB_DRAG_MODE)) return TRUE;
1545 dragPos = TRACKBAR_ConvertPlaceToPosition (infoPtr, clickPlace,
1546 dwStyle & TBS_VERT);
1547 if (dragPos == oldPos) return TRUE;
1549 infoPtr->lPos = dragPos;
1551 infoPtr->flags |= TB_THUMBPOSCHANGED;
1552 notify_with_scroll (infoPtr, TB_THUMBTRACK | (infoPtr->lPos<<16));
1555 TRACKBAR_InvalidateThumbMove(infoPtr, oldPos, dragPos);
1556 UpdateWindow (infoPtr->hwndSelf);
1558 return TRUE;
1561 static BOOL
1562 TRACKBAR_KeyDown (TRACKBAR_INFO *infoPtr, INT nVirtKey, DWORD lKeyData)
1564 DWORD style = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
1565 BOOL downIsLeft = style & TBS_DOWNISLEFT;
1566 BOOL vert = style & TBS_VERT;
1567 LONG pos = infoPtr->lPos;
1569 TRACE("%x\n", nVirtKey);
1571 switch (nVirtKey) {
1572 case VK_UP:
1573 if (!vert && downIsLeft) TRACKBAR_LineDown(infoPtr);
1574 else TRACKBAR_LineUp(infoPtr);
1575 break;
1576 case VK_LEFT:
1577 if (vert && downIsLeft) TRACKBAR_LineDown(infoPtr);
1578 else TRACKBAR_LineUp(infoPtr);
1579 break;
1580 case VK_DOWN:
1581 if (!vert && downIsLeft) TRACKBAR_LineUp(infoPtr);
1582 else TRACKBAR_LineDown(infoPtr);
1583 break;
1584 case VK_RIGHT:
1585 if (vert && downIsLeft) TRACKBAR_LineUp(infoPtr);
1586 else TRACKBAR_LineDown(infoPtr);
1587 break;
1588 case VK_NEXT:
1589 if (!vert && downIsLeft) TRACKBAR_PageUp(infoPtr);
1590 else TRACKBAR_PageDown(infoPtr);
1591 break;
1592 case VK_PRIOR:
1593 if (!vert && downIsLeft) TRACKBAR_PageDown(infoPtr);
1594 else TRACKBAR_PageUp(infoPtr);
1595 break;
1596 case VK_HOME:
1597 if (infoPtr->lPos == infoPtr->lRangeMin) return FALSE;
1598 infoPtr->lPos = infoPtr->lRangeMin;
1599 notify_with_scroll (infoPtr, TB_TOP);
1600 break;
1601 case VK_END:
1602 if (infoPtr->lPos == infoPtr->lRangeMax) return FALSE;
1603 infoPtr->lPos = infoPtr->lRangeMax;
1604 notify_with_scroll (infoPtr, TB_BOTTOM);
1605 break;
1608 if (pos != infoPtr->lPos) {
1609 infoPtr->flags |=TB_THUMBPOSCHANGED;
1610 TRACKBAR_InvalidateThumbMove (infoPtr, pos, infoPtr->lPos);
1613 return TRUE;
1617 static BOOL inline
1618 TRACKBAR_KeyUp (TRACKBAR_INFO *infoPtr, INT nVirtKey, DWORD lKeyData)
1620 switch (nVirtKey) {
1621 case VK_LEFT:
1622 case VK_UP:
1623 case VK_RIGHT:
1624 case VK_DOWN:
1625 case VK_NEXT:
1626 case VK_PRIOR:
1627 case VK_HOME:
1628 case VK_END:
1629 notify_with_scroll (infoPtr, TB_ENDTRACK);
1631 return TRUE;
1635 static LRESULT WINAPI
1636 TRACKBAR_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1638 TRACKBAR_INFO *infoPtr = (TRACKBAR_INFO *)GetWindowLongPtrW (hwnd, 0);
1640 TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1642 if (!infoPtr && (uMsg != WM_CREATE))
1643 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1645 switch (uMsg)
1647 case TBM_CLEARSEL:
1648 return TRACKBAR_ClearSel (infoPtr, (BOOL)wParam);
1650 case TBM_CLEARTICS:
1651 return TRACKBAR_ClearTics (infoPtr, (BOOL)wParam);
1653 case TBM_GETBUDDY:
1654 return (LRESULT)(wParam ? infoPtr->hwndBuddyLA : infoPtr->hwndBuddyRB);
1656 case TBM_GETCHANNELRECT:
1657 return TRACKBAR_GetChannelRect (infoPtr, (LPRECT)lParam);
1659 case TBM_GETLINESIZE:
1660 return infoPtr->lLineSize;
1662 case TBM_GETNUMTICS:
1663 return TRACKBAR_GetNumTics (infoPtr);
1665 case TBM_GETPAGESIZE:
1666 return infoPtr->lPageSize;
1668 case TBM_GETPOS:
1669 return infoPtr->lPos;
1671 case TBM_GETPTICS:
1672 return (LRESULT)infoPtr->tics;
1674 case TBM_GETRANGEMAX:
1675 return infoPtr->lRangeMax;
1677 case TBM_GETRANGEMIN:
1678 return infoPtr->lRangeMin;
1680 case TBM_GETSELEND:
1681 return infoPtr->lSelMax;
1683 case TBM_GETSELSTART:
1684 return infoPtr->lSelMin;
1686 case TBM_GETTHUMBLENGTH:
1687 return infoPtr->uThumbLen;
1689 case TBM_GETTHUMBRECT:
1690 return CopyRect((LPRECT)lParam, &infoPtr->rcThumb);
1692 case TBM_GETTIC:
1693 return TRACKBAR_GetTic (infoPtr, (INT)wParam);
1695 case TBM_GETTICPOS:
1696 return TRACKBAR_GetTicPos (infoPtr, (INT)wParam);
1698 case TBM_GETTOOLTIPS:
1699 return (LRESULT)infoPtr->hwndToolTip;
1701 case TBM_GETUNICODEFORMAT:
1702 return infoPtr->bUnicode;
1704 case TBM_SETBUDDY:
1705 return (LRESULT) TRACKBAR_SetBuddy(infoPtr, (BOOL)wParam, (HWND)lParam);
1707 case TBM_SETLINESIZE:
1708 return TRACKBAR_SetLineSize (infoPtr, (LONG)lParam);
1710 case TBM_SETPAGESIZE:
1711 return TRACKBAR_SetPageSize (infoPtr, (LONG)lParam);
1713 case TBM_SETPOS:
1714 return TRACKBAR_SetPos (infoPtr, (BOOL)wParam, (LONG)lParam);
1716 case TBM_SETRANGE:
1717 return TRACKBAR_SetRange (infoPtr, (BOOL)wParam, (LONG)lParam);
1719 case TBM_SETRANGEMAX:
1720 return TRACKBAR_SetRangeMax (infoPtr, (BOOL)wParam, (LONG)lParam);
1722 case TBM_SETRANGEMIN:
1723 return TRACKBAR_SetRangeMin (infoPtr, (BOOL)wParam, (LONG)lParam);
1725 case TBM_SETSEL:
1726 return TRACKBAR_SetSel (infoPtr, (BOOL)wParam, (LONG)lParam);
1728 case TBM_SETSELEND:
1729 return TRACKBAR_SetSelEnd (infoPtr, (BOOL)wParam, (LONG)lParam);
1731 case TBM_SETSELSTART:
1732 return TRACKBAR_SetSelStart (infoPtr, (BOOL)wParam, (LONG)lParam);
1734 case TBM_SETTHUMBLENGTH:
1735 return TRACKBAR_SetThumbLength (infoPtr, (UINT)wParam);
1737 case TBM_SETTIC:
1738 return TRACKBAR_SetTic (infoPtr, (LONG)lParam);
1740 case TBM_SETTICFREQ:
1741 return TRACKBAR_SetTicFreq (infoPtr, (WORD)wParam);
1743 case TBM_SETTIPSIDE:
1744 return TRACKBAR_SetTipSide (infoPtr, (INT)wParam);
1746 case TBM_SETTOOLTIPS:
1747 return TRACKBAR_SetToolTips (infoPtr, (HWND)wParam);
1749 case TBM_SETUNICODEFORMAT:
1750 return TRACKBAR_SetUnicodeFormat (infoPtr, (BOOL)wParam);
1753 case WM_CAPTURECHANGED:
1754 return TRACKBAR_CaptureChanged (infoPtr);
1756 case WM_CREATE:
1757 return TRACKBAR_Create (hwnd, (LPCREATESTRUCTW)lParam);
1759 case WM_DESTROY:
1760 return TRACKBAR_Destroy (infoPtr);
1762 /* case WM_ENABLE: */
1764 case WM_ERASEBKGND:
1765 return 0;
1767 case WM_GETDLGCODE:
1768 return DLGC_WANTARROWS;
1770 case WM_KEYDOWN:
1771 return TRACKBAR_KeyDown (infoPtr, (INT)wParam, (DWORD)lParam);
1773 case WM_KEYUP:
1774 return TRACKBAR_KeyUp (infoPtr, (INT)wParam, (DWORD)lParam);
1776 case WM_KILLFOCUS:
1777 return TRACKBAR_KillFocus (infoPtr, (HWND)wParam);
1779 case WM_LBUTTONDOWN:
1780 return TRACKBAR_LButtonDown (infoPtr, wParam, MAKEPOINTS(lParam));
1782 case WM_LBUTTONUP:
1783 return TRACKBAR_LButtonUp (infoPtr, wParam, MAKEPOINTS(lParam));
1785 case WM_MOUSEMOVE:
1786 return TRACKBAR_MouseMove (infoPtr, wParam, MAKEPOINTS(lParam));
1788 case WM_PAINT:
1789 return TRACKBAR_Paint (infoPtr, (HDC)wParam);
1791 case WM_SETFOCUS:
1792 return TRACKBAR_SetFocus (infoPtr, (HWND)wParam);
1794 case WM_SIZE:
1795 return TRACKBAR_Size (infoPtr, wParam, LOWORD(lParam), HIWORD(lParam));
1797 case WM_TIMER:
1798 return TRACKBAR_Timer (infoPtr, (INT)wParam, (TIMERPROC *)lParam);
1800 case WM_WININICHANGE:
1801 return TRACKBAR_InitializeThumb (infoPtr);
1803 default:
1804 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
1805 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
1806 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1808 return 0;
1812 void TRACKBAR_Register (void)
1814 WNDCLASSW wndClass;
1816 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
1817 wndClass.style = CS_GLOBALCLASS;
1818 wndClass.lpfnWndProc = (WNDPROC)TRACKBAR_WindowProc;
1819 wndClass.cbClsExtra = 0;
1820 wndClass.cbWndExtra = sizeof(TRACKBAR_INFO *);
1821 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1822 wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1823 wndClass.lpszClassName = TRACKBAR_CLASSW;
1825 RegisterClassW (&wndClass);
1829 void TRACKBAR_Unregister (void)
1831 UnregisterClassW (TRACKBAR_CLASSW, NULL);