4 * Copyright 1998, 1999 Eric Kohl
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Sep. 18, 2004, by Robert Shearman.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features or bugs please note them below.
30 * Implement repetitive button press.
31 * Adjust arrow size relative to size of button.
32 * Allow border size changes.
38 * WM_PRINT and/or WM_PRINTCLIENT
41 * Tested primarily with the controlspy Pager application.
42 * Susan Farley (susan@codeweavers.com)
44 * IMPLEMENTATION NOTES:
45 * This control uses WM_NCPAINT instead of WM_PAINT to paint itself
46 * as we need to scroll a child window. In order to do this we move
47 * the child window in the control's client area, using the clipping
48 * region that is automatically set around the client area. As the
49 * entire client area now consists of the child window, we must
50 * allocate space (WM_NCCALCSIZE) for the buttons and draw them as
51 * a non-client area (WM_NCPAINT).
52 * Robert Shearman <rob@codeweavers.com>
57 WINE_DEFAULT_DEBUG_CHANNEL(pager
);
61 HWND hwndSelf
; /* handle of the control wnd */
62 HWND hwndChild
; /* handle of the contained wnd */
63 HWND hwndNotify
; /* handle of the parent wnd */
64 DWORD dwStyle
; /* styles for this control */
65 COLORREF clrBk
; /* background color */
66 INT nBorder
; /* border size for the control */
67 INT nButtonSize
;/* size of the pager btns */
68 INT nPos
; /* scroll position */
69 INT nWidth
; /* from child wnd's response to PGN_CALCSIZE */
70 INT nHeight
; /* from child wnd's response to PGN_CALCSIZE */
71 BOOL bForward
; /* forward WM_MOUSEMOVE msgs to the contained wnd */
72 BOOL bCapture
; /* we have captured the mouse */
73 INT TLbtnState
; /* state of top or left btn */
74 INT BRbtnState
; /* state of bottom or right btn */
75 INT direction
; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
80 #define INITIAL_DELAY 500
81 #define REPEAT_DELAY 50
84 PAGER_GetButtonRects(const PAGER_INFO
* infoPtr
, RECT
* prcTopLeft
, RECT
* prcBottomRight
, BOOL bClientCoords
)
87 GetWindowRect (infoPtr
->hwndSelf
, &rcWindow
);
90 MapWindowPoints( 0, infoPtr
->hwndSelf
, (POINT
*)&rcWindow
, 2 );
92 OffsetRect(&rcWindow
, -rcWindow
.left
, -rcWindow
.top
);
94 *prcTopLeft
= *prcBottomRight
= rcWindow
;
95 if (infoPtr
->dwStyle
& PGS_HORZ
)
97 prcTopLeft
->right
= prcTopLeft
->left
+ infoPtr
->nButtonSize
;
98 prcBottomRight
->left
= prcBottomRight
->right
- infoPtr
->nButtonSize
;
102 prcTopLeft
->bottom
= prcTopLeft
->top
+ infoPtr
->nButtonSize
;
103 prcBottomRight
->top
= prcBottomRight
->bottom
- infoPtr
->nButtonSize
;
108 PAGER_DrawButton(HDC hdc
, COLORREF clrBk
, RECT rc
,
109 BOOL horz
, BOOL topLeft
, INT btnState
)
113 TRACE("rc = %s, btnState = %d\n", wine_dbgstr_rect(&rc
), btnState
);
115 if (btnState
== PGF_INVISIBLE
)
118 if ((rc
.right
- rc
.left
<= 0) || (rc
.bottom
- rc
.top
<= 0))
122 flags
= topLeft
? DFCS_SCROLLLEFT
: DFCS_SCROLLRIGHT
;
124 flags
= topLeft
? DFCS_SCROLLUP
: DFCS_SCROLLDOWN
;
134 flags
|= DFCS_PUSHED
;
137 flags
|= DFCS_INACTIVE
| DFCS_FLAT
;
140 DrawFrameControl( hdc
, &rc
, DFC_SCROLL
, flags
);
143 /* << PAGER_GetDropTarget >> */
145 static inline LRESULT
146 PAGER_ForwardMouse (PAGER_INFO
* infoPtr
, BOOL bFwd
)
148 TRACE("[%p]\n", infoPtr
->hwndSelf
);
150 infoPtr
->bForward
= bFwd
;
155 static inline LRESULT
156 PAGER_GetButtonState (const PAGER_INFO
* infoPtr
, INT btn
)
158 LRESULT btnState
= PGF_INVISIBLE
;
159 TRACE("[%p]\n", infoPtr
->hwndSelf
);
161 if (btn
== PGB_TOPORLEFT
)
162 btnState
= infoPtr
->TLbtnState
;
163 else if (btn
== PGB_BOTTOMORRIGHT
)
164 btnState
= infoPtr
->BRbtnState
;
171 PAGER_GetPos(const PAGER_INFO
*infoPtr
)
173 TRACE("[%p] returns %d\n", infoPtr
->hwndSelf
, infoPtr
->nPos
);
174 return infoPtr
->nPos
;
178 PAGER_GetButtonSize(const PAGER_INFO
*infoPtr
)
180 TRACE("[%p] returns %d\n", infoPtr
->hwndSelf
, infoPtr
->nButtonSize
);
181 return infoPtr
->nButtonSize
;
185 PAGER_GetBorder(const PAGER_INFO
*infoPtr
)
187 TRACE("[%p] returns %d\n", infoPtr
->hwndSelf
, infoPtr
->nBorder
);
188 return infoPtr
->nBorder
;
191 static inline COLORREF
192 PAGER_GetBkColor(const PAGER_INFO
*infoPtr
)
194 TRACE("[%p] returns %06x\n", infoPtr
->hwndSelf
, infoPtr
->clrBk
);
195 return infoPtr
->clrBk
;
199 PAGER_CalcSize( PAGER_INFO
*infoPtr
)
202 ZeroMemory (&nmpgcs
, sizeof (NMPGCALCSIZE
));
203 nmpgcs
.hdr
.hwndFrom
= infoPtr
->hwndSelf
;
204 nmpgcs
.hdr
.idFrom
= GetWindowLongPtrW (infoPtr
->hwndSelf
, GWLP_ID
);
205 nmpgcs
.hdr
.code
= PGN_CALCSIZE
;
206 nmpgcs
.dwFlag
= (infoPtr
->dwStyle
& PGS_HORZ
) ? PGF_CALCWIDTH
: PGF_CALCHEIGHT
;
207 nmpgcs
.iWidth
= infoPtr
->nWidth
;
208 nmpgcs
.iHeight
= infoPtr
->nHeight
;
209 SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
, nmpgcs
.hdr
.idFrom
, (LPARAM
)&nmpgcs
);
211 if (infoPtr
->dwStyle
& PGS_HORZ
)
212 infoPtr
->nWidth
= nmpgcs
.iWidth
;
214 infoPtr
->nHeight
= nmpgcs
.iHeight
;
216 TRACE("[%p] PGN_CALCSIZE returns %dx%d\n", infoPtr
->hwndSelf
, nmpgcs
.iWidth
, nmpgcs
.iHeight
);
220 PAGER_PositionChildWnd(PAGER_INFO
* infoPtr
)
222 if (infoPtr
->hwndChild
)
225 int nPos
= infoPtr
->nPos
;
227 /* compensate for a grayed btn, which will soon become invisible */
228 if (infoPtr
->TLbtnState
== PGF_GRAYED
)
229 nPos
+= infoPtr
->nButtonSize
;
231 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
233 if (infoPtr
->dwStyle
& PGS_HORZ
)
235 int wndSize
= max(0, rcClient
.right
- rcClient
.left
);
236 if (infoPtr
->nWidth
< wndSize
)
237 infoPtr
->nWidth
= wndSize
;
239 TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr
->hwndSelf
,
240 infoPtr
->nWidth
, infoPtr
->nHeight
,
242 SetWindowPos(infoPtr
->hwndChild
, 0,
244 infoPtr
->nWidth
, infoPtr
->nHeight
,
249 int wndSize
= max(0, rcClient
.bottom
- rcClient
.top
);
250 if (infoPtr
->nHeight
< wndSize
)
251 infoPtr
->nHeight
= wndSize
;
253 TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr
->hwndSelf
,
254 infoPtr
->nWidth
, infoPtr
->nHeight
,
256 SetWindowPos(infoPtr
->hwndChild
, 0,
258 infoPtr
->nWidth
, infoPtr
->nHeight
,
262 InvalidateRect(infoPtr
->hwndChild
, NULL
, TRUE
);
267 PAGER_GetScrollRange(PAGER_INFO
* infoPtr
)
271 if (infoPtr
->hwndChild
)
273 INT wndSize
, childSize
;
275 GetWindowRect(infoPtr
->hwndSelf
, &wndRect
);
277 PAGER_CalcSize(infoPtr
);
278 if (infoPtr
->dwStyle
& PGS_HORZ
)
280 wndSize
= wndRect
.right
- wndRect
.left
;
281 childSize
= infoPtr
->nWidth
;
285 wndSize
= wndRect
.bottom
- wndRect
.top
;
286 childSize
= infoPtr
->nHeight
;
289 TRACE("childSize = %d, wndSize = %d\n", childSize
, wndSize
);
290 if (childSize
> wndSize
)
291 scrollRange
= childSize
- wndSize
+ infoPtr
->nButtonSize
;
294 TRACE("[%p] returns %d\n", infoPtr
->hwndSelf
, scrollRange
);
299 PAGER_UpdateBtns(PAGER_INFO
*infoPtr
, INT scrollRange
, BOOL hideGrayBtns
)
303 INT oldTLbtnState
= infoPtr
->TLbtnState
;
304 INT oldBRbtnState
= infoPtr
->BRbtnState
;
306 RECT rcTopLeft
, rcBottomRight
;
308 /* get button rects */
309 PAGER_GetButtonRects(infoPtr
, &rcTopLeft
, &rcBottomRight
, TRUE
);
312 ScreenToClient( infoPtr
->hwndSelf
, &pt
);
314 /* update states based on scroll position */
315 if (infoPtr
->nPos
> 0)
317 if (infoPtr
->TLbtnState
== PGF_INVISIBLE
|| infoPtr
->TLbtnState
== PGF_GRAYED
)
318 infoPtr
->TLbtnState
= PGF_NORMAL
;
320 else if (!hideGrayBtns
&& PtInRect(&rcTopLeft
, pt
))
321 infoPtr
->TLbtnState
= PGF_GRAYED
;
323 infoPtr
->TLbtnState
= PGF_INVISIBLE
;
325 if (scrollRange
<= 0)
327 infoPtr
->TLbtnState
= PGF_INVISIBLE
;
328 infoPtr
->BRbtnState
= PGF_INVISIBLE
;
330 else if (infoPtr
->nPos
< scrollRange
)
332 if (infoPtr
->BRbtnState
== PGF_INVISIBLE
|| infoPtr
->BRbtnState
== PGF_GRAYED
)
333 infoPtr
->BRbtnState
= PGF_NORMAL
;
335 else if (!hideGrayBtns
&& PtInRect(&rcBottomRight
, pt
))
336 infoPtr
->BRbtnState
= PGF_GRAYED
;
338 infoPtr
->BRbtnState
= PGF_INVISIBLE
;
340 /* only need to resize when entering or leaving PGF_INVISIBLE state */
342 ((oldTLbtnState
== PGF_INVISIBLE
) != (infoPtr
->TLbtnState
== PGF_INVISIBLE
)) ||
343 ((oldBRbtnState
== PGF_INVISIBLE
) != (infoPtr
->BRbtnState
== PGF_INVISIBLE
));
344 /* initiate NCCalcSize to resize client wnd if necessary */
346 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0,
347 SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOMOVE
|
348 SWP_NOZORDER
| SWP_NOACTIVATE
);
350 /* repaint when changing any state */
351 repaintBtns
= (oldTLbtnState
!= infoPtr
->TLbtnState
) ||
352 (oldBRbtnState
!= infoPtr
->BRbtnState
);
354 SendMessageW(infoPtr
->hwndSelf
, WM_NCPAINT
, 0, 0);
358 PAGER_SetPos(PAGER_INFO
* infoPtr
, INT newPos
, BOOL fromBtnPress
)
360 INT scrollRange
= PAGER_GetScrollRange(infoPtr
);
361 INT oldPos
= infoPtr
->nPos
;
363 if ((scrollRange
<= 0) || (newPos
< 0))
365 else if (newPos
> scrollRange
)
366 infoPtr
->nPos
= scrollRange
;
368 infoPtr
->nPos
= newPos
;
370 TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr
->hwndSelf
, infoPtr
->nPos
, oldPos
);
372 if (infoPtr
->nPos
!= oldPos
)
374 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
375 PAGER_UpdateBtns(infoPtr
, scrollRange
, !fromBtnPress
);
376 PAGER_PositionChildWnd(infoPtr
);
383 PAGER_WindowPosChanging(PAGER_INFO
* infoPtr
, WINDOWPOS
*winpos
)
385 if ((infoPtr
->dwStyle
& CCS_NORESIZE
) && !(winpos
->flags
& SWP_NOSIZE
))
387 /* don't let the app resize the nonscrollable dimension of a control
388 * that was created with CCS_NORESIZE style
389 * (i.e. height for a horizontal pager, or width for a vertical one) */
391 /* except if the current dimension is 0 and app is setting for
392 * first time, then save amount as dimension. - GA 8/01 */
394 if (infoPtr
->dwStyle
& PGS_HORZ
)
395 if (!infoPtr
->nHeight
&& winpos
->cy
)
396 infoPtr
->nHeight
= winpos
->cy
;
398 winpos
->cy
= infoPtr
->nHeight
;
400 if (!infoPtr
->nWidth
&& winpos
->cx
)
401 infoPtr
->nWidth
= winpos
->cx
;
403 winpos
->cx
= infoPtr
->nWidth
;
407 return DefWindowProcW (infoPtr
->hwndSelf
, WM_WINDOWPOSCHANGING
, 0, (LPARAM
)winpos
);
410 /******************************************************************
411 * For the PGM_RECALCSIZE message (but not the other uses in *
412 * this module), the native control does only the following: *
414 * if (some condition) *
415 * PostMessageW(hwnd, EM_FMTLINES, 0, 0); *
416 * return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0); *
418 * When we figure out what the "some condition" is we will *
419 * implement that for the message processing. *
420 ******************************************************************/
423 PAGER_RecalcSize(PAGER_INFO
*infoPtr
)
425 TRACE("[%p]\n", infoPtr
->hwndSelf
);
427 if (infoPtr
->hwndChild
)
429 INT scrollRange
= PAGER_GetScrollRange(infoPtr
);
431 if (scrollRange
<= 0)
434 PAGER_SetPos(infoPtr
, 0, FALSE
);
437 PAGER_PositionChildWnd(infoPtr
);
445 PAGER_SetBkColor (PAGER_INFO
* infoPtr
, COLORREF clrBk
)
447 COLORREF clrTemp
= infoPtr
->clrBk
;
449 infoPtr
->clrBk
= clrBk
;
450 TRACE("[%p] %06x\n", infoPtr
->hwndSelf
, infoPtr
->clrBk
);
452 /* the native control seems to do things this way */
453 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0,
454 SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOMOVE
|
455 SWP_NOZORDER
| SWP_NOACTIVATE
);
457 RedrawWindow(infoPtr
->hwndSelf
, 0, 0, RDW_ERASE
| RDW_INVALIDATE
);
464 PAGER_SetBorder (PAGER_INFO
* infoPtr
, INT iBorder
)
466 INT nTemp
= infoPtr
->nBorder
;
468 infoPtr
->nBorder
= iBorder
;
469 TRACE("[%p] %d\n", infoPtr
->hwndSelf
, infoPtr
->nBorder
);
471 PAGER_RecalcSize(infoPtr
);
478 PAGER_SetButtonSize (PAGER_INFO
* infoPtr
, INT iButtonSize
)
480 INT nTemp
= infoPtr
->nButtonSize
;
482 infoPtr
->nButtonSize
= iButtonSize
;
483 TRACE("[%p] %d\n", infoPtr
->hwndSelf
, infoPtr
->nButtonSize
);
485 PAGER_RecalcSize(infoPtr
);
492 PAGER_SetChild (PAGER_INFO
* infoPtr
, HWND hwndChild
)
494 infoPtr
->hwndChild
= IsWindow (hwndChild
) ? hwndChild
: 0;
496 if (infoPtr
->hwndChild
)
498 TRACE("[%p] hwndChild=%p\n", infoPtr
->hwndSelf
, infoPtr
->hwndChild
);
500 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0,
501 SWP_FRAMECHANGED
| SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOSIZE
| SWP_NOACTIVATE
);
503 /* position child within the page scroller */
504 SetWindowPos(infoPtr
->hwndChild
, HWND_TOP
,
506 SWP_SHOWWINDOW
| SWP_NOSIZE
); /* native is 0 */
509 PAGER_SetPos(infoPtr
, 0, FALSE
);
516 PAGER_Scroll(PAGER_INFO
* infoPtr
, INT dir
)
518 NMPGSCROLL nmpgScroll
;
521 if (infoPtr
->hwndChild
)
523 ZeroMemory (&nmpgScroll
, sizeof (NMPGSCROLL
));
524 nmpgScroll
.hdr
.hwndFrom
= infoPtr
->hwndSelf
;
525 nmpgScroll
.hdr
.idFrom
= GetWindowLongPtrW (infoPtr
->hwndSelf
, GWLP_ID
);
526 nmpgScroll
.hdr
.code
= PGN_SCROLL
;
528 GetWindowRect(infoPtr
->hwndSelf
, &rcWnd
);
529 GetClientRect(infoPtr
->hwndSelf
, &nmpgScroll
.rcParent
);
530 nmpgScroll
.iXpos
= nmpgScroll
.iYpos
= 0;
531 nmpgScroll
.iDir
= dir
;
533 if (infoPtr
->dwStyle
& PGS_HORZ
)
535 nmpgScroll
.iScroll
= rcWnd
.right
- rcWnd
.left
;
536 nmpgScroll
.iXpos
= infoPtr
->nPos
;
540 nmpgScroll
.iScroll
= rcWnd
.bottom
- rcWnd
.top
;
541 nmpgScroll
.iYpos
= infoPtr
->nPos
;
543 nmpgScroll
.iScroll
-= 2*infoPtr
->nButtonSize
;
545 SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
, nmpgScroll
.hdr
.idFrom
, (LPARAM
)&nmpgScroll
);
547 TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr
->hwndSelf
, nmpgScroll
.iScroll
);
549 if (nmpgScroll
.iScroll
> 0)
551 infoPtr
->direction
= dir
;
553 if (dir
== PGF_SCROLLLEFT
|| dir
== PGF_SCROLLUP
)
554 PAGER_SetPos(infoPtr
, infoPtr
->nPos
- nmpgScroll
.iScroll
, TRUE
);
556 PAGER_SetPos(infoPtr
, infoPtr
->nPos
+ nmpgScroll
.iScroll
, TRUE
);
559 infoPtr
->direction
= -1;
564 PAGER_FmtLines(const PAGER_INFO
*infoPtr
)
566 /* initiate NCCalcSize to resize client wnd and get size */
567 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0,
568 SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOMOVE
|
569 SWP_NOZORDER
| SWP_NOACTIVATE
);
571 SetWindowPos(infoPtr
->hwndChild
, 0,
572 0,0,infoPtr
->nWidth
,infoPtr
->nHeight
,
575 return DefWindowProcW (infoPtr
->hwndSelf
, EM_FMTLINES
, 0, 0);
579 PAGER_Create (HWND hwnd
, const CREATESTRUCTW
*lpcs
)
583 /* allocate memory for info structure */
584 infoPtr
= Alloc (sizeof(PAGER_INFO
));
585 if (!infoPtr
) return -1;
586 SetWindowLongPtrW (hwnd
, 0, (DWORD_PTR
)infoPtr
);
588 /* set default settings */
589 infoPtr
->hwndSelf
= hwnd
;
590 infoPtr
->hwndChild
= NULL
;
591 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
592 infoPtr
->dwStyle
= lpcs
->style
;
593 infoPtr
->clrBk
= GetSysColor(COLOR_BTNFACE
);
594 infoPtr
->nBorder
= 0;
595 infoPtr
->nButtonSize
= 12;
598 infoPtr
->nHeight
= 0;
599 infoPtr
->bForward
= FALSE
;
600 infoPtr
->bCapture
= FALSE
;
601 infoPtr
->TLbtnState
= PGF_INVISIBLE
;
602 infoPtr
->BRbtnState
= PGF_INVISIBLE
;
603 infoPtr
->direction
= -1;
605 if (infoPtr
->dwStyle
& PGS_DRAGNDROP
)
606 FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr
->hwndSelf
);
613 PAGER_Destroy (PAGER_INFO
*infoPtr
)
615 SetWindowLongPtrW (infoPtr
->hwndSelf
, 0, 0);
616 Free (infoPtr
); /* free pager info data */
621 PAGER_NCCalcSize(PAGER_INFO
* infoPtr
, WPARAM wParam
, LPRECT lpRect
)
623 RECT rcChild
, rcWindow
;
626 * lpRect points to a RECT struct. On entry, the struct
627 * contains the proposed wnd rectangle for the window.
628 * On exit, the struct should contain the screen
629 * coordinates of the corresponding window's client area.
632 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCCALCSIZE
, wParam
, (LPARAM
)lpRect
);
634 TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect
));
636 GetWindowRect (infoPtr
->hwndChild
, &rcChild
);
637 MapWindowPoints (0, infoPtr
->hwndSelf
, (LPPOINT
)&rcChild
, 2); /* FIXME: RECT != 2 POINTS */
638 GetWindowRect (infoPtr
->hwndSelf
, &rcWindow
);
640 infoPtr
->nWidth
= lpRect
->right
- lpRect
->left
;
641 infoPtr
->nHeight
= lpRect
->bottom
- lpRect
->top
;
642 PAGER_CalcSize( infoPtr
);
644 if (infoPtr
->dwStyle
& PGS_HORZ
)
646 if (infoPtr
->TLbtnState
&& (lpRect
->left
+ infoPtr
->nButtonSize
< lpRect
->right
))
647 lpRect
->left
+= infoPtr
->nButtonSize
;
648 if (infoPtr
->BRbtnState
&& (lpRect
->right
- infoPtr
->nButtonSize
> lpRect
->left
))
649 lpRect
->right
-= infoPtr
->nButtonSize
;
653 if (infoPtr
->TLbtnState
&& (lpRect
->top
+ infoPtr
->nButtonSize
< lpRect
->bottom
))
654 lpRect
->top
+= infoPtr
->nButtonSize
;
655 if (infoPtr
->BRbtnState
&& (lpRect
->bottom
- infoPtr
->nButtonSize
> lpRect
->top
))
656 lpRect
->bottom
-= infoPtr
->nButtonSize
;
659 TRACE("nPos=%d, nHeight=%d, window=%s\n",
660 infoPtr
->nPos
, infoPtr
->nHeight
,
661 wine_dbgstr_rect(&rcWindow
));
663 TRACE("[%p] client rect set to %dx%d at (%d,%d) BtnState[%d,%d]\n",
664 infoPtr
->hwndSelf
, lpRect
->right
-lpRect
->left
, lpRect
->bottom
-lpRect
->top
,
665 lpRect
->left
, lpRect
->top
,
666 infoPtr
->TLbtnState
, infoPtr
->BRbtnState
);
672 PAGER_NCPaint (const PAGER_INFO
* infoPtr
, HRGN hRgn
)
674 RECT rcBottomRight
, rcTopLeft
;
677 if (infoPtr
->dwStyle
& WS_MINIMIZE
)
680 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)hRgn
, 0);
682 if (!(hdc
= GetDCEx (infoPtr
->hwndSelf
, 0, DCX_USESTYLE
| DCX_WINDOW
)))
685 PAGER_GetButtonRects(infoPtr
, &rcTopLeft
, &rcBottomRight
, FALSE
);
687 PAGER_DrawButton(hdc
, infoPtr
->clrBk
, rcTopLeft
,
688 infoPtr
->dwStyle
& PGS_HORZ
, TRUE
, infoPtr
->TLbtnState
);
689 PAGER_DrawButton(hdc
, infoPtr
->clrBk
, rcBottomRight
,
690 infoPtr
->dwStyle
& PGS_HORZ
, FALSE
, infoPtr
->BRbtnState
);
692 ReleaseDC( infoPtr
->hwndSelf
, hdc
);
697 PAGER_HitTest (const PAGER_INFO
* infoPtr
, const POINT
* pt
)
699 RECT clientRect
, rcTopLeft
, rcBottomRight
;
702 GetClientRect (infoPtr
->hwndSelf
, &clientRect
);
704 if (PtInRect(&clientRect
, *pt
))
711 PAGER_GetButtonRects(infoPtr
, &rcTopLeft
, &rcBottomRight
, TRUE
);
713 if ((infoPtr
->TLbtnState
!= PGF_INVISIBLE
) && PtInRect(&rcTopLeft
, ptWindow
))
715 TRACE("PGB_TOPORLEFT\n");
716 return PGB_TOPORLEFT
;
718 else if ((infoPtr
->BRbtnState
!= PGF_INVISIBLE
) && PtInRect(&rcBottomRight
, ptWindow
))
720 TRACE("PGB_BOTTOMORRIGHT\n");
721 return PGB_BOTTOMORRIGHT
;
729 PAGER_NCHitTest (const PAGER_INFO
* infoPtr
, INT x
, INT y
)
737 ScreenToClient (infoPtr
->hwndSelf
, &pt
);
738 nHit
= PAGER_HitTest(infoPtr
, &pt
);
740 return (nHit
< 0) ? HTTRANSPARENT
: HTCLIENT
;
744 PAGER_MouseMove (PAGER_INFO
* infoPtr
, INT keys
, INT x
, INT y
)
747 RECT wnrect
, *btnrect
= NULL
;
748 BOOL topLeft
= FALSE
;
756 TRACE("[%p] to (%d,%d)\n", infoPtr
->hwndSelf
, x
, y
);
757 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
758 GetWindowRect(infoPtr
->hwndSelf
, &wnrect
);
759 if (PtInRect(&wnrect
, pt
)) {
760 RECT TLbtnrect
, BRbtnrect
;
761 PAGER_GetButtonRects(infoPtr
, &TLbtnrect
, &BRbtnrect
, FALSE
);
764 MapWindowPoints(0, infoPtr
->hwndSelf
, &clpt
, 1);
765 hit
= PAGER_HitTest(infoPtr
, &clpt
);
766 if ((hit
== PGB_TOPORLEFT
) && (infoPtr
->TLbtnState
== PGF_NORMAL
))
769 btnrect
= &TLbtnrect
;
770 infoPtr
->TLbtnState
= PGF_HOT
;
771 btnstate
= infoPtr
->TLbtnState
;
773 else if ((hit
== PGB_BOTTOMORRIGHT
) && (infoPtr
->BRbtnState
== PGF_NORMAL
))
776 btnrect
= &BRbtnrect
;
777 infoPtr
->BRbtnState
= PGF_HOT
;
778 btnstate
= infoPtr
->BRbtnState
;
781 /* If in one of the buttons the capture and draw buttons */
784 TRACE("[%p] draw btn (%s), Capture %s, style %08x\n",
785 infoPtr
->hwndSelf
, wine_dbgstr_rect(btnrect
),
786 (infoPtr
->bCapture
) ? "TRUE" : "FALSE",
788 if (!infoPtr
->bCapture
)
790 TRACE("[%p] SetCapture\n", infoPtr
->hwndSelf
);
791 SetCapture(infoPtr
->hwndSelf
);
792 infoPtr
->bCapture
= TRUE
;
794 if (infoPtr
->dwStyle
& PGS_AUTOSCROLL
)
795 SetTimer(infoPtr
->hwndSelf
, TIMERID1
, 0x3e, 0);
796 hdc
= GetWindowDC(infoPtr
->hwndSelf
);
797 /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
798 PAGER_DrawButton(hdc
, infoPtr
->clrBk
, *btnrect
,
799 infoPtr
->dwStyle
& PGS_HORZ
, topLeft
, btnstate
);
800 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
805 /* If we think we are captured, then do release */
806 if (infoPtr
->bCapture
&& (WindowFromPoint(pt
) != infoPtr
->hwndSelf
))
810 infoPtr
->bCapture
= FALSE
;
812 if (GetCapture() == infoPtr
->hwndSelf
)
816 if (infoPtr
->TLbtnState
== PGF_GRAYED
)
818 infoPtr
->TLbtnState
= PGF_INVISIBLE
;
819 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0,
820 SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOMOVE
|
821 SWP_NOZORDER
| SWP_NOACTIVATE
);
823 else if (infoPtr
->TLbtnState
== PGF_HOT
)
825 infoPtr
->TLbtnState
= PGF_NORMAL
;
826 /* FIXME: just invalidate button rect */
827 RedrawWindow(infoPtr
->hwndSelf
, NULL
, NULL
, RDW_FRAME
| RDW_INVALIDATE
);
830 if (infoPtr
->BRbtnState
== PGF_GRAYED
)
832 infoPtr
->BRbtnState
= PGF_INVISIBLE
;
833 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0,
834 SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOMOVE
|
835 SWP_NOZORDER
| SWP_NOACTIVATE
);
837 else if (infoPtr
->BRbtnState
== PGF_HOT
)
839 infoPtr
->BRbtnState
= PGF_NORMAL
;
840 /* FIXME: just invalidate button rect */
841 RedrawWindow(infoPtr
->hwndSelf
, NULL
, NULL
, RDW_FRAME
| RDW_INVALIDATE
);
844 /* Notify parent of released mouse capture */
845 memset(&nmhdr
, 0, sizeof(NMHDR
));
846 nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
847 nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
848 nmhdr
.code
= NM_RELEASEDCAPTURE
;
849 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmhdr
.idFrom
, (LPARAM
)&nmhdr
);
851 if (IsWindow(infoPtr
->hwndSelf
))
852 KillTimer(infoPtr
->hwndSelf
, TIMERID1
);
858 PAGER_LButtonDown (PAGER_INFO
* infoPtr
, INT keys
, INT x
, INT y
)
860 BOOL repaintBtns
= FALSE
;
867 TRACE("[%p] at (%d,%d)\n", infoPtr
->hwndSelf
, x
, y
);
869 hit
= PAGER_HitTest(infoPtr
, &pt
);
871 /* put btn in DEPRESSED state */
872 if (hit
== PGB_TOPORLEFT
)
874 repaintBtns
= infoPtr
->TLbtnState
!= PGF_DEPRESSED
;
875 infoPtr
->TLbtnState
= PGF_DEPRESSED
;
876 SetTimer(infoPtr
->hwndSelf
, TIMERID1
, INITIAL_DELAY
, 0);
878 else if (hit
== PGB_BOTTOMORRIGHT
)
880 repaintBtns
= infoPtr
->BRbtnState
!= PGF_DEPRESSED
;
881 infoPtr
->BRbtnState
= PGF_DEPRESSED
;
882 SetTimer(infoPtr
->hwndSelf
, TIMERID1
, INITIAL_DELAY
, 0);
886 SendMessageW(infoPtr
->hwndSelf
, WM_NCPAINT
, 0, 0);
891 if (infoPtr
->dwStyle
& PGS_HORZ
)
893 TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr
->hwndSelf
);
894 PAGER_Scroll(infoPtr
, PGF_SCROLLLEFT
);
898 TRACE("[%p] PGF_SCROLLUP\n", infoPtr
->hwndSelf
);
899 PAGER_Scroll(infoPtr
, PGF_SCROLLUP
);
902 case PGB_BOTTOMORRIGHT
:
903 if (infoPtr
->dwStyle
& PGS_HORZ
)
905 TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr
->hwndSelf
);
906 PAGER_Scroll(infoPtr
, PGF_SCROLLRIGHT
);
910 TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr
->hwndSelf
);
911 PAGER_Scroll(infoPtr
, PGF_SCROLLDOWN
);
922 PAGER_LButtonUp (PAGER_INFO
* infoPtr
, INT keys
, INT x
, INT y
)
924 TRACE("[%p]\n", infoPtr
->hwndSelf
);
926 KillTimer (infoPtr
->hwndSelf
, TIMERID1
);
927 KillTimer (infoPtr
->hwndSelf
, TIMERID2
);
929 /* make PRESSED btns NORMAL but don't hide gray btns */
930 if (infoPtr
->TLbtnState
& (PGF_HOT
| PGF_DEPRESSED
))
931 infoPtr
->TLbtnState
= PGF_NORMAL
;
932 if (infoPtr
->BRbtnState
& (PGF_HOT
| PGF_DEPRESSED
))
933 infoPtr
->BRbtnState
= PGF_NORMAL
;
939 PAGER_Timer (PAGER_INFO
* infoPtr
, INT nTimerId
)
943 /* if initial timer, kill it and start the repeat timer */
944 if (nTimerId
== TIMERID1
) {
945 if (infoPtr
->TLbtnState
== PGF_HOT
)
946 dir
= (infoPtr
->dwStyle
& PGS_HORZ
) ?
947 PGF_SCROLLLEFT
: PGF_SCROLLUP
;
949 dir
= (infoPtr
->dwStyle
& PGS_HORZ
) ?
950 PGF_SCROLLRIGHT
: PGF_SCROLLDOWN
;
951 TRACE("[%p] TIMERID1: style=%08x, dir=%d\n",
952 infoPtr
->hwndSelf
, infoPtr
->dwStyle
, dir
);
953 KillTimer(infoPtr
->hwndSelf
, TIMERID1
);
954 SetTimer(infoPtr
->hwndSelf
, TIMERID1
, REPEAT_DELAY
, 0);
955 if (infoPtr
->dwStyle
& PGS_AUTOSCROLL
) {
956 PAGER_Scroll(infoPtr
, dir
);
957 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0,
958 SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOMOVE
|
959 SWP_NOZORDER
| SWP_NOACTIVATE
);
965 TRACE("[%p] TIMERID2: dir=%d\n", infoPtr
->hwndSelf
, infoPtr
->direction
);
966 KillTimer(infoPtr
->hwndSelf
, TIMERID2
);
967 if (infoPtr
->direction
> 0) {
968 PAGER_Scroll(infoPtr
, infoPtr
->direction
);
969 SetTimer(infoPtr
->hwndSelf
, TIMERID2
, REPEAT_DELAY
, 0);
975 PAGER_EraseBackground (const PAGER_INFO
* infoPtr
, HDC hdc
)
982 parent
= GetParent(infoPtr
->hwndSelf
);
983 MapWindowPoints(infoPtr
->hwndSelf
, parent
, &pt
, 1);
984 OffsetWindowOrgEx (hdc
, pt
.x
, pt
.y
, &ptorig
);
985 SendMessageW (parent
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
986 SetWindowOrgEx (hdc
, ptorig
.x
, ptorig
.y
, 0);
993 PAGER_Size (PAGER_INFO
* infoPtr
, INT type
, INT x
, INT y
)
995 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
997 TRACE("[%p] %d,%d\n", infoPtr
->hwndSelf
, x
, y
);
999 if (infoPtr
->dwStyle
& PGS_HORZ
)
1000 infoPtr
->nHeight
= y
;
1002 infoPtr
->nWidth
= x
;
1004 return PAGER_RecalcSize(infoPtr
);
1009 PAGER_StyleChanged(PAGER_INFO
*infoPtr
, WPARAM wStyleType
, const STYLESTRUCT
*lpss
)
1011 DWORD oldStyle
= infoPtr
->dwStyle
;
1013 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
1014 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
1016 if (wStyleType
!= GWL_STYLE
) return 0;
1018 infoPtr
->dwStyle
= lpss
->styleNew
;
1020 if ((oldStyle
^ lpss
->styleNew
) & (PGS_HORZ
| PGS_VERT
))
1022 PAGER_RecalcSize(infoPtr
);
1028 static LRESULT WINAPI
1029 PAGER_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1031 PAGER_INFO
*infoPtr
= (PAGER_INFO
*)GetWindowLongPtrW(hwnd
, 0);
1033 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
1034 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
1039 return PAGER_FmtLines(infoPtr
);
1041 case PGM_FORWARDMOUSE
:
1042 return PAGER_ForwardMouse (infoPtr
, (BOOL
)wParam
);
1044 case PGM_GETBKCOLOR
:
1045 return PAGER_GetBkColor(infoPtr
);
1048 return PAGER_GetBorder(infoPtr
);
1050 case PGM_GETBUTTONSIZE
:
1051 return PAGER_GetButtonSize(infoPtr
);
1054 return PAGER_GetPos(infoPtr
);
1056 case PGM_GETBUTTONSTATE
:
1057 return PAGER_GetButtonState (infoPtr
, (INT
)lParam
);
1059 /* case PGM_GETDROPTARGET: */
1061 case PGM_RECALCSIZE
:
1062 return PAGER_RecalcSize(infoPtr
);
1064 case PGM_SETBKCOLOR
:
1065 return PAGER_SetBkColor (infoPtr
, (COLORREF
)lParam
);
1068 return PAGER_SetBorder (infoPtr
, (INT
)lParam
);
1070 case PGM_SETBUTTONSIZE
:
1071 return PAGER_SetButtonSize (infoPtr
, (INT
)lParam
);
1074 return PAGER_SetChild (infoPtr
, (HWND
)lParam
);
1077 return PAGER_SetPos(infoPtr
, (INT
)lParam
, FALSE
);
1080 return PAGER_Create (hwnd
, (LPCREATESTRUCTW
)lParam
);
1083 return PAGER_Destroy (infoPtr
);
1086 return PAGER_Size (infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
1089 return PAGER_NCPaint (infoPtr
, (HRGN
)wParam
);
1091 case WM_WINDOWPOSCHANGING
:
1092 return PAGER_WindowPosChanging (infoPtr
, (WINDOWPOS
*)lParam
);
1094 case WM_STYLECHANGED
:
1095 return PAGER_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
1098 return PAGER_NCCalcSize (infoPtr
, wParam
, (LPRECT
)lParam
);
1101 return PAGER_NCHitTest (infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
1104 if (infoPtr
->bForward
&& infoPtr
->hwndChild
)
1105 PostMessageW(infoPtr
->hwndChild
, WM_MOUSEMOVE
, wParam
, lParam
);
1106 return PAGER_MouseMove (infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
1108 case WM_LBUTTONDOWN
:
1109 return PAGER_LButtonDown (infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
1112 return PAGER_LButtonUp (infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
1115 return PAGER_EraseBackground (infoPtr
, (HDC
)wParam
);
1118 return PAGER_Timer (infoPtr
, (INT
)wParam
);
1122 return SendMessageW (infoPtr
->hwndNotify
, uMsg
, wParam
, lParam
);
1125 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
1131 PAGER_Register (void)
1135 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
1136 wndClass
.style
= CS_GLOBALCLASS
;
1137 wndClass
.lpfnWndProc
= PAGER_WindowProc
;
1138 wndClass
.cbClsExtra
= 0;
1139 wndClass
.cbWndExtra
= sizeof(PAGER_INFO
*);
1140 wndClass
.hCursor
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
1141 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+1);
1142 wndClass
.lpszClassName
= WC_PAGESCROLLERW
;
1144 RegisterClassW (&wndClass
);
1149 PAGER_Unregister (void)
1151 UnregisterClassW (WC_PAGESCROLLERW
, NULL
);