Rename COMCTL32_{Alloc,ReAlloc,Free} to {Alloc,ReAlloc,Free}.
[wine/hacks.git] / dlls / comctl32 / pager.c
blob362f42c8a8d72df31731291949d8b658ec144554
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 <stdarg.h>
32 #include <string.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wingdi.h"
36 #include "winuser.h"
37 #include "winnls.h"
38 #include "commctrl.h"
39 #include "comctl32.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(pager);
44 typedef struct
46 HWND hwndChild; /* handle of the contained wnd */
47 BOOL bNoResize; /* set when created with CCS_NORESIZE */
48 COLORREF clrBk; /* background color */
49 INT nBorder; /* border size for the control */
50 INT nButtonSize;/* size of the pager btns */
51 INT nPos; /* scroll position */
52 INT nWidth; /* from child wnd's response to PGN_CALCSIZE */
53 INT nHeight; /* from child wnd's response to PGN_CALCSIZE */
54 BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */
55 BOOL bCapture; /* we have captured the mouse */
56 INT TLbtnState; /* state of top or left btn */
57 INT BRbtnState; /* state of bottom or right btn */
58 INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
59 } PAGER_INFO;
61 #define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0))
62 #define PAGER_IsHorizontal(hwnd) ((GetWindowLongA (hwnd, GWL_STYLE) & PGS_HORZ))
64 #define MIN_ARROW_WIDTH 8
65 #define MIN_ARROW_HEIGHT 5
67 #define TIMERID1 1
68 #define TIMERID2 2
69 #define INITIAL_DELAY 500
70 #define REPEAT_DELAY 50
72 /* the horizontal arrows are:
74 * 01234 01234
75 * 1 * *
76 * 2 ** **
77 * 3*** ***
78 * 4*** ***
79 * 5 ** **
80 * 6 * *
81 * 7
84 static void
85 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
87 INT x, y, w, h;
88 HPEN hPen, hOldPen;
90 w = r.right - r.left + 1;
91 h = r.bottom - r.top + 1;
92 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
93 return; /* refuse to draw partial arrow */
95 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
96 hOldPen = SelectObject ( hdc, hPen );
97 if (left)
99 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
100 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
101 MoveToEx (hdc, x, y, NULL);
102 LineTo (hdc, x--, y+5); y++;
103 MoveToEx (hdc, x, y, NULL);
104 LineTo (hdc, x--, y+3); y++;
105 MoveToEx (hdc, x, y, NULL);
106 LineTo (hdc, x, y+1);
108 else
110 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
111 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
112 MoveToEx (hdc, x, y, NULL);
113 LineTo (hdc, x++, y+5); y++;
114 MoveToEx (hdc, x, y, NULL);
115 LineTo (hdc, x++, y+3); y++;
116 MoveToEx (hdc, x, y, NULL);
117 LineTo (hdc, x, y+1);
120 SelectObject( hdc, hOldPen );
121 DeleteObject( hPen );
124 /* the vertical arrows are:
126 * 01234567 01234567
127 * 1****** **
128 * 2 **** ****
129 * 3 ** ******
133 static void
134 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
136 INT x, y, w, h;
137 HPEN hPen, hOldPen;
139 w = r.right - r.left + 1;
140 h = r.bottom - r.top + 1;
141 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
142 return; /* refuse to draw partial arrow */
144 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
145 hOldPen = SelectObject ( hdc, hPen );
146 if (up)
148 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
149 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
150 MoveToEx (hdc, x, y, NULL);
151 LineTo (hdc, x+5, y--); x++;
152 MoveToEx (hdc, x, y, NULL);
153 LineTo (hdc, x+3, y--); x++;
154 MoveToEx (hdc, x, y, NULL);
155 LineTo (hdc, x+1, y);
157 else
159 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
160 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
161 MoveToEx (hdc, x, y, NULL);
162 LineTo (hdc, x+5, y++); x++;
163 MoveToEx (hdc, x, y, NULL);
164 LineTo (hdc, x+3, y++); x++;
165 MoveToEx (hdc, x, y, NULL);
166 LineTo (hdc, x+1, y);
169 SelectObject( hdc, hOldPen );
170 DeleteObject( hPen );
173 static void
174 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
175 BOOL horz, BOOL topLeft, INT btnState)
177 HBRUSH hBrush, hOldBrush;
178 RECT rc = arrowRect;
180 if (!btnState) /* PGF_INVISIBLE */
181 return;
183 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
184 return;
186 hBrush = CreateSolidBrush(clrBk);
187 hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
189 FillRect(hdc, &rc, hBrush);
191 if (btnState == PGF_HOT)
193 DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
194 if (horz)
195 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
196 else
197 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
199 else if (btnState == PGF_NORMAL)
201 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
202 if (horz)
203 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
204 else
205 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
207 else if (btnState == PGF_DEPRESSED)
209 DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
210 if (horz)
211 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
212 else
213 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
215 else if (btnState == PGF_GRAYED)
217 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
218 if (horz)
220 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
221 rc.left++, rc.top++; rc.right++, rc.bottom++;
222 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
224 else
226 PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
227 rc.left++, rc.top++; rc.right++, rc.bottom++;
228 PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
232 SelectObject( hdc, hOldBrush );
233 DeleteObject(hBrush);
236 static void PAGER_CaptureandTrack(PAGER_INFO *infoPtr, HWND hwnd)
238 TRACKMOUSEEVENT trackinfo;
240 TRACE("[%p] SetCapture\n", hwnd);
241 SetCapture(hwnd);
242 infoPtr->bCapture = TRUE;
244 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
245 trackinfo.dwFlags = TME_QUERY;
246 trackinfo.hwndTrack = hwnd;
247 trackinfo.dwHoverTime = HOVER_DEFAULT;
249 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
250 _TrackMouseEvent(&trackinfo);
252 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
253 if(!(trackinfo.dwFlags & TME_LEAVE)) {
254 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
256 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
257 /* and can properly deactivate the hot button */
258 _TrackMouseEvent(&trackinfo);
263 /* << PAGER_GetDropTarget >> */
265 static inline LRESULT
266 PAGER_ForwardMouse (HWND hwnd, WPARAM wParam)
268 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
269 TRACE("[%p]\n", hwnd);
271 infoPtr->bForward = (BOOL)wParam;
273 return 0;
276 static inline LRESULT
277 PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam)
279 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
280 LRESULT btnState = PGF_INVISIBLE;
281 INT btn = (INT)lParam;
282 TRACE("[%p]\n", hwnd);
284 if (btn == PGB_TOPORLEFT)
285 btnState = infoPtr->TLbtnState;
286 else if (btn == PGB_BOTTOMORRIGHT)
287 btnState = infoPtr->BRbtnState;
289 return btnState;
293 static inline LRESULT
294 PAGER_GetPos(HWND hwnd)
296 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
297 TRACE("[%p] returns %d\n", hwnd, infoPtr->nPos);
298 return (LRESULT)infoPtr->nPos;
301 static inline LRESULT
302 PAGER_GetButtonSize(HWND hwnd)
304 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
305 TRACE("[%p] returns %d\n", hwnd, infoPtr->nButtonSize);
306 return (LRESULT)infoPtr->nButtonSize;
309 static inline LRESULT
310 PAGER_GetBorder(HWND hwnd)
312 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
313 TRACE("[%p] returns %d\n", hwnd, infoPtr->nBorder);
314 return (LRESULT)infoPtr->nBorder;
317 static inline LRESULT
318 PAGER_GetBkColor(HWND hwnd)
320 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
321 TRACE("[%p] returns %06lx\n", hwnd, infoPtr->clrBk);
322 return (LRESULT)infoPtr->clrBk;
325 static void
326 PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth)
328 NMPGCALCSIZE nmpgcs;
329 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
330 nmpgcs.hdr.hwndFrom = hwnd;
331 nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
332 nmpgcs.hdr.code = PGN_CALCSIZE;
333 nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
334 nmpgcs.iWidth = getWidth ? *size : 0;
335 nmpgcs.iHeight = getWidth ? 0 : *size;
336 SendMessageA (GetParent (hwnd), WM_NOTIFY,
337 (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
339 *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
341 TRACE("[%p] PGN_CALCSIZE returns %s=%d\n", hwnd,
342 getWidth ? "width" : "height", *size);
345 static void
346 PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr)
348 if (infoPtr->hwndChild)
350 RECT rcClient;
351 int nPos = infoPtr->nPos;
353 /* compensate for a grayed btn, which will soon become invisible */
354 if (infoPtr->TLbtnState == PGF_GRAYED)
355 nPos += infoPtr->nButtonSize;
357 GetClientRect(hwnd, &rcClient);
359 if (PAGER_IsHorizontal(hwnd))
361 int wndSize = max(0, rcClient.right - rcClient.left);
362 if (infoPtr->nWidth < wndSize)
363 infoPtr->nWidth = wndSize;
365 TRACE("[%p] SWP %dx%d at (%d,%d)\n", hwnd,
366 infoPtr->nWidth, infoPtr->nHeight,
367 -nPos, 0);
368 SetWindowPos(infoPtr->hwndChild, 0,
369 -nPos, 0,
370 infoPtr->nWidth, infoPtr->nHeight,
371 SWP_NOZORDER);
373 else
375 int wndSize = max(0, rcClient.bottom - rcClient.top);
376 if (infoPtr->nHeight < wndSize)
377 infoPtr->nHeight = wndSize;
379 TRACE("[%p] SWP %dx%d at (%d,%d)\n", hwnd,
380 infoPtr->nWidth, infoPtr->nHeight,
381 0, -nPos);
382 SetWindowPos(infoPtr->hwndChild, 0,
383 0, -nPos,
384 infoPtr->nWidth, infoPtr->nHeight,
385 SWP_NOZORDER);
388 InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
392 static INT
393 PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr)
395 INT scrollRange = 0;
397 if (infoPtr->hwndChild)
399 INT wndSize, childSize;
400 RECT wndRect;
401 GetWindowRect(hwnd, &wndRect);
403 if (PAGER_IsHorizontal(hwnd))
405 wndSize = wndRect.right - wndRect.left;
406 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
407 childSize = infoPtr->nWidth;
409 else
411 wndSize = wndRect.bottom - wndRect.top;
412 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
413 childSize = infoPtr->nHeight;
416 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
417 if (childSize > wndSize)
418 scrollRange = childSize - wndSize + infoPtr->nButtonSize;
421 TRACE("[%p] returns %d\n", hwnd, scrollRange);
422 return scrollRange;
425 static void
426 PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange,
427 BOOL* needsResize, BOOL* needsRepaint)
429 if (infoPtr->nPos > 0)
431 *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */
432 if (infoPtr->TLbtnState != PGF_DEPRESSED)
433 infoPtr->TLbtnState = PGF_NORMAL;
435 else
437 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
438 infoPtr->TLbtnState = PGF_GRAYED;
441 if (scrollRange <= 0)
443 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
444 infoPtr->TLbtnState = PGF_GRAYED;
445 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
446 infoPtr->BRbtnState = PGF_GRAYED;
448 else if (infoPtr->nPos < scrollRange)
450 *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */
451 if (infoPtr->BRbtnState != PGF_DEPRESSED)
452 infoPtr->BRbtnState = PGF_NORMAL;
454 else
456 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
457 infoPtr->BRbtnState = PGF_GRAYED;
462 static void
463 PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint)
465 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
467 infoPtr->TLbtnState = PGF_NORMAL;
468 *needsRepaint = TRUE;
471 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
473 infoPtr->BRbtnState = PGF_NORMAL;
474 *needsRepaint = TRUE;
478 static void
479 PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize)
481 if (infoPtr->TLbtnState == PGF_GRAYED)
483 infoPtr->TLbtnState = PGF_INVISIBLE;
484 *needsResize = TRUE;
487 if (infoPtr->BRbtnState == PGF_GRAYED)
489 infoPtr->BRbtnState = PGF_INVISIBLE;
490 *needsResize = TRUE;
494 static void
495 PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr,
496 INT scrollRange, BOOL hideGrayBtns)
498 BOOL resizeClient = FALSE;
499 BOOL repaintBtns = FALSE;
501 if (scrollRange < 0)
502 PAGER_NormalizeBtns(infoPtr, &repaintBtns);
503 else
504 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
506 if (hideGrayBtns)
507 PAGER_HideGrayBtns(infoPtr, &resizeClient);
509 if (resizeClient) /* initiate NCCalcSize to resize client wnd */ {
510 SetWindowPos(hwnd, 0,0,0,0,0,
511 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
512 SWP_NOZORDER | SWP_NOACTIVATE);
515 if (repaintBtns)
516 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
519 static LRESULT
520 PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress)
522 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
523 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
524 INT oldPos = infoPtr->nPos;
526 if ((scrollRange <= 0) || (newPos < 0))
527 infoPtr->nPos = 0;
528 else if (newPos > scrollRange)
529 infoPtr->nPos = scrollRange;
530 else
531 infoPtr->nPos = newPos;
533 TRACE("[%p] pos=%d, oldpos=%d\n", hwnd, infoPtr->nPos, oldPos);
535 if (infoPtr->nPos != oldPos)
537 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
538 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress);
539 PAGER_PositionChildWnd(hwnd, infoPtr);
542 return 0;
545 static LRESULT
546 PAGER_HandleWindowPosChanging(HWND hwnd, WPARAM wParam, WINDOWPOS *winpos)
548 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
550 if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE))
552 /* don't let the app resize the nonscrollable dimension of a control
553 * that was created with CCS_NORESIZE style
554 * (i.e. height for a horizontal pager, or width for a vertical one) */
556 /* except if the current dimension is 0 and app is setting for
557 * first time, then save amount as dimension. - GA 8/01 */
559 if (PAGER_IsHorizontal(hwnd))
560 if (!infoPtr->nHeight && winpos->cy)
561 infoPtr->nHeight = winpos->cy;
562 else
563 winpos->cy = infoPtr->nHeight;
564 else
565 if (!infoPtr->nWidth && winpos->cx)
566 infoPtr->nWidth = winpos->cx;
567 else
568 winpos->cx = infoPtr->nWidth;
569 return 0;
572 DefWindowProcA (hwnd, WM_WINDOWPOSCHANGING, wParam, (LPARAM)winpos);
574 return 1;
577 static INT
578 PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr)
580 /* Must set the non-scrollable dimension to be less than the full height/width
581 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
582 * size, and experimentation shows that affect is almost right. */
584 RECT wndRect;
585 INT delta, h;
586 GetWindowRect(hwnd, &wndRect);
588 /* see what the app says for btn width */
589 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
591 if (infoPtr->bNoResize)
593 delta = wndRect.right - wndRect.left - infoPtr->nWidth;
594 if (delta > infoPtr->nButtonSize)
595 infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
596 else if (delta > 0)
597 infoPtr->nWidth += infoPtr->nButtonSize / 3;
600 h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
602 TRACE("[%p] infoPtr->nWidth set to %d\n",
603 hwnd, infoPtr->nWidth);
605 return h;
608 static INT
609 PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr)
611 /* Must set the non-scrollable dimension to be less than the full height/width
612 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
613 * size, and experimentation shows that affect is almost right. */
615 RECT wndRect;
616 INT delta, w;
617 GetWindowRect(hwnd, &wndRect);
619 /* see what the app says for btn height */
620 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
622 if (infoPtr->bNoResize)
624 delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
625 if (delta > infoPtr->nButtonSize)
626 infoPtr->nHeight += infoPtr->nButtonSize;
627 else if (delta > 0)
628 infoPtr->nHeight += infoPtr->nButtonSize / 3;
631 w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
633 TRACE("[%p] infoPtr->nHeight set to %d\n",
634 hwnd, infoPtr->nHeight);
636 return w;
639 /******************************************************************
640 * For the PGM_RECALCSIZE message (but not the other uses in *
641 * this module), the native control does only the following: *
643 * if (some condition) *
644 * PostMessageA(hwnd, EM_FMTLINES, 0, 0); *
645 * return DefWindowProcA(hwnd, PGM_RECALCSIZE, 0, 0); *
647 * When we figure out what the "some condition" is we will *
648 * implement that for the message processing. *
649 ******************************************************************/
651 static LRESULT
652 PAGER_RecalcSize(HWND hwnd)
654 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
656 TRACE("[%p]\n", hwnd);
658 if (infoPtr->hwndChild)
660 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
662 if (scrollRange <= 0)
664 infoPtr->nPos = -1;
665 PAGER_SetPos(hwnd, 0, FALSE);
667 else
669 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
670 PAGER_PositionChildWnd(hwnd, infoPtr);
674 return 1;
678 static LRESULT
679 PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
681 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
682 COLORREF clrTemp = infoPtr->clrBk;
684 infoPtr->clrBk = (COLORREF)lParam;
685 TRACE("[%p] %06lx\n", hwnd, infoPtr->clrBk);
687 /* the native control seems to do things this way */
688 SetWindowPos(hwnd, 0,0,0,0,0,
689 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
690 SWP_NOZORDER | SWP_NOACTIVATE);
692 RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE);
694 return (LRESULT)clrTemp;
698 static LRESULT
699 PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
701 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
702 INT nTemp = infoPtr->nBorder;
704 infoPtr->nBorder = (INT)lParam;
705 TRACE("[%p] %d\n", hwnd, infoPtr->nBorder);
707 PAGER_RecalcSize(hwnd);
709 return (LRESULT)nTemp;
713 static LRESULT
714 PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
716 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
717 INT nTemp = infoPtr->nButtonSize;
719 infoPtr->nButtonSize = (INT)lParam;
720 TRACE("[%p] %d\n", hwnd, infoPtr->nButtonSize);
722 PAGER_RecalcSize(hwnd);
724 return (LRESULT)nTemp;
728 static LRESULT
729 PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
731 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
732 INT hw;
734 infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
736 if (infoPtr->hwndChild)
738 TRACE("[%p] hwndChild=%p\n", hwnd, infoPtr->hwndChild);
740 if (PAGER_IsHorizontal(hwnd)) {
741 hw = PAGER_SetFixedHeight(hwnd, infoPtr);
742 /* adjust non-scrollable dimension to fit the child */
743 SetWindowPos(hwnd, 0, 0,0, hw, infoPtr->nHeight,
744 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
745 SWP_NOSIZE | SWP_NOACTIVATE);
747 else {
748 hw = PAGER_SetFixedWidth(hwnd, infoPtr);
749 /* adjust non-scrollable dimension to fit the child */
750 SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, hw,
751 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
752 SWP_NOSIZE | SWP_NOACTIVATE);
755 /* position child within the page scroller */
756 SetWindowPos(infoPtr->hwndChild, HWND_TOP,
757 0,0,0,0,
758 SWP_SHOWWINDOW | SWP_NOSIZE); /* native is 0 */
760 infoPtr->nPos = -1;
761 PAGER_SetPos(hwnd, 0, FALSE);
764 return 0;
767 static void
768 PAGER_Scroll(HWND hwnd, INT dir)
770 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
771 NMPGSCROLL nmpgScroll;
772 RECT rcWnd;
774 if (infoPtr->hwndChild)
776 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
777 nmpgScroll.hdr.hwndFrom = hwnd;
778 nmpgScroll.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
779 nmpgScroll.hdr.code = PGN_SCROLL;
781 GetWindowRect(hwnd, &rcWnd);
782 GetClientRect(hwnd, &nmpgScroll.rcParent);
783 nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
784 nmpgScroll.iDir = dir;
786 if (PAGER_IsHorizontal(hwnd))
788 nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
789 nmpgScroll.iXpos = infoPtr->nPos;
791 else
793 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
794 nmpgScroll.iYpos = infoPtr->nPos;
796 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
798 SendMessageA (GetParent(hwnd), WM_NOTIFY,
799 (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
801 TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
803 if (nmpgScroll.iScroll > 0)
805 infoPtr->direction = dir;
807 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
808 PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
809 else
810 PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
812 else
813 infoPtr->direction = -1;
817 static LRESULT
818 PAGER_FmtLines(HWND hwnd)
820 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
822 /* initiate NCCalcSize to resize client wnd and get size */
823 SetWindowPos(hwnd, 0, 0,0,0,0,
824 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
825 SWP_NOZORDER | SWP_NOACTIVATE);
827 SetWindowPos(infoPtr->hwndChild, 0,
828 0,0,infoPtr->nWidth,infoPtr->nHeight,
831 return DefWindowProcA (hwnd, EM_FMTLINES, 0, 0);
834 static LRESULT
835 PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
837 PAGER_INFO *infoPtr;
838 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
840 /* allocate memory for info structure */
841 infoPtr = (PAGER_INFO *)Alloc (sizeof(PAGER_INFO));
842 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
844 /* set default settings */
845 infoPtr->hwndChild = NULL;
846 infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
847 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
848 infoPtr->nBorder = 0;
849 infoPtr->nButtonSize = 12;
850 infoPtr->nPos = 0;
851 infoPtr->nWidth = 0;
852 infoPtr->nHeight = 0;
853 infoPtr->bForward = FALSE;
854 infoPtr->bCapture = FALSE;
855 infoPtr->TLbtnState = PGF_INVISIBLE;
856 infoPtr->BRbtnState = PGF_INVISIBLE;
857 infoPtr->direction = -1;
859 if (dwStyle & PGS_DRAGNDROP)
860 FIXME("[%p] Drag and Drop style is not implemented yet.\n", hwnd);
862 * If neither horizontal nor vertical style specified, default to vertical.
863 * This is probably not necessary, since the style may be set later on as
864 * the control is initialized, but just in case it isn't, set it here.
866 if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
868 dwStyle |= PGS_VERT;
869 SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
872 return 0;
876 static LRESULT
877 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
879 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
880 /* free pager info data */
881 Free (infoPtr);
882 SetWindowLongA (hwnd, 0, 0);
883 return 0;
886 static LRESULT
887 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
889 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
890 LPRECT lpRect = (LPRECT)lParam;
891 RECT rcChildw, rcmyw, wnrc, ltrc, rbrc;
892 POINT cursor;
893 BOOL resizeClient = FALSE;
894 BOOL repaintBtns = FALSE;
895 INT scrollRange;
898 * lParam points to a RECT struct. On entry, the struct
899 * contains the proposed wnd rectangle for the window.
900 * On exit, the struct should contain the screen
901 * coordinates of the corresponding window's client area.
904 DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam);
906 TRACE("orig rect=(%ld,%ld)-(%ld,%ld)\n",
907 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
909 if (PAGER_IsHorizontal(hwnd))
911 infoPtr->nWidth = lpRect->right - lpRect->left;
912 PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE);
913 GetWindowRect (infoPtr->hwndChild, &rcChildw);
914 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
915 GetCursorPos (&cursor);
916 GetWindowRect (hwnd, &rcmyw);
918 /* Reset buttons and hide any grey ones */
919 scrollRange = infoPtr->nWidth - (rcmyw.right - rcmyw.left);
921 TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%ld,%ld)-(%ld,%ld), cursor=(%ld,%ld)\n",
922 infoPtr->nPos, scrollRange, infoPtr->nHeight,
923 rcmyw.left, rcmyw.top,
924 rcmyw.right, rcmyw.bottom,
925 cursor.x, cursor.y);
926 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
927 PAGER_HideGrayBtns(infoPtr, &resizeClient);
929 if (PtInRect (&rcmyw, cursor)) {
930 GetWindowRect (hwnd, &wnrc);
931 ltrc = wnrc;
932 ltrc.right = ltrc.left + infoPtr->nButtonSize;
933 rbrc = wnrc;
934 rbrc.left = rbrc.right - infoPtr->nButtonSize;
935 TRACE("horz lt rect=(%ld,%ld)-(%ld,%ld), rb rect=(%ld,%ld)-(%ld,%ld)\n",
936 ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
937 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
938 if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
939 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
940 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
941 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
943 if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
944 lpRect->left += infoPtr->nButtonSize;
945 if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
946 lpRect->right -= infoPtr->nButtonSize;
948 else
950 /* native does: (from trace of IE4 opening "Favorites" frame)
951 * DefWindowProc
952 * WM_NOITFY PGN_CALCSIZE w/ dwFlag=2
953 * GetWindowRect (child, &rc)
954 * MapWindowPoints (0, syspager, &rc, 2)
955 * GetCursorPos( &cur )
956 * GetWindowRect (syspager, &rc2)
957 * PtInRect (&rc2, cur.x, cur.y) rtns 0
958 * returns with rect empty
960 infoPtr->nHeight = lpRect->bottom - lpRect->top;
961 PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE);
962 GetWindowRect (infoPtr->hwndChild, &rcChildw);
963 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
964 GetCursorPos (&cursor);
965 GetWindowRect (hwnd, &rcmyw);
967 /* Reset buttons and hide any grey ones */
968 scrollRange = infoPtr->nHeight - (rcmyw.bottom - rcmyw.top);
970 TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%ld,%ld)-(%ld,%ld), cursor=(%ld,%ld)\n",
971 infoPtr->nPos, scrollRange, infoPtr->nHeight,
972 rcmyw.left, rcmyw.top,
973 rcmyw.right, rcmyw.bottom,
974 cursor.x, cursor.y);
975 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
976 PAGER_HideGrayBtns(infoPtr, &resizeClient);
978 if (PtInRect (&rcmyw, cursor)) {
980 /* native does:
981 * GetWindowRect(pager, &rc)
982 * PtInRect(btn-left????, cur.x, cur.y)
983 * if true -> ???
984 * PtInRect(btn-right????, cur.x, cur.y)
985 * if true
986 * RedrawWindow(pager, 0, 0, 5)
987 * return TRUE
990 GetWindowRect (hwnd, &wnrc);
991 ltrc = wnrc;
992 ltrc.right = ltrc.left + infoPtr->nButtonSize;
993 rbrc = wnrc;
994 rbrc.left = rbrc.right - infoPtr->nButtonSize;
995 TRACE("vert lt rect=(%ld,%ld)-(%ld,%ld), rb rect=(%ld,%ld)-(%ld,%ld)\n",
996 ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
997 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
998 if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
999 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
1000 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
1001 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
1003 if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
1004 lpRect->top += infoPtr->nButtonSize;
1005 if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
1006 lpRect->bottom -= infoPtr->nButtonSize;
1009 TRACE("[%p] client rect set to %ldx%ld at (%ld,%ld) BtnState[%d,%d]\n",
1010 hwnd, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
1011 lpRect->left, lpRect->top,
1012 infoPtr->TLbtnState, infoPtr->BRbtnState);
1014 return 0;
1017 static LRESULT
1018 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
1020 PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
1021 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1022 RECT rcWindow, rcBottomRight, rcTopLeft;
1023 HDC hdc;
1024 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1026 if (dwStyle & WS_MINIMIZE)
1027 return 0;
1029 DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
1031 if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
1032 return 0;
1034 GetWindowRect (hwnd, &rcWindow);
1035 OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
1037 rcTopLeft = rcBottomRight = rcWindow;
1038 if (bHorizontal)
1040 rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
1041 rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
1043 else
1045 rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
1046 rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
1049 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
1050 bHorizontal, TRUE, infoPtr->TLbtnState);
1051 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
1052 bHorizontal, FALSE, infoPtr->BRbtnState);
1054 ReleaseDC( hwnd, hdc );
1055 return 0;
1058 static INT
1059 PAGER_HitTest (HWND hwnd, LPPOINT pt)
1061 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1062 RECT clientRect;
1063 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1065 GetClientRect (hwnd, &clientRect);
1067 if (PtInRect(&clientRect, *pt))
1069 TRACE("HTCLIENT\n");
1070 return HTCLIENT;
1073 if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
1075 if (bHorizontal)
1077 if (pt->x < clientRect.left)
1079 TRACE("HTLEFT\n");
1080 return HTLEFT;
1083 else
1085 if (pt->y < clientRect.top)
1087 TRACE("HTTOP\n");
1088 return HTTOP;
1093 if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
1095 if (bHorizontal)
1097 if (pt->x > clientRect.right)
1099 TRACE("HTRIGHT\n");
1100 return HTRIGHT;
1103 else
1105 if (pt->y > clientRect.bottom)
1107 TRACE("HTBOTTOM\n");
1108 return HTBOTTOM;
1113 TRACE("HTNOWHERE\n");
1114 return HTNOWHERE;
1117 static LRESULT
1118 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
1120 POINT pt;
1122 pt.x = (short)LOWORD(lParam);
1123 pt.y = (short)HIWORD(lParam);
1125 ScreenToClient (hwnd, &pt);
1126 return PAGER_HitTest(hwnd, &pt);
1129 static LRESULT
1130 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
1132 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1133 BOOL notCaptured = FALSE;
1135 switch(LOWORD(lParam))
1137 case HTLEFT:
1138 case HTTOP:
1139 if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
1140 infoPtr->TLbtnState = PGF_HOT;
1141 break;
1142 case HTRIGHT:
1143 case HTBOTTOM:
1144 if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
1145 infoPtr->BRbtnState = PGF_HOT;
1146 break;
1147 default:
1148 return FALSE;
1151 if (notCaptured)
1153 PAGER_CaptureandTrack(infoPtr, hwnd);
1155 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1158 return TRUE;
1161 static LRESULT
1162 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
1164 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1166 KillTimer (hwnd, TIMERID1);
1167 KillTimer (hwnd, TIMERID2);
1169 TRACE("[%p] ReleaseCapture\n", hwnd);
1170 ReleaseCapture();
1171 infoPtr->bCapture = FALSE;
1173 /* Notify parent of released mouse capture */
1175 NMHDR nmhdr;
1176 ZeroMemory (&nmhdr, sizeof (NMHDR));
1177 nmhdr.hwndFrom = hwnd;
1178 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1179 nmhdr.code = NM_RELEASEDCAPTURE;
1180 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1181 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1184 /* make HOT btns NORMAL and hide gray btns */
1185 PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
1187 return TRUE;
1190 static LRESULT
1191 PAGER_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
1193 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1194 POINT clpt, pt;
1195 RECT wnrect, TLbtnrect, BRbtnrect, *btnrect = NULL;
1196 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1197 BOOL topLeft = FALSE;
1198 INT btnstate = 0;
1199 INT hit;
1200 HDC hdc;
1202 pt.x = (short)LOWORD(lParam);
1203 pt.y = (short)HIWORD(lParam);
1205 TRACE("[%p] to (%ld,%ld)\n", hwnd, pt.x, pt.y);
1206 ClientToScreen(hwnd, &pt);
1207 GetWindowRect(hwnd, &wnrect);
1208 if (PtInRect(&wnrect, pt)) {
1209 TLbtnrect = wnrect;
1210 BRbtnrect = wnrect;
1211 if (dwStyle & PGS_HORZ) {
1212 TLbtnrect.right = TLbtnrect.left + infoPtr->nButtonSize;
1213 BRbtnrect.left = BRbtnrect.right - infoPtr->nButtonSize;
1215 else {
1216 TLbtnrect.bottom = TLbtnrect.top + infoPtr->nButtonSize;
1217 BRbtnrect.top = BRbtnrect.bottom - infoPtr->nButtonSize;
1220 clpt = pt;
1221 MapWindowPoints(0, hwnd, &clpt, 1);
1222 hit = PAGER_HitTest(hwnd, &clpt);
1223 if (hit == HTLEFT || hit == HTTOP) {
1224 topLeft = TRUE;
1225 btnrect = &TLbtnrect;
1226 infoPtr->TLbtnState = PGF_DEPRESSED;
1227 btnstate = infoPtr->TLbtnState;
1229 else if (hit == HTRIGHT || hit == HTBOTTOM) {
1230 topLeft = FALSE;
1231 btnrect = &BRbtnrect;
1232 infoPtr->BRbtnState = PGF_DEPRESSED;
1233 btnstate = infoPtr->BRbtnState;
1236 /* If in one of the buttons the capture and draw buttons */
1237 if (btnrect) {
1238 TRACE("[%p] draw btn (%ld,%ld)-(%ld,%ld), Capture %s, style %08lx\n",
1239 hwnd, btnrect->left, btnrect->top,
1240 btnrect->right, btnrect->bottom,
1241 (infoPtr->bCapture) ? "TRUE" : "FALSE",
1242 dwStyle);
1243 if (!infoPtr->bCapture)
1244 PAGER_CaptureandTrack(infoPtr, hwnd);
1245 if (dwStyle & PGS_AUTOSCROLL)
1246 SetTimer(hwnd, TIMERID1, 0x3e, 0);
1247 MapWindowPoints(0, hwnd, (LPPOINT)btnrect, 2);
1248 hdc = GetWindowDC(hwnd);
1249 /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
1250 PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1251 PAGER_IsHorizontal(hwnd), topLeft, btnstate);
1252 ReleaseDC(hwnd, hdc);
1253 return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1257 /* If we think we are captured, then do release */
1258 if (infoPtr->bCapture) {
1259 infoPtr->bCapture = FALSE;
1261 if (GetCapture() == hwnd) {
1262 ReleaseCapture();
1263 /* Notify parent of released mouse capture */
1265 NMHDR nmhdr;
1266 ZeroMemory (&nmhdr, sizeof (NMHDR));
1267 nmhdr.hwndFrom = hwnd;
1268 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1269 nmhdr.code = NM_RELEASEDCAPTURE;
1270 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1271 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1274 if (IsWindow(hwnd))
1275 KillTimer(hwnd, TIMERID1);
1277 return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1280 static LRESULT
1281 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1283 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1284 BOOL repaintBtns = FALSE;
1285 POINT pt;
1286 INT hit;
1288 pt.x = (short)LOWORD(lParam);
1289 pt.y = (short)HIWORD(lParam);
1291 TRACE("[%p] at (%d,%d)\n", hwnd, (short)LOWORD(lParam), (short)HIWORD(lParam));
1293 hit = PAGER_HitTest(hwnd, &pt);
1295 /* put btn in DEPRESSED state */
1296 if (hit == HTLEFT || hit == HTTOP)
1298 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1299 infoPtr->TLbtnState = PGF_DEPRESSED;
1300 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1302 else if (hit == HTRIGHT || hit == HTBOTTOM)
1304 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1305 infoPtr->BRbtnState = PGF_DEPRESSED;
1306 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1309 if (repaintBtns)
1310 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1312 switch(hit)
1314 case HTLEFT:
1315 TRACE("[%p] PGF_SCROLLLEFT\n", hwnd);
1316 PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1317 break;
1318 case HTTOP:
1319 TRACE("[%p] PGF_SCROLLUP\n", hwnd);
1320 PAGER_Scroll(hwnd, PGF_SCROLLUP);
1321 break;
1322 case HTRIGHT:
1323 TRACE("[%p] PGF_SCROLLRIGHT\n", hwnd);
1324 PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1325 break;
1326 case HTBOTTOM:
1327 TRACE("[%p] PGF_SCROLLDOWN\n", hwnd);
1328 PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1329 break;
1330 default:
1331 break;
1334 return TRUE;
1337 static LRESULT
1338 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1340 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1341 TRACE("[%p]\n", hwnd);
1343 KillTimer (hwnd, TIMERID1);
1344 KillTimer (hwnd, TIMERID2);
1346 /* make PRESSED btns NORMAL but don't hide gray btns */
1347 PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1349 return 0;
1352 static LRESULT
1353 PAGER_NCLButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1355 POINT pt;
1357 pt.x = (short)LOWORD(lParam);
1358 pt.y = (short)HIWORD(lParam);
1360 TRACE("[%p] at (%ld,%ld)\n", hwnd, pt.x, pt.y );
1361 MapWindowPoints(0, hwnd, &pt, 1);
1362 lParam = MAKELONG(pt.x, pt.y);
1363 return PAGER_LButtonDown (hwnd, wParam, lParam);
1366 static LRESULT
1367 PAGER_Timer (HWND hwnd, WPARAM wParam)
1369 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1370 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1371 INT dir;
1373 /* if initial timer, kill it and start the repeat timer */
1374 if (wParam == TIMERID1) {
1375 if (PAGER_IsHorizontal(hwnd)) {
1376 dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ?
1377 PGF_SCROLLLEFT : PGF_SCROLLRIGHT;
1379 else {
1380 dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ?
1381 PGF_SCROLLUP : PGF_SCROLLDOWN;
1383 TRACE("[%p] TIMERID1: style=%08lx, dir=%d\n", hwnd, dwStyle, dir);
1384 KillTimer(hwnd, TIMERID1);
1385 SetTimer(hwnd, TIMERID1, REPEAT_DELAY, 0);
1386 if (dwStyle & PGS_AUTOSCROLL) {
1387 PAGER_Scroll(hwnd, dir);
1388 SetWindowPos(hwnd, 0,0,0,0,0,
1389 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1390 SWP_NOZORDER | SWP_NOACTIVATE);
1392 return 0;
1396 TRACE("[%p] TIMERID2: dir=%d\n", hwnd, infoPtr->direction);
1397 KillTimer(hwnd, TIMERID2);
1398 if (infoPtr->direction > 0) {
1399 PAGER_Scroll(hwnd, infoPtr->direction);
1400 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1402 return 0;
1405 static LRESULT
1406 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1408 POINT pt, ptorig;
1409 HDC hdc = (HDC)wParam;
1410 HWND parent;
1412 /* native does:
1413 * parent = GetParent(pager)
1414 * pt.x=0; pt.y=0; ?????
1415 * MapWindowPoints(pager, parent, &pt, 1)
1416 * OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg)
1417 * SendMessageA(parent, WM_ERASEBKGND, hdc, 0)
1418 * SetWindowOrgEx(hdc, 0, 0, 0)
1421 pt.x = 0;
1422 pt.y = 0;
1423 parent = GetParent(hwnd);
1424 MapWindowPoints(hwnd, parent, &pt, 1);
1425 OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1426 SendMessageA (parent, WM_ERASEBKGND, wParam, lParam);
1427 SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1430 #if 0
1431 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1432 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1433 RECT rect;
1435 GetClientRect (hwnd, &rect);
1436 FillRect ((HDC)wParam, &rect, hBrush);
1438 /* background color of the child should be the same as the pager */
1439 if (infoPtr->hwndChild)
1441 GetClientRect (infoPtr->hwndChild, &rect);
1442 FillRect ((HDC)wParam, &rect, hBrush);
1445 DeleteObject (hBrush);
1446 #endif
1448 return TRUE;
1452 static LRESULT
1453 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1455 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1457 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1458 TRACE("[%p] %dx%d\n", hwnd, (short)LOWORD(lParam), (short)HIWORD(lParam));
1460 if (PAGER_IsHorizontal(hwnd))
1461 infoPtr->nHeight = (short)HIWORD(lParam);
1462 else
1463 infoPtr->nWidth = (short)LOWORD(lParam);
1465 return PAGER_RecalcSize(hwnd);
1469 static LRESULT WINAPI
1470 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1472 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1474 if (!infoPtr && (uMsg != WM_CREATE))
1475 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1477 switch (uMsg)
1479 case EM_FMTLINES:
1480 return PAGER_FmtLines(hwnd);
1482 case PGM_FORWARDMOUSE:
1483 return PAGER_ForwardMouse (hwnd, wParam);
1485 case PGM_GETBKCOLOR:
1486 return PAGER_GetBkColor(hwnd);
1488 case PGM_GETBORDER:
1489 return PAGER_GetBorder(hwnd);
1491 case PGM_GETBUTTONSIZE:
1492 return PAGER_GetButtonSize(hwnd);
1494 case PGM_GETPOS:
1495 return PAGER_GetPos(hwnd);
1497 case PGM_GETBUTTONSTATE:
1498 return PAGER_GetButtonState (hwnd, wParam, lParam);
1500 /* case PGM_GETDROPTARGET: */
1502 case PGM_RECALCSIZE:
1503 return PAGER_RecalcSize(hwnd);
1505 case PGM_SETBKCOLOR:
1506 return PAGER_SetBkColor (hwnd, wParam, lParam);
1508 case PGM_SETBORDER:
1509 return PAGER_SetBorder (hwnd, wParam, lParam);
1511 case PGM_SETBUTTONSIZE:
1512 return PAGER_SetButtonSize (hwnd, wParam, lParam);
1514 case PGM_SETCHILD:
1515 return PAGER_SetChild (hwnd, wParam, lParam);
1517 case PGM_SETPOS:
1518 return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1520 case WM_CREATE:
1521 return PAGER_Create (hwnd, wParam, lParam);
1523 case WM_DESTROY:
1524 return PAGER_Destroy (hwnd, wParam, lParam);
1526 case WM_SIZE:
1527 return PAGER_Size (hwnd, wParam, lParam);
1529 case WM_NCPAINT:
1530 return PAGER_NCPaint (hwnd, wParam, lParam);
1532 case WM_WINDOWPOSCHANGING:
1533 return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam);
1535 case WM_NCCALCSIZE:
1536 return PAGER_NCCalcSize (hwnd, wParam, lParam);
1538 case WM_NCHITTEST:
1539 return PAGER_NCHitTest (hwnd, wParam, lParam);
1541 case WM_SETCURSOR:
1543 if (hwnd == (HWND)wParam)
1544 return PAGER_SetCursor(hwnd, wParam, lParam);
1545 else /* its for the child */
1546 return 0;
1549 case WM_MOUSEMOVE:
1550 if (infoPtr->bForward && infoPtr->hwndChild)
1551 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1552 return PAGER_MouseMove (hwnd, wParam, lParam);
1554 case WM_MOUSELEAVE:
1555 return PAGER_MouseLeave (hwnd, wParam, lParam);
1557 case WM_NCLBUTTONDOWN:
1558 return PAGER_NCLButtonDown (hwnd, wParam, lParam);
1560 case WM_LBUTTONDOWN:
1561 return PAGER_LButtonDown (hwnd, wParam, lParam);
1563 case WM_NCLBUTTONUP:
1564 case WM_LBUTTONUP:
1565 return PAGER_LButtonUp (hwnd, wParam, lParam);
1567 case WM_ERASEBKGND:
1568 return PAGER_EraseBackground (hwnd, wParam, lParam);
1570 case WM_PAINT:
1571 return PAGER_Paint (hwnd, wParam);
1573 case WM_TIMER:
1574 return PAGER_Timer (hwnd, wParam);
1576 case WM_NOTIFY:
1577 case WM_COMMAND:
1578 return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1580 default:
1581 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1584 return 0;
1588 VOID
1589 PAGER_Register (void)
1591 WNDCLASSA wndClass;
1593 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1594 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1595 wndClass.lpfnWndProc = (WNDPROC)PAGER_WindowProc;
1596 wndClass.cbClsExtra = 0;
1597 wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1598 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
1599 wndClass.hbrBackground = 0;
1600 wndClass.lpszClassName = WC_PAGESCROLLERA;
1602 RegisterClassA (&wndClass);
1606 VOID
1607 PAGER_Unregister (void)
1609 UnregisterClassA (WC_PAGESCROLLERA, NULL);