935871a3ba54e6378ca344e3585fed8cc4f986b6
[reactos.git] / lib / user32 / controls / scrollbar.c
1 /* $Id$
2  *
3  * COPYRIGHT:        See COPYING in the top level directory
4  * PROJECT:          ReactOS kernel
5  * PURPOSE:          Windows
6  * FILE:             subsys/win32k/ntuser/window.c
7  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
8  *                   Thomas Weidenmueller (w3seek@users.sourceforge.net)
9  * REVISION HISTORY:
10  *       06-06-2001  CSH  Created
11  */
12 /* INCLUDES ******************************************************************/
13
14 #include <windows.h>
15 #include <user32.h>
16 #include <debug.h>
17 #include <draw.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 /* GLOBAL VARIABLES **********************************************************/
22 /*
23 static HBITMAP hUpArrow;
24 static HBITMAP hDnArrow;
25 static HBITMAP hLfArrow;
26 static HBITMAP hRgArrow;
27 static HBITMAP hUpArrowD;
28 static HBITMAP hDnArrowD;
29 static HBITMAP hLfArrowD;
30 static HBITMAP hRgArrowD;
31 static HBITMAP hUpArrowI;
32 static HBITMAP hDnArrowI;
33 static HBITMAP hLfArrowI;
34 static HBITMAP hRgArrowI;
35 */
36 #define TOP_ARROW(flags,pressed) \
37    (((flags)&ESB_DISABLE_UP) ? hUpArrowI : ((pressed) ? hUpArrowD:hUpArrow))
38 #define BOTTOM_ARROW(flags,pressed) \
39    (((flags)&ESB_DISABLE_DOWN) ? hDnArrowI : ((pressed) ? hDnArrowD:hDnArrow))
40 #define LEFT_ARROW(flags,pressed) \
41    (((flags)&ESB_DISABLE_LEFT) ? hLfArrowI : ((pressed) ? hLfArrowD:hLfArrow))
42 #define RIGHT_ARROW(flags,pressed) \
43    (((flags)&ESB_DISABLE_RIGHT) ? hRgArrowI : ((pressed) ? hRgArrowD:hRgArrow))
44
45 #define SCROLL_MIN_THUMB 6               /* Minimum size of the thumb in pixels */
46 #define SCROLL_FIRST_DELAY   200         /* Delay (in ms) before first repetition when holding the button down */
47 #define SCROLL_REPEAT_DELAY  50          /* Delay (in ms) between scroll repetitions */
48 #define SCROLL_TIMER   0                 /* Scroll timer id */
49
50  /* What to do after SCROLL_SetScrollInfo() */
51 #define SA_SSI_HIDE             0x0001
52 #define SA_SSI_SHOW             0x0002
53 #define SA_SSI_REFRESH          0x0004
54 #define SA_SSI_REPAINT_ARROWS   0x0008
55
56 /* Scroll-bar hit testing */
57 #define SCROLL_NOWHERE  0x00    /* Outside the scroll bar */
58 #define SCROLL_TOP_ARROW    0x01    /* Top or left arrow */
59 #define SCROLL_TOP_RECT 0x02    /* Rectangle between the top arrow and the thumb */
60 #define SCROLL_THUMB    0x03    /* Thumb rectangle */
61 #define SCROLL_BOTTOM_RECT  0x04    /* Rectangle between the thumb and the bottom arrow */
62 #define SCROLL_BOTTOM_ARROW 0x05    /* Bottom or right arrow */
63
64 static BOOL SCROLL_MovingThumb = FALSE; /* Is the moving thumb being displayed? */
65
66 /* Thumb-tracking info */
67 static HWND SCROLL_TrackingWin = 0;
68 static INT SCROLL_TrackingBar = 0;
69 static INT SCROLL_TrackingPos = 0;
70 /* static INT  SCROLL_TrackingVal = 0; */
71 static DWORD SCROLL_trackHitTest; /* Hit test code of the last button-down event */
72 static BOOL SCROLL_trackVertical;
73
74 /* INTERNAL FUNCTIONS *********************************************************/
75
76 HBRUSH DefWndControlColor (HDC hDC, UINT ctlType);
77
78 /* Ported from WINE20020904 */
79 /* Draw the scroll bar interior (everything except the arrows). */
80 static void
81 SCROLL_DrawInterior (HWND hwnd, HDC hdc, INT nBar, BOOL vertical, INT
82 arrowSize, PSCROLLBARINFO psbi)
83 {
84   INT thumbSize = psbi->xyThumbBottom - psbi->xyThumbTop;
85   RECT rc;
86   HPEN hSavePen;
87   HBRUSH hSaveBrush, hBrush;
88   BOOLEAN top_selected = FALSE, bottom_selected = FALSE;
89 DbgPrint("[SCROLL_DrawInterior:%d]\n", nBar);
90   if(psbi->rgstate[SCROLL_TOP_RECT] & STATE_SYSTEM_PRESSED)
91   {
92     top_selected = TRUE;
93   }
94   if(psbi->rgstate[SCROLL_BOTTOM_RECT] & STATE_SYSTEM_PRESSED)
95   {
96     bottom_selected = TRUE;
97   }
98
99   /* Only scrollbar controls send WM_CTLCOLORSCROLLBAR.
100    * The window-owned scrollbars need to call DefWndControlColor
101    * to correctly setup default scrollbar colors
102    */
103   if ( nBar == SB_CTL )
104   {
105     hBrush = (HBRUSH) NtUserSendMessage (GetParent (hwnd), WM_CTLCOLORSCROLLBAR, (WPARAM) hdc, (LPARAM) hwnd);
106     if(!hBrush)
107       hBrush = GetSysColorBrush(COLOR_SCROLLBAR);
108   }
109   else
110   {
111 /*    hBrush = NtUserGetControlColor (hdc, CTLCOLOR_SCROLLBAR); FIXME
112 */ /* DefWndControlColor */
113     hBrush = GetSysColorBrush(COLOR_SCROLLBAR);
114   }
115
116   hSavePen = SelectObject (hdc, GetSysColorPen (COLOR_WINDOWFRAME));
117   hSaveBrush = SelectObject (hdc, hBrush);
118
119   /* Calculate the scroll rectangle */
120   if (vertical)
121   {
122     rc.top = psbi->rcScrollBar.top + arrowSize;
123     rc.bottom = psbi->rcScrollBar.bottom - arrowSize;
124     rc.left = psbi->rcScrollBar.left;
125     rc.right = psbi->rcScrollBar.right;
126   }
127   else
128   {
129     rc.top = psbi->rcScrollBar.top;
130     rc.bottom = psbi->rcScrollBar.bottom;
131     rc.left = psbi->rcScrollBar.left + arrowSize;
132     rc.right = psbi->rcScrollBar.right - arrowSize;
133   }
134
135   /* Draw the scroll rectangles and thumb */
136   if (!psbi->xyThumbBottom)             /* No thumb to draw */
137   {
138     PatBlt (hdc,
139             rc.left,
140             rc.top,
141             rc.right - rc.left,
142             rc.bottom - rc.top,
143             PATCOPY);
144
145     /* cleanup and return */
146     SelectObject (hdc, hSavePen);
147     SelectObject (hdc, hSaveBrush);
148     return;
149   }
150   
151   psbi->xyThumbTop -= arrowSize;
152
153   if (vertical)
154   {
155     PatBlt (hdc,
156             rc.left,
157             rc.top,
158             rc.right - rc.left,
159             psbi->dxyLineButton,
160             top_selected ? 0x0f0000 : PATCOPY);
161     rc.top += psbi->xyThumbTop;
162     PatBlt (hdc,
163             rc.left,
164             rc.top + thumbSize,
165             rc.right - rc.left,
166             rc.bottom - rc.top - thumbSize,
167             bottom_selected ? 0x0f0000 : PATCOPY);
168     rc.bottom = rc.top + thumbSize;
169   }
170   else
171   {
172     PatBlt (hdc,
173             rc.left,
174             rc.top,
175             psbi->xyThumbTop,
176             rc.bottom - rc.top,
177             top_selected ? 0x0f0000 : PATCOPY);
178     rc.left += psbi->xyThumbTop;
179     PatBlt (hdc,
180             rc.left + thumbSize,
181             rc.top,
182             rc.right - rc.left - thumbSize,
183             rc.bottom - rc.top,
184             bottom_selected ? 0x0f0000 : PATCOPY);
185     rc.right = rc.left + thumbSize;
186   }
187
188   /* Draw the thumb */
189   DrawEdge (hdc, &rc, EDGE_RAISED, BF_RECT | BF_MIDDLE);
190
191   /* cleanup */
192   SelectObject (hdc, hSavePen);
193   SelectObject (hdc, hSaveBrush);
194 }
195
196 /* Ported from WINE20020904 */
197 static void
198 SCROLL_DrawMovingThumb (HDC hdc, RECT * rect, BOOL vertical, int arrowSize, int thumbSize, PSCROLLBARINFO psbi)
199 {
200   INT pos = SCROLL_TrackingPos;
201   INT max_size;
202
203   if ( vertical )
204     max_size = psbi->rcScrollBar.bottom - psbi->rcScrollBar.top;
205   else
206     max_size = psbi->rcScrollBar.right - psbi->rcScrollBar.left;
207
208   max_size -= arrowSize + thumbSize;
209
210   if (pos < arrowSize)
211     pos = arrowSize;
212   else if (pos > max_size)
213     pos = max_size;
214
215   SCROLL_DrawInterior (SCROLL_TrackingWin, hdc, SCROLL_TrackingBar, vertical, arrowSize, psbi);
216
217   SCROLL_MovingThumb = !SCROLL_MovingThumb;
218 }
219
220 /* Ported from WINE20020904 */
221 /* Draw the scroll bar arrows. */
222 static void
223 SCROLL_DrawArrows (HDC hdc, PSCROLLBARINFO info,
224            RECT * rect, INT arrowSize, BOOL vertical,
225            BOOL top_pressed, BOOL bottom_pressed)
226 {
227   RECT r1, r2;
228   int scrollDirFlag1, scrollDirFlag2;
229
230   r1 = r2 = *rect;
231   if (vertical)
232   {
233     scrollDirFlag1 = DFCS_SCROLLUP;
234     scrollDirFlag2 = DFCS_SCROLLDOWN;
235     r1.bottom = r1.top + arrowSize;
236     r2.top = r2.bottom - arrowSize;
237   }
238   else
239   {
240     scrollDirFlag1 = DFCS_SCROLLLEFT;
241     scrollDirFlag2 = DFCS_SCROLLRIGHT;
242     r1.right = r1.left + arrowSize;
243     r2.left = r2.right - arrowSize;
244   }
245
246   DrawFrameControl (hdc, &r1, DFC_SCROLL,
247             scrollDirFlag1 | (top_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0)
248             /* | (info.flags&ESB_DISABLE_LTUP ? DFCS_INACTIVE : 0) */
249     );
250   DrawFrameControl (hdc, &r2, DFC_SCROLL,
251             scrollDirFlag2 | (bottom_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0)
252             /* | (info.flags&ESB_DISABLE_RTDN ? DFCS_INACTIVE : 0) */
253     );
254 }
255
256 /* Ported from WINE20020904 */
257 /* Redraw the whole scrollbar. */
258 void
259 SCROLL_DrawScrollBar (HWND hwnd, HDC hdc, INT nBar,
260                       BOOL arrows, BOOL interior)
261 {
262   INT arrowSize = 0;
263   INT thumbSize;
264   SCROLLBARINFO info;
265   BOOL Save_SCROLL_MovingThumb = SCROLL_MovingThumb;
266   BOOL vertical;
267
268   info.cbSize = sizeof(SCROLLBARINFO);
269
270   switch ( nBar )
271   {
272   case SB_HORZ:
273     vertical = FALSE;
274     NtUserGetScrollBarInfo (hwnd, OBJID_HSCROLL, &info);
275     break;
276   case SB_VERT:
277     vertical = TRUE;
278     NtUserGetScrollBarInfo (hwnd, OBJID_VSCROLL, &info);
279     break;
280   case SB_CTL:
281     vertical = (GetWindowLongW(hwnd,GWL_STYLE)&SBS_VERT) != 0;
282     NtUserGetScrollBarInfo (hwnd, OBJID_CLIENT, &info);
283     break;
284 #ifdef DBG
285   default:
286     DASSERT(!"SCROLL_DrawScrollBar() called with invalid nBar");
287     break;
288 #endif /* DBG */
289   }
290   
291   thumbSize = info.xyThumbBottom - info.xyThumbTop;
292
293   if (vertical)
294     arrowSize = GetSystemMetrics(SM_CXVSCROLL);
295   else
296     arrowSize = GetSystemMetrics(SM_CYHSCROLL);
297
298   if (IsRectEmpty (&(info.rcScrollBar))) goto END;
299
300   if (Save_SCROLL_MovingThumb && (SCROLL_TrackingWin == hwnd) && (SCROLL_TrackingBar == nBar))
301   {
302     SCROLL_DrawMovingThumb (hdc, &(info.rcScrollBar), vertical, arrowSize, thumbSize, &info);
303   }
304
305   /* Draw the arrows */
306   if (arrows && arrowSize)
307   {
308     if (SCROLL_trackVertical == TRUE /* && GetCapture () == hwnd */)
309     {
310       SCROLL_DrawArrows (hdc, &info, &(info.rcScrollBar), arrowSize, vertical,
311                          (SCROLL_trackHitTest == SCROLL_TOP_ARROW),
312                          (SCROLL_trackHitTest == SCROLL_BOTTOM_ARROW));
313     }
314     else
315     {
316       SCROLL_DrawArrows (hdc, &info, &(info.rcScrollBar), arrowSize, vertical, FALSE, FALSE);
317     }
318   }
319
320   if (interior)
321   {
322     SCROLL_DrawInterior (hwnd, hdc, nBar, vertical, arrowSize, &info);
323   }
324
325   if (Save_SCROLL_MovingThumb &&
326       (SCROLL_TrackingWin == hwnd) && (SCROLL_TrackingBar == nBar))
327     SCROLL_DrawMovingThumb (hdc, &info.rcScrollBar, vertical, arrowSize, thumbSize, &info);
328   /* if scroll bar has focus, reposition the caret */
329
330 /*  if (hwnd == GetFocus () && (nBar == SB_CTL))
331     {
332       if (nBar == SB_HORZ)
333       {
334         SetCaretPos (info.dxyLineButton + 1, info.rcScrollBar.top + 1);
335       }
336       else if (nBAR == SB_VERT)
337       {
338         SetCaretPos (info.rcScrollBar.top + 1, info.dxyLineButton + 1);
339       }
340     } */
341 END:;
342 /*    WIN_ReleaseWndPtr(wndPtr); */
343 }
344
345 static BOOL 
346 SCROLL_PtInRectEx( LPRECT lpRect, POINT pt, BOOL vertical )
347 {
348   RECT rect = *lpRect;
349
350   if (vertical)
351   {
352     rect.left -= lpRect->right - lpRect->left;
353     rect.right += lpRect->right - lpRect->left;
354   }
355   else
356   {
357     rect.top -= lpRect->bottom - lpRect->top;
358     rect.bottom += lpRect->bottom - lpRect->top;
359   }
360   return PtInRect( &rect, pt );
361 }
362
363 DWORD
364 SCROLL_HitTest( HWND hwnd, INT nBar, POINT pt, BOOL bDragging )
365 {
366   SCROLLBARINFO sbi;
367   RECT wndrect;
368   INT arrowSize, thumbSize, thumbPos;
369   BOOL vertical = (nBar == SB_VERT); /* FIXME - ((Window->Style & SBS_VERT) != 0) */
370   
371   NtUserGetWindowRect(hwnd, &wndrect);
372   
373   sbi.cbSize = sizeof(SCROLLBARINFO);
374   NtUserGetScrollBarInfo(hwnd, vertical ? OBJID_VSCROLL : OBJID_HSCROLL, &sbi);
375   
376   OffsetRect(&sbi.rcScrollBar, wndrect.left, wndrect.top);
377
378   if ( (bDragging && !SCROLL_PtInRectEx( &sbi.rcScrollBar, pt, vertical )) ||
379        (!PtInRect( &sbi.rcScrollBar, pt )) ) return SCROLL_NOWHERE;
380
381   thumbPos = sbi.xyThumbTop;
382   thumbSize = sbi.xyThumbBottom - thumbPos;
383   arrowSize = sbi.dxyLineButton;
384
385   if (vertical) 
386   {
387     if (pt.y < sbi.rcScrollBar.top + arrowSize) return SCROLL_TOP_ARROW;
388     if (pt.y >= sbi.rcScrollBar.bottom - arrowSize) return SCROLL_BOTTOM_ARROW;
389     if (!thumbPos) return SCROLL_TOP_RECT;
390       pt.y -= sbi.rcScrollBar.top;
391     if (pt.y < thumbPos) return SCROLL_TOP_RECT;
392     if (pt.y >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
393   }
394   else  /* horizontal */
395   {
396     if (pt.x < sbi.rcScrollBar.left + arrowSize) return SCROLL_TOP_ARROW;
397     if (pt.x >= sbi.rcScrollBar.right - arrowSize) return SCROLL_BOTTOM_ARROW;
398     if (!thumbPos) return SCROLL_TOP_RECT;
399     pt.x -= sbi.rcScrollBar.left;
400     if (pt.x < thumbPos) return SCROLL_TOP_RECT;
401     if (pt.x >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
402   }
403   return SCROLL_THUMB;
404 }
405
406
407 /* FUNCTIONS ******************************************************************/
408
409
410 /*
411  * @implemented
412  */
413 WINBOOL STDCALL
414 EnableScrollBar(HWND hWnd, UINT wSBflags, UINT wArrows)
415 {
416   return NtUserEnableScrollBar(hWnd, wSBflags, wArrows);
417 }
418
419
420 /*
421  * @implemented
422  */
423 WINBOOL STDCALL
424 GetScrollBarInfo(HWND hwnd, LONG idObject, PSCROLLBARINFO psbi)
425 {
426   SCROLLBARINFO sbi;
427   WINBOOL ret;
428   
429   if(!psbi)
430   {
431     SetLastError(ERROR_INVALID_PARAMETER);
432     return FALSE;
433   }
434   
435   RtlCopyMemory(&sbi, psbi, sizeof(SCROLLBARINFO));
436   ret = NtUserGetScrollBarInfo (hwnd, idObject, psbi);
437   if(ret)
438   {
439     RtlCopyMemory(psbi, &sbi, sizeof(SCROLLBARINFO));
440   }
441
442   return ret;
443 }
444
445
446 /*
447  * @implemented
448  */
449 WINBOOL STDCALL
450 GetScrollInfo (HWND hwnd, int fnBar, LPSCROLLINFO lpsi)
451 {
452   WINBOOL res;
453   SCROLLINFO si;
454   
455   if(!lpsi || 
456      ((lpsi->cbSize != sizeof(SCROLLINFO)) && (lpsi->cbSize != sizeof(SCROLLINFO) - sizeof(si.nTrackPos))))
457   {
458     SetLastError(ERROR_INVALID_PARAMETER);
459     return 0;
460   }
461   
462   RtlZeroMemory(&si, sizeof(SCROLLINFO));
463   si.cbSize = lpsi->cbSize;
464   si.fMask = lpsi->fMask;
465   
466   res = (WINBOOL)NtUserGetScrollInfo(hwnd, fnBar, &si);
467   
468   if(res)
469   {
470     RtlCopyMemory(lpsi, &si, lpsi->cbSize);
471   }
472   
473   return res;
474 }
475
476
477 /*
478  * @implemented
479  */
480 int STDCALL
481 GetScrollPos (HWND hWnd, int nBar)
482 {
483   SCROLLINFO si;
484   BOOL ret;
485   int res = 0;
486   
487   si.cbSize = sizeof(SCROLLINFO);
488   si.fMask = SIF_POS;
489   ret = NtUserGetScrollInfo(hWnd, nBar, &si);
490   if(ret)
491     res = si.nPos;
492   return res;  
493 }
494
495
496 /*
497  * @implemented
498  */
499 WINBOOL STDCALL
500 GetScrollRange (HWND hWnd, int nBar, LPINT lpMinPos, LPINT lpMaxPos)
501 {
502   WINBOOL ret;
503   SCROLLINFO si;
504   
505   if(!lpMinPos || !lpMaxPos)
506   {
507     SetLastError(ERROR_INVALID_PARAMETER);
508     return FALSE;
509   }
510   
511   si.cbSize = sizeof(SCROLLINFO);
512   si.fMask = SIF_RANGE;
513   ret = NtUserGetScrollInfo(hWnd, nBar, &si);
514   if(ret)
515   {
516     *lpMinPos = si.nMin;
517     *lpMaxPos = si.nMax;
518   }
519   
520   return ret;
521 }
522
523
524 /*
525  * @implemented
526  */
527 int STDCALL
528 SetScrollInfo (HWND hwnd, int fnBar, LPCSCROLLINFO lpsi, WINBOOL fRedraw)
529 {
530   SCROLLINFO si;
531   
532   if(!lpsi || 
533      ((lpsi->cbSize != sizeof(SCROLLINFO)) && (lpsi->cbSize != sizeof(SCROLLINFO) - sizeof(si.nTrackPos))))
534   {
535     SetLastError(ERROR_INVALID_PARAMETER);
536     return 0;
537   }
538   RtlCopyMemory(&si, lpsi, lpsi->cbSize);
539   return (int)NtUserSetScrollInfo(hwnd, fnBar, &si, fRedraw);
540 }
541
542
543 /*
544  * @implemented
545  */
546 int STDCALL
547 SetScrollPos (HWND hWnd, int nBar, int nPos, WINBOOL bRedraw)
548 {
549   int Res = 0;
550   BOOL ret;
551   SCROLLINFO si;
552   
553   si.cbSize = sizeof(SCROLLINFO);
554   si.fMask = SIF_POS;
555   
556   /* call NtUserGetScrollInfo() because we need to return the previous position */
557   ret = NtUserGetScrollInfo(hWnd, nBar, &si);
558   
559   if(ret)
560   {
561     Res = si.nPos;
562     
563     if(Res != nPos)
564     {
565       si.nPos = nPos;
566       /* finally set the new position */
567       NtUserSetScrollInfo(hWnd, nBar, &si, bRedraw);
568     }
569   }
570   
571   return Res;
572 }
573
574
575 /*
576  * @implemented
577  */
578 WINBOOL STDCALL
579 SetScrollRange (HWND hWnd,
580                 int nBar, int nMinPos, int nMaxPos, WINBOOL bRedraw)
581 {
582   SCROLLINFO si;
583   
584   si.cbSize = sizeof(SCROLLINFO);
585   si.fMask = SIF_RANGE;
586   si.nMin = nMinPos;
587   si.nMax = nMaxPos;
588   
589   NtUserSetScrollInfo(hWnd, nBar, &si, bRedraw);
590   /* FIXME - check if called successfully */
591   
592   return TRUE;
593 }
594
595
596 /*
597  * @implemented
598  */
599 WINBOOL STDCALL
600 ShowScrollBar (HWND hWnd, int wBar, WINBOOL bShow)
601 {
602   return (WINBOOL)NtUserShowScrollBar (hWnd, wBar, bShow);
603 }