xref: /reactos/dll/win32/uxtheme/ncscrollbar.c (revision 1734f297)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS uxtheme.dll
4  * FILE:            dll/win32/uxtheme/ncscrollbar.c
5  * PURPOSE:         uxtheme scrollbar support
6  * PROGRAMMER:      Giannis Adamopoulos
7  *                  This file is heavily based on code from the wine project:
8  *                  Copyright 1993 Martin Ayotte
9  *                  Copyright 1994, 1996 Alexandre Julliard
10  */
11 
12 #include "uxthemep.h"
13 
14 #include <assert.h>
15 
16 static void ScreenToWindow( HWND hWnd, POINT* pt)
17 {
18     RECT rcWnd;
19     GetWindowRect(hWnd, &rcWnd);
20     pt->x -= rcWnd.left;
21     pt->y -= rcWnd.top;
22 }
23 
24 static BOOL SCROLL_IsVertical(HWND hwnd, INT nBar)
25 {
26     switch(nBar)
27     {
28     case SB_HORZ:
29         return FALSE;
30     case SB_VERT:
31         return TRUE;
32     default:
33         assert(FALSE);
34         return FALSE;
35     }
36 }
37 
38 static LONG SCROLL_getObjectId(INT nBar)
39 {
40     switch(nBar)
41     {
42     case SB_HORZ:
43         return OBJID_HSCROLL;
44     case SB_VERT:
45         return OBJID_VSCROLL;
46     default:
47         assert(FALSE);
48         return 0;
49     }
50 }
51 
52 /***********************************************************************
53  *           SCROLL_PtInRectEx
54  */
55 static BOOL SCROLL_PtInRectEx( LPRECT lpRect, POINT pt, BOOL vertical )
56 {
57     RECT rect = *lpRect;
58     int scrollbarWidth;
59 
60     /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
61      * still be considered in the scrollbar. */
62     if (vertical)
63     {
64         scrollbarWidth = lpRect->right - lpRect->left;
65         rect.left -= scrollbarWidth*8;
66         rect.right += scrollbarWidth*8;
67         rect.top -= scrollbarWidth*2;
68         rect.bottom += scrollbarWidth*2;
69     }
70     else
71     {
72         scrollbarWidth = lpRect->bottom - lpRect->top;
73         rect.left -= scrollbarWidth*2;
74         rect.right += scrollbarWidth*2;
75         rect.top -= scrollbarWidth*8;
76         rect.bottom += scrollbarWidth*8;
77     }
78     return PtInRect( &rect, pt );
79 }
80 
81 
82 /***********************************************************************
83  *           SCROLL_HitTest
84  *
85  * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
86  */
87 static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, SCROLLBARINFO* psbi, BOOL vertical,
88                                            POINT pt, BOOL bDragging )
89 {
90     if ( (bDragging && !SCROLL_PtInRectEx( &psbi->rcScrollBar, pt, vertical )) ||
91 	     (!PtInRect( &psbi->rcScrollBar, pt )) )
92     {
93          return SCROLL_NOWHERE;
94     }
95 
96     if (vertical)
97     {
98         if (pt.y < psbi->rcScrollBar.top + psbi->dxyLineButton)
99             return SCROLL_TOP_ARROW;
100         if (pt.y >= psbi->rcScrollBar.bottom - psbi->dxyLineButton)
101             return SCROLL_BOTTOM_ARROW;
102         if (!psbi->xyThumbTop)
103             return SCROLL_TOP_RECT;
104         pt.y -= psbi->rcScrollBar.top;
105         if (pt.y < psbi->xyThumbTop)
106             return SCROLL_TOP_RECT;
107         if (pt.y >= psbi->xyThumbBottom)
108             return SCROLL_BOTTOM_RECT;
109     }
110     else  /* horizontal */
111     {
112         if (pt.x < psbi->rcScrollBar.left + psbi->dxyLineButton)
113             return SCROLL_TOP_ARROW;
114         if (pt.x >= psbi->rcScrollBar.right - psbi->dxyLineButton)
115             return SCROLL_BOTTOM_ARROW;
116         if (!psbi->xyThumbTop)
117             return SCROLL_TOP_RECT;
118         pt.x -= psbi->rcScrollBar.left;
119         if (pt.x < psbi->xyThumbTop)
120             return SCROLL_TOP_RECT;
121         if (pt.x >= psbi->xyThumbBottom)
122             return SCROLL_BOTTOM_RECT;
123     }
124     return SCROLL_THUMB;
125 }
126 
127 static void SCROLL_ThemeDrawPart(PDRAW_CONTEXT pcontext, int iPartId,int iStateId,  SCROLLBARINFO* psbi, int htCurrent, int htDown, int htHot, RECT* r)
128 {
129     if (r->right <= r->left || r->bottom <= r->top)
130         return;
131 
132     if(psbi->rgstate[htCurrent] & STATE_SYSTEM_UNAVAILABLE)
133         iStateId += BUTTON_DISABLED - BUTTON_NORMAL;
134     else if (htHot == htCurrent)
135         iStateId += BUTTON_HOT - BUTTON_NORMAL;
136     else if (htDown == htCurrent)
137         iStateId += BUTTON_PRESSED - BUTTON_NORMAL;
138 
139     DrawThemeBackground(pcontext->scrolltheme, pcontext->hDC, iPartId, iStateId, r, NULL);
140 }
141 
142 /***********************************************************************
143  *           SCROLL_DrawArrows
144  *
145  * Draw the scroll bar arrows.
146  */
147 static void SCROLL_DrawArrows( PDRAW_CONTEXT pcontext, SCROLLBARINFO* psbi,
148                                BOOL vertical, int htDown, int htHot )
149 {
150     RECT r;
151     int iStateId;
152 
153     r = psbi->rcScrollBar;
154     if( vertical )
155     {
156         r.bottom = r.top + psbi->dxyLineButton;
157         iStateId = ABS_UPNORMAL;
158     }
159     else
160     {
161         r.right = r.left + psbi->dxyLineButton;
162         iStateId = ABS_LEFTNORMAL;
163     }
164 
165     SCROLL_ThemeDrawPart(pcontext, SBP_ARROWBTN, iStateId, psbi, SCROLL_TOP_ARROW, htDown, htHot, &r);
166 
167     r = psbi->rcScrollBar;
168     if( vertical )
169     {
170         r.top = r.bottom - psbi->dxyLineButton;
171         iStateId = ABS_DOWNNORMAL;
172     }
173     else
174     {
175         iStateId = ABS_RIGHTNORMAL;
176         r.left = r.right - psbi->dxyLineButton;
177     }
178 
179     SCROLL_ThemeDrawPart(pcontext, SBP_ARROWBTN, iStateId, psbi, SCROLL_BOTTOM_ARROW, htDown, htHot, &r);
180 }
181 
182 static void SCROLL_DrawInterior( PDRAW_CONTEXT pcontext, SCROLLBARINFO* psbi,
183                                   INT thumbPos, BOOL vertical,
184                                   int htDown, int htHot )
185 {
186     RECT r, rcPart;
187 
188     /* thumbPos is relative to the edge of the scrollbar */
189 
190     r = psbi->rcScrollBar;
191     if (vertical)
192     {
193         if (thumbPos)
194             thumbPos += pcontext->wi.rcClient.top - pcontext->wi.rcWindow.top;
195         r.top    += psbi->dxyLineButton;
196         r.bottom -= (psbi->dxyLineButton);
197     }
198     else
199     {
200         if (thumbPos)
201             thumbPos += pcontext->wi.rcClient.left - pcontext->wi.rcWindow.left;
202         r.left  += psbi->dxyLineButton;
203         r.right -= psbi->dxyLineButton;
204     }
205 
206     if (r.right <= r.left || r.bottom <= r.top)
207         return;
208 
209     /* Draw the scroll rectangles and thumb */
210 
211     if (!thumbPos)  /* No thumb to draw */
212     {
213         rcPart = r;
214         SCROLL_ThemeDrawPart(pcontext, vertical ? SBP_UPPERTRACKVERT: SBP_UPPERTRACKHORZ , BUTTON_NORMAL, psbi, SCROLL_THUMB, 0, 0, &rcPart);
215         return;
216     }
217 
218     /* Some themes have different bitmaps for the upper and lower tracks
219        It seems that windows use the bitmap for the lower track in the upper track */
220     if (vertical)
221     {
222         rcPart = r;
223         rcPart.bottom = thumbPos;
224         SCROLL_ThemeDrawPart(pcontext, SBP_LOWERTRACKVERT, BUTTON_NORMAL, psbi, SCROLL_TOP_RECT, htDown, htHot, &rcPart);
225         r.top = rcPart.bottom;
226 
227         rcPart = r;
228         rcPart.top += psbi->xyThumbBottom - psbi->xyThumbTop;
229         SCROLL_ThemeDrawPart(pcontext, SBP_UPPERTRACKVERT, BUTTON_NORMAL, psbi, SCROLL_BOTTOM_RECT, htDown, htHot, &rcPart);
230         r.bottom = rcPart.top;
231 
232         SCROLL_ThemeDrawPart(pcontext, SBP_THUMBBTNVERT, BUTTON_NORMAL, psbi, SCROLL_THUMB, htDown, htHot, &r);
233         SCROLL_ThemeDrawPart(pcontext, SBP_GRIPPERVERT, BUTTON_NORMAL, psbi, SCROLL_THUMB, htDown, htHot, &r);
234     }
235     else  /* horizontal */
236     {
237         rcPart = r;
238         rcPart.right = thumbPos;
239         SCROLL_ThemeDrawPart(pcontext, SBP_LOWERTRACKHORZ, BUTTON_NORMAL, psbi, SCROLL_TOP_RECT, htDown, htHot, &rcPart);
240         r.left = rcPart.right;
241 
242         rcPart = r;
243         rcPart.left += psbi->xyThumbBottom - psbi->xyThumbTop;
244         SCROLL_ThemeDrawPart(pcontext, SBP_UPPERTRACKHORZ, BUTTON_NORMAL, psbi, SCROLL_BOTTOM_RECT, htDown, htHot, &rcPart);
245         r.right = rcPart.left;
246 
247         SCROLL_ThemeDrawPart(pcontext, SBP_THUMBBTNHORZ, BUTTON_NORMAL, psbi, SCROLL_THUMB, htDown, htHot, &r);
248         SCROLL_ThemeDrawPart(pcontext, SBP_GRIPPERHORZ, BUTTON_NORMAL, psbi, SCROLL_THUMB, htDown, htHot, &r);
249     }
250 }
251 
252 static void SCROLL_DrawMovingThumb(PWND_DATA pwndData, PDRAW_CONTEXT pcontext, SCROLLBARINFO* psbi,  BOOL vertical)
253 {
254   INT pos = pwndData->SCROLL_TrackingPos;
255   INT max_size;
256 
257   if( vertical )
258       max_size = psbi->rcScrollBar.bottom - psbi->rcScrollBar.top;
259   else
260       max_size = psbi->rcScrollBar.right - psbi->rcScrollBar.left;
261 
262   max_size -= psbi->xyThumbBottom - psbi->xyThumbTop + psbi->dxyLineButton;
263 
264   if( pos < (psbi->dxyLineButton) )
265     pos = (psbi->dxyLineButton);
266   else if( pos > max_size )
267     pos = max_size;
268 
269   SCROLL_DrawInterior(pcontext, psbi, pos, vertical, SCROLL_THUMB, 0);
270 
271   pwndData->SCROLL_MovingThumb = !pwndData->SCROLL_MovingThumb;
272 }
273 
274 
275 void
276 ThemeDrawScrollBar(PDRAW_CONTEXT pcontext, INT nBar, POINT* pt)
277 {
278     SCROLLINFO si;
279     SCROLLBARINFO sbi;
280     BOOL vertical;
281     enum SCROLL_HITTEST htHot = SCROLL_NOWHERE;
282     PWND_DATA pwndData;
283 
284     if (((nBar == SB_VERT) && !(pcontext->wi.dwStyle & WS_VSCROLL)) ||
285         ((nBar == SB_HORZ) && !(pcontext->wi.dwStyle & WS_HSCROLL))) return;
286 
287     if (!(pwndData = ThemeGetWndData(pcontext->hWnd)))
288         return;
289 
290     if (pwndData->SCROLL_TrackingWin)
291         return;
292 
293     /* Retrieve scrollbar info */
294     sbi.cbSize = sizeof(sbi);
295     si.cbSize = sizeof(si);
296     si.fMask = SIF_ALL ;
297     GetScrollInfo(pcontext->hWnd, nBar, &si);
298     GetScrollBarInfo(pcontext->hWnd, SCROLL_getObjectId(nBar), &sbi);
299     vertical = SCROLL_IsVertical(pcontext->hWnd, nBar);
300     if(sbi.rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_UNAVAILABLE  &&
301        sbi.rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_UNAVAILABLE  )
302     {
303         sbi.xyThumbTop = 0;
304     }
305 
306     /* The scrollbar rect is in screen coordinates */
307     OffsetRect(&sbi.rcScrollBar, -pcontext->wi.rcWindow.left, -pcontext->wi.rcWindow.top);
308 
309     if(pt)
310     {
311         ScreenToWindow(pcontext->hWnd, pt);
312         htHot = SCROLL_HitTest(pcontext->hWnd, &sbi, vertical, *pt, FALSE);
313     }
314 
315     /* do not draw if the scrollbar rectangle is empty */
316     if(IsRectEmpty(&sbi.rcScrollBar)) return;
317 
318     /* Draw the scrollbar */
319     SCROLL_DrawArrows( pcontext, &sbi, vertical, 0, htHot );
320 	SCROLL_DrawInterior( pcontext, &sbi, sbi.xyThumbTop, vertical, 0, htHot );
321 }
322 
323 
324 
325 /***********************************************************************
326  *           SCROLL_ClipPos
327  */
328 static POINT SCROLL_ClipPos( LPRECT lpRect, POINT pt )
329 {
330     if( pt.x < lpRect->left )
331 	    pt.x = lpRect->left;
332     else
333         if( pt.x > lpRect->right )
334 	pt.x = lpRect->right;
335 
336     if( pt.y < lpRect->top )
337 	    pt.y = lpRect->top;
338     else
339     if( pt.y > lpRect->bottom )
340 	    pt.y = lpRect->bottom;
341 
342     return pt;
343 }
344 
345 
346 
347 /***********************************************************************
348  *           SCROLL_GetThumbVal
349  *
350  * Compute the current scroll position based on the thumb position in pixels
351  * from the top of the scroll-bar.
352  */
353 static UINT SCROLL_GetThumbVal( SCROLLINFO *psi, RECT *rect,
354                                   BOOL vertical, INT pos )
355 {
356     INT thumbSize;
357     INT pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
358     INT range;
359 
360     if ((pixels -= 2*(GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP)) <= 0)
361         return psi->nMin;
362 
363     if (psi->nPage)
364     {
365         thumbSize = MulDiv(pixels,psi->nPage,(psi->nMax-psi->nMin+1));
366         if (thumbSize < SCROLL_MIN_THUMB) thumbSize = SCROLL_MIN_THUMB;
367     }
368     else thumbSize = GetSystemMetrics(SM_CXVSCROLL);
369 
370     if ((pixels -= thumbSize) <= 0) return psi->nMin;
371 
372     pos = max( 0, pos - (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP) );
373     if (pos > pixels) pos = pixels;
374 
375     if (!psi->nPage)
376         range = psi->nMax - psi->nMin;
377     else
378         range = psi->nMax - psi->nMin - psi->nPage + 1;
379 
380     return psi->nMin + MulDiv(pos, range, pixels);
381 }
382 
383 static void
384 SCROLL_HandleScrollEvent(PWND_DATA pwndData, HWND hwnd, INT nBar, UINT msg, POINT pt)
385 {
386       /* Previous mouse position for timer events */
387     static POINT prevPt;
388       /* Thumb position when tracking started. */
389     static UINT trackThumbPos;
390       /* Position in the scroll-bar of the last button-down event. */
391     static INT lastClickPos;
392       /* Position in the scroll-bar of the last mouse event. */
393     static INT lastMousePos;
394 
395     enum SCROLL_HITTEST hittest;
396     HWND hwndOwner, hwndCtl;
397     BOOL vertical;
398     SCROLLINFO si;
399     SCROLLBARINFO sbi;
400     DRAW_CONTEXT context;
401 
402     si.cbSize = sizeof(si);
403     sbi.cbSize = sizeof(sbi);
404     si.fMask = SIF_ALL;
405     GetScrollInfo(hwnd, nBar, &si);
406     GetScrollBarInfo(hwnd, SCROLL_getObjectId(nBar), &sbi);
407     vertical = SCROLL_IsVertical(hwnd, nBar);
408     if(sbi.rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_UNAVAILABLE  &&
409        sbi.rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_UNAVAILABLE  )
410     {
411         return;
412     }
413 
414     if ((pwndData->SCROLL_trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN))
415 		  return;
416 
417     ThemeInitDrawContext(&context, hwnd, 0);
418 
419     /* The scrollbar rect is in screen coordinates */
420     OffsetRect(&sbi.rcScrollBar, -context.wi.rcWindow.left, -context.wi.rcWindow.top);
421 
422     hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
423     hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;
424 
425     switch(msg)
426     {
427       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
428           HideCaret(hwnd);  /* hide caret while holding down LBUTTON */
429           pwndData->SCROLL_trackVertical = vertical;
430           pwndData->SCROLL_trackHitTest  = hittest = SCROLL_HitTest( hwnd, &sbi, vertical, pt, FALSE );
431           lastClickPos  = vertical ? (pt.y - sbi.rcScrollBar.top) : (pt.x - sbi.rcScrollBar.left);
432           lastMousePos  = lastClickPos;
433           trackThumbPos = sbi.xyThumbTop;
434           prevPt = pt;
435           SetCapture( hwnd );
436           break;
437 
438       case WM_MOUSEMOVE:
439           hittest = SCROLL_HitTest( hwnd, &sbi, vertical, pt, TRUE );
440           prevPt = pt;
441           break;
442 
443       case WM_LBUTTONUP:
444           hittest = SCROLL_NOWHERE;
445           ReleaseCapture();
446           /* if scrollbar has focus, show back caret */
447           if (hwnd==GetFocus())
448               ShowCaret(hwnd);
449           break;
450 
451       case WM_SYSTIMER:
452           pt = prevPt;
453           hittest = SCROLL_HitTest( hwnd, &sbi, vertical, pt, FALSE );
454           break;
455 
456       default:
457           return;  /* Should never happen */
458     }
459 
460     //TRACE("Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n",
461     //      hwnd, nBar, SPY_GetMsgName(msg,hwnd), pt.x, pt.y, hittest );
462 
463     switch(pwndData->SCROLL_trackHitTest)
464     {
465     case SCROLL_NOWHERE:  /* No tracking in progress */
466         break;
467 
468     case SCROLL_TOP_ARROW:
469         if (hittest == pwndData->SCROLL_trackHitTest)
470         {
471             SCROLL_DrawArrows( &context, &sbi, vertical, pwndData->SCROLL_trackHitTest, 0 );
472             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
473             {
474                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
475                                 SB_LINEUP, (LPARAM)hwndCtl );
476 	        }
477 
478         SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
479                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
480         }
481         else
482         {
483             SCROLL_DrawArrows( &context, &sbi, vertical, 0, 0 );
484             KillSystemTimer( hwnd, SCROLL_TIMER );
485         }
486 
487         break;
488 
489     case SCROLL_TOP_RECT:
490         SCROLL_DrawInterior( &context, &sbi, sbi.xyThumbTop, vertical, pwndData->SCROLL_trackHitTest, 0);
491         if (hittest == pwndData->SCROLL_trackHitTest)
492         {
493             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
494             {
495                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
496                                 SB_PAGEUP, (LPARAM)hwndCtl );
497             }
498             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
499                               SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
500         }
501         else KillSystemTimer( hwnd, SCROLL_TIMER );
502         break;
503 
504     case SCROLL_THUMB:
505         if (msg == WM_LBUTTONDOWN)
506         {
507             pwndData->SCROLL_TrackingWin = hwnd;
508             pwndData->SCROLL_TrackingBar = nBar;
509             pwndData->SCROLL_TrackingPos = trackThumbPos + lastMousePos - lastClickPos;
510             pwndData->SCROLL_TrackingVal = SCROLL_GetThumbVal( &si, &sbi.rcScrollBar,
511                                                      vertical, pwndData->SCROLL_TrackingPos );
512 	        if (!pwndData->SCROLL_MovingThumb)
513 		        SCROLL_DrawMovingThumb(pwndData, &context, &sbi, vertical);
514         }
515         else if (msg == WM_LBUTTONUP)
516         {
517 	        if (pwndData->SCROLL_MovingThumb)
518 		        SCROLL_DrawMovingThumb(pwndData, &context, &sbi, vertical);
519 
520             SCROLL_DrawInterior(  &context, &sbi, sbi.xyThumbTop, vertical, 0, pwndData->SCROLL_trackHitTest );
521         }
522         else  /* WM_MOUSEMOVE */
523         {
524             INT pos;
525 
526             if (!SCROLL_PtInRectEx( &sbi.rcScrollBar, pt, vertical ))
527                 pos = lastClickPos;
528             else
529             {
530                 pt = SCROLL_ClipPos( &sbi.rcScrollBar, pt );
531                 pos = vertical ? (pt.y - sbi.rcScrollBar.top) : (pt.x - sbi.rcScrollBar.left);
532             }
533             if ( (pos != lastMousePos) || (!pwndData->SCROLL_MovingThumb) )
534             {
535                 if (pwndData->SCROLL_MovingThumb)
536                     SCROLL_DrawMovingThumb(pwndData, &context, &sbi, vertical);
537                 lastMousePos = pos;
538                 pwndData->SCROLL_TrackingPos = trackThumbPos + pos - lastClickPos;
539                 pwndData->SCROLL_TrackingVal = SCROLL_GetThumbVal( &si, &sbi.rcScrollBar,
540                                                          vertical,
541                                                          pwndData->SCROLL_TrackingPos );
542                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
543                                 MAKEWPARAM( SB_THUMBTRACK, pwndData->SCROLL_TrackingVal),
544                                 (LPARAM)hwndCtl );
545                 if (!pwndData->SCROLL_MovingThumb)
546                     SCROLL_DrawMovingThumb(pwndData, &context, &sbi, vertical);
547             }
548         }
549         break;
550 
551     case SCROLL_BOTTOM_RECT:
552         if (hittest == pwndData->SCROLL_trackHitTest)
553         {
554             SCROLL_DrawInterior(  &context, &sbi, sbi.xyThumbTop, vertical, pwndData->SCROLL_trackHitTest, 0 );
555             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
556             {
557                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
558                                 SB_PAGEDOWN, (LPARAM)hwndCtl );
559             }
560             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
561                               SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
562         }
563         else
564         {
565             SCROLL_DrawInterior(  &context, &sbi, sbi.xyThumbTop, vertical, 0, 0 );
566             KillSystemTimer( hwnd, SCROLL_TIMER );
567         }
568         break;
569 
570     case SCROLL_BOTTOM_ARROW:
571         if (hittest == pwndData->SCROLL_trackHitTest)
572         {
573             SCROLL_DrawArrows(  &context, &sbi, vertical, pwndData->SCROLL_trackHitTest, 0 );
574             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
575             {
576                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
577                                 SB_LINEDOWN, (LPARAM)hwndCtl );
578 	        }
579 
580         SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
581                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
582         }
583         else
584         {
585             SCROLL_DrawArrows(  &context, &sbi, vertical, 0, 0 );
586             KillSystemTimer( hwnd, SCROLL_TIMER );
587         }
588         break;
589     }
590 
591     if (msg == WM_LBUTTONDOWN)
592     {
593 
594         if (hittest == SCROLL_THUMB)
595         {
596             UINT val = SCROLL_GetThumbVal( &si, &sbi.rcScrollBar, vertical,
597                                  trackThumbPos + lastMousePos - lastClickPos );
598             SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
599                             MAKEWPARAM( SB_THUMBTRACK, val ), (LPARAM)hwndCtl );
600         }
601     }
602 
603     if (msg == WM_LBUTTONUP)
604     {
605         hittest = pwndData->SCROLL_trackHitTest;
606         pwndData->SCROLL_trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
607 
608         if (hittest == SCROLL_THUMB)
609         {
610             UINT val = SCROLL_GetThumbVal( &si, &sbi.rcScrollBar, vertical,
611                                  trackThumbPos + lastMousePos - lastClickPos );
612             SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
613                             MAKEWPARAM( SB_THUMBPOSITION, val ), (LPARAM)hwndCtl );
614         }
615         /* SB_ENDSCROLL doesn't report thumb position */
616         SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
617                           SB_ENDSCROLL, (LPARAM)hwndCtl );
618 
619         /* Terminate tracking */
620         pwndData->SCROLL_TrackingWin = 0;
621     }
622 
623     ThemeCleanupDrawContext(&context);
624 }
625 
626 static void
627 SCROLL_TrackScrollBar( HWND hwnd, INT scrollbar, POINT pt )
628 {
629     MSG msg;
630     PWND_DATA pwndData = ThemeGetWndData(hwnd);
631     if(!pwndData)
632         return;
633 
634     ScreenToWindow(hwnd, &pt);
635 
636     SCROLL_HandleScrollEvent(pwndData, hwnd, scrollbar, WM_LBUTTONDOWN, pt );
637 
638     do
639     {
640         if (!GetMessageW( &msg, 0, 0, 0 )) break;
641         if (CallMsgFilterW( &msg, MSGF_SCROLLBAR )) continue;
642         if (msg.message == WM_LBUTTONUP ||
643             msg.message == WM_MOUSEMOVE ||
644             (msg.message == WM_SYSTIMER && msg.wParam == SCROLL_TIMER))
645         {
646             pt.x = GET_X_LPARAM(msg.lParam);
647             pt.y = GET_Y_LPARAM(msg.lParam);
648             ClientToScreen(hwnd, &pt);
649             ScreenToWindow(hwnd, &pt);
650             SCROLL_HandleScrollEvent(pwndData, hwnd, scrollbar, msg.message, pt );
651         }
652         else
653         {
654             TranslateMessage( &msg );
655             DispatchMessageW( &msg );
656         }
657         if (!IsWindow( hwnd ))
658         {
659             ReleaseCapture();
660             break;
661         }
662     } while (msg.message != WM_LBUTTONUP && GetCapture() == hwnd);
663 }
664 
665 void NC_TrackScrollBar( HWND hwnd, WPARAM wParam, POINT pt )
666 {
667     INT scrollbar;
668 
669     if ((wParam & 0xfff0) == SC_HSCROLL)
670     {
671         if ((wParam & 0x0f) != HTHSCROLL) return;
672         scrollbar = SB_HORZ;
673     }
674     else  /* SC_VSCROLL */
675     {
676         if ((wParam & 0x0f) != HTVSCROLL) return;
677         scrollbar = SB_VERT;
678     }
679     SCROLL_TrackScrollBar( hwnd, scrollbar, pt );
680 }
681