Fixed silly EINTR bug with starting dosmod.
[wine] / controls / scroll.c
1 /*              
2  * Scrollbar control
3  *
4  * Copyright 1993 Martin Ayotte
5  * Copyright 1994, 1996 Alexandre Julliard
6  */
7
8 #include "wine/winuser16.h"
9 #include "scroll.h"
10 #include "heap.h"
11 #include "win.h"
12 #include "debugtools.h"
13
14 DEFAULT_DEBUG_CHANNEL(scroll)
15
16
17 static HBITMAP hUpArrow = 0;
18 static HBITMAP hDnArrow = 0;
19 static HBITMAP hLfArrow = 0;
20 static HBITMAP hRgArrow = 0;
21 static HBITMAP hUpArrowD = 0;
22 static HBITMAP hDnArrowD = 0;
23 static HBITMAP hLfArrowD = 0;
24 static HBITMAP hRgArrowD = 0;
25 static HBITMAP hUpArrowI = 0;
26 static HBITMAP hDnArrowI = 0;
27 static HBITMAP hLfArrowI = 0;
28 static HBITMAP hRgArrowI = 0;
29
30 #define TOP_ARROW(flags,pressed) \
31    (((flags)&ESB_DISABLE_UP) ? hUpArrowI : ((pressed) ? hUpArrowD:hUpArrow))
32 #define BOTTOM_ARROW(flags,pressed) \
33    (((flags)&ESB_DISABLE_DOWN) ? hDnArrowI : ((pressed) ? hDnArrowD:hDnArrow))
34 #define LEFT_ARROW(flags,pressed) \
35    (((flags)&ESB_DISABLE_LEFT) ? hLfArrowI : ((pressed) ? hLfArrowD:hLfArrow))
36 #define RIGHT_ARROW(flags,pressed) \
37    (((flags)&ESB_DISABLE_RIGHT) ? hRgArrowI : ((pressed) ? hRgArrowD:hRgArrow))
38
39
40   /* Minimum size of the rectangle between the arrows */
41 #define SCROLL_MIN_RECT  4  
42
43   /* Minimum size of the thumb in pixels */
44 #define SCROLL_MIN_THUMB 6
45
46   /* Overlap between arrows and thumb */
47 #define SCROLL_ARROW_THUMB_OVERLAP 1
48
49   /* Delay (in ms) before first repetition when holding the button down */
50 #define SCROLL_FIRST_DELAY   200
51
52   /* Delay (in ms) between scroll repetitions */
53 #define SCROLL_REPEAT_DELAY  50
54
55   /* Scroll timer id */
56 #define SCROLL_TIMER   0
57
58   /* Scroll-bar hit testing */
59 enum SCROLL_HITTEST
60 {
61     SCROLL_NOWHERE,      /* Outside the scroll bar */
62     SCROLL_TOP_ARROW,    /* Top or left arrow */
63     SCROLL_TOP_RECT,     /* Rectangle between the top arrow and the thumb */
64     SCROLL_THUMB,        /* Thumb rectangle */
65     SCROLL_BOTTOM_RECT,  /* Rectangle between the thumb and the bottom arrow */
66     SCROLL_BOTTOM_ARROW  /* Bottom or right arrow */
67 };
68
69  /* What to do after SCROLL_SetScrollInfo() */
70 #define SA_SSI_HIDE             0x0001
71 #define SA_SSI_SHOW             0x0002
72 #define SA_SSI_REFRESH          0x0004
73 #define SA_SSI_REPAINT_ARROWS   0x0008
74
75  /* Thumb-tracking info */
76 static HWND SCROLL_TrackingWin = 0;
77 static INT  SCROLL_TrackingBar = 0;
78 static INT  SCROLL_TrackingPos = 0;
79 static INT  SCROLL_TrackingVal = 0;
80  /* Hit test code of the last button-down event */
81 static enum SCROLL_HITTEST SCROLL_trackHitTest;
82 static BOOL SCROLL_trackVertical;
83
84  /* Is the moving thumb being displayed? */
85 static BOOL SCROLL_MovingThumb = FALSE;
86
87  /* Local functions */
88 static BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar, 
89                                     BOOL fShowH, BOOL fShowV );
90 static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar, 
91                                    const SCROLLINFO *info, INT *action );
92
93 /***********************************************************************
94  *           SCROLL_LoadBitmaps
95  */
96 static void SCROLL_LoadBitmaps(void)
97 {
98     hUpArrow  = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_UPARROW) );
99     hDnArrow  = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_DNARROW) );
100     hLfArrow  = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_LFARROW) );
101     hRgArrow  = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_RGARROW) );
102     hUpArrowD = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_UPARROWD) );
103     hDnArrowD = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_DNARROWD) );
104     hLfArrowD = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_LFARROWD) );
105     hRgArrowD = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_RGARROWD) );
106     hUpArrowI = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_UPARROWI) );
107     hDnArrowI = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_DNARROWI) );
108     hLfArrowI = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_LFARROWI) );
109     hRgArrowI = LoadBitmapA( 0, MAKEINTRESOURCEA(OBM_RGARROWI) );
110 }
111
112
113 /***********************************************************************
114  *           SCROLL_GetPtrScrollInfo
115  */
116 static SCROLLBAR_INFO *SCROLL_GetPtrScrollInfo( WND* wndPtr, INT nBar )
117 {
118     SCROLLBAR_INFO *infoPtr;
119
120     if (!wndPtr) return NULL;
121     switch(nBar)
122     {
123         case SB_HORZ: infoPtr = (SCROLLBAR_INFO *)wndPtr->pHScroll; break;
124         case SB_VERT: infoPtr = (SCROLLBAR_INFO *)wndPtr->pVScroll; break;
125         case SB_CTL:  infoPtr = (SCROLLBAR_INFO *)wndPtr->wExtra; break;
126         default:      return NULL;
127     }
128
129     if (!infoPtr)  /* Create the info structure if needed */
130     {
131         if ((infoPtr = HeapAlloc( SystemHeap, 0, sizeof(SCROLLBAR_INFO) )))
132         {
133             infoPtr->MinVal = infoPtr->CurVal = infoPtr->Page = 0;
134             infoPtr->MaxVal = 100;
135             infoPtr->flags  = ESB_ENABLE_BOTH;
136             if (nBar == SB_HORZ) wndPtr->pHScroll = infoPtr;
137             else wndPtr->pVScroll = infoPtr;
138         }
139         if (!hUpArrow) SCROLL_LoadBitmaps();
140     }
141     return infoPtr;
142 }
143
144
145 /***********************************************************************
146  *           SCROLL_GetScrollInfo
147  */
148 static SCROLLBAR_INFO *SCROLL_GetScrollInfo( HWND hwnd, INT nBar )
149 {
150    SCROLLBAR_INFO *retvalue;
151    WND *wndPtr = WIN_FindWndPtr( hwnd );
152    retvalue = SCROLL_GetPtrScrollInfo( wndPtr, nBar );
153    WIN_ReleaseWndPtr(wndPtr);
154    return retvalue;
155 }
156
157
158 /***********************************************************************
159  *           SCROLL_GetScrollBarRect
160  *
161  * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
162  * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
163  * 'arrowSize' returns the width or height of an arrow (depending on
164  * the orientation of the scrollbar), 'thumbSize' returns the size of
165  * the thumb, and 'thumbPos' returns the position of the thumb
166  * relative to the left or to the top.
167  * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
168  */
169 static BOOL SCROLL_GetScrollBarRect( HWND hwnd, INT nBar, RECT *lprect,
170                                        INT *arrowSize, INT *thumbSize,
171                                        INT *thumbPos )
172 {
173     INT pixels;
174     BOOL vertical;
175     WND *wndPtr = WIN_FindWndPtr( hwnd );
176
177     switch(nBar)
178     {
179       case SB_HORZ:
180         lprect->left   = wndPtr->rectClient.left - wndPtr->rectWindow.left;
181         lprect->top    = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
182         lprect->right  = wndPtr->rectClient.right - wndPtr->rectWindow.left;
183         lprect->bottom = lprect->top + GetSystemMetrics(SM_CYHSCROLL);
184         if(wndPtr->dwStyle & WS_BORDER) {
185           lprect->left--;
186           lprect->right++;
187         } else if(wndPtr->dwStyle & WS_VSCROLL)
188           lprect->right++;
189         vertical = FALSE;
190         break;
191
192       case SB_VERT:
193         lprect->left   = wndPtr->rectClient.right - wndPtr->rectWindow.left;
194         lprect->top    = wndPtr->rectClient.top - wndPtr->rectWindow.top;
195         lprect->right  = lprect->left + GetSystemMetrics(SM_CXVSCROLL);
196         lprect->bottom = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
197         if(wndPtr->dwStyle & WS_BORDER) {
198           lprect->top--;
199           lprect->bottom++;
200         } else if(wndPtr->dwStyle & WS_HSCROLL)
201           lprect->bottom++;
202         vertical = TRUE;
203         break;
204
205       case SB_CTL:
206         GetClientRect( hwnd, lprect );
207         vertical = ((wndPtr->dwStyle & SBS_VERT) != 0);
208         break;
209
210     default:
211         WIN_ReleaseWndPtr(wndPtr);
212         return FALSE;
213     }
214
215     if (vertical) pixels = lprect->bottom - lprect->top;
216     else pixels = lprect->right - lprect->left;
217
218     if (pixels <= 2*GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT)
219     {
220         if (pixels > SCROLL_MIN_RECT)
221             *arrowSize = (pixels - SCROLL_MIN_RECT) / 2;
222         else
223             *arrowSize = 0;
224         *thumbPos = *thumbSize = 0;
225     }
226     else
227     {
228         SCROLLBAR_INFO *info = SCROLL_GetPtrScrollInfo( wndPtr, nBar );
229
230         *arrowSize = GetSystemMetrics(SM_CXVSCROLL);
231         pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP));
232
233         if (info->Page)
234         {
235             *thumbSize = pixels * info->Page / (info->MaxVal-info->MinVal+1);
236             if (*thumbSize < SCROLL_MIN_THUMB) *thumbSize = SCROLL_MIN_THUMB;
237         }
238         else *thumbSize = GetSystemMetrics(SM_CXVSCROLL);
239
240         if (((pixels -= *thumbSize ) < 0) ||
241             ((info->flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH))
242         {
243             /* Rectangle too small or scrollbar disabled -> no thumb */
244             *thumbPos = *thumbSize = 0;
245         }
246         else
247         {
248             INT max = info->MaxVal - MAX( info->Page-1, 0 );
249             if (info->MinVal >= max)
250                 *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
251             else
252                 *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP
253                  + pixels * (info->CurVal-info->MinVal) / (max - info->MinVal);
254         }
255     }
256     WIN_ReleaseWndPtr(wndPtr);
257     return vertical;
258 }
259
260
261 /***********************************************************************
262  *           SCROLL_GetThumbVal
263  *
264  * Compute the current scroll position based on the thumb position in pixels
265  * from the top of the scroll-bar.
266  */
267 static UINT SCROLL_GetThumbVal( SCROLLBAR_INFO *infoPtr, RECT *rect,
268                                   BOOL vertical, INT pos )
269 {
270     INT thumbSize;
271     INT pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
272
273     if ((pixels -= 2*(GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP)) <= 0)
274         return infoPtr->MinVal;
275
276     if (infoPtr->Page)
277     {
278         thumbSize = pixels * infoPtr->Page/(infoPtr->MaxVal-infoPtr->MinVal+1);
279         if (thumbSize < SCROLL_MIN_THUMB) thumbSize = SCROLL_MIN_THUMB;
280     }
281     else thumbSize = GetSystemMetrics(SM_CXVSCROLL);
282
283     if ((pixels -= thumbSize) <= 0) return infoPtr->MinVal;
284
285     pos = MAX( 0, pos - (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP) );
286     if (pos > pixels) pos = pixels;
287
288     if (!infoPtr->Page) pos *= infoPtr->MaxVal - infoPtr->MinVal;
289     else pos *= infoPtr->MaxVal - infoPtr->MinVal - infoPtr->Page + 1;
290     return infoPtr->MinVal + ((pos + pixels / 2) / pixels);
291 }
292
293 /***********************************************************************
294  *           SCROLL_PtInRectEx
295  */
296 static BOOL SCROLL_PtInRectEx( LPRECT lpRect, POINT pt, BOOL vertical )
297 {
298     RECT rect = *lpRect;
299
300     if (vertical)
301     {
302         rect.left -= lpRect->right - lpRect->left;
303         rect.right += lpRect->right - lpRect->left;
304     }
305     else
306     {
307         rect.top -= lpRect->bottom - lpRect->top;
308         rect.bottom += lpRect->bottom - lpRect->top;
309     }
310     return PtInRect( &rect, pt );
311 }
312
313 /***********************************************************************
314  *           SCROLL_ClipPos
315  */
316 static POINT SCROLL_ClipPos( LPRECT lpRect, POINT pt )
317 {
318     if( pt.x < lpRect->left )
319         pt.x = lpRect->left;
320     else
321     if( pt.x > lpRect->right )
322         pt.x = lpRect->right;
323
324     if( pt.y < lpRect->top )
325         pt.y = lpRect->top;
326     else
327     if( pt.y > lpRect->bottom )
328         pt.y = lpRect->bottom;
329
330     return pt;
331 }
332
333
334 /***********************************************************************
335  *           SCROLL_HitTest
336  *
337  * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
338  */
339 static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, INT nBar,
340                                            POINT pt, BOOL bDragging )
341 {
342     INT arrowSize, thumbSize, thumbPos;
343     RECT rect;
344
345     BOOL vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
346                                            &arrowSize, &thumbSize, &thumbPos );
347
348     if ( (bDragging && !SCROLL_PtInRectEx( &rect, pt, vertical )) ||
349          (!PtInRect( &rect, pt )) ) return SCROLL_NOWHERE;
350
351     if (vertical)
352     {
353         if (pt.y < rect.top + arrowSize) return SCROLL_TOP_ARROW;
354         if (pt.y >= rect.bottom - arrowSize) return SCROLL_BOTTOM_ARROW;
355         if (!thumbPos) return SCROLL_TOP_RECT;
356         pt.y -= rect.top;
357         if (pt.y < thumbPos) return SCROLL_TOP_RECT;
358         if (pt.y >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
359     }
360     else  /* horizontal */
361     {
362         if (pt.x < rect.left + arrowSize) return SCROLL_TOP_ARROW;
363         if (pt.x >= rect.right - arrowSize) return SCROLL_BOTTOM_ARROW;
364         if (!thumbPos) return SCROLL_TOP_RECT;
365         pt.x -= rect.left;
366         if (pt.x < thumbPos) return SCROLL_TOP_RECT;
367         if (pt.x >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
368     }
369     return SCROLL_THUMB;
370 }
371
372
373 /***********************************************************************
374  *           SCROLL_DrawArrows
375  *
376  * Draw the scroll bar arrows.
377  */
378 static void SCROLL_DrawArrows( HDC hdc, SCROLLBAR_INFO *infoPtr,
379                                RECT *rect, INT arrowSize, BOOL vertical,
380                                BOOL top_pressed, BOOL bottom_pressed )
381 {
382     HDC hdcMem = CreateCompatibleDC( hdc );
383     HBITMAP hbmpPrev = SelectObject( hdcMem, vertical ?
384                                     TOP_ARROW(infoPtr->flags, top_pressed)
385                                     : LEFT_ARROW(infoPtr->flags, top_pressed));
386
387     SetStretchBltMode( hdc, STRETCH_DELETESCANS );
388     StretchBlt( hdc, rect->left, rect->top,
389                   vertical ? rect->right-rect->left : arrowSize,
390                   vertical ? arrowSize : rect->bottom-rect->top,
391                   hdcMem, 0, 0,
392                   GetSystemMetrics(SM_CXVSCROLL),GetSystemMetrics(SM_CYHSCROLL),
393                   SRCCOPY );
394
395     SelectObject( hdcMem, vertical ?
396                     BOTTOM_ARROW( infoPtr->flags, bottom_pressed )
397                     : RIGHT_ARROW( infoPtr->flags, bottom_pressed ) );
398     if (vertical)
399         StretchBlt( hdc, rect->left, rect->bottom - arrowSize,
400                       rect->right - rect->left, arrowSize,
401                       hdcMem, 0, 0,
402                       GetSystemMetrics(SM_CXVSCROLL),GetSystemMetrics(SM_CYHSCROLL),
403                       SRCCOPY );
404     else
405         StretchBlt( hdc, rect->right - arrowSize, rect->top,
406                       arrowSize, rect->bottom - rect->top,
407                       hdcMem, 0, 0,
408                       GetSystemMetrics(SM_CXVSCROLL), GetSystemMetrics(SM_CYHSCROLL),
409                       SRCCOPY );
410     SelectObject( hdcMem, hbmpPrev );
411     DeleteDC( hdcMem );
412 }
413
414
415 /***********************************************************************
416  *           SCROLL_DrawMovingThumb
417  *
418  * Draw the moving thumb rectangle.
419  */
420 static void SCROLL_DrawMovingThumb( HDC hdc, RECT *rect, BOOL vertical,
421                                     INT arrowSize, INT thumbSize )
422 {
423     RECT r = *rect;
424     if (vertical)
425     {
426         r.top += SCROLL_TrackingPos;
427         if (r.top < rect->top + arrowSize - SCROLL_ARROW_THUMB_OVERLAP)
428             r.top = rect->top + arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
429         if (r.top + thumbSize >
430                        rect->bottom - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP))
431             r.top = rect->bottom - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP)
432                                                                   - thumbSize;
433         r.bottom = r.top + thumbSize;
434     }
435     else
436     {
437         r.left += SCROLL_TrackingPos;
438         if (r.left < rect->left + arrowSize - SCROLL_ARROW_THUMB_OVERLAP)
439             r.left = rect->left + arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
440         if (r.left + thumbSize >
441                        rect->right - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP))
442             r.left = rect->right - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP) 
443                                                                   - thumbSize;
444         r.right = r.left + thumbSize;
445     }
446     DrawFocusRect( hdc, &r );
447     SCROLL_MovingThumb = !SCROLL_MovingThumb;
448 }
449
450
451 /***********************************************************************
452  *           SCROLL_DrawInterior
453  *
454  * Draw the scroll bar interior (everything except the arrows).
455  */
456 static void SCROLL_DrawInterior( HWND hwnd, HDC hdc, INT nBar, 
457                                  RECT *rect, INT arrowSize,
458                                  INT thumbSize, INT thumbPos,
459                                  UINT flags, BOOL vertical,
460                                  BOOL top_selected, BOOL bottom_selected )
461 {
462     RECT r;
463
464       /* Select the correct brush and pen */
465
466     SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
467     if ((flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH)
468     {
469           /* This ought to be the color of the parent window */
470         SelectObject( hdc, GetSysColorBrush(COLOR_WINDOW) );
471     }
472     else
473     {
474         if (nBar == SB_CTL)  /* Only scrollbar controls send WM_CTLCOLOR */
475         {
476             HBRUSH hbrush = SendMessageA(GetParent(hwnd),
477                                              WM_CTLCOLORSCROLLBAR, hdc, hwnd );
478             SelectObject( hdc, hbrush );
479         }
480         else SelectObject( hdc, GetSysColorBrush(COLOR_SCROLLBAR) );
481     }
482
483       /* Calculate the scroll rectangle */
484
485     r = *rect;
486     if (vertical)
487     {
488         r.top    += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
489         r.bottom -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
490     }
491     else
492     {
493         r.left  += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
494         r.right -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
495     }
496
497       /* Draw the scroll bar frame */
498
499     Rectangle( hdc, r.left, r.top, r.right, r.bottom );
500
501       /* Draw the scroll rectangles and thumb */
502
503     if (!thumbPos)  /* No thumb to draw */
504     {
505         PatBlt( hdc, r.left+1, r.top+1, r.right - r.left - 2,
506                   r.bottom - r.top - 2, PATCOPY );
507         return;
508     }
509
510     if (vertical)
511     {
512         PatBlt( hdc, r.left + 1, r.top + 1,
513                   r.right - r.left - 2,
514                   thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP) - 1,
515                   top_selected ? 0x0f0000 : PATCOPY );
516         r.top += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
517         PatBlt( hdc, r.left + 1, r.top + thumbSize,
518                   r.right - r.left - 2,
519                   r.bottom - r.top - thumbSize - 1,
520                   bottom_selected ? 0x0f0000 : PATCOPY );
521         r.bottom = r.top + thumbSize;
522     }
523     else  /* horizontal */
524     {
525         PatBlt( hdc, r.left + 1, r.top + 1,
526                   thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP) - 1,
527                   r.bottom - r.top - 2,
528                   top_selected ? 0x0f0000 : PATCOPY );
529         r.left += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
530         PatBlt( hdc, r.left + thumbSize, r.top + 1,
531                   r.right - r.left - thumbSize - 1,
532                   r.bottom - r.top - 2,
533                   bottom_selected ? 0x0f0000 : PATCOPY );
534         r.right = r.left + thumbSize;
535     }
536
537       /* Draw the thumb */
538
539     SelectObject( hdc, GetSysColorBrush(COLOR_BTNFACE) );
540     Rectangle( hdc, r.left, r.top, r.right, r.bottom );
541     r.top++, r.left++;
542     DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
543     if (SCROLL_MovingThumb &&
544         (SCROLL_TrackingWin == hwnd) &&
545         (SCROLL_TrackingBar == nBar))
546     {
547         SCROLL_DrawMovingThumb( hdc, rect, vertical, arrowSize, thumbSize );
548         SCROLL_MovingThumb = TRUE;
549     }
550 }
551
552
553 /***********************************************************************
554  *           SCROLL_DrawScrollBar
555  *
556  * Redraw the whole scrollbar.
557  */
558 void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT nBar, 
559                            BOOL arrows, BOOL interior )
560 {
561     INT arrowSize, thumbSize, thumbPos;
562     RECT rect;
563     BOOL vertical;
564     WND *wndPtr = WIN_FindWndPtr( hwnd );
565     SCROLLBAR_INFO *infoPtr = SCROLL_GetPtrScrollInfo( wndPtr, nBar );
566
567     if (!wndPtr || !infoPtr ||
568         ((nBar == SB_VERT) && !(wndPtr->dwStyle & WS_VSCROLL)) ||
569         ((nBar == SB_HORZ) && !(wndPtr->dwStyle & WS_HSCROLL))) goto END;
570     if (!WIN_IsWindowDrawable( wndPtr, FALSE )) goto END;
571
572     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
573                                         &arrowSize, &thumbSize, &thumbPos );
574
575       /* Draw the arrows */
576
577     if (arrows && arrowSize)
578     {
579         if( vertical == SCROLL_trackVertical && GetCapture() == hwnd )
580             SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
581                                (SCROLL_trackHitTest == SCROLL_TOP_ARROW),
582                                (SCROLL_trackHitTest == SCROLL_BOTTOM_ARROW) );
583         else
584             SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical, 
585                                                                FALSE, FALSE );
586     }
587     if( interior )
588         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
589                          thumbPos, infoPtr->flags, vertical, FALSE, FALSE );
590 END:
591     WIN_ReleaseWndPtr(wndPtr);
592 }
593
594
595 /***********************************************************************
596  *           SCROLL_RefreshScrollBar
597  *
598  * Repaint the scroll bar interior after a SetScrollRange() or
599  * SetScrollPos() call.
600  */
601 static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar, 
602                                      BOOL arrows, BOOL interior )
603 {
604     HDC hdc = GetDCEx( hwnd, 0,
605                            DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
606     if (!hdc) return;
607
608     SCROLL_DrawScrollBar( hwnd, hdc, nBar, arrows, interior );
609     ReleaseDC( hwnd, hdc );
610 }
611
612
613 /***********************************************************************
614  *           SCROLL_HandleKbdEvent
615  *
616  * Handle a keyboard event (only for SB_CTL scrollbars).
617  */
618 static void SCROLL_HandleKbdEvent( HWND hwnd, WPARAM wParam )
619 {
620     WND *wndPtr = WIN_FindWndPtr( hwnd );
621     WPARAM msg;
622     
623     switch(wParam)
624     {
625     case VK_PRIOR: msg = SB_PAGEUP; break;
626     case VK_NEXT:  msg = SB_PAGEDOWN; break;
627     case VK_HOME:  msg = SB_TOP; break;
628     case VK_END:   msg = SB_BOTTOM; break;
629     case VK_UP:    msg = SB_LINEUP; break;
630     case VK_DOWN:  msg = SB_LINEDOWN; break;
631     default:
632         WIN_ReleaseWndPtr(wndPtr);
633         return;
634     }
635     SendMessageA( GetParent(hwnd),
636                     (wndPtr->dwStyle & SBS_VERT) ? WM_VSCROLL : WM_HSCROLL,
637                     msg, hwnd );
638     WIN_ReleaseWndPtr(wndPtr);
639 }
640
641
642 /***********************************************************************
643  *           SCROLL_HandleScrollEvent
644  *
645  * Handle a mouse or timer event for the scrollbar.
646  * 'pt' is the location of the mouse event in client (for SB_CTL) or
647  * windows coordinates.
648  */
649 void SCROLL_HandleScrollEvent( HWND hwnd, INT nBar, UINT msg, POINT pt)
650 {
651       /* Previous mouse position for timer events */
652     static POINT prevPt;
653       /* Thumb position when tracking started. */
654     static UINT trackThumbPos;
655       /* Position in the scroll-bar of the last button-down event. */
656     static INT lastClickPos;
657       /* Position in the scroll-bar of the last mouse event. */
658     static INT lastMousePos;
659
660     enum SCROLL_HITTEST hittest;
661     HWND hwndOwner, hwndCtl;
662     BOOL vertical;
663     INT arrowSize, thumbSize, thumbPos;
664     RECT rect;
665     HDC hdc;
666
667     SCROLLBAR_INFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
668     if (!infoPtr) return;
669     if ((SCROLL_trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN)) 
670                   return;
671
672     hdc = GetDCEx( hwnd, 0, DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW));
673     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
674                                         &arrowSize, &thumbSize, &thumbPos );
675     hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
676     hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;
677
678     switch(msg)
679     {
680       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
681           SCROLL_trackVertical = vertical;
682           SCROLL_trackHitTest  = hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
683           lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
684           lastMousePos  = lastClickPos;
685           trackThumbPos = thumbPos;
686           prevPt = pt;
687           SetCapture( hwnd );
688           if (nBar == SB_CTL) SetFocus( hwnd );
689           break;
690
691       case WM_MOUSEMOVE:
692           hittest = SCROLL_HitTest( hwnd, nBar, pt, TRUE );
693           prevPt = pt;
694           break;
695
696       case WM_LBUTTONUP:
697           hittest = SCROLL_NOWHERE;
698           ReleaseCapture();
699           break;
700
701       case WM_SYSTIMER:
702           pt = prevPt;
703           hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
704           break;
705
706       default:
707           return;  /* Should never happen */
708     }
709
710     TRACE("Event: hwnd=%04x bar=%d msg=%x pt=%ld,%ld hit=%d\n",
711                  hwnd, nBar, msg, pt.x, pt.y, hittest );
712
713     switch(SCROLL_trackHitTest)
714     {
715     case SCROLL_NOWHERE:  /* No tracking in progress */
716         break;
717
718     case SCROLL_TOP_ARROW:
719         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
720                            (hittest == SCROLL_trackHitTest), FALSE );
721         if (hittest == SCROLL_trackHitTest)
722         {
723             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
724             {
725                 SendMessageA( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
726                                 SB_LINEUP, hwndCtl );
727                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
728                                   SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
729                                   (TIMERPROC)0 );
730             }
731         }
732         else KillSystemTimer( hwnd, SCROLL_TIMER );
733         break;
734
735     case SCROLL_TOP_RECT:
736         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
737                              thumbPos, infoPtr->flags, vertical,
738                              (hittest == SCROLL_trackHitTest), FALSE );
739         if (hittest == SCROLL_trackHitTest)
740         {
741             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
742             {
743                 SendMessageA( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
744                                 SB_PAGEUP, hwndCtl );
745                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
746                                   SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
747                                   (TIMERPROC)0 );
748             }
749         }
750         else KillSystemTimer( hwnd, SCROLL_TIMER );
751         break;
752
753     case SCROLL_THUMB:
754         if (msg == WM_LBUTTONDOWN)
755         {
756             SCROLL_TrackingWin = hwnd;
757             SCROLL_TrackingBar = nBar;
758             SCROLL_TrackingPos = trackThumbPos + lastMousePos - lastClickPos;
759             SCROLL_DrawMovingThumb(hdc, &rect, vertical, arrowSize, thumbSize);
760         }
761         else if (msg == WM_LBUTTONUP)
762         {
763             SCROLL_TrackingWin = 0;
764             SCROLL_MovingThumb = FALSE;
765             SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
766                                  thumbPos, infoPtr->flags, vertical,
767                                  FALSE, FALSE );
768         }
769         else  /* WM_MOUSEMOVE */
770         {
771             UINT pos;
772
773             if (!SCROLL_PtInRectEx( &rect, pt, vertical )) pos = lastClickPos;
774             else
775             {
776                 pt = SCROLL_ClipPos( &rect, pt );
777                 pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
778             }
779             if (pos != lastMousePos)
780             {
781                 SCROLL_DrawMovingThumb( hdc, &rect, vertical,
782                                         arrowSize, thumbSize );
783                 lastMousePos = pos;
784                 SCROLL_TrackingPos = trackThumbPos + pos - lastClickPos;
785                 SCROLL_TrackingVal = SCROLL_GetThumbVal( infoPtr, &rect,
786                                                          vertical,
787                                                          SCROLL_TrackingPos );
788                 SendMessageA( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
789                                 MAKEWPARAM( SB_THUMBTRACK, SCROLL_TrackingVal),
790                                 hwndCtl );
791                 SCROLL_DrawMovingThumb( hdc, &rect, vertical,
792                                         arrowSize, thumbSize );
793             }
794         }
795         break;
796         
797     case SCROLL_BOTTOM_RECT:
798         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
799                              thumbPos, infoPtr->flags, vertical,
800                              FALSE, (hittest == SCROLL_trackHitTest) );
801         if (hittest == SCROLL_trackHitTest)
802         {
803             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
804             {
805                 SendMessageA( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
806                                 SB_PAGEDOWN, hwndCtl );
807                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
808                                   SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
809                                   (TIMERPROC)0 );
810             }
811         }
812         else KillSystemTimer( hwnd, SCROLL_TIMER );
813         break;
814         
815     case SCROLL_BOTTOM_ARROW:
816         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
817                            FALSE, (hittest == SCROLL_trackHitTest) );
818         if (hittest == SCROLL_trackHitTest)
819         {
820             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
821             {
822                 SendMessageA( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
823                                 SB_LINEDOWN, hwndCtl );
824                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
825                                   SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
826                                   (TIMERPROC)0 );
827             }
828         }
829         else KillSystemTimer( hwnd, SCROLL_TIMER );
830         break;
831     }
832
833     if (msg == WM_LBUTTONUP)
834     {
835         hittest = SCROLL_trackHitTest;
836         SCROLL_trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
837
838         if (hittest == SCROLL_THUMB)
839         {
840             UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
841                                  trackThumbPos + lastMousePos - lastClickPos );
842             SendMessageA( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
843                             MAKEWPARAM( SB_THUMBPOSITION, val ), hwndCtl );
844         }
845         else
846             SendMessageA( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
847                             SB_ENDSCROLL, hwndCtl );
848     }
849
850     ReleaseDC( hwnd, hdc );
851 }
852
853
854 /***********************************************************************
855  *           ScrollBarWndProc
856  */
857 LRESULT WINAPI ScrollBarWndProc( HWND hwnd, UINT message, WPARAM wParam,
858                                  LPARAM lParam )
859 {
860     switch(message)
861     {
862     case WM_CREATE:
863         {
864             CREATESTRUCTA *lpCreat = (CREATESTRUCTA *)lParam;
865             if (lpCreat->style & SBS_SIZEBOX)
866             {
867                 FIXME("Unimplemented style SBS_SIZEBOX.\n" );
868                 return 0;
869             }
870             
871             if (lpCreat->style & SBS_VERT)
872             {
873                 if (lpCreat->style & SBS_LEFTALIGN)
874                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
875                                   GetSystemMetrics(SM_CXVSCROLL)+1, lpCreat->cy, FALSE );
876                 else if (lpCreat->style & SBS_RIGHTALIGN)
877                     MoveWindow( hwnd, 
878                                   lpCreat->x+lpCreat->cx-GetSystemMetrics(SM_CXVSCROLL)-1,
879                                   lpCreat->y,
880                                   GetSystemMetrics(SM_CXVSCROLL)+1, lpCreat->cy, FALSE );
881             }
882             else  /* SBS_HORZ */
883             {
884                 if (lpCreat->style & SBS_TOPALIGN)
885                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
886                                   lpCreat->cx, GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
887                 else if (lpCreat->style & SBS_BOTTOMALIGN)
888                     MoveWindow( hwnd, 
889                                   lpCreat->x,
890                                   lpCreat->y+lpCreat->cy-GetSystemMetrics(SM_CYHSCROLL)-1,
891                                   lpCreat->cx, GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
892             }
893         }
894         if (!hUpArrow) SCROLL_LoadBitmaps();
895         TRACE("ScrollBar creation, hwnd=%04x\n", hwnd );
896         return 0;
897         
898     case WM_LBUTTONDOWN:
899     case WM_LBUTTONUP:
900     case WM_MOUSEMOVE:
901     case WM_SYSTIMER:
902         {
903             POINT pt;
904             CONV_POINT16TO32( (POINT16 *)&lParam, &pt );
905             SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, pt );
906         }
907         break;
908
909     case WM_KEYDOWN:
910         SCROLL_HandleKbdEvent( hwnd, wParam );
911         break;
912
913     case WM_ERASEBKGND:
914          return 1;
915
916     case WM_GETDLGCODE:
917          return DLGC_WANTARROWS; /* Windows returns this value */
918
919     case WM_PAINT:
920         {
921             PAINTSTRUCT ps;
922             HDC hdc = BeginPaint( hwnd, &ps );
923             SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL, TRUE, TRUE );
924             EndPaint( hwnd, &ps );
925         }
926         break;
927
928     case SBM_SETPOS16:
929     case SBM_SETPOS:
930         return SetScrollPos( hwnd, SB_CTL, wParam, (BOOL)lParam );
931
932     case SBM_GETPOS16:
933     case SBM_GETPOS:
934         return GetScrollPos( hwnd, SB_CTL );
935
936     case SBM_SETRANGE16:
937         SetScrollRange( hwnd, SB_CTL, LOWORD(lParam), HIWORD(lParam),
938                           wParam  /* FIXME: Is this correct? */ );
939         return 0;
940
941     case SBM_SETRANGE:
942         SetScrollRange( hwnd, SB_CTL, wParam, lParam, FALSE );
943         return 0;  /* FIXME: return previous position */
944
945     case SBM_GETRANGE16:
946         FIXME("don't know how to handle SBM_GETRANGE16 (wp=%04x,lp=%08lx)\n", wParam, lParam );
947         return 0;
948
949     case SBM_GETRANGE:
950         GetScrollRange( hwnd, SB_CTL, (LPINT)wParam, (LPINT)lParam );
951         return 0;
952
953     case SBM_ENABLE_ARROWS16:
954     case SBM_ENABLE_ARROWS:
955         return EnableScrollBar( hwnd, SB_CTL, wParam );
956
957     case SBM_SETRANGEREDRAW:
958         SetScrollRange( hwnd, SB_CTL, wParam, lParam, TRUE );
959         return 0;  /* FIXME: return previous position */
960         
961     case SBM_SETSCROLLINFO:
962         return SetScrollInfo( hwnd, SB_CTL, (SCROLLINFO *)lParam, wParam );
963
964     case SBM_GETSCROLLINFO:
965         return GetScrollInfo( hwnd, SB_CTL, (SCROLLINFO *)lParam );
966
967     case 0x00e5:
968     case 0x00e7:
969     case 0x00e8:
970     case 0x00eb:
971     case 0x00ec:
972     case 0x00ed:
973     case 0x00ee:
974     case 0x00ef:
975         ERR("unknown Win32 msg %04x wp=%08x lp=%08lx\n",
976                     message, wParam, lParam );
977         break;
978
979     default:
980         if (message >= WM_USER)
981             WARN("unknown msg %04x wp=%04x lp=%08lx\n",
982                          message, wParam, lParam );
983         return DefWindowProcA( hwnd, message, wParam, lParam );
984     }
985     return 0;
986 }
987
988
989 /*************************************************************************
990  *           SetScrollInfo16   (USER.475)
991  */
992 INT16 WINAPI SetScrollInfo16( HWND16 hwnd, INT16 nBar, const SCROLLINFO *info,
993                               BOOL16 bRedraw )
994 {
995     return (INT16)SetScrollInfo( hwnd, nBar, info, bRedraw );
996 }
997
998
999 /*************************************************************************
1000  *           SetScrollInfo32   (USER32.501)
1001  * SetScrollInfo32 can be used to set the position, upper bound, 
1002  * lower bound, and page size of a scrollbar control.
1003  *
1004  * RETURNS
1005  *    Scrollbar position
1006  *
1007  * NOTE
1008  *    For 100 lines of text to be displayed in a window of 25 lines,
1009  *  one would for instance use info->nMin=0, info->nMax=75
1010  *  (corresponding to the 76 different positions of the window on
1011  *  the text), and info->nPage=25.
1012  */
1013 INT WINAPI SetScrollInfo( 
1014 HWND hwnd /* [I] Handle of window whose scrollbar will be affected */, 
1015 INT nBar /* [I] One of SB_HORZ, SB_VERT, or SB_CTL */, 
1016 const SCROLLINFO *info /* [I] Specifies what to change and new values */,
1017 BOOL bRedraw /* [I] Should scrollbar be redrawn afterwards ? */)
1018 {
1019     INT action;
1020     INT retVal = SCROLL_SetScrollInfo( hwnd, nBar, info, &action );
1021
1022     if( action & SA_SSI_HIDE )
1023         SCROLL_ShowScrollBar( hwnd, nBar, FALSE, FALSE );
1024     else
1025     {
1026         if( action & SA_SSI_SHOW )
1027             if( SCROLL_ShowScrollBar( hwnd, nBar, TRUE, TRUE ) )
1028                 return retVal; /* SetWindowPos() already did the painting */
1029
1030         if( bRedraw && (action & SA_SSI_REFRESH))
1031             SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
1032         else if( action & SA_SSI_REPAINT_ARROWS )
1033             SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, FALSE );
1034     }
1035     return retVal;
1036 }
1037
1038 INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar, 
1039                             const SCROLLINFO *info, INT *action  )
1040 {
1041     /* Update the scrollbar state and set action flags according to 
1042      * what has to be done graphics wise. */
1043
1044     SCROLLBAR_INFO *infoPtr;
1045     UINT new_flags;
1046
1047     dbg_decl_str(scroll, 256);
1048
1049    *action = 0;
1050
1051     if (!(infoPtr = SCROLL_GetScrollInfo(hwnd, nBar))) return 0;
1052     if (info->fMask & ~(SIF_ALL | SIF_DISABLENOSCROLL)) return 0;
1053     if ((info->cbSize != sizeof(*info)) &&
1054         (info->cbSize != sizeof(*info)-sizeof(info->nTrackPos))) return 0;
1055
1056     /* Set the page size */
1057
1058     if (info->fMask & SIF_PAGE)
1059     {
1060         dsprintf(scroll, " page=%d", info->nPage );
1061         if( infoPtr->Page != info->nPage )
1062         {
1063             infoPtr->Page = info->nPage;
1064            *action |= SA_SSI_REFRESH;
1065         }
1066     }
1067
1068     /* Set the scroll pos */
1069
1070     if (info->fMask & SIF_POS)
1071     {
1072         dsprintf(scroll, " pos=%d", info->nPos );
1073         if( infoPtr->CurVal != info->nPos )
1074         {
1075             infoPtr->CurVal = info->nPos;
1076            *action |= SA_SSI_REFRESH;
1077         }
1078     }
1079
1080     /* Set the scroll range */
1081
1082     if (info->fMask & SIF_RANGE)
1083     {
1084         dsprintf(scroll, " min=%d max=%d", info->nMin, info->nMax );
1085
1086         /* Invalid range -> range is set to (0,0) */
1087         if ((info->nMin > info->nMax) ||
1088             ((UINT)(info->nMax - info->nMin) >= 0x80000000))
1089         {
1090             infoPtr->MinVal = 0;
1091             infoPtr->MaxVal = 0;
1092         }
1093         else
1094         {
1095             if( infoPtr->MinVal != info->nMin ||
1096                 infoPtr->MaxVal != info->nMax )
1097             {
1098                *action |= SA_SSI_REFRESH;
1099                 infoPtr->MinVal = info->nMin;
1100                 infoPtr->MaxVal = info->nMax;
1101             }
1102         }
1103     }
1104
1105     TRACE("hwnd=%04x bar=%d %s\n", 
1106                     hwnd, nBar, dbg_str(scroll));
1107
1108     /* Make sure the page size is valid */
1109
1110     if (infoPtr->Page < 0) infoPtr->Page = 0;
1111     else if (infoPtr->Page > infoPtr->MaxVal - infoPtr->MinVal + 1 )
1112         infoPtr->Page = infoPtr->MaxVal - infoPtr->MinVal + 1;
1113
1114     /* Make sure the pos is inside the range */
1115
1116     if (infoPtr->CurVal < infoPtr->MinVal)
1117         infoPtr->CurVal = infoPtr->MinVal;
1118     else if (infoPtr->CurVal > infoPtr->MaxVal - MAX( infoPtr->Page-1, 0 ))
1119         infoPtr->CurVal = infoPtr->MaxVal - MAX( infoPtr->Page-1, 0 );
1120
1121     TRACE("    new values: page=%d pos=%d min=%d max=%d\n",
1122                  infoPtr->Page, infoPtr->CurVal,
1123                  infoPtr->MinVal, infoPtr->MaxVal );
1124
1125     /* Check if the scrollbar should be hidden or disabled */
1126
1127     if (info->fMask & (SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL))
1128     {
1129         new_flags = infoPtr->flags;
1130         if (infoPtr->MinVal >= infoPtr->MaxVal - MAX( infoPtr->Page-1, 0 ))
1131         {
1132             /* Hide or disable scroll-bar */
1133             if (info->fMask & SIF_DISABLENOSCROLL)
1134             {
1135                 new_flags = ESB_DISABLE_BOTH;
1136                *action |= SA_SSI_REFRESH;
1137             }
1138             else if (nBar != SB_CTL)
1139             {
1140                 *action = SA_SSI_HIDE;
1141                 goto done;
1142             }
1143         }
1144         else  /* Show and enable scroll-bar */
1145         {
1146             new_flags = 0;
1147             if (nBar != SB_CTL)
1148                 *action |= SA_SSI_SHOW;
1149         }
1150
1151         if (infoPtr->flags != new_flags) /* check arrow flags */
1152         {
1153             infoPtr->flags = new_flags;
1154            *action |= SA_SSI_REPAINT_ARROWS;
1155         }
1156     }
1157
1158 done:
1159     /* Return current position */
1160
1161     return infoPtr->CurVal;
1162 }
1163
1164
1165 /*************************************************************************
1166  *           GetScrollInfo16   (USER.476)
1167  */
1168 BOOL16 WINAPI GetScrollInfo16( HWND16 hwnd, INT16 nBar, LPSCROLLINFO info )
1169 {
1170     return GetScrollInfo( hwnd, nBar, info );
1171 }
1172
1173
1174 /*************************************************************************
1175  *           GetScrollInfo32   (USER32.284)
1176  * GetScrollInfo32 can be used to retrieve the position, upper bound, 
1177  * lower bound, and page size of a scrollbar control.
1178  *
1179  * RETURNS STD
1180  */
1181 BOOL WINAPI GetScrollInfo( 
1182   HWND hwnd /* [I] Handle of window */ , 
1183   INT nBar /* [I] One of SB_HORZ, SB_VERT, or SB_CTL */, 
1184   LPSCROLLINFO info /* [IO] (info.fMask [I] specifies which values are to retrieve) */)
1185 {
1186     SCROLLBAR_INFO *infoPtr;
1187
1188     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return FALSE;
1189     if (info->fMask & ~(SIF_ALL | SIF_DISABLENOSCROLL)) return FALSE;
1190     if ((info->cbSize != sizeof(*info)) &&
1191         (info->cbSize != sizeof(*info)-sizeof(info->nTrackPos))) return FALSE;
1192
1193     if (info->fMask & SIF_PAGE) info->nPage = infoPtr->Page;
1194     if (info->fMask & SIF_POS) info->nPos = infoPtr->CurVal;
1195     if ((info->fMask & SIF_TRACKPOS) && (info->cbSize == sizeof(*info)))
1196         info->nTrackPos = (SCROLL_TrackingWin==hwnd) ? SCROLL_TrackingVal : 0;
1197     if (info->fMask & SIF_RANGE)
1198     {
1199         info->nMin = infoPtr->MinVal;
1200         info->nMax = infoPtr->MaxVal;
1201     }
1202     return (info->fMask & SIF_ALL) != 0;
1203 }
1204
1205
1206 /*************************************************************************
1207  *           SetScrollPos16   (USER.62)
1208  */
1209 INT16 WINAPI SetScrollPos16( HWND16 hwnd, INT16 nBar, INT16 nPos,
1210                              BOOL16 bRedraw )
1211 {
1212     return (INT16)SetScrollPos( hwnd, nBar, nPos, bRedraw );
1213 }
1214
1215
1216 /*************************************************************************
1217  *           SetScrollPos32   (USER32.502)
1218  *
1219  * RETURNS
1220  *    Success: Scrollbar position
1221  *    Failure: 0
1222  *
1223  * REMARKS
1224  *    Note the ambiguity when 0 is returned.  Use GetLastError
1225  *    to make sure there was an error (and to know which one).
1226  */
1227 INT WINAPI SetScrollPos( 
1228 HWND hwnd /* [I] Handle of window whose scrollbar will be affected */,
1229 INT nBar /* [I] One of SB_HORZ, SB_VERT, or SB_CTL */,
1230 INT nPos /* [I] New value */,
1231 BOOL bRedraw /* [I] Should scrollbar be redrawn afterwards ? */ )
1232 {
1233     SCROLLINFO info;
1234     SCROLLBAR_INFO *infoPtr;
1235     INT oldPos;
1236
1237     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
1238     oldPos      = infoPtr->CurVal;
1239     info.cbSize = sizeof(info);
1240     info.nPos   = nPos;
1241     info.fMask  = SIF_POS;
1242     SetScrollInfo( hwnd, nBar, &info, bRedraw );
1243     return oldPos;
1244 }
1245
1246
1247 /*************************************************************************
1248  *           GetScrollPos16   (USER.63)
1249  */
1250 INT16 WINAPI GetScrollPos16( HWND16 hwnd, INT16 nBar )
1251 {
1252     return (INT16)GetScrollPos( hwnd, nBar );
1253 }
1254
1255
1256 /*************************************************************************
1257  *           GetScrollPos32   (USER32.285)
1258  *
1259  * RETURNS
1260  *    Success: Current position
1261  *    Failure: 0   
1262  *
1263  * REMARKS
1264  *    Note the ambiguity when 0 is returned.  Use GetLastError
1265  *    to make sure there was an error (and to know which one).
1266  */
1267 INT WINAPI GetScrollPos( 
1268 HWND hwnd, /* [I] Handle of window */
1269 INT nBar /* [I] One of SB_HORZ, SB_VERT, or SB_CTL */)
1270 {
1271     SCROLLBAR_INFO *infoPtr;
1272
1273     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
1274     return infoPtr->CurVal;
1275 }
1276
1277
1278 /*************************************************************************
1279  *           SetScrollRange16   (USER.64)
1280  */
1281 void WINAPI SetScrollRange16( HWND16 hwnd, INT16 nBar,
1282                               INT16 MinVal, INT16 MaxVal, BOOL16 bRedraw )
1283 {
1284     /* Invalid range -> range is set to (0,0) */
1285     if ((INT)MaxVal - (INT)MinVal > 0x7fff) MinVal = MaxVal = 0;
1286     SetScrollRange( hwnd, nBar, MinVal, MaxVal, bRedraw );
1287 }
1288
1289
1290 /*************************************************************************
1291  *           SetScrollRange32   (USER32.503)
1292  *
1293  * RETURNS STD
1294  */
1295 BOOL WINAPI SetScrollRange( 
1296 HWND hwnd, /* [I] Handle of window whose scrollbar will be affected */
1297 INT nBar, /* [I] One of SB_HORZ, SB_VERT, or SB_CTL */
1298 INT MinVal, /* [I] New minimum value */
1299 INT MaxVal, /* [I] New maximum value */
1300 BOOL bRedraw /* [I] Should scrollbar be redrawn afterwards ? */)
1301 {
1302     SCROLLINFO info;
1303
1304     info.cbSize = sizeof(info);
1305     info.nMin   = MinVal;
1306     info.nMax   = MaxVal;
1307     info.fMask  = SIF_RANGE;
1308     SetScrollInfo( hwnd, nBar, &info, bRedraw );
1309     return TRUE;
1310 }
1311
1312
1313 /*************************************************************************
1314  *           SCROLL_SetNCSbState
1315  *
1316  * Updates both scrollbars at the same time. Used by MDI CalcChildScroll().
1317  */
1318 INT SCROLL_SetNCSbState(WND* wndPtr, int vMin, int vMax, int vPos,
1319                                        int hMin, int hMax, int hPos)
1320 {
1321     INT vA, hA;
1322     SCROLLINFO vInfo, hInfo;
1323  
1324     vInfo.cbSize = hInfo.cbSize = sizeof(SCROLLINFO);
1325     vInfo.nMin   = vMin;         hInfo.nMin   = hMin;
1326     vInfo.nMax   = vMax;         hInfo.nMax   = hMax;
1327     vInfo.nPos   = vPos;         hInfo.nPos   = hPos;
1328     vInfo.fMask  = hInfo.fMask = SIF_RANGE | SIF_POS;
1329
1330     SCROLL_SetScrollInfo( wndPtr->hwndSelf, SB_VERT, &vInfo, &vA );
1331     SCROLL_SetScrollInfo( wndPtr->hwndSelf, SB_HORZ, &hInfo, &hA );
1332
1333     if( !SCROLL_ShowScrollBar( wndPtr->hwndSelf, SB_BOTH,
1334                               (hA & SA_SSI_SHOW),(vA & SA_SSI_SHOW) ) )
1335     {
1336         /* SetWindowPos() wasn't called, just redraw the scrollbars if needed */
1337         if( vA & SA_SSI_REFRESH )
1338             SCROLL_RefreshScrollBar( wndPtr->hwndSelf, SB_VERT, FALSE, TRUE );
1339
1340         if( hA & SA_SSI_REFRESH )
1341             SCROLL_RefreshScrollBar( wndPtr->hwndSelf, SB_HORZ, FALSE, TRUE );
1342     }
1343     return 0;
1344 }
1345
1346
1347 /*************************************************************************
1348  *           GetScrollRange16   (USER.65)
1349  */
1350 BOOL16 WINAPI GetScrollRange16( HWND16 hwnd, INT16 nBar,
1351                                 LPINT16 lpMin, LPINT16 lpMax)
1352 {
1353     INT min, max;
1354     BOOL16 ret = GetScrollRange( hwnd, nBar, &min, &max );
1355     if (lpMin) *lpMin = min;
1356     if (lpMax) *lpMax = max;
1357     return ret;
1358 }
1359
1360
1361 /*************************************************************************
1362  *           GetScrollRange32   (USER32.286)
1363  *
1364  * RETURNS STD
1365  */
1366 BOOL WINAPI GetScrollRange( 
1367 HWND hwnd, /* [I] Handle of window */
1368 INT nBar, /* [I] One of SB_HORZ, SB_VERT, or SB_CTL  */
1369 LPINT lpMin, /* [O] Where to store minimum value */
1370 LPINT lpMax /* [O] Where to store maximum value */)
1371 {
1372     SCROLLBAR_INFO *infoPtr;
1373
1374     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar )))
1375     {
1376         if (lpMin) lpMin = 0;
1377         if (lpMax) lpMax = 0;
1378         return FALSE;
1379     }
1380     if (lpMin) *lpMin = infoPtr->MinVal;
1381     if (lpMax) *lpMax = infoPtr->MaxVal;
1382     return TRUE;
1383 }
1384
1385
1386 /*************************************************************************
1387  *           SCROLL_ShowScrollBar()
1388  *
1389  * Back-end for ShowScrollBar(). Returns FALSE if no action was taken.
1390  * NOTE: fShowV/fShowH must be zero when nBar is SB_HORZ/SB_VERT.
1391  */
1392 BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar, 
1393                              BOOL fShowH, BOOL fShowV )
1394 {
1395     WND *wndPtr = WIN_FindWndPtr( hwnd );
1396     BOOL retvalue = FALSE;
1397
1398     if (!wndPtr) return FALSE;
1399     TRACE("hwnd=%04x bar=%d horz=%d, vert=%d\n",
1400                     hwnd, nBar, fShowH, fShowV );
1401
1402     switch(nBar)
1403     {
1404     case SB_CTL:
1405         ShowWindow( hwnd, fShowH ? SW_SHOW : SW_HIDE );
1406         retvalue = TRUE;
1407         goto END;
1408
1409     case SB_BOTH:
1410     case SB_HORZ:
1411         if (fShowH)
1412         {
1413             fShowH = !(wndPtr->dwStyle & WS_HSCROLL);
1414             wndPtr->dwStyle |= WS_HSCROLL;
1415         }
1416         else  /* hide it */
1417         {
1418             fShowH = (wndPtr->dwStyle & WS_HSCROLL);
1419             wndPtr->dwStyle &= ~WS_HSCROLL;
1420         }
1421         if( nBar == SB_HORZ ) break; 
1422         /* fall through */
1423
1424     case SB_VERT:
1425         if (fShowV)
1426         {
1427             fShowV = !(wndPtr->dwStyle & WS_VSCROLL);
1428             wndPtr->dwStyle |= WS_VSCROLL;
1429         }
1430         else  /* hide it */
1431         {
1432             fShowV = (wndPtr->dwStyle & WS_VSCROLL);
1433             wndPtr->dwStyle &= ~WS_VSCROLL;
1434         }
1435         break;
1436
1437     default:
1438         retvalue = FALSE;  /* Nothing to do! */
1439         goto END;
1440     }
1441
1442     if( fShowH || fShowV ) /* frame has been changed, let the window redraw itself */
1443     {
1444         SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE
1445                     | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1446         retvalue = TRUE;
1447         goto END;
1448     }
1449
1450     retvalue = FALSE; /* no frame changes */
1451 END:
1452     WIN_ReleaseWndPtr(wndPtr);
1453     return retvalue;
1454 }
1455
1456
1457 /*************************************************************************
1458  *           ShowScrollBar16   (USER.267)
1459  */
1460 void WINAPI ShowScrollBar16( HWND16 hwnd, INT16 nBar, BOOL16 fShow )
1461 {
1462     SCROLL_ShowScrollBar( hwnd, nBar, (nBar == SB_VERT) ? 0 : fShow,
1463                                       (nBar == SB_HORZ) ? 0 : fShow );
1464 }
1465
1466
1467 /*************************************************************************
1468  *           ShowScrollBar32   (USER32.532)
1469  *
1470  * RETURNS STD
1471  */
1472 BOOL WINAPI ShowScrollBar(
1473 HWND hwnd, /* [I] Handle of window whose scrollbar(s) will be affected   */
1474 INT nBar, /* [I] One of SB_HORZ, SB_VERT, SB_BOTH or SB_CTL */
1475 BOOL fShow /* [I] TRUE = show, FALSE = hide  */)
1476 {
1477     SCROLL_ShowScrollBar( hwnd, nBar, (nBar == SB_VERT) ? 0 : fShow,
1478                                       (nBar == SB_HORZ) ? 0 : fShow );
1479     return TRUE;
1480 }
1481
1482
1483 /*************************************************************************
1484  *           EnableScrollBar16   (USER.482)
1485  */
1486 BOOL16 WINAPI EnableScrollBar16( HWND16 hwnd, INT16 nBar, UINT16 flags )
1487 {
1488     return EnableScrollBar( hwnd, nBar, flags );
1489 }
1490
1491
1492 /*************************************************************************
1493  *           EnableScrollBar32   (USER32.171)
1494  */
1495 BOOL WINAPI EnableScrollBar( HWND hwnd, INT nBar, UINT flags )
1496 {
1497     BOOL bFineWithMe;
1498     SCROLLBAR_INFO *infoPtr;
1499
1500     TRACE("%04x %d %d\n", hwnd, nBar, flags );
1501
1502     flags &= ESB_DISABLE_BOTH;
1503
1504     if (nBar == SB_BOTH)
1505     {
1506         if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, SB_VERT ))) return FALSE;
1507         if (!(bFineWithMe = (infoPtr->flags == flags)) )
1508         {
1509             infoPtr->flags = flags;
1510             SCROLL_RefreshScrollBar( hwnd, SB_VERT, TRUE, TRUE );
1511         }
1512         nBar = SB_HORZ;
1513     }
1514     else
1515         bFineWithMe = TRUE;
1516    
1517     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return FALSE;
1518     if (bFineWithMe && infoPtr->flags == flags) return FALSE;
1519     infoPtr->flags = flags;
1520
1521     SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
1522     return TRUE;
1523 }