Fixed some issues found by winapi_check.
[wine/dcerpc.git] / dlls / comctl32 / pager.c
blobcdf5e7f0adbad54e5d21517bb634e94ad6c4c753
1 /*
2 * Pager control
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * NOTES
21 * Tested primarily with the controlspy Pager application.
22 * Susan Farley (susan@codeweavers.com)
24 * TODO:
25 * Implement repetitive button press.
26 * Adjust arrow size relative to size of button.
27 * Allow border size changes.
28 * Implement drag and drop style.
31 #include <string.h>
32 #include "winbase.h"
33 #include "commctrl.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(pager);
38 typedef struct
40 HWND hwndChild; /* handle of the contained wnd */
41 BOOL bNoResize; /* set when created with CCS_NORESIZE */
42 COLORREF clrBk; /* background color */
43 INT nBorder; /* border size for the control */
44 INT nButtonSize;/* size of the pager btns */
45 INT nPos; /* scroll position */
46 INT nWidth; /* from child wnd's response to PGN_CALCSIZE */
47 INT nHeight; /* from child wnd's response to PGN_CALCSIZE */
48 BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */
49 BOOL bCapture; /* we have captured the mouse */
50 INT TLbtnState; /* state of top or left btn */
51 INT BRbtnState; /* state of bottom or right btn */
52 INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
53 } PAGER_INFO;
55 #define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0))
56 #define PAGER_IsHorizontal(hwnd) ((GetWindowLongA (hwnd, GWL_STYLE) & PGS_HORZ))
58 #define MIN_ARROW_WIDTH 8
59 #define MIN_ARROW_HEIGHT 5
61 #define TIMERID1 1
62 #define TIMERID2 2
63 #define INITIAL_DELAY 500
64 #define REPEAT_DELAY 50
66 /* the horizontal arrows are:
68 * 01234 01234
69 * 1 * *
70 * 2 ** **
71 * 3*** ***
72 * 4*** ***
73 * 5 ** **
74 * 6 * *
75 * 7
78 static void
79 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
81 INT x, y, w, h;
82 HPEN hPen, hOldPen;
84 w = r.right - r.left + 1;
85 h = r.bottom - r.top + 1;
86 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
87 return; /* refuse to draw partial arrow */
89 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
90 hOldPen = SelectObject ( hdc, hPen );
91 if (left)
93 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
94 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
95 MoveToEx (hdc, x, y, NULL);
96 LineTo (hdc, x--, y+5); y++;
97 MoveToEx (hdc, x, y, NULL);
98 LineTo (hdc, x--, y+3); y++;
99 MoveToEx (hdc, x, y, NULL);
100 LineTo (hdc, x, y+1);
102 else
104 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
105 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
106 MoveToEx (hdc, x, y, NULL);
107 LineTo (hdc, x++, y+5); y++;
108 MoveToEx (hdc, x, y, NULL);
109 LineTo (hdc, x++, y+3); y++;
110 MoveToEx (hdc, x, y, NULL);
111 LineTo (hdc, x, y+1);
114 SelectObject( hdc, hOldPen );
115 DeleteObject( hPen );
118 /* the vertical arrows are:
120 * 01234567 01234567
121 * 1****** **
122 * 2 **** ****
123 * 3 ** ******
127 static void
128 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
130 INT x, y, w, h;
131 HPEN hPen, hOldPen;
133 w = r.right - r.left + 1;
134 h = r.bottom - r.top + 1;
135 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
136 return; /* refuse to draw partial arrow */
138 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
139 hOldPen = SelectObject ( hdc, hPen );
140 if (up)
142 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
143 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
144 MoveToEx (hdc, x, y, NULL);
145 LineTo (hdc, x+5, y--); x++;
146 MoveToEx (hdc, x, y, NULL);
147 LineTo (hdc, x+3, y--); x++;
148 MoveToEx (hdc, x, y, NULL);
149 LineTo (hdc, x+1, y);
151 else
153 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
154 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
155 MoveToEx (hdc, x, y, NULL);
156 LineTo (hdc, x+5, y++); x++;
157 MoveToEx (hdc, x, y, NULL);
158 LineTo (hdc, x+3, y++); x++;
159 MoveToEx (hdc, x, y, NULL);
160 LineTo (hdc, x+1, y);
163 SelectObject( hdc, hOldPen );
164 DeleteObject( hPen );
167 static void
168 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
169 BOOL horz, BOOL topLeft, INT btnState)
171 HBRUSH hBrush, hOldBrush;
172 RECT rc = arrowRect;
174 if (!btnState) /* PGF_INVISIBLE */
175 return;
177 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
178 return;
180 hBrush = CreateSolidBrush(clrBk);
181 hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
183 FillRect(hdc, &rc, hBrush);
185 if (btnState == PGF_HOT)
187 DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
188 if (horz)
189 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
190 else
191 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
193 else if (btnState == PGF_NORMAL)
195 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
196 if (horz)
197 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
198 else
199 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
201 else if (btnState == PGF_DEPRESSED)
203 DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
204 if (horz)
205 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
206 else
207 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
209 else if (btnState == PGF_GRAYED)
211 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
212 if (horz)
214 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
215 rc.left++, rc.top++; rc.right++, rc.bottom++;
216 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
218 else
220 PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
221 rc.left++, rc.top++; rc.right++, rc.bottom++;
222 PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
226 SelectObject( hdc, hOldBrush );
227 DeleteObject(hBrush);
230 static void PAGER_CaptureandTrack(PAGER_INFO *infoPtr, HWND hwnd)
232 TRACKMOUSEEVENT trackinfo;
234 TRACE("[%08x] SetCapture\n", hwnd);
235 SetCapture(hwnd);
236 infoPtr->bCapture = TRUE;
238 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
239 trackinfo.dwFlags = TME_QUERY;
240 trackinfo.hwndTrack = hwnd;
241 trackinfo.dwHoverTime = HOVER_DEFAULT;
243 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
244 _TrackMouseEvent(&trackinfo);
246 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
247 if(!(trackinfo.dwFlags & TME_LEAVE)) {
248 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
250 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
251 /* and can properly deactivate the hot button */
252 _TrackMouseEvent(&trackinfo);
257 /* << PAGER_GetDropTarget >> */
259 static inline LRESULT
260 PAGER_ForwardMouse (HWND hwnd, WPARAM wParam)
262 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
263 TRACE("[%08x]\n", hwnd);
265 infoPtr->bForward = (BOOL)wParam;
267 return 0;
270 static inline LRESULT
271 PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam)
273 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
274 LRESULT btnState = PGF_INVISIBLE;
275 INT btn = (INT)lParam;
276 TRACE("[%08x]\n", hwnd);
278 if (btn == PGB_TOPORLEFT)
279 btnState = infoPtr->TLbtnState;
280 else if (btn == PGB_BOTTOMORRIGHT)
281 btnState = infoPtr->BRbtnState;
283 return btnState;
287 static inline LRESULT
288 PAGER_GetPos(HWND hwnd)
290 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
291 TRACE("[%08x] returns %d\n", hwnd, infoPtr->nPos);
292 return (LRESULT)infoPtr->nPos;
295 static inline LRESULT
296 PAGER_GetButtonSize(HWND hwnd)
298 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
299 TRACE("[%08x] returns %d\n", hwnd, infoPtr->nButtonSize);
300 return (LRESULT)infoPtr->nButtonSize;
303 static inline LRESULT
304 PAGER_GetBorder(HWND hwnd)
306 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
307 TRACE("[%08x] returns %d\n", hwnd, infoPtr->nBorder);
308 return (LRESULT)infoPtr->nBorder;
311 static inline LRESULT
312 PAGER_GetBkColor(HWND hwnd)
314 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
315 TRACE("[%08x] returns %06lx\n", hwnd, infoPtr->clrBk);
316 return (LRESULT)infoPtr->clrBk;
319 static void
320 PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth)
322 NMPGCALCSIZE nmpgcs;
323 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
324 nmpgcs.hdr.hwndFrom = hwnd;
325 nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
326 nmpgcs.hdr.code = PGN_CALCSIZE;
327 nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
328 nmpgcs.iWidth = getWidth ? *size : 0;
329 nmpgcs.iHeight = getWidth ? 0 : *size;
330 SendMessageA (GetParent (hwnd), WM_NOTIFY,
331 (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
333 *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
335 TRACE("[%08x] PGN_CALCSIZE returns %s=%d\n", hwnd,
336 getWidth ? "width" : "height", *size);
339 static void
340 PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr)
342 if (infoPtr->hwndChild)
344 RECT rcClient;
345 int nPos = infoPtr->nPos;
347 /* compensate for a grayed btn, which will soon become invisible */
348 if (infoPtr->TLbtnState == PGF_GRAYED)
349 nPos += infoPtr->nButtonSize;
351 GetClientRect(hwnd, &rcClient);
353 if (PAGER_IsHorizontal(hwnd))
355 int wndSize = max(0, rcClient.right - rcClient.left);
356 if (infoPtr->nWidth < wndSize)
357 infoPtr->nWidth = wndSize;
359 TRACE("[%08x] SWP %dx%d at (%d,%d)\n", hwnd,
360 infoPtr->nWidth, infoPtr->nHeight,
361 -nPos, 0);
362 SetWindowPos(infoPtr->hwndChild, 0,
363 -nPos, 0,
364 infoPtr->nWidth, infoPtr->nHeight,
365 SWP_NOZORDER);
367 else
369 int wndSize = max(0, rcClient.bottom - rcClient.top);
370 if (infoPtr->nHeight < wndSize)
371 infoPtr->nHeight = wndSize;
373 TRACE("[%08x] SWP %dx%d at (%d,%d)\n", hwnd,
374 infoPtr->nWidth, infoPtr->nHeight,
375 0, -nPos);
376 SetWindowPos(infoPtr->hwndChild, 0,
377 0, -nPos,
378 infoPtr->nWidth, infoPtr->nHeight,
379 SWP_NOZORDER);
382 InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
386 static INT
387 PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr)
389 INT scrollRange = 0;
391 if (infoPtr->hwndChild)
393 INT wndSize, childSize;
394 RECT wndRect;
395 GetWindowRect(hwnd, &wndRect);
397 if (PAGER_IsHorizontal(hwnd))
399 wndSize = wndRect.right - wndRect.left;
400 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
401 childSize = infoPtr->nWidth;
403 else
405 wndSize = wndRect.bottom - wndRect.top;
406 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
407 childSize = infoPtr->nHeight;
410 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
411 if (childSize > wndSize)
412 scrollRange = childSize - wndSize + infoPtr->nButtonSize;
415 TRACE("[%08x] returns %d\n", hwnd, scrollRange);
416 return scrollRange;
419 static void
420 PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange,
421 BOOL* needsResize, BOOL* needsRepaint)
423 if (infoPtr->nPos > 0)
425 *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */
426 if (infoPtr->TLbtnState != PGF_DEPRESSED)
427 infoPtr->TLbtnState = PGF_NORMAL;
429 else
431 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
432 infoPtr->TLbtnState = PGF_GRAYED;
435 if (scrollRange <= 0)
437 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
438 infoPtr->TLbtnState = PGF_GRAYED;
439 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
440 infoPtr->BRbtnState = PGF_GRAYED;
442 else if (infoPtr->nPos < scrollRange)
444 *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */
445 if (infoPtr->BRbtnState != PGF_DEPRESSED)
446 infoPtr->BRbtnState = PGF_NORMAL;
448 else
450 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
451 infoPtr->BRbtnState = PGF_GRAYED;
456 static void
457 PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint)
459 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
461 infoPtr->TLbtnState = PGF_NORMAL;
462 *needsRepaint = TRUE;
465 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
467 infoPtr->BRbtnState = PGF_NORMAL;
468 *needsRepaint = TRUE;
472 static void
473 PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize)
475 if (infoPtr->TLbtnState == PGF_GRAYED)
477 infoPtr->TLbtnState = PGF_INVISIBLE;
478 *needsResize = TRUE;
481 if (infoPtr->BRbtnState == PGF_GRAYED)
483 infoPtr->BRbtnState = PGF_INVISIBLE;
484 *needsResize = TRUE;
488 static void
489 PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr,
490 INT scrollRange, BOOL hideGrayBtns)
492 BOOL resizeClient = FALSE;
493 BOOL repaintBtns = FALSE;
495 if (scrollRange < 0)
496 PAGER_NormalizeBtns(infoPtr, &repaintBtns);
497 else
498 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
500 if (hideGrayBtns)
501 PAGER_HideGrayBtns(infoPtr, &resizeClient);
503 if (resizeClient) /* initiate NCCalcSize to resize client wnd */ {
504 SetWindowPos(hwnd, 0,0,0,0,0,
505 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
506 SWP_NOZORDER | SWP_NOACTIVATE);
509 if (repaintBtns)
510 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
513 static LRESULT
514 PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress)
516 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
517 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
518 INT oldPos = infoPtr->nPos;
520 if ((scrollRange <= 0) || (newPos < 0))
521 infoPtr->nPos = 0;
522 else if (newPos > scrollRange)
523 infoPtr->nPos = scrollRange;
524 else
525 infoPtr->nPos = newPos;
527 TRACE("[%08x] pos=%d, oldpos=%d\n", hwnd, infoPtr->nPos, oldPos);
529 if (infoPtr->nPos != oldPos)
531 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
532 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress);
533 PAGER_PositionChildWnd(hwnd, infoPtr);
536 return 0;
539 static LRESULT
540 PAGER_HandleWindowPosChanging(HWND hwnd, WPARAM wParam, WINDOWPOS *winpos)
542 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
544 if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE))
546 /* don't let the app resize the nonscrollable dimension of a control
547 * that was created with CCS_NORESIZE style
548 * (i.e. height for a horizontal pager, or width for a vertical one) */
550 /* except if the current dimension is 0 and app is setting for
551 * first time, then save amount as dimension. - GA 8/01 */
553 if (PAGER_IsHorizontal(hwnd))
554 if (!infoPtr->nHeight && winpos->cy)
555 infoPtr->nHeight = winpos->cy;
556 else
557 winpos->cy = infoPtr->nHeight;
558 else
559 if (!infoPtr->nWidth && winpos->cx)
560 infoPtr->nWidth = winpos->cx;
561 else
562 winpos->cx = infoPtr->nWidth;
563 return 0;
566 DefWindowProcA (hwnd, WM_WINDOWPOSCHANGING, wParam, (LPARAM)winpos);
568 return 1;
571 static INT
572 PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr)
574 /* Must set the non-scrollable dimension to be less than the full height/width
575 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
576 * size, and experimentation shows that affect is almost right. */
578 RECT wndRect;
579 INT delta, h;
580 GetWindowRect(hwnd, &wndRect);
582 /* see what the app says for btn width */
583 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
585 if (infoPtr->bNoResize)
587 delta = wndRect.right - wndRect.left - infoPtr->nWidth;
588 if (delta > infoPtr->nButtonSize)
589 infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
590 else if (delta > 0)
591 infoPtr->nWidth += infoPtr->nButtonSize / 3;
594 h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
596 TRACE("[%08x] infoPtr->nWidth set to %d\n",
597 hwnd, infoPtr->nWidth);
599 return h;
602 static INT
603 PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr)
605 /* Must set the non-scrollable dimension to be less than the full height/width
606 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
607 * size, and experimentation shows that affect is almost right. */
609 RECT wndRect;
610 INT delta, w;
611 GetWindowRect(hwnd, &wndRect);
613 /* see what the app says for btn height */
614 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
616 if (infoPtr->bNoResize)
618 delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
619 if (delta > infoPtr->nButtonSize)
620 infoPtr->nHeight += infoPtr->nButtonSize;
621 else if (delta > 0)
622 infoPtr->nHeight += infoPtr->nButtonSize / 3;
625 w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
627 TRACE("[%08x] infoPtr->nHeight set to %d\n",
628 hwnd, infoPtr->nHeight);
630 return w;
633 /******************************************************************
634 * For the PGM_RECALCSIZE message (but not the other uses in *
635 * this module), the native control does only the following: *
637 * if (some condition) *
638 * PostMessageA(hwnd, EM_FMTLINES, 0, 0); *
639 * return DefWindowProcA(hwnd, PGM_RECALCSIZE, 0, 0); *
641 * When we figure out what the "some condition" is we will *
642 * implement that for the message processing. *
643 ******************************************************************/
645 static LRESULT
646 PAGER_RecalcSize(HWND hwnd)
648 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
650 TRACE("[%08x]\n", hwnd);
652 if (infoPtr->hwndChild)
654 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
656 if (scrollRange <= 0)
658 infoPtr->nPos = -1;
659 PAGER_SetPos(hwnd, 0, FALSE);
661 else
663 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
664 PAGER_PositionChildWnd(hwnd, infoPtr);
668 return 1;
672 static LRESULT
673 PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
675 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
676 COLORREF clrTemp = infoPtr->clrBk;
678 infoPtr->clrBk = (COLORREF)lParam;
679 TRACE("[%08x] %06lx\n", hwnd, infoPtr->clrBk);
681 /* the native control seems to do things this way */
682 SetWindowPos(hwnd, 0,0,0,0,0,
683 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
684 SWP_NOZORDER | SWP_NOACTIVATE);
686 RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE);
688 return (LRESULT)clrTemp;
692 static LRESULT
693 PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
695 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
696 INT nTemp = infoPtr->nBorder;
698 infoPtr->nBorder = (INT)lParam;
699 TRACE("[%08x] %d\n", hwnd, infoPtr->nBorder);
701 PAGER_RecalcSize(hwnd);
703 return (LRESULT)nTemp;
707 static LRESULT
708 PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
710 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
711 INT nTemp = infoPtr->nButtonSize;
713 infoPtr->nButtonSize = (INT)lParam;
714 TRACE("[%08x] %d\n", hwnd, infoPtr->nButtonSize);
716 PAGER_RecalcSize(hwnd);
718 return (LRESULT)nTemp;
722 static LRESULT
723 PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
725 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
726 INT hw;
728 infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
730 if (infoPtr->hwndChild)
732 TRACE("[%08x] hwndChild=%08x\n", hwnd, infoPtr->hwndChild);
734 if (PAGER_IsHorizontal(hwnd)) {
735 hw = PAGER_SetFixedHeight(hwnd, infoPtr);
736 /* adjust non-scrollable dimension to fit the child */
737 SetWindowPos(hwnd, 0, 0,0, hw, infoPtr->nHeight,
738 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
739 SWP_NOSIZE | SWP_NOACTIVATE);
741 else {
742 hw = PAGER_SetFixedWidth(hwnd, infoPtr);
743 /* adjust non-scrollable dimension to fit the child */
744 SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, hw,
745 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
746 SWP_NOSIZE | SWP_NOACTIVATE);
749 /* position child within the page scroller */
750 SetWindowPos(infoPtr->hwndChild, HWND_TOP,
751 0,0,0,0,
752 SWP_SHOWWINDOW | SWP_NOSIZE); /* native is 0 */
754 infoPtr->nPos = -1;
755 PAGER_SetPos(hwnd, 0, FALSE);
758 return 0;
761 static void
762 PAGER_Scroll(HWND hwnd, INT dir)
764 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
765 NMPGSCROLL nmpgScroll;
766 RECT rcWnd;
768 if (infoPtr->hwndChild)
770 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
771 nmpgScroll.hdr.hwndFrom = hwnd;
772 nmpgScroll.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
773 nmpgScroll.hdr.code = PGN_SCROLL;
775 GetWindowRect(hwnd, &rcWnd);
776 GetClientRect(hwnd, &nmpgScroll.rcParent);
777 nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
778 nmpgScroll.iDir = dir;
780 if (PAGER_IsHorizontal(hwnd))
782 nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
783 nmpgScroll.iXpos = infoPtr->nPos;
785 else
787 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
788 nmpgScroll.iYpos = infoPtr->nPos;
790 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
792 SendMessageA (GetParent(hwnd), WM_NOTIFY,
793 (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
795 TRACE("[%08x] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
797 if (nmpgScroll.iScroll > 0)
799 infoPtr->direction = dir;
801 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
802 PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
803 else
804 PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
806 else
807 infoPtr->direction = -1;
811 static LRESULT
812 PAGER_FmtLines(HWND hwnd)
814 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
816 /* initiate NCCalcSize to resize client wnd and get size */
817 SetWindowPos(hwnd, 0, 0,0,0,0,
818 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
819 SWP_NOZORDER | SWP_NOACTIVATE);
821 SetWindowPos(infoPtr->hwndChild, 0,
822 0,0,infoPtr->nWidth,infoPtr->nHeight,
825 return DefWindowProcA (hwnd, EM_FMTLINES, 0, 0);
828 static LRESULT
829 PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
831 PAGER_INFO *infoPtr;
832 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
834 /* allocate memory for info structure */
835 infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO));
836 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
838 /* set default settings */
839 infoPtr->hwndChild = (HWND)NULL;
840 infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
841 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
842 infoPtr->nBorder = 0;
843 infoPtr->nButtonSize = 12;
844 infoPtr->nPos = 0;
845 infoPtr->nWidth = 0;
846 infoPtr->nHeight = 0;
847 infoPtr->bForward = FALSE;
848 infoPtr->bCapture = FALSE;
849 infoPtr->TLbtnState = PGF_INVISIBLE;
850 infoPtr->BRbtnState = PGF_INVISIBLE;
851 infoPtr->direction = -1;
853 if (dwStyle & PGS_DRAGNDROP)
854 FIXME("[%08x] Drag and Drop style is not implemented yet.\n", hwnd);
856 * If neither horizontal nor vertical style specified, default to vertical.
857 * This is probably not necessary, since the style may be set later on as
858 * the control is initialized, but just in case it isn't, set it here.
860 if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
862 dwStyle |= PGS_VERT;
863 SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
866 return 0;
870 static LRESULT
871 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
873 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
874 /* free pager info data */
875 COMCTL32_Free (infoPtr);
876 SetWindowLongA (hwnd, 0, 0);
877 return 0;
880 static LRESULT
881 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
883 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
884 LPRECT lpRect = (LPRECT)lParam;
885 RECT rcChildw, rcmyw, wnrc, ltrc, rbrc;
886 POINT cursor;
887 BOOL resizeClient = FALSE;
888 BOOL repaintBtns = FALSE;
889 INT scrollRange;
892 * lParam points to a RECT struct. On entry, the struct
893 * contains the proposed wnd rectangle for the window.
894 * On exit, the struct should contain the screen
895 * coordinates of the corresponding window's client area.
898 DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam);
900 TRACE("orig rect=(%d,%d)-(%d,%d)\n",
901 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
903 if (PAGER_IsHorizontal(hwnd))
905 infoPtr->nWidth = lpRect->right - lpRect->left;
906 PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE);
907 GetWindowRect (infoPtr->hwndChild, &rcChildw);
908 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
909 GetCursorPos (&cursor);
910 GetWindowRect (hwnd, &rcmyw);
912 /* Reset buttons and hide any grey ones */
913 scrollRange = infoPtr->nWidth - (rcmyw.right - rcmyw.left);
915 TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%d,%d)-(%d,%d), cursor=(%ld,%ld)\n",
916 infoPtr->nPos, scrollRange, infoPtr->nHeight,
917 rcmyw.left, rcmyw.top,
918 rcmyw.right, rcmyw.bottom,
919 cursor.x, cursor.y);
920 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
921 PAGER_HideGrayBtns(infoPtr, &resizeClient);
923 if (PtInRect (&rcmyw, cursor)) {
924 GetWindowRect (hwnd, &wnrc);
925 ltrc = wnrc;
926 ltrc.right = ltrc.left + infoPtr->nButtonSize;
927 rbrc = wnrc;
928 rbrc.left = rbrc.right - infoPtr->nButtonSize;
929 TRACE("horz lt rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
930 ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
931 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
932 if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
933 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
934 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
935 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
937 if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
938 lpRect->left += infoPtr->nButtonSize;
939 if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
940 lpRect->right -= infoPtr->nButtonSize;
942 else
944 /* native does: (from trace of IE4 opening "Favorites" frame)
945 * DefWindowProc
946 * WM_NOITFY PGN_CALCSIZE w/ dwFlag=2
947 * GetWindowRect (child, &rc)
948 * MapWindowPoints (0, syspager, &rc, 2)
949 * GetCursorPos( &cur )
950 * GetWindowRect (syspager, &rc2)
951 * PtInRect (&rc2, cur.x, cur.y) rtns 0
952 * returns with rect empty
954 infoPtr->nHeight = lpRect->bottom - lpRect->top;
955 PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE);
956 GetWindowRect (infoPtr->hwndChild, &rcChildw);
957 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
958 GetCursorPos (&cursor);
959 GetWindowRect (hwnd, &rcmyw);
961 /* Reset buttons and hide any grey ones */
962 scrollRange = infoPtr->nHeight - (rcmyw.bottom - rcmyw.top);
964 TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%d,%d)-(%d,%d), cursor=(%ld,%ld)\n",
965 infoPtr->nPos, scrollRange, infoPtr->nHeight,
966 rcmyw.left, rcmyw.top,
967 rcmyw.right, rcmyw.bottom,
968 cursor.x, cursor.y);
969 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
970 PAGER_HideGrayBtns(infoPtr, &resizeClient);
972 if (PtInRect (&rcmyw, cursor)) {
974 /* native does:
975 * GetWindowRect(pager, &rc)
976 * PtInRect(btn-left????, cur.x, cur.y)
977 * if true -> ???
978 * PtInRect(btn-right????, cur.x, cur.y)
979 * if true
980 * RedrawWindow(pager, 0, 0, 5)
981 * return TRUE
984 GetWindowRect (hwnd, &wnrc);
985 ltrc = wnrc;
986 ltrc.right = ltrc.left + infoPtr->nButtonSize;
987 rbrc = wnrc;
988 rbrc.left = rbrc.right - infoPtr->nButtonSize;
989 TRACE("vert lt rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
990 ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
991 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
992 if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
993 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
994 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
995 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
997 if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
998 lpRect->top += infoPtr->nButtonSize;
999 if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
1000 lpRect->bottom -= infoPtr->nButtonSize;
1001 /* ???? */
1002 if ((lpRect->bottom < 0) || (lpRect->bottom > infoPtr->nHeight))
1003 lpRect->bottom = infoPtr->nHeight;
1006 TRACE("[%08x] client rect set to %dx%d at (%d,%d) BtnState[%d,%d]\n",
1007 hwnd, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
1008 lpRect->left, lpRect->top,
1009 infoPtr->TLbtnState, infoPtr->BRbtnState);
1011 return 0;
1014 static LRESULT
1015 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
1017 PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
1018 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1019 RECT rcWindow, rcBottomRight, rcTopLeft;
1020 HDC hdc;
1021 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1023 if (dwStyle & WS_MINIMIZE)
1024 return 0;
1026 DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
1028 if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
1029 return 0;
1031 GetWindowRect (hwnd, &rcWindow);
1032 OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
1034 rcTopLeft = rcBottomRight = rcWindow;
1035 if (bHorizontal)
1037 rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
1038 rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
1040 else
1042 rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
1043 rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
1046 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
1047 bHorizontal, TRUE, infoPtr->TLbtnState);
1048 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
1049 bHorizontal, FALSE, infoPtr->BRbtnState);
1051 ReleaseDC( hwnd, hdc );
1052 return 0;
1055 static INT
1056 PAGER_HitTest (HWND hwnd, LPPOINT pt)
1058 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1059 RECT clientRect;
1060 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1062 GetClientRect (hwnd, &clientRect);
1064 if (PtInRect(&clientRect, *pt))
1066 TRACE("HTCLIENT\n");
1067 return HTCLIENT;
1070 if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
1072 if (bHorizontal)
1074 if (pt->x < clientRect.left)
1076 TRACE("HTLEFT\n");
1077 return HTLEFT;
1080 else
1082 if (pt->y < clientRect.top)
1084 TRACE("HTTOP\n");
1085 return HTTOP;
1090 if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
1092 if (bHorizontal)
1094 if (pt->x > clientRect.right)
1096 TRACE("HTRIGHT\n");
1097 return HTRIGHT;
1100 else
1102 if (pt->y > clientRect.bottom)
1104 TRACE("HTBOTTOM\n");
1105 return HTBOTTOM;
1110 TRACE("HTNOWHERE\n");
1111 return HTNOWHERE;
1114 static LRESULT
1115 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
1117 POINT pt;
1119 pt.x = SLOWORD(lParam);
1120 pt.y = SHIWORD(lParam);
1122 ScreenToClient (hwnd, &pt);
1123 return PAGER_HitTest(hwnd, &pt);
1126 static LRESULT
1127 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
1129 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1130 BOOL notCaptured = FALSE;
1132 switch(LOWORD(lParam))
1134 case HTLEFT:
1135 case HTTOP:
1136 if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
1137 infoPtr->TLbtnState = PGF_HOT;
1138 break;
1139 case HTRIGHT:
1140 case HTBOTTOM:
1141 if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
1142 infoPtr->BRbtnState = PGF_HOT;
1143 break;
1144 default:
1145 return FALSE;
1148 if (notCaptured)
1150 PAGER_CaptureandTrack(infoPtr, hwnd);
1152 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1155 return TRUE;
1158 static LRESULT
1159 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
1161 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1163 KillTimer (hwnd, TIMERID1);
1164 KillTimer (hwnd, TIMERID2);
1166 TRACE("[%08x] ReleaseCapture\n", hwnd);
1167 ReleaseCapture();
1168 infoPtr->bCapture = FALSE;
1170 /* Notify parent of released mouse capture */
1172 NMHDR nmhdr;
1173 ZeroMemory (&nmhdr, sizeof (NMHDR));
1174 nmhdr.hwndFrom = hwnd;
1175 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1176 nmhdr.code = NM_RELEASEDCAPTURE;
1177 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1178 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1181 /* make HOT btns NORMAL and hide gray btns */
1182 PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
1184 return TRUE;
1187 static LRESULT
1188 PAGER_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
1190 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1191 POINT clpt, pt;
1192 RECT wnrect, TLbtnrect, BRbtnrect, *btnrect = NULL;
1193 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1194 BOOL topLeft = FALSE;
1195 INT btnstate = 0;
1196 INT hit;
1197 HDC hdc;
1199 pt.x = SLOWORD(lParam);
1200 pt.y = SHIWORD(lParam);
1202 TRACE("[%08x] to (%ld,%ld)\n", hwnd, pt.x, pt.y);
1203 ClientToScreen(hwnd, &pt);
1204 GetWindowRect(hwnd, &wnrect);
1205 if (PtInRect(&wnrect, pt)) {
1206 TLbtnrect = wnrect;
1207 BRbtnrect = wnrect;
1208 if (dwStyle & PGS_HORZ) {
1209 TLbtnrect.right = TLbtnrect.left + infoPtr->nButtonSize;
1210 BRbtnrect.left = BRbtnrect.right - infoPtr->nButtonSize;
1212 else {
1213 TLbtnrect.bottom = TLbtnrect.top + infoPtr->nButtonSize;
1214 BRbtnrect.top = BRbtnrect.bottom - infoPtr->nButtonSize;
1217 clpt = pt;
1218 MapWindowPoints(0, hwnd, &clpt, 1);
1219 hit = PAGER_HitTest(hwnd, &clpt);
1220 if (hit == HTLEFT || hit == HTTOP) {
1221 topLeft = TRUE;
1222 btnrect = &TLbtnrect;
1223 infoPtr->TLbtnState = PGF_DEPRESSED;
1224 btnstate = infoPtr->TLbtnState;
1226 else if (hit == HTRIGHT || hit == HTBOTTOM) {
1227 topLeft = FALSE;
1228 btnrect = &BRbtnrect;
1229 infoPtr->BRbtnState = PGF_DEPRESSED;
1230 btnstate = infoPtr->BRbtnState;
1233 /* If in one of the buttons the capture and draw buttons */
1234 if (btnrect) {
1235 TRACE("[%08x] draw btn (%d,%d)-(%d,%d), Capture %s, style %08lx\n",
1236 hwnd, btnrect->left, btnrect->top,
1237 btnrect->right, btnrect->bottom,
1238 (infoPtr->bCapture) ? "TRUE" : "FALSE",
1239 dwStyle);
1240 if (!infoPtr->bCapture)
1241 PAGER_CaptureandTrack(infoPtr, hwnd);
1242 if (dwStyle & PGS_AUTOSCROLL)
1243 SetTimer(hwnd, TIMERID1, 0x3e, 0);
1244 MapWindowPoints(0, hwnd, (LPPOINT)btnrect, 2);
1245 hdc = GetWindowDC(hwnd);
1246 /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
1247 PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1248 PAGER_IsHorizontal(hwnd), topLeft, btnstate);
1249 ReleaseDC(hwnd, hdc);
1250 return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1254 /* If we think we are captured, then do release */
1255 if (infoPtr->bCapture) {
1256 infoPtr->bCapture = FALSE;
1258 if (GetCapture() == hwnd) {
1259 ReleaseCapture();
1260 /* Notify parent of released mouse capture */
1262 NMHDR nmhdr;
1263 ZeroMemory (&nmhdr, sizeof (NMHDR));
1264 nmhdr.hwndFrom = hwnd;
1265 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1266 nmhdr.code = NM_RELEASEDCAPTURE;
1267 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1268 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1271 if (IsWindow(hwnd))
1272 KillTimer(hwnd, TIMERID1);
1274 return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1277 static LRESULT
1278 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1280 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1281 BOOL repaintBtns = FALSE;
1282 POINT pt;
1283 INT hit;
1285 pt.x = SLOWORD(lParam);
1286 pt.y = SHIWORD(lParam);
1288 TRACE("[%08x] at (%d,%d)\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1290 hit = PAGER_HitTest(hwnd, &pt);
1292 /* put btn in DEPRESSED state */
1293 if (hit == HTLEFT || hit == HTTOP)
1295 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1296 infoPtr->TLbtnState = PGF_DEPRESSED;
1297 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1299 else if (hit == HTRIGHT || hit == HTBOTTOM)
1301 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1302 infoPtr->BRbtnState = PGF_DEPRESSED;
1303 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1306 if (repaintBtns)
1307 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1309 switch(hit)
1311 case HTLEFT:
1312 TRACE("[%08x] PGF_SCROLLLEFT\n", hwnd);
1313 PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1314 break;
1315 case HTTOP:
1316 TRACE("[%08x] PGF_SCROLLUP\n", hwnd);
1317 PAGER_Scroll(hwnd, PGF_SCROLLUP);
1318 break;
1319 case HTRIGHT:
1320 TRACE("[%08x] PGF_SCROLLRIGHT\n", hwnd);
1321 PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1322 break;
1323 case HTBOTTOM:
1324 TRACE("[%08x] PGF_SCROLLDOWN\n", hwnd);
1325 PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1326 break;
1327 default:
1328 break;
1331 return TRUE;
1334 static LRESULT
1335 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1337 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1338 TRACE("[%08x]\n", hwnd);
1340 KillTimer (hwnd, TIMERID1);
1341 KillTimer (hwnd, TIMERID2);
1343 /* make PRESSED btns NORMAL but don't hide gray btns */
1344 PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1346 return 0;
1349 static LRESULT
1350 PAGER_NCLButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1352 POINT pt;
1354 pt.x = SLOWORD(lParam);
1355 pt.y = SHIWORD(lParam);
1357 TRACE("[%08x] at (%d,%d)\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1358 MapWindowPoints(0, hwnd, &pt, 1);
1359 lParam = MAKELONG(pt.x, pt.y);
1360 return PAGER_LButtonDown (hwnd, wParam, lParam);
1363 static LRESULT
1364 PAGER_Timer (HWND hwnd, WPARAM wParam)
1366 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1367 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1368 INT dir;
1370 /* if initial timer, kill it and start the repeat timer */
1371 if (wParam == TIMERID1) {
1372 if (PAGER_IsHorizontal(hwnd)) {
1373 dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ?
1374 PGF_SCROLLLEFT : PGF_SCROLLRIGHT;
1376 else {
1377 dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ?
1378 PGF_SCROLLUP : PGF_SCROLLDOWN;
1380 TRACE("[%08x] TIMERID1: style=%08lx, dir=%d\n", hwnd, dwStyle, dir);
1381 KillTimer(hwnd, TIMERID1);
1382 SetTimer(hwnd, TIMERID1, REPEAT_DELAY, 0);
1383 if (dwStyle & PGS_AUTOSCROLL) {
1384 PAGER_Scroll(hwnd, dir);
1385 SetWindowPos(hwnd, 0,0,0,0,0,
1386 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1387 SWP_NOZORDER | SWP_NOACTIVATE);
1389 return 0;
1393 TRACE("[%08x] TIMERID2: dir=%d\n", hwnd, infoPtr->direction);
1394 KillTimer(hwnd, TIMERID2);
1395 if (infoPtr->direction > 0) {
1396 PAGER_Scroll(hwnd, infoPtr->direction);
1397 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1399 return 0;
1402 static LRESULT
1403 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1405 POINT pt, ptorig;
1406 HDC hdc = (HDC)wParam;
1407 HWND parent;
1409 /* native does:
1410 * parent = GetParent(pager)
1411 * pt.x=0; pt.y=0; ?????
1412 * MapWindowPoints(pager, parent, &pt, 1)
1413 * OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg)
1414 * SendMessageA(parent, WM_ERASEBKGND, hdc, 0)
1415 * SetWindowOrgEx(hdc, 0, 0, 0)
1418 pt.x = 0;
1419 pt.y = 0;
1420 parent = GetParent(hwnd);
1421 MapWindowPoints(hwnd, parent, &pt, 1);
1422 OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1423 SendMessageA (parent, WM_ERASEBKGND, wParam, lParam);
1424 SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1427 #if 0
1428 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1429 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1430 RECT rect;
1432 GetClientRect (hwnd, &rect);
1433 FillRect ((HDC)wParam, &rect, hBrush);
1435 /* background color of the child should be the same as the pager */
1436 if (infoPtr->hwndChild)
1438 GetClientRect (infoPtr->hwndChild, &rect);
1439 FillRect ((HDC)wParam, &rect, hBrush);
1442 DeleteObject (hBrush);
1443 #endif
1445 return TRUE;
1449 static LRESULT
1450 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1452 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1454 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1455 TRACE("[%08x] %dx%d\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1457 if (PAGER_IsHorizontal(hwnd))
1458 infoPtr->nHeight = SHIWORD(lParam);
1459 else
1460 infoPtr->nWidth = SLOWORD(lParam);
1462 return PAGER_RecalcSize(hwnd);
1466 static LRESULT WINAPI
1467 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1469 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1471 if (!infoPtr && (uMsg != WM_CREATE))
1472 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1474 switch (uMsg)
1476 case EM_FMTLINES:
1477 return PAGER_FmtLines(hwnd);
1479 case PGM_FORWARDMOUSE:
1480 return PAGER_ForwardMouse (hwnd, wParam);
1482 case PGM_GETBKCOLOR:
1483 return PAGER_GetBkColor(hwnd);
1485 case PGM_GETBORDER:
1486 return PAGER_GetBorder(hwnd);
1488 case PGM_GETBUTTONSIZE:
1489 return PAGER_GetButtonSize(hwnd);
1491 case PGM_GETPOS:
1492 return PAGER_GetPos(hwnd);
1494 case PGM_GETBUTTONSTATE:
1495 return PAGER_GetButtonState (hwnd, wParam, lParam);
1497 /* case PGM_GETDROPTARGET: */
1499 case PGM_RECALCSIZE:
1500 return PAGER_RecalcSize(hwnd);
1502 case PGM_SETBKCOLOR:
1503 return PAGER_SetBkColor (hwnd, wParam, lParam);
1505 case PGM_SETBORDER:
1506 return PAGER_SetBorder (hwnd, wParam, lParam);
1508 case PGM_SETBUTTONSIZE:
1509 return PAGER_SetButtonSize (hwnd, wParam, lParam);
1511 case PGM_SETCHILD:
1512 return PAGER_SetChild (hwnd, wParam, lParam);
1514 case PGM_SETPOS:
1515 return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1517 case WM_CREATE:
1518 return PAGER_Create (hwnd, wParam, lParam);
1520 case WM_DESTROY:
1521 return PAGER_Destroy (hwnd, wParam, lParam);
1523 case WM_SIZE:
1524 return PAGER_Size (hwnd, wParam, lParam);
1526 case WM_NCPAINT:
1527 return PAGER_NCPaint (hwnd, wParam, lParam);
1529 case WM_WINDOWPOSCHANGING:
1530 return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam);
1532 case WM_NCCALCSIZE:
1533 return PAGER_NCCalcSize (hwnd, wParam, lParam);
1535 case WM_NCHITTEST:
1536 return PAGER_NCHitTest (hwnd, wParam, lParam);
1538 case WM_SETCURSOR:
1540 if (hwnd == (HWND)wParam)
1541 return PAGER_SetCursor(hwnd, wParam, lParam);
1542 else /* its for the child */
1543 return 0;
1546 case WM_MOUSEMOVE:
1547 if (infoPtr->bForward && infoPtr->hwndChild)
1548 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1549 return PAGER_MouseMove (hwnd, wParam, lParam);
1551 case WM_MOUSELEAVE:
1552 return PAGER_MouseLeave (hwnd, wParam, lParam);
1554 case WM_NCLBUTTONDOWN:
1555 return PAGER_NCLButtonDown (hwnd, wParam, lParam);
1557 case WM_LBUTTONDOWN:
1558 return PAGER_LButtonDown (hwnd, wParam, lParam);
1560 case WM_NCLBUTTONUP:
1561 case WM_LBUTTONUP:
1562 return PAGER_LButtonUp (hwnd, wParam, lParam);
1564 case WM_ERASEBKGND:
1565 return PAGER_EraseBackground (hwnd, wParam, lParam);
1567 case WM_PAINT:
1568 return PAGER_Paint (hwnd, wParam);
1570 case WM_TIMER:
1571 return PAGER_Timer (hwnd, wParam);
1573 case WM_NOTIFY:
1574 case WM_COMMAND:
1575 return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1577 default:
1578 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1581 return 0;
1585 VOID
1586 PAGER_Register (void)
1588 WNDCLASSA wndClass;
1590 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1591 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1592 wndClass.lpfnWndProc = (WNDPROC)PAGER_WindowProc;
1593 wndClass.cbClsExtra = 0;
1594 wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1595 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1596 wndClass.hbrBackground = 0;
1597 wndClass.lpszClassName = WC_PAGESCROLLERA;
1599 RegisterClassA (&wndClass);
1603 VOID
1604 PAGER_Unregister (void)
1606 UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL);