Release 950109
[wine] / controls / scroll.c
1 /*              
2  * Interface code to SCROLLBAR widget
3  *
4  * Copyright  Martin Ayotte, 1993
5  * Copyright  Alexandre Julliard, 1994
6  *
7  * Small fixes and implemented SB_THUMBPOSITION
8  * by Peter Broadhurst, 940611
9  */
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include "windows.h"
16 #include "syscolor.h"
17 #include "sysmetrics.h"
18 #include "scroll.h"
19 #include "user.h"
20 #include "graphics.h"
21 #include "win.h"
22 #include "stddebug.h"
23 /* #define DEBUG_SCROLL */
24 #include "debug.h"
25
26
27 static HBITMAP hUpArrow = 0;
28 static HBITMAP hDnArrow = 0;
29 static HBITMAP hLfArrow = 0;
30 static HBITMAP hRgArrow = 0;
31 static HBITMAP hUpArrowD = 0;
32 static HBITMAP hDnArrowD = 0;
33 static HBITMAP hLfArrowD = 0;
34 static HBITMAP hRgArrowD = 0;
35 static HBITMAP hUpArrowI = 0;
36 static HBITMAP hDnArrowI = 0;
37 static HBITMAP hLfArrowI = 0;
38 static HBITMAP hRgArrowI = 0;
39
40 #define TOP_ARROW(flags,pressed) \
41    (((flags)&ESB_DISABLE_UP) ? hUpArrowI : ((pressed) ? hUpArrowD:hUpArrow))
42 #define BOTTOM_ARROW(flags,pressed) \
43    (((flags)&ESB_DISABLE_DOWN) ? hDnArrowI : ((pressed) ? hDnArrowD:hDnArrow))
44 #define LEFT_ARROW(flags,pressed) \
45    (((flags)&ESB_DISABLE_LEFT) ? hLfArrowI : ((pressed) ? hLfArrowD:hLfArrow))
46 #define RIGHT_ARROW(flags,pressed) \
47    (((flags)&ESB_DISABLE_RIGHT) ? hRgArrowI : ((pressed) ? hRgArrowD:hRgArrow))
48
49
50   /* Minimum size of the rectangle between the arrows */
51 #define SCROLL_MIN_RECT  4  
52
53   /* Delay (in ms) before first repetition when holding the button down */
54 #define SCROLL_FIRST_DELAY   200
55
56   /* Delay (in ms) between scroll repetitions */
57 #define SCROLL_REPEAT_DELAY  100
58
59   /* Scroll timer id */
60 #define SCROLL_TIMER   0
61
62   /* Scroll-bar hit testing */
63 enum SCROLL_HITTEST
64 {
65     SCROLL_NOWHERE,      /* Outside the scroll bar */
66     SCROLL_TOP_ARROW,    /* Top or left arrow */
67     SCROLL_TOP_RECT,     /* Rectangle between the top arrow and the thumb */
68     SCROLL_THUMB,        /* Thumb rectangle */
69     SCROLL_BOTTOM_RECT,  /* Rectangle between the thumb and the bottom arrow */
70     SCROLL_BOTTOM_ARROW  /* Bottom or right arrow */
71 };
72
73
74 /***********************************************************************
75  *           SCROLL_LoadBitmaps
76  */
77 static void SCROLL_LoadBitmaps(void)
78 {
79     hUpArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROW));
80     hDnArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROW));
81     hLfArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROW));
82     hRgArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROW));
83     hUpArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROWD));
84     hDnArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROWD));
85     hLfArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROWD));
86     hRgArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROWD));
87     hUpArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROWI));
88     hDnArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROWI));
89     hLfArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROWI));
90     hRgArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROWI));
91 }
92
93
94 /***********************************************************************
95  *           SCROLL_GetScrollInfo
96  */
97 static SCROLLINFO *SCROLL_GetScrollInfo( HWND hwnd, int nBar )
98 {
99     HANDLE handle;
100     WND *wndPtr = WIN_FindWndPtr( hwnd );
101
102     switch(nBar)
103     {
104         case SB_HORZ: handle = wndPtr->hHScroll; break;
105         case SB_VERT: handle = wndPtr->hVScroll; break;
106         case SB_CTL:  return (SCROLLINFO *)wndPtr->wExtra;
107         default:      return NULL;
108     }
109
110     if (!handle)  /* Create the info structure if needed */
111     {
112         if ((handle = USER_HEAP_ALLOC( GMEM_MOVEABLE, sizeof(SCROLLINFO) )))
113         {
114             SCROLLINFO *infoPtr = (SCROLLINFO *) USER_HEAP_ADDR( handle );
115             infoPtr->MinVal = infoPtr->CurVal = 0;
116             infoPtr->MaxVal = 100;
117             infoPtr->flags  = ESB_ENABLE_BOTH;
118             if (nBar == SB_HORZ) wndPtr->hHScroll = handle;
119             else wndPtr->hVScroll = handle;
120         }
121         if (!hUpArrow) SCROLL_LoadBitmaps();
122     }
123     return (SCROLLINFO *) USER_HEAP_ADDR( handle );
124 }
125
126
127 /***********************************************************************
128  *           SCROLL_GetScrollBarRect
129  *
130  * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
131  * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
132  * 'arrowSize' returns the width or height of an arrow (depending on the
133  * orientation of the scrollbar), and 'thumbPos' returns the position of
134  * the thumb relative to the left or to the top.
135  * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
136  */
137 static BOOL SCROLL_GetScrollBarRect( HWND hwnd, int nBar, RECT *lprect,
138                                      WORD *arrowSize, WORD *thumbPos )
139 {
140     int pixels;
141     BOOL vertical;
142     WND *wndPtr = WIN_FindWndPtr( hwnd );
143
144     switch(nBar)
145     {
146       case SB_HORZ:
147         lprect->left   = wndPtr->rectClient.left - wndPtr->rectWindow.left - 1;
148         lprect->top    = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
149         lprect->right  = wndPtr->rectClient.right - wndPtr->rectWindow.left +1;
150         lprect->bottom = lprect->top + SYSMETRICS_CYHSCROLL + 1;
151         vertical = FALSE;
152         break;
153
154       case SB_VERT:
155         lprect->left   = wndPtr->rectClient.right - wndPtr->rectWindow.left;
156         lprect->top    = wndPtr->rectClient.top - wndPtr->rectWindow.top - 1;
157         lprect->right  = lprect->left + SYSMETRICS_CXVSCROLL + 1;
158         lprect->bottom = wndPtr->rectClient.bottom - wndPtr->rectWindow.top +1;
159         vertical = TRUE;
160         break;
161
162       case SB_CTL:
163         GetClientRect( hwnd, lprect );
164         vertical = ((wndPtr->dwStyle & SBS_VERT) != 0);
165         break;
166
167     default:
168         return FALSE;
169     }
170
171     if (vertical) pixels = lprect->bottom - lprect->top;
172     else pixels = lprect->right - lprect->left;
173
174     if (pixels > 2*SYSMETRICS_CXVSCROLL + SCROLL_MIN_RECT)
175     {
176         *arrowSize = SYSMETRICS_CXVSCROLL;
177     }
178     else if (pixels > SCROLL_MIN_RECT)
179     {
180         *arrowSize = (pixels - SCROLL_MIN_RECT) / 2;
181     }
182     else *arrowSize = 0;
183     
184     if ((pixels -= 3*SYSMETRICS_CXVSCROLL+1) > 0)
185     {
186         SCROLLINFO *info = SCROLL_GetScrollInfo( hwnd, nBar );
187         if ((info->flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH)
188             *thumbPos = 0;
189         else if (info->MinVal == info->MaxVal)
190             *thumbPos = *arrowSize;
191         else
192             *thumbPos = *arrowSize + pixels * (info->CurVal - info->MinVal) /
193                                               (info->MaxVal - info->MinVal);
194     }
195     else *thumbPos = 0;
196     return vertical;
197 }
198
199
200 /***********************************************************************
201  *           SCROLL_GetThumbVal
202  *
203  * Compute the current scroll position based on the thumb position in pixels
204  * from the top of the scroll-bar.
205  */
206 static UINT SCROLL_GetThumbVal( SCROLLINFO *infoPtr, RECT *rect,
207                                 WORD arrowSize, BOOL vertical, WORD pos )
208 {
209     int pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
210     if ((pixels -= 3*SYSMETRICS_CXVSCROLL+1) <= 0) return infoPtr->MinVal;
211     pos = max( 0, pos - SYSMETRICS_CXVSCROLL );
212     if (pos > pixels) pos = pixels;
213     dprintf_scroll(stddeb,"GetThumbVal: pos=%d ret=%d\n", pos,
214                    (infoPtr->MinVal
215             + (UINT)(((int)pos * (infoPtr->MaxVal-infoPtr->MinVal) + pixels/2)
216                      / pixels)) );
217     return (infoPtr->MinVal
218             + (UINT)(((int)pos * (infoPtr->MaxVal-infoPtr->MinVal) + pixels/2)
219                      / pixels));
220 }
221
222
223 /***********************************************************************
224  *           SCROLL_HitTest
225  *
226  * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
227  */
228 static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, int nBar, POINT pt )
229 {
230     WORD arrowSize, thumbPos;
231     RECT rect;
232
233     BOOL vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
234                                              &arrowSize, &thumbPos );
235     if (!PtInRect( &rect, pt )) return SCROLL_NOWHERE;
236
237     if (vertical)
238     {
239         if (pt.y <= rect.top + arrowSize + 1) return SCROLL_TOP_ARROW;
240         if (pt.y >= rect.bottom - arrowSize) return SCROLL_BOTTOM_ARROW;
241         if (!thumbPos) return SCROLL_TOP_RECT;
242         pt.y -= rect.top;
243         if (pt.y < (INT)thumbPos) return SCROLL_TOP_RECT;
244         if (pt.y > thumbPos+SYSMETRICS_CYHSCROLL) return SCROLL_BOTTOM_RECT;
245         return SCROLL_THUMB;
246     }
247     else  /* horizontal */
248     {
249         if (pt.x <= rect.left + arrowSize) return SCROLL_TOP_ARROW;
250         if (pt.x >= rect.right - arrowSize) return SCROLL_BOTTOM_ARROW;
251         if (!thumbPos) return SCROLL_TOP_RECT;
252         pt.x -= rect.left;
253         if (pt.x < (INT)thumbPos) return SCROLL_TOP_RECT;
254         if (pt.x > thumbPos+SYSMETRICS_CXVSCROLL) return SCROLL_BOTTOM_RECT;
255         return SCROLL_THUMB;
256     }
257 }
258
259
260 /***********************************************************************
261  *           SCROLL_DrawArrows
262  *
263  * Draw the scroll bar arrows.
264  */
265 static void SCROLL_DrawArrows( HDC hdc, SCROLLINFO *infoPtr, RECT *rect,
266                                WORD arrowSize, BOOL vertical,
267                                BOOL top_pressed, BOOL bottom_pressed )
268 {
269     HDC hdcMem = CreateCompatibleDC( hdc );
270     HBITMAP hbmpPrev = SelectObject( hdcMem, vertical ?
271                                     TOP_ARROW(infoPtr->flags, top_pressed)
272                                     : LEFT_ARROW(infoPtr->flags, top_pressed));
273     SetStretchBltMode( hdc, STRETCH_DELETESCANS );
274     StretchBlt( hdc, rect->left, rect->top,
275                 vertical ? rect->right-rect->left : arrowSize+1,
276                 vertical ? arrowSize+1 : rect->bottom-rect->top,
277                 hdcMem, 0, 0,
278                 SYSMETRICS_CXVSCROLL + 1, SYSMETRICS_CYHSCROLL + 1,
279                 SRCCOPY );
280
281     SelectObject( hdcMem, vertical ?
282                   BOTTOM_ARROW( infoPtr->flags, bottom_pressed )
283                   : RIGHT_ARROW( infoPtr->flags, bottom_pressed ) );
284     if (vertical)
285         StretchBlt( hdc, rect->left, rect->bottom - arrowSize - 1,
286                    rect->right - rect->left, arrowSize + 1,
287                    hdcMem, 0, 0,
288                    SYSMETRICS_CXVSCROLL + 1, SYSMETRICS_CYHSCROLL + 1,
289                    SRCCOPY );
290     else
291         StretchBlt( hdc, rect->right - arrowSize - 1, rect->top,
292                    arrowSize + 1, rect->bottom - rect->top,
293                    hdcMem, 0, 0,
294                    SYSMETRICS_CXVSCROLL + 1, SYSMETRICS_CYHSCROLL + 1,
295                    SRCCOPY );
296     SelectObject( hdcMem, hbmpPrev );
297     DeleteDC( hdcMem );
298 }
299
300
301 /***********************************************************************
302  *           SCROLL_DrawMovingThumb
303  *
304  * Draw the moving thumb rectangle.
305  */
306 static void SCROLL_DrawMovingThumb( HDC hdc, RECT *rect, BOOL vertical,
307                                     WORD arrowSize, WORD thumbPos )
308 {
309     RECT r = *rect;
310     if (vertical)
311     {
312         r.top += thumbPos;
313         if (r.top < rect->top + arrowSize) r.top = rect->top + arrowSize;
314         if (r.top + SYSMETRICS_CYHSCROLL >= rect->bottom - arrowSize)
315             r.top = rect->bottom - arrowSize - SYSMETRICS_CYHSCROLL - 1;
316         r.bottom = r.top + SYSMETRICS_CYHSCROLL + 1;
317     }
318     else
319     {
320         r.left += thumbPos;
321         if (r.left < rect->left + arrowSize) r.left = rect->left + arrowSize;
322         if (r.left + SYSMETRICS_CXVSCROLL >= rect->right - arrowSize)
323             r.left = rect->right - arrowSize - SYSMETRICS_CXVSCROLL - 1;
324         r.right = r.left + SYSMETRICS_CXVSCROLL + 1;
325     }
326     InflateRect( &r, -1, -1 );
327     DrawFocusRect( hdc, &r );
328 }
329
330
331 /***********************************************************************
332  *           SCROLL_DrawInterior
333  *
334  * Draw the scroll bar interior (everything except the arrows).
335  */
336 static void SCROLL_DrawInterior( HWND hwnd, HDC hdc, int nBar, RECT *rect,
337                                  WORD arrowSize, WORD thumbPos, WORD flags,
338                                  BOOL vertical, BOOL top_selected,
339                                  BOOL bottom_selected )
340 {
341     RECT r;
342     WND *wndPtr = WIN_FindWndPtr( hwnd );
343     if (((nBar == SB_VERT) && !(wndPtr->dwStyle & WS_VSCROLL))
344         || ((nBar == SB_HORZ) && (!wndPtr->dwStyle & WS_HSCROLL))) return;
345
346       /* Select the correct brush and pen */
347
348     SelectObject( hdc, sysColorObjects.hpenWindowFrame );
349     if ((flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH)
350     {
351           /* This ought to be the color of the parent window */
352         SelectObject( hdc, sysColorObjects.hbrushWindow );
353     }
354     else
355     {
356         if (nBar == SB_CTL)  /* Only scrollbar controls send WM_CTLCOLOR */
357         {
358             HBRUSH hbrush = SendMessage( GetParent(hwnd), WM_CTLCOLOR, hdc,
359                                          MAKELONG(hwnd, CTLCOLOR_SCROLLBAR) );
360             SelectObject( hdc, hbrush );
361         }
362         else SelectObject( hdc, sysColorObjects.hbrushScrollbar );
363     }
364
365       /* Calculate the scroll rectangle */
366
367     r = *rect;
368     if (vertical)
369     {
370         r.top    += arrowSize;
371         r.bottom -= arrowSize;
372     }
373     else
374     {
375         r.left  += arrowSize;
376         r.right -= arrowSize;
377     }
378
379       /* Draw the scroll bar frame */
380
381     MoveTo( hdc, r.left, r.top );
382     LineTo( hdc, r.right-1, r.top );
383     LineTo( hdc, r.right-1, r.bottom-1 );
384     LineTo( hdc, r.left, r.bottom-1 );
385     LineTo( hdc, r.left, r.top );
386
387       /* Draw the scroll rectangles and thumb */
388
389     if (!thumbPos)  /* No thumb to draw */
390     {
391         PatBlt( hdc, r.left+1, r.top+1, r.right - r.left - 2,
392                 r.bottom - r.top - 2, PATCOPY );
393         return;
394     }
395
396     if (vertical)
397     {
398         PatBlt( hdc, r.left + 1, r.top + 1,
399                 r.right - r.left - 2,
400                 thumbPos - arrowSize,
401                 top_selected ? 0x0f0000 : PATCOPY );
402         r.top += thumbPos - arrowSize;
403         PatBlt( hdc, r.left + 1, r.top + SYSMETRICS_CYHSCROLL + 1,
404                 r.right - r.left - 2,
405                 r.bottom - r.top - SYSMETRICS_CYHSCROLL - 2,
406                 bottom_selected ? 0x0f0000 : PATCOPY );
407         r.bottom = r.top + SYSMETRICS_CYHSCROLL + 1;
408     }
409     else  /* horizontal */
410     {
411         PatBlt( hdc, r.left + 1, r.top + 1,
412                 thumbPos - arrowSize,
413                 r.bottom - r.top - 2,
414                 top_selected ? 0x0f0000 : PATCOPY );
415         r.left += thumbPos - arrowSize;
416         PatBlt( hdc, r.left + SYSMETRICS_CYHSCROLL + 1, r.top + 1,
417                 r.right - r.left - SYSMETRICS_CYHSCROLL - 2,
418                 r.bottom - r.top - 2,
419                 bottom_selected ? 0x0f0000 : PATCOPY );
420         r.right = r.left + SYSMETRICS_CXVSCROLL + 1;
421     }
422
423       /* Draw the thumb */
424
425     SelectObject( hdc, sysColorObjects.hbrushBtnFace );
426     Rectangle( hdc, r.left, r.top, r.right, r.bottom );
427     InflateRect( &r, -1, -1 );
428     GRAPH_DrawReliefRect( hdc, &r, 1, 2, FALSE );
429 }
430
431
432 /***********************************************************************
433  *           SCROLL_DrawScrollBar
434  *
435  * Redraw the whole scrollbar.
436  */
437 void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, int nBar )
438 {
439     WORD arrowSize, thumbPos;
440     RECT rect;
441     BOOL vertical;
442
443     SCROLLINFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
444     if (!infoPtr) return;
445     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
446                                         &arrowSize, &thumbPos );
447       /* Draw the arrows */
448
449     if (arrowSize) SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize,
450                                       vertical, FALSE, FALSE );
451     
452     SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
453                          infoPtr->flags, vertical, FALSE, FALSE );
454 }
455
456
457 /***********************************************************************
458  *           SCROLL_RefreshScrollBar
459  *
460  * Repaint the scroll bar interior after a SetScrollRange() or
461  * SetScrollPos() call.
462  */
463 static void SCROLL_RefreshScrollBar( HWND hwnd, int nBar )
464 {
465     WORD arrowSize, thumbPos;
466     RECT rect;
467     BOOL vertical;
468     HDC hdc;
469
470     SCROLLINFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
471     if (!infoPtr) return;
472     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
473                                         &arrowSize, &thumbPos );
474     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
475     if (!hdc) return;
476     SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
477                          infoPtr->flags, vertical, FALSE, FALSE );
478     ReleaseDC( hwnd, hdc );
479 }
480
481
482 /***********************************************************************
483  *           SCROLL_HandleKbdEvent
484  *
485  * Handle a keyboard event (only for SB_CTL scrollbars).
486  */
487 static void SCROLL_HandleKbdEvent( HWND hwnd, WORD wParam )
488 {
489     WND *wndPtr = WIN_FindWndPtr( hwnd );
490     WORD msg;
491     
492     switch(wParam)
493     {
494     case VK_PRIOR: msg = SB_PAGEUP; break;
495     case VK_NEXT:  msg = SB_PAGEDOWN; break;
496     case VK_HOME:  msg = SB_TOP; break;
497     case VK_END:   msg = SB_BOTTOM; break;
498     case VK_UP:    msg = SB_LINEUP; break;
499     case VK_DOWN:  msg = SB_LINEDOWN; break;
500     default:
501         return;
502     }
503     SendMessage( GetParent(hwnd),
504                  (wndPtr->dwStyle & SBS_VERT) ? WM_VSCROLL : WM_HSCROLL,
505                  msg, MAKELONG( 0, hwnd ));
506 }
507
508
509 /***********************************************************************
510  *           SCROLL_HandleScrollEvent
511  *
512  * Handle a mouse or timer event for the scrollbar.
513  * 'pt' is the location of the mouse event in client (for SB_CTL) or
514  * windows coordinates.
515  */
516 void SCROLL_HandleScrollEvent( HWND hwnd, int nBar, WORD msg, POINT pt )
517 {
518       /* Previous mouse position for timer events */
519     static POINT prevPt;
520       /* Hit test code of the last button-down event */
521     static enum SCROLL_HITTEST trackHitTest;
522       /* Thumb position when tracking started. */
523     static UINT trackThumbPos;
524       /* Position in the scroll-bar of the last button-down event. */
525     static int lastClickPos;
526       /* Position in the scroll-bar of the last mouse event. */
527     static int lastMousePos;
528
529     enum SCROLL_HITTEST hittest;
530     HWND hwndOwner, hwndCtl;
531     BOOL vertical;
532     WORD arrowSize, thumbPos;
533     RECT rect;
534     HDC hdc;
535
536     SCROLLINFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
537     if (!infoPtr) return;
538     if ((trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN)) return;
539
540     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
541     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
542                                         &arrowSize, &thumbPos );
543     hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
544     hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;
545
546     switch(msg)
547     {
548       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
549           trackHitTest  = hittest = SCROLL_HitTest( hwnd, nBar, pt );
550           lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
551           lastMousePos  = lastClickPos;
552           trackThumbPos = thumbPos;
553           prevPt = pt;
554           SetCapture( hwnd );
555           if (nBar == SB_CTL) SetFocus( hwnd );
556           break;
557
558       case WM_MOUSEMOVE:
559           hittest = SCROLL_HitTest( hwnd, nBar, pt );
560           prevPt = pt;
561           break;
562
563       case WM_LBUTTONUP:
564           hittest = SCROLL_NOWHERE;
565           ReleaseCapture();
566           break;
567
568       case WM_SYSTIMER:
569           pt = prevPt;
570           hittest = SCROLL_HitTest( hwnd, nBar, pt );
571           break;
572
573       default:
574           return;  /* Should never happen */
575     }
576
577     dprintf_scroll( stddeb, "ScrollBar Event: hwnd=%x bar=%d msg=%x pt=%d,%d hit=%d\n",
578                     hwnd, nBar, msg, pt.x, pt.y, hittest );
579
580     switch(trackHitTest)
581     {
582     case SCROLL_NOWHERE:  /* No tracking in progress */
583         break;
584
585     case SCROLL_TOP_ARROW:
586         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
587                            (hittest == trackHitTest), FALSE );
588         if (hittest == trackHitTest)
589         {
590             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
591                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
592             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
593                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
594                              SB_LINEUP, MAKELONG( 0, hwndCtl ));
595         }
596         else KillSystemTimer( hwnd, SCROLL_TIMER );
597         break;
598
599     case SCROLL_TOP_RECT:
600         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
601                              infoPtr->flags, vertical,
602                              (hittest == trackHitTest), FALSE );
603         if (hittest == trackHitTest)
604         {
605             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
606                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
607             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
608                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
609                              SB_PAGEUP, MAKELONG( 0, hwndCtl ));
610         }
611         else KillSystemTimer( hwnd, SCROLL_TIMER );
612         break;
613
614     case SCROLL_THUMB:
615         if (msg == WM_LBUTTONDOWN)
616             SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
617                                  trackThumbPos + lastMousePos - lastClickPos );
618         else if (msg == WM_LBUTTONUP)
619             SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
620                                  infoPtr->flags, vertical, FALSE, FALSE );
621         else  /* WM_MOUSEMOVE */
622         {
623             UINT pos, val;
624
625             if (!PtInRect( &rect, pt )) pos = lastClickPos;
626             else pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
627             if (pos != lastMousePos)
628             {
629                 SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
630                                  trackThumbPos + lastMousePos - lastClickPos );
631                 SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
632                                        trackThumbPos + pos - lastClickPos );
633                 lastMousePos = pos;
634                 val = SCROLL_GetThumbVal( infoPtr, &rect, vertical, arrowSize,
635                                  trackThumbPos + lastMousePos - lastClickPos );
636                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
637                              SB_THUMBTRACK, MAKELONG( val, hwndCtl ));
638             }
639         }
640         break;
641         
642     case SCROLL_BOTTOM_RECT:
643         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
644                              infoPtr->flags, vertical,
645                              FALSE, (hittest == trackHitTest) );
646         if (hittest == trackHitTest)
647         {
648             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
649                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
650             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
651                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
652                              SB_PAGEDOWN, MAKELONG( 0, hwndCtl ));
653         }
654         else KillSystemTimer( hwnd, SCROLL_TIMER );
655         break;
656         
657     case SCROLL_BOTTOM_ARROW:
658         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
659                            FALSE, (hittest == trackHitTest) );
660         if (hittest == trackHitTest)
661         {
662             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
663                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
664             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
665                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
666                              SB_LINEDOWN, MAKELONG( 0, hwndCtl ));
667         }
668         else KillSystemTimer( hwnd, SCROLL_TIMER );
669         break;
670     }
671
672     if (msg == WM_LBUTTONUP)
673     {
674         if (trackHitTest == SCROLL_THUMB)
675         {
676             UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical, arrowSize,
677                                  trackThumbPos + lastMousePos - lastClickPos );
678             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
679                          SB_THUMBPOSITION, MAKELONG( val, hwndCtl ) );
680         }
681         else
682             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
683                          SB_ENDSCROLL, MAKELONG( 0, hwndCtl ) );
684         trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
685     }
686
687     ReleaseDC( hwnd, hdc );
688 }
689
690
691 /***********************************************************************
692  *           ScrollBarWndProc
693  */
694 LONG ScrollBarWndProc( HWND hwnd, WORD message, WORD wParam, LONG lParam )
695 {    
696     switch(message)
697     {
698     case WM_CREATE:
699         {
700             CREATESTRUCT *lpCreat = (CREATESTRUCT *)lParam;
701             if (lpCreat->style & SBS_SIZEBOX)
702             {
703                 fprintf( stdnimp, "Unimplemented style SBS_SIZEBOX.\n" );
704                 return -1;
705             }
706             
707             if (lpCreat->style & SBS_VERT)
708             {
709                 if (lpCreat->style & SBS_LEFTALIGN)
710                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
711                                 SYSMETRICS_CXVSCROLL+1, lpCreat->cy, FALSE );
712                 else if (lpCreat->style & SBS_RIGHTALIGN)
713                     MoveWindow( hwnd, 
714                                 lpCreat->x+lpCreat->cx-SYSMETRICS_CXVSCROLL-1,
715                                 lpCreat->y,
716                                 SYSMETRICS_CXVSCROLL + 1, lpCreat->cy, FALSE );
717             }
718             else  /* SBS_HORZ */
719             {
720                 if (lpCreat->style & SBS_TOPALIGN)
721                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
722                                 lpCreat->cx, SYSMETRICS_CYHSCROLL+1, FALSE );
723                 else if (lpCreat->style & SBS_BOTTOMALIGN)
724                     MoveWindow( hwnd, 
725                                 lpCreat->x,
726                                 lpCreat->y+lpCreat->cy-SYSMETRICS_CYHSCROLL-1,
727                                 lpCreat->cx, SYSMETRICS_CYHSCROLL+1, FALSE );
728             }
729         }
730         if (!hUpArrow) SCROLL_LoadBitmaps();
731         dprintf_scroll( stddeb, "ScrollBar creation, hwnd=%d\n", hwnd );
732         return 0;
733         
734     case WM_LBUTTONDOWN:
735     case WM_LBUTTONUP:
736     case WM_MOUSEMOVE:
737     case WM_SYSTIMER:
738         SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, MAKEPOINT(lParam) );
739         break;
740
741     case WM_KEYDOWN:
742         SCROLL_HandleKbdEvent( hwnd, wParam );
743         break;
744
745     case WM_ERASEBKGND:
746         break;
747
748     case WM_PAINT:
749         {
750             PAINTSTRUCT ps;
751             HDC hdc = BeginPaint( hwnd, &ps );
752             SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL );
753             EndPaint( hwnd, &ps );
754         }
755         break;
756
757     default:
758         return DefWindowProc( hwnd, message, wParam, lParam );
759     }
760     return 0;
761 }
762
763
764 /*************************************************************************
765  *           SetScrollPos   (USER.62)
766  */
767 int SetScrollPos( HWND hwnd, int nBar, int nPos, BOOL bRedraw )
768 {
769     SCROLLINFO *infoPtr;
770     INT oldPos;
771
772     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
773
774     dprintf_scroll( stddeb,"SetScrollPos min=%d max=%d pos=%d\n", 
775                     infoPtr->MinVal, infoPtr->MaxVal, nPos );
776
777     if (nPos < infoPtr->MinVal) nPos = infoPtr->MinVal;
778     else if (nPos > infoPtr->MaxVal) nPos = infoPtr->MaxVal;
779     oldPos = infoPtr->CurVal;
780     infoPtr->CurVal = nPos;
781     if (bRedraw) SCROLL_RefreshScrollBar( hwnd, nBar );
782     return oldPos;
783 }
784
785
786 /*************************************************************************
787  *           GetScrollPos   (USER.63)
788  */
789 int GetScrollPos( HWND hwnd, int nBar )
790 {
791     SCROLLINFO *infoPtr;
792
793     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
794     return infoPtr->CurVal;
795 }
796
797
798 /*************************************************************************
799  *           SetScrollRange   (USER.64)
800  */
801 void SetScrollRange(HWND hwnd, int nBar, int MinVal, int MaxVal, BOOL bRedraw)
802 {
803     SCROLLINFO *infoPtr;
804
805     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return;
806
807     dprintf_scroll( stddeb,"SetScrollRange hwnd=%x bar=%d min=%d max=%d\n",
808                     hwnd, nBar, MinVal, MaxVal );
809
810       /* Invalid range -> range is set to (0,0) */
811     if ((MinVal > MaxVal) || ((long)MaxVal - MinVal > 32767L))
812         MinVal = MaxVal = 0;
813     if (infoPtr->CurVal < MinVal) infoPtr->CurVal = MinVal;
814     else if (infoPtr->CurVal > MaxVal) infoPtr->CurVal = MaxVal;
815     infoPtr->MinVal = MinVal;
816     infoPtr->MaxVal = MaxVal;
817
818       /* Non-client scroll-bar is hidden if min==max */
819     if (nBar != SB_CTL) ShowScrollBar( hwnd, nBar, (MinVal != MaxVal) );
820     if (bRedraw) SCROLL_RefreshScrollBar( hwnd, nBar );
821 }
822
823
824 /*************************************************************************
825  *           GetScrollRange   (USER.65)
826  */
827 void GetScrollRange(HWND hwnd, int nBar, LPINT lpMin, LPINT lpMax)
828 {
829     SCROLLINFO *infoPtr;
830
831     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return;
832     if (lpMin) *lpMin = infoPtr->MinVal;
833     if (lpMax) *lpMax = infoPtr->MaxVal;
834 }
835
836
837 /*************************************************************************
838  *           ShowScrollBar   (USER.267)
839  */
840 void ShowScrollBar( HWND hwnd, WORD wBar, BOOL fShow )
841 {
842     WND *wndPtr = WIN_FindWndPtr( hwnd );
843
844     if (!wndPtr) return;
845     dprintf_scroll( stddeb, "ShowScrollBar: hwnd=%x bar=%d on=%d\n", hwnd, wBar, fShow );
846
847     switch(wBar)
848     {
849     case SB_CTL:
850         ShowWindow( hwnd, fShow ? SW_SHOW : SW_HIDE );
851         return;
852
853     case SB_HORZ:
854         if (fShow)
855         {
856             if (wndPtr->dwStyle & WS_HSCROLL) return;
857             wndPtr->dwStyle |= WS_HSCROLL;
858         }
859         else  /* hide it */
860         {
861             if (!(wndPtr->dwStyle & WS_HSCROLL)) return;
862             wndPtr->dwStyle &= ~WS_HSCROLL;
863         }
864         break;
865
866     case SB_VERT:
867         if (fShow)
868         {
869             if (wndPtr->dwStyle & WS_VSCROLL) return;
870             wndPtr->dwStyle |= WS_VSCROLL;
871         }
872         else  /* hide it */
873         {
874             if (!(wndPtr->dwStyle & WS_VSCROLL)) return;
875             wndPtr->dwStyle &= ~WS_VSCROLL;
876         }
877         break;
878
879     case SB_BOTH:
880         if (fShow)
881         {
882             if ((wndPtr->dwStyle & WS_HSCROLL)
883                 && (wndPtr->dwStyle & WS_VSCROLL)) return;
884             wndPtr->dwStyle |= WS_HSCROLL | WS_VSCROLL;
885         }
886         else  /* hide it */
887         {
888             if (!(wndPtr->dwStyle & WS_HSCROLL)
889                 && !(wndPtr->dwStyle & WS_HSCROLL)) return;
890             wndPtr->dwStyle &= ~(WS_HSCROLL | WS_VSCROLL);
891         }
892         break;
893
894     default:
895         return;  /* Nothing to do! */
896     }
897     SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE
898                  | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
899       /* FIXME: Hack until SetWindowPos works correctly */
900     InvalidateRect( hwnd, NULL, TRUE );
901 }
902
903
904 /*************************************************************************
905  *           EnableScrollBar   (USER.482)
906  */
907 BOOL EnableScrollBar( HWND hwnd, INT nBar, UINT flags )
908 {
909     SCROLLINFO *infoPtr;
910     HDC hdc;
911
912     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return FALSE;
913     dprintf_scroll( stddeb, "EnableScrollBar: %x %d %d\n", hwnd, nBar, flags );
914     flags &= ESB_DISABLE_BOTH;
915     if (infoPtr->flags == flags) return FALSE;
916     infoPtr->flags = flags;
917
918       /* Redraw the whole scroll bar */
919     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
920     SCROLL_DrawScrollBar( hwnd, hdc, nBar );
921     ReleaseDC( hwnd, hdc );
922     return TRUE;
923 }