reshuffling of dlls
[reactos.git] / reactos / dll / win32 / comctl32 / pager.c
1 /*
2 * Pager control
3 *
4 * Copyright 1998, 1999 Eric Kohl
5 *
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.
10 *
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.
15 *
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
19 *
20 * NOTES
21 *
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.
24 *
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.
28 *
29 * TODO:
30 * Implement repetitive button press.
31 * Adjust arrow size relative to size of button.
32 * Allow border size changes.
33 * Styles:
34 * PGS_DRAGNDROP
35 * Notifications:
36 * PGN_HOTITEMCHANGE
37 * Messages:
38 * WM_PRINT and/or WM_PRINTCLIENT
39 *
40 * TESTING:
41 * Tested primarily with the controlspy Pager application.
42 * Susan Farley (susan@codeweavers.com)
43 *
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>
53 */
54
55 #include <stdarg.h>
56 #include <string.h>
57 #include "windef.h"
58 #include "winbase.h"
59 #include "wingdi.h"
60 #include "winuser.h"
61 #include "winnls.h"
62 #include "windowsx.h"
63 #include "commctrl.h"
64 #include "comctl32.h"
65 #include "wine/debug.h"
66
67 WINE_DEFAULT_DEBUG_CHANNEL(pager);
68
69 typedef struct
70 {
71 HWND hwndSelf; /* handle of the control wnd */
72 HWND hwndChild; /* handle of the contained wnd */
73 HWND hwndNotify; /* handle of the parent wnd */
74 DWORD dwStyle; /* styles for this control */
75 COLORREF clrBk; /* background color */
76 INT nBorder; /* border size for the control */
77 INT nButtonSize;/* size of the pager btns */
78 INT nPos; /* scroll position */
79 INT nWidth; /* from child wnd's response to PGN_CALCSIZE */
80 INT nHeight; /* from child wnd's response to PGN_CALCSIZE */
81 BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */
82 BOOL bCapture; /* we have captured the mouse */
83 INT TLbtnState; /* state of top or left btn */
84 INT BRbtnState; /* state of bottom or right btn */
85 INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
86 } PAGER_INFO;
87
88 #define MIN_ARROW_WIDTH 8
89 #define MIN_ARROW_HEIGHT 5
90
91 #define TIMERID1 1
92 #define TIMERID2 2
93 #define INITIAL_DELAY 500
94 #define REPEAT_DELAY 50
95
96 static void
97 PAGER_GetButtonRects(PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
98 {
99 RECT rcWindow;
100 GetWindowRect (infoPtr->hwndSelf, &rcWindow);
101
102 if (bClientCoords)
103 {
104 POINT pt = {rcWindow.left, rcWindow.top};
105 ScreenToClient(infoPtr->hwndSelf, &pt);
106 OffsetRect(&rcWindow, -(rcWindow.left-pt.x), -(rcWindow.top-pt.y));
107 }
108 else
109 OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
110
111 *prcTopLeft = *prcBottomRight = rcWindow;
112 if (infoPtr->dwStyle & PGS_HORZ)
113 {
114 prcTopLeft->right = prcTopLeft->left + infoPtr->nButtonSize;
115 prcBottomRight->left = prcBottomRight->right - infoPtr->nButtonSize;
116 }
117 else
118 {
119 prcTopLeft->bottom = prcTopLeft->top + infoPtr->nButtonSize;
120 prcBottomRight->top = prcBottomRight->bottom - infoPtr->nButtonSize;
121 }
122 }
123
124 /* the horizontal arrows are:
125 *
126 * 01234 01234
127 * 1 * *
128 * 2 ** **
129 * 3*** ***
130 * 4*** ***
131 * 5 ** **
132 * 6 * *
133 * 7
134 *
135 */
136 static void
137 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
138 {
139 INT x, y, w, h;
140 HPEN hPen, hOldPen;
141
142 w = r.right - r.left + 1;
143 h = r.bottom - r.top + 1;
144 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
145 return; /* refuse to draw partial arrow */
146
147 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
148 hOldPen = SelectObject ( hdc, hPen );
149 if (left)
150 {
151 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
152 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
153 MoveToEx (hdc, x, y, NULL);
154 LineTo (hdc, x--, y+5); y++;
155 MoveToEx (hdc, x, y, NULL);
156 LineTo (hdc, x--, y+3); y++;
157 MoveToEx (hdc, x, y, NULL);
158 LineTo (hdc, x, y+1);
159 }
160 else
161 {
162 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
163 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
164 MoveToEx (hdc, x, y, NULL);
165 LineTo (hdc, x++, y+5); y++;
166 MoveToEx (hdc, x, y, NULL);
167 LineTo (hdc, x++, y+3); y++;
168 MoveToEx (hdc, x, y, NULL);
169 LineTo (hdc, x, y+1);
170 }
171
172 SelectObject( hdc, hOldPen );
173 DeleteObject( hPen );
174 }
175
176 /* the vertical arrows are:
177 *
178 * 01234567 01234567
179 * 1****** **
180 * 2 **** ****
181 * 3 ** ******
182 * 4
183 *
184 */
185 static void
186 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
187 {
188 INT x, y, w, h;
189 HPEN hPen, hOldPen;
190
191 w = r.right - r.left + 1;
192 h = r.bottom - r.top + 1;
193 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
194 return; /* refuse to draw partial arrow */
195
196 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
197 hOldPen = SelectObject ( hdc, hPen );
198 if (up)
199 {
200 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
201 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
202 MoveToEx (hdc, x, y, NULL);
203 LineTo (hdc, x+5, y--); x++;
204 MoveToEx (hdc, x, y, NULL);
205 LineTo (hdc, x+3, y--); x++;
206 MoveToEx (hdc, x, y, NULL);
207 LineTo (hdc, x+1, y);
208 }
209 else
210 {
211 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
212 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
213 MoveToEx (hdc, x, y, NULL);
214 LineTo (hdc, x+5, y++); x++;
215 MoveToEx (hdc, x, y, NULL);
216 LineTo (hdc, x+3, y++); x++;
217 MoveToEx (hdc, x, y, NULL);
218 LineTo (hdc, x+1, y);
219 }
220
221 SelectObject( hdc, hOldPen );
222 DeleteObject( hPen );
223 }
224
225 static void
226 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
227 BOOL horz, BOOL topLeft, INT btnState)
228 {
229 HBRUSH hBrush, hOldBrush;
230 RECT rc = arrowRect;
231
232 TRACE("arrowRect = %s, btnState = %d\n", wine_dbgstr_rect(&arrowRect), btnState);
233
234 if (btnState == PGF_INVISIBLE)
235 return;
236
237 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
238 return;
239
240 hBrush = CreateSolidBrush(clrBk);
241 hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
242
243 FillRect(hdc, &rc, hBrush);
244
245 if (btnState == PGF_HOT)
246 {
247 DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
248 if (horz)
249 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
250 else
251 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
252 }
253 else if (btnState == PGF_NORMAL)
254 {
255 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
256 if (horz)
257 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
258 else
259 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
260 }
261 else if (btnState == PGF_DEPRESSED)
262 {
263 DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
264 if (horz)
265 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
266 else
267 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
268 }
269 else if (btnState == PGF_GRAYED)
270 {
271 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
272 if (horz)
273 {
274 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
275 rc.left++, rc.top++; rc.right++, rc.bottom++;
276 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
277 }
278 else
279 {
280 PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
281 rc.left++, rc.top++; rc.right++, rc.bottom++;
282 PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
283 }
284 }
285
286 SelectObject( hdc, hOldBrush );
287 DeleteObject(hBrush);
288 }
289
290 /* << PAGER_GetDropTarget >> */
291
292 static inline LRESULT
293 PAGER_ForwardMouse (PAGER_INFO* infoPtr, BOOL bFwd)
294 {
295 TRACE("[%p]\n", infoPtr->hwndSelf);
296
297 infoPtr->bForward = bFwd;
298
299 return 0;
300 }
301
302 static inline LRESULT
303 PAGER_GetButtonState (PAGER_INFO* infoPtr, INT btn)
304 {
305 LRESULT btnState = PGF_INVISIBLE;
306 TRACE("[%p]\n", infoPtr->hwndSelf);
307
308 if (btn == PGB_TOPORLEFT)
309 btnState = infoPtr->TLbtnState;
310 else if (btn == PGB_BOTTOMORRIGHT)
311 btnState = infoPtr->BRbtnState;
312
313 return btnState;
314 }
315
316
317 static inline INT
318 PAGER_GetPos(PAGER_INFO *infoPtr)
319 {
320 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nPos);
321 return infoPtr->nPos;
322 }
323
324 static inline INT
325 PAGER_GetButtonSize(PAGER_INFO *infoPtr)
326 {
327 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
328 return infoPtr->nButtonSize;
329 }
330
331 static inline INT
332 PAGER_GetBorder(PAGER_INFO *infoPtr)
333 {
334 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
335 return infoPtr->nBorder;
336 }
337
338 static inline COLORREF
339 PAGER_GetBkColor(PAGER_INFO *infoPtr)
340 {
341 TRACE("[%p] returns %06lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
342 return infoPtr->clrBk;
343 }
344
345 static void
346 PAGER_CalcSize (PAGER_INFO *infoPtr, INT* size, BOOL getWidth)
347 {
348 NMPGCALCSIZE nmpgcs;
349 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
350 nmpgcs.hdr.hwndFrom = infoPtr->hwndSelf;
351 nmpgcs.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
352 nmpgcs.hdr.code = PGN_CALCSIZE;
353 nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
354 nmpgcs.iWidth = getWidth ? *size : 0;
355 nmpgcs.iHeight = getWidth ? 0 : *size;
356 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
357 (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
358
359 *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
360
361 TRACE("[%p] PGN_CALCSIZE returns %s=%d\n", infoPtr->hwndSelf,
362 getWidth ? "width" : "height", *size);
363 }
364
365 static void
366 PAGER_PositionChildWnd(PAGER_INFO* infoPtr)
367 {
368 if (infoPtr->hwndChild)
369 {
370 RECT rcClient;
371 int nPos = infoPtr->nPos;
372
373 /* compensate for a grayed btn, which will soon become invisible */
374 if (infoPtr->TLbtnState == PGF_GRAYED)
375 nPos += infoPtr->nButtonSize;
376
377 GetClientRect(infoPtr->hwndSelf, &rcClient);
378
379 if (infoPtr->dwStyle & PGS_HORZ)
380 {
381 int wndSize = max(0, rcClient.right - rcClient.left);
382 if (infoPtr->nWidth < wndSize)
383 infoPtr->nWidth = wndSize;
384
385 TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
386 infoPtr->nWidth, infoPtr->nHeight,
387 -nPos, 0);
388 SetWindowPos(infoPtr->hwndChild, 0,
389 -nPos, 0,
390 infoPtr->nWidth, infoPtr->nHeight,
391 SWP_NOZORDER);
392 }
393 else
394 {
395 int wndSize = max(0, rcClient.bottom - rcClient.top);
396 if (infoPtr->nHeight < wndSize)
397 infoPtr->nHeight = wndSize;
398
399 TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
400 infoPtr->nWidth, infoPtr->nHeight,
401 0, -nPos);
402 SetWindowPos(infoPtr->hwndChild, 0,
403 0, -nPos,
404 infoPtr->nWidth, infoPtr->nHeight,
405 SWP_NOZORDER);
406 }
407
408 InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
409 }
410 }
411
412 static INT
413 PAGER_GetScrollRange(PAGER_INFO* infoPtr)
414 {
415 INT scrollRange = 0;
416
417 if (infoPtr->hwndChild)
418 {
419 INT wndSize, childSize;
420 RECT wndRect;
421 GetWindowRect(infoPtr->hwndSelf, &wndRect);
422
423 if (infoPtr->dwStyle & PGS_HORZ)
424 {
425 wndSize = wndRect.right - wndRect.left;
426 PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
427 childSize = infoPtr->nWidth;
428 }
429 else
430 {
431 wndSize = wndRect.bottom - wndRect.top;
432 PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
433 childSize = infoPtr->nHeight;
434 }
435
436 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
437 if (childSize > wndSize)
438 scrollRange = childSize - wndSize + infoPtr->nButtonSize;
439 }
440
441 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange);
442 return scrollRange;
443 }
444
445 static void
446 PAGER_UpdateBtns(PAGER_INFO *infoPtr, INT scrollRange, BOOL hideGrayBtns)
447 {
448 BOOL resizeClient;
449 BOOL repaintBtns;
450 INT oldTLbtnState = infoPtr->TLbtnState;
451 INT oldBRbtnState = infoPtr->BRbtnState;
452 POINT pt;
453 RECT rcTopLeft, rcBottomRight;
454
455 /* get button rects */
456 PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
457
458 GetCursorPos(&pt);
459
460 /* update states based on scroll position */
461 if (infoPtr->nPos > 0)
462 {
463 if (infoPtr->TLbtnState == PGF_INVISIBLE || infoPtr->TLbtnState == PGF_GRAYED)
464 infoPtr->TLbtnState = PGF_NORMAL;
465 }
466 else if (PtInRect(&rcTopLeft, pt))
467 infoPtr->TLbtnState = PGF_GRAYED;
468 else
469 infoPtr->TLbtnState = PGF_INVISIBLE;
470
471 if (scrollRange <= 0)
472 {
473 infoPtr->TLbtnState = PGF_INVISIBLE;
474 infoPtr->BRbtnState = PGF_INVISIBLE;
475 }
476 else if (infoPtr->nPos < scrollRange)
477 {
478 if (infoPtr->BRbtnState == PGF_INVISIBLE || infoPtr->BRbtnState == PGF_GRAYED)
479 infoPtr->BRbtnState = PGF_NORMAL;
480 }
481 else if (PtInRect(&rcBottomRight, pt))
482 infoPtr->BRbtnState = PGF_GRAYED;
483 else
484 infoPtr->BRbtnState = PGF_INVISIBLE;
485
486 /* only need to resize when entering or leaving PGF_INVISIBLE state */
487 resizeClient =
488 ((oldTLbtnState == PGF_INVISIBLE) != (infoPtr->TLbtnState == PGF_INVISIBLE)) ||
489 ((oldBRbtnState == PGF_INVISIBLE) != (infoPtr->BRbtnState == PGF_INVISIBLE));
490 /* initiate NCCalcSize to resize client wnd if necessary */
491 if (resizeClient)
492 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
493 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
494 SWP_NOZORDER | SWP_NOACTIVATE);
495
496 /* repaint when changing any state */
497 repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) ||
498 (oldBRbtnState != infoPtr->BRbtnState);
499 if (repaintBtns)
500 SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
501 }
502
503 static LRESULT
504 PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress)
505 {
506 INT scrollRange = PAGER_GetScrollRange(infoPtr);
507 INT oldPos = infoPtr->nPos;
508
509 if ((scrollRange <= 0) || (newPos < 0))
510 infoPtr->nPos = 0;
511 else if (newPos > scrollRange)
512 infoPtr->nPos = scrollRange;
513 else
514 infoPtr->nPos = newPos;
515
516 TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr->hwndSelf, infoPtr->nPos, oldPos);
517
518 if (infoPtr->nPos != oldPos)
519 {
520 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
521 PAGER_UpdateBtns(infoPtr, scrollRange, !fromBtnPress);
522 PAGER_PositionChildWnd(infoPtr);
523 }
524
525 return 0;
526 }
527
528 static LRESULT
529 PAGER_WindowPosChanging(PAGER_INFO* infoPtr, WINDOWPOS *winpos)
530 {
531 if ((infoPtr->dwStyle & CCS_NORESIZE) && !(winpos->flags & SWP_NOSIZE))
532 {
533 /* don't let the app resize the nonscrollable dimension of a control
534 * that was created with CCS_NORESIZE style
535 * (i.e. height for a horizontal pager, or width for a vertical one) */
536
537 /* except if the current dimension is 0 and app is setting for
538 * first time, then save amount as dimension. - GA 8/01 */
539
540 if (infoPtr->dwStyle & PGS_HORZ)
541 if (!infoPtr->nHeight && winpos->cy)
542 infoPtr->nHeight = winpos->cy;
543 else
544 winpos->cy = infoPtr->nHeight;
545 else
546 if (!infoPtr->nWidth && winpos->cx)
547 infoPtr->nWidth = winpos->cx;
548 else
549 winpos->cx = infoPtr->nWidth;
550 return 0;
551 }
552
553 return DefWindowProcW (infoPtr->hwndSelf, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos);
554 }
555
556 static INT
557 PAGER_SetFixedWidth(PAGER_INFO* infoPtr)
558 {
559 /* Must set the non-scrollable dimension to be less than the full height/width
560 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
561 * size, and experimentation shows that affect is almost right. */
562
563 RECT wndRect;
564 INT delta, h;
565 GetWindowRect(infoPtr->hwndSelf, &wndRect);
566
567 /* see what the app says for btn width */
568 PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
569
570 if (infoPtr->dwStyle & CCS_NORESIZE)
571 {
572 delta = wndRect.right - wndRect.left - infoPtr->nWidth;
573 if (delta > infoPtr->nButtonSize)
574 infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
575 else if (delta > 0)
576 infoPtr->nWidth += infoPtr->nButtonSize / 3;
577 }
578
579 h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
580
581 TRACE("[%p] infoPtr->nWidth set to %d\n",
582 infoPtr->hwndSelf, infoPtr->nWidth);
583
584 return h;
585 }
586
587 static INT
588 PAGER_SetFixedHeight(PAGER_INFO* infoPtr)
589 {
590 /* Must set the non-scrollable dimension to be less than the full height/width
591 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
592 * size, and experimentation shows that affect is almost right. */
593
594 RECT wndRect;
595 INT delta, w;
596 GetWindowRect(infoPtr->hwndSelf, &wndRect);
597
598 /* see what the app says for btn height */
599 PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
600
601 if (infoPtr->dwStyle & CCS_NORESIZE)
602 {
603 delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
604 if (delta > infoPtr->nButtonSize)
605 infoPtr->nHeight += infoPtr->nButtonSize;
606 else if (delta > 0)
607 infoPtr->nHeight += infoPtr->nButtonSize / 3;
608 }
609
610 w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
611
612 TRACE("[%p] infoPtr->nHeight set to %d\n",
613 infoPtr->hwndSelf, infoPtr->nHeight);
614
615 return w;
616 }
617
618 /******************************************************************
619 * For the PGM_RECALCSIZE message (but not the other uses in *
620 * this module), the native control does only the following: *
621 * *
622 * if (some condition) *
623 * PostMessageW(hwnd, EM_FMTLINES, 0, 0); *
624 * return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0); *
625 * *
626 * When we figure out what the "some condition" is we will *
627 * implement that for the message processing. *
628 ******************************************************************/
629
630 static LRESULT
631 PAGER_RecalcSize(PAGER_INFO *infoPtr)
632 {
633 TRACE("[%p]\n", infoPtr->hwndSelf);
634
635 if (infoPtr->hwndChild)
636 {
637 INT scrollRange = PAGER_GetScrollRange(infoPtr);
638
639 if (scrollRange <= 0)
640 {
641 infoPtr->nPos = -1;
642 PAGER_SetPos(infoPtr, 0, FALSE);
643 }
644 else
645 PAGER_PositionChildWnd(infoPtr);
646 }
647
648 return 1;
649 }
650
651
652 static COLORREF
653 PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk)
654 {
655 COLORREF clrTemp = infoPtr->clrBk;
656
657 infoPtr->clrBk = clrBk;
658 TRACE("[%p] %06lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
659
660 /* the native control seems to do things this way */
661 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
662 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
663 SWP_NOZORDER | SWP_NOACTIVATE);
664
665 RedrawWindow(infoPtr->hwndSelf, 0, 0, RDW_ERASE | RDW_INVALIDATE);
666
667 return clrTemp;
668 }
669
670
671 static INT
672 PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder)
673 {
674 INT nTemp = infoPtr->nBorder;
675
676 infoPtr->nBorder = iBorder;
677 TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
678
679 PAGER_RecalcSize(infoPtr);
680
681 return nTemp;
682 }
683
684
685 static INT
686 PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize)
687 {
688 INT nTemp = infoPtr->nButtonSize;
689
690 infoPtr->nButtonSize = iButtonSize;
691 TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
692
693 PAGER_RecalcSize(infoPtr);
694
695 return nTemp;
696 }
697
698
699 static LRESULT
700 PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild)
701 {
702 INT hw;
703
704 infoPtr->hwndChild = IsWindow (hwndChild) ? hwndChild : 0;
705
706 if (infoPtr->hwndChild)
707 {
708 TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild);
709
710 if (infoPtr->dwStyle & PGS_HORZ) {
711 hw = PAGER_SetFixedHeight(infoPtr);
712 /* adjust non-scrollable dimension to fit the child */
713 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, hw, infoPtr->nHeight,
714 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
715 SWP_NOSIZE | SWP_NOACTIVATE);
716 }
717 else {
718 hw = PAGER_SetFixedWidth(infoPtr);
719 /* adjust non-scrollable dimension to fit the child */
720 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, infoPtr->nWidth, hw,
721 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
722 SWP_NOSIZE | SWP_NOACTIVATE);
723 }
724
725 /* position child within the page scroller */
726 SetWindowPos(infoPtr->hwndChild, HWND_TOP,
727 0,0,0,0,
728 SWP_SHOWWINDOW | SWP_NOSIZE); /* native is 0 */
729
730 infoPtr->nPos = -1;
731 PAGER_SetPos(infoPtr, 0, FALSE);
732 }
733
734 return 0;
735 }
736
737 static void
738 PAGER_Scroll(PAGER_INFO* infoPtr, INT dir)
739 {
740 NMPGSCROLL nmpgScroll;
741 RECT rcWnd;
742
743 if (infoPtr->hwndChild)
744 {
745 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
746 nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf;
747 nmpgScroll.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
748 nmpgScroll.hdr.code = PGN_SCROLL;
749
750 GetWindowRect(infoPtr->hwndSelf, &rcWnd);
751 GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent);
752 nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
753 nmpgScroll.iDir = dir;
754
755 if (infoPtr->dwStyle & PGS_HORZ)
756 {
757 nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
758 nmpgScroll.iXpos = infoPtr->nPos;
759 }
760 else
761 {
762 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
763 nmpgScroll.iYpos = infoPtr->nPos;
764 }
765 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
766
767 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
768 (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
769
770 TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr->hwndSelf, nmpgScroll.iScroll);
771
772 if (nmpgScroll.iScroll > 0)
773 {
774 infoPtr->direction = dir;
775
776 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
777 PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
778 else
779 PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
780 }
781 else
782 infoPtr->direction = -1;
783 }
784 }
785
786 static LRESULT
787 PAGER_FmtLines(PAGER_INFO *infoPtr)
788 {
789 /* initiate NCCalcSize to resize client wnd and get size */
790 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
791 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
792 SWP_NOZORDER | SWP_NOACTIVATE);
793
794 SetWindowPos(infoPtr->hwndChild, 0,
795 0,0,infoPtr->nWidth,infoPtr->nHeight,
796 0);
797
798 return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0);
799 }
800
801 static LRESULT
802 PAGER_Create (HWND hwnd, LPCREATESTRUCTW lpcs)
803 {
804 PAGER_INFO *infoPtr;
805
806 /* allocate memory for info structure */
807 infoPtr = (PAGER_INFO *)Alloc (sizeof(PAGER_INFO));
808 if (!infoPtr) return -1;
809 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
810
811 /* set default settings */
812 infoPtr->hwndSelf = hwnd;
813 infoPtr->hwndChild = NULL;
814 infoPtr->hwndNotify = lpcs->hwndParent;
815 infoPtr->dwStyle = lpcs->style;
816 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
817 infoPtr->nBorder = 0;
818 infoPtr->nButtonSize = 12;
819 infoPtr->nPos = 0;
820 infoPtr->nWidth = 0;
821 infoPtr->nHeight = 0;
822 infoPtr->bForward = FALSE;
823 infoPtr->bCapture = FALSE;
824 infoPtr->TLbtnState = PGF_INVISIBLE;
825 infoPtr->BRbtnState = PGF_INVISIBLE;
826 infoPtr->direction = -1;
827
828 if (infoPtr->dwStyle & PGS_DRAGNDROP)
829 FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf);
830
831 return 0;
832 }
833
834
835 static LRESULT
836 PAGER_Destroy (PAGER_INFO *infoPtr)
837 {
838 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
839 Free (infoPtr); /* free pager info data */
840 return 0;
841 }
842
843 static LRESULT
844 PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect)
845 {
846 RECT rcChild, rcWindow;
847 INT scrollRange;
848
849 /*
850 * lpRect points to a RECT struct. On entry, the struct
851 * contains the proposed wnd rectangle for the window.
852 * On exit, the struct should contain the screen
853 * coordinates of the corresponding window's client area.
854 */
855
856 DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect);
857
858 TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect));
859
860 GetWindowRect (infoPtr->hwndChild, &rcChild);
861 MapWindowPoints (0, infoPtr->hwndSelf, (LPPOINT)&rcChild, 2); /* FIXME: RECT != 2 POINTS */
862 GetWindowRect (infoPtr->hwndSelf, &rcWindow);
863
864 if (infoPtr->dwStyle & PGS_HORZ)
865 {
866 infoPtr->nWidth = lpRect->right - lpRect->left;
867 PAGER_CalcSize (infoPtr, &infoPtr->nWidth, TRUE);
868
869 scrollRange = infoPtr->nWidth - (rcWindow.right - rcWindow.left);
870
871 if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
872 lpRect->left += infoPtr->nButtonSize;
873 if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
874 lpRect->right -= infoPtr->nButtonSize;
875 }
876 else
877 {
878 infoPtr->nHeight = lpRect->bottom - lpRect->top;
879 PAGER_CalcSize (infoPtr, &infoPtr->nHeight, FALSE);
880
881 scrollRange = infoPtr->nHeight - (rcWindow.bottom - rcWindow.top);
882
883 if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
884 lpRect->top += infoPtr->nButtonSize;
885 if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
886 lpRect->bottom -= infoPtr->nButtonSize;
887 }
888
889 TRACE("nPos=%d, nHeigth=%d, window=%s\n",
890 infoPtr->nPos, infoPtr->nHeight,
891 wine_dbgstr_rect(&rcWindow));
892
893 TRACE("[%p] client rect set to %ldx%ld at (%ld,%ld) BtnState[%d,%d]\n",
894 infoPtr->hwndSelf, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
895 lpRect->left, lpRect->top,
896 infoPtr->TLbtnState, infoPtr->BRbtnState);
897
898 return 0;
899 }
900
901 static LRESULT
902 PAGER_NCPaint (PAGER_INFO* infoPtr, HRGN hRgn)
903 {
904 RECT rcBottomRight, rcTopLeft;
905 HDC hdc;
906
907 if (infoPtr->dwStyle & WS_MINIMIZE)
908 return 0;
909
910 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0);
911
912 if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW)))
913 return 0;
914
915 PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
916
917 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
918 infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState);
919 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
920 infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState);
921
922 ReleaseDC( infoPtr->hwndSelf, hdc );
923 return 0;
924 }
925
926 static INT
927 PAGER_HitTest (PAGER_INFO* infoPtr, const POINT * pt)
928 {
929 RECT clientRect, rcTopLeft, rcBottomRight;
930 POINT ptWindow;
931
932 GetClientRect (infoPtr->hwndSelf, &clientRect);
933
934 if (PtInRect(&clientRect, *pt))
935 {
936 TRACE("child\n");
937 return -1;
938 }
939
940 ptWindow = *pt;
941 PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
942
943 if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow))
944 {
945 TRACE("PGB_TOPORLEFT\n");
946 return PGB_TOPORLEFT;
947 }
948 else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow))
949 {
950 TRACE("PGB_BOTTOMORRIGHT\n");
951 return PGB_BOTTOMORRIGHT;
952 }
953
954 TRACE("nowhere\n");
955 return -1;
956 }
957
958 static LRESULT
959 PAGER_NCHitTest (PAGER_INFO* infoPtr, INT x, INT y)
960 {
961 POINT pt;
962 INT nHit;
963
964 pt.x = x;
965 pt.y = y;
966
967 ScreenToClient (infoPtr->hwndSelf, &pt);
968 nHit = PAGER_HitTest(infoPtr, &pt);
969
970 return (nHit < 0) ? HTTRANSPARENT : HTCLIENT;
971 }
972
973 static LRESULT
974 PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
975 {
976 POINT clpt, pt;
977 RECT wnrect, *btnrect = NULL;
978 BOOL topLeft = FALSE;
979 INT btnstate = 0;
980 INT hit;
981 HDC hdc;
982
983 pt.x = x;
984 pt.y = y;
985
986 TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y);
987 ClientToScreen(infoPtr->hwndSelf, &pt);
988 GetWindowRect(infoPtr->hwndSelf, &wnrect);
989 if (PtInRect(&wnrect, pt)) {
990 RECT TLbtnrect, BRbtnrect;
991 PAGER_GetButtonRects(infoPtr, &TLbtnrect, &BRbtnrect, FALSE);
992
993 clpt = pt;
994 MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1);
995 hit = PAGER_HitTest(infoPtr, &clpt);
996 if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL))
997 {
998 topLeft = TRUE;
999 btnrect = &TLbtnrect;
1000 infoPtr->TLbtnState = PGF_HOT;
1001 btnstate = infoPtr->TLbtnState;
1002 }
1003 else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL))
1004 {
1005 topLeft = FALSE;
1006 btnrect = &BRbtnrect;
1007 infoPtr->BRbtnState = PGF_HOT;
1008 btnstate = infoPtr->BRbtnState;
1009 }
1010
1011 /* If in one of the buttons the capture and draw buttons */
1012 if (btnrect)
1013 {
1014 TRACE("[%p] draw btn (%ld,%ld)-(%ld,%ld), Capture %s, style %08lx\n",
1015 infoPtr->hwndSelf, btnrect->left, btnrect->top,
1016 btnrect->right, btnrect->bottom,
1017 (infoPtr->bCapture) ? "TRUE" : "FALSE",
1018 infoPtr->dwStyle);
1019 if (!infoPtr->bCapture)
1020 {
1021 TRACE("[%p] SetCapture\n", infoPtr->hwndSelf);
1022 SetCapture(infoPtr->hwndSelf);
1023 infoPtr->bCapture = TRUE;
1024 }
1025 if (infoPtr->dwStyle & PGS_AUTOSCROLL)
1026 SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0);
1027 hdc = GetWindowDC(infoPtr->hwndSelf);
1028 /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
1029 PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1030 infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate);
1031 ReleaseDC(infoPtr->hwndSelf, hdc);
1032 return 0;
1033 }
1034 }
1035
1036 /* If we think we are captured, then do release */
1037 if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf))
1038 {
1039 NMHDR nmhdr;
1040
1041 infoPtr->bCapture = FALSE;
1042
1043 if (GetCapture() == infoPtr->hwndSelf)
1044 {
1045 ReleaseCapture();
1046
1047 if (infoPtr->TLbtnState == PGF_GRAYED)
1048 {
1049 infoPtr->TLbtnState = PGF_INVISIBLE;
1050 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1051 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1052 SWP_NOZORDER | SWP_NOACTIVATE);
1053 }
1054 else if (infoPtr->TLbtnState == PGF_HOT)
1055 {
1056 infoPtr->TLbtnState = PGF_NORMAL;
1057 /* FIXME: just invalidate button rect */
1058 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1059 }
1060
1061 if (infoPtr->BRbtnState == PGF_GRAYED)
1062 {
1063 infoPtr->BRbtnState = PGF_INVISIBLE;
1064 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1065 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1066 SWP_NOZORDER | SWP_NOACTIVATE);
1067 }
1068 else if (infoPtr->BRbtnState == PGF_HOT)
1069 {
1070 infoPtr->BRbtnState = PGF_NORMAL;
1071 /* FIXME: just invalidate button rect */
1072 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1073 }
1074
1075 /* Notify parent of released mouse capture */
1076 memset(&nmhdr, 0, sizeof(NMHDR));
1077 nmhdr.hwndFrom = infoPtr->hwndSelf;
1078 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1079 nmhdr.code = NM_RELEASEDCAPTURE;
1080 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
1081 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1082 }
1083 if (IsWindow(infoPtr->hwndSelf))
1084 KillTimer(infoPtr->hwndSelf, TIMERID1);
1085 }
1086 return 0;
1087 }
1088
1089 static LRESULT
1090 PAGER_LButtonDown (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
1091 {
1092 BOOL repaintBtns = FALSE;
1093 POINT pt;
1094 INT hit;
1095
1096 pt.x = x;
1097 pt.y = y;
1098
1099 TRACE("[%p] at (%d,%d)\n", infoPtr->hwndSelf, x, y);
1100
1101 hit = PAGER_HitTest(infoPtr, &pt);
1102
1103 /* put btn in DEPRESSED state */
1104 if (hit == PGB_TOPORLEFT)
1105 {
1106 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1107 infoPtr->TLbtnState = PGF_DEPRESSED;
1108 SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1109 }
1110 else if (hit == PGB_BOTTOMORRIGHT)
1111 {
1112 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1113 infoPtr->BRbtnState = PGF_DEPRESSED;
1114 SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1115 }
1116
1117 if (repaintBtns)
1118 SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
1119
1120 switch(hit)
1121 {
1122 case PGB_TOPORLEFT:
1123 if (infoPtr->dwStyle & PGS_HORZ)
1124 {
1125 TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr->hwndSelf);
1126 PAGER_Scroll(infoPtr, PGF_SCROLLLEFT);
1127 }
1128 else
1129 {
1130 TRACE("[%p] PGF_SCROLLUP\n", infoPtr->hwndSelf);
1131 PAGER_Scroll(infoPtr, PGF_SCROLLUP);
1132 }
1133 break;
1134 case PGB_BOTTOMORRIGHT:
1135 if (infoPtr->dwStyle & PGS_HORZ)
1136 {
1137 TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr->hwndSelf);
1138 PAGER_Scroll(infoPtr, PGF_SCROLLRIGHT);
1139 }
1140 else
1141 {
1142 TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr->hwndSelf);
1143 PAGER_Scroll(infoPtr, PGF_SCROLLDOWN);
1144 }
1145 break;
1146 default:
1147 break;
1148 }
1149
1150 return 0;
1151 }
1152
1153 static LRESULT
1154 PAGER_LButtonUp (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
1155 {
1156 TRACE("[%p]\n", infoPtr->hwndSelf);
1157
1158 KillTimer (infoPtr->hwndSelf, TIMERID1);
1159 KillTimer (infoPtr->hwndSelf, TIMERID2);
1160
1161 /* make PRESSED btns NORMAL but don't hide gray btns */
1162 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
1163 infoPtr->TLbtnState = PGF_NORMAL;
1164 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
1165 infoPtr->BRbtnState = PGF_NORMAL;
1166
1167 return 0;
1168 }
1169
1170 static LRESULT
1171 PAGER_Timer (PAGER_INFO* infoPtr, INT nTimerId)
1172 {
1173 INT dir;
1174
1175 /* if initial timer, kill it and start the repeat timer */
1176 if (nTimerId == TIMERID1) {
1177 if (infoPtr->TLbtnState == PGF_HOT)
1178 dir = (infoPtr->dwStyle & PGS_HORZ) ?
1179 PGF_SCROLLLEFT : PGF_SCROLLUP;
1180 else
1181 dir = (infoPtr->dwStyle & PGS_HORZ) ?
1182 PGF_SCROLLRIGHT : PGF_SCROLLDOWN;
1183 TRACE("[%p] TIMERID1: style=%08lx, dir=%d\n",
1184 infoPtr->hwndSelf, infoPtr->dwStyle, dir);
1185 KillTimer(infoPtr->hwndSelf, TIMERID1);
1186 SetTimer(infoPtr->hwndSelf, TIMERID1, REPEAT_DELAY, 0);
1187 if (infoPtr->dwStyle & PGS_AUTOSCROLL) {
1188 PAGER_Scroll(infoPtr, dir);
1189 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1190 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1191 SWP_NOZORDER | SWP_NOACTIVATE);
1192 }
1193 return 0;
1194
1195 }
1196
1197 TRACE("[%p] TIMERID2: dir=%d\n", infoPtr->hwndSelf, infoPtr->direction);
1198 KillTimer(infoPtr->hwndSelf, TIMERID2);
1199 if (infoPtr->direction > 0) {
1200 PAGER_Scroll(infoPtr, infoPtr->direction);
1201 SetTimer(infoPtr->hwndSelf, TIMERID2, REPEAT_DELAY, 0);
1202 }
1203 return 0;
1204 }
1205
1206 static LRESULT
1207 PAGER_EraseBackground (PAGER_INFO* infoPtr, HDC hdc)
1208 {
1209 POINT pt, ptorig;
1210 HWND parent;
1211
1212 pt.x = 0;
1213 pt.y = 0;
1214 parent = GetParent(infoPtr->hwndSelf);
1215 MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1);
1216 OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1217 SendMessageW (parent, WM_ERASEBKGND, (WPARAM)hdc, 0);
1218 SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1219
1220 return 0;
1221 }
1222
1223
1224 static LRESULT
1225 PAGER_Size (PAGER_INFO* infoPtr, INT type, INT x, INT y)
1226 {
1227 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1228
1229 TRACE("[%p] %d,%d\n", infoPtr->hwndSelf, x, y);
1230
1231 if (infoPtr->dwStyle & PGS_HORZ)
1232 infoPtr->nHeight = y;
1233 else
1234 infoPtr->nWidth = x;
1235
1236 return PAGER_RecalcSize(infoPtr);
1237 }
1238
1239
1240 static LRESULT
1241 PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, LPSTYLESTRUCT lpss)
1242 {
1243 DWORD oldStyle = infoPtr->dwStyle;
1244
1245 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
1246 wStyleType, lpss->styleOld, lpss->styleNew);
1247
1248 if (wStyleType != GWL_STYLE) return 0;
1249
1250 infoPtr->dwStyle = lpss->styleNew;
1251
1252 if ((oldStyle ^ lpss->styleNew) & (PGS_HORZ | PGS_VERT))
1253 {
1254 PAGER_RecalcSize(infoPtr);
1255 }
1256
1257 return 0;
1258 }
1259
1260 static LRESULT WINAPI
1261 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1262 {
1263 PAGER_INFO *infoPtr = (PAGER_INFO *)GetWindowLongPtrW(hwnd, 0);
1264
1265 if (!infoPtr && (uMsg != WM_CREATE))
1266 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1267
1268 switch (uMsg)
1269 {
1270 case EM_FMTLINES:
1271 return PAGER_FmtLines(infoPtr);
1272
1273 case PGM_FORWARDMOUSE:
1274 return PAGER_ForwardMouse (infoPtr, (BOOL)wParam);
1275
1276 case PGM_GETBKCOLOR:
1277 return PAGER_GetBkColor(infoPtr);
1278
1279 case PGM_GETBORDER:
1280 return PAGER_GetBorder(infoPtr);
1281
1282 case PGM_GETBUTTONSIZE:
1283 return PAGER_GetButtonSize(infoPtr);
1284
1285 case PGM_GETPOS:
1286 return PAGER_GetPos(infoPtr);
1287
1288 case PGM_GETBUTTONSTATE:
1289 return PAGER_GetButtonState (infoPtr, (INT)lParam);
1290
1291 /* case PGM_GETDROPTARGET: */
1292
1293 case PGM_RECALCSIZE:
1294 return PAGER_RecalcSize(infoPtr);
1295
1296 case PGM_SETBKCOLOR:
1297 return PAGER_SetBkColor (infoPtr, (COLORREF)lParam);
1298
1299 case PGM_SETBORDER:
1300 return PAGER_SetBorder (infoPtr, (INT)lParam);
1301
1302 case PGM_SETBUTTONSIZE:
1303 return PAGER_SetButtonSize (infoPtr, (INT)lParam);
1304
1305 case PGM_SETCHILD:
1306 return PAGER_SetChild (infoPtr, (HWND)lParam);
1307
1308 case PGM_SETPOS:
1309 return PAGER_SetPos(infoPtr, (INT)lParam, FALSE);
1310
1311 case WM_CREATE:
1312 return PAGER_Create (hwnd, (LPCREATESTRUCTW)lParam);
1313
1314 case WM_DESTROY:
1315 return PAGER_Destroy (infoPtr);
1316
1317 case WM_SIZE:
1318 return PAGER_Size (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1319
1320 case WM_NCPAINT:
1321 return PAGER_NCPaint (infoPtr, (HRGN)wParam);
1322
1323 case WM_WINDOWPOSCHANGING:
1324 return PAGER_WindowPosChanging (infoPtr, (WINDOWPOS*)lParam);
1325
1326 case WM_STYLECHANGED:
1327 return PAGER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
1328
1329 case WM_NCCALCSIZE:
1330 return PAGER_NCCalcSize (infoPtr, wParam, (LPRECT)lParam);
1331
1332 case WM_NCHITTEST:
1333 return PAGER_NCHitTest (infoPtr, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1334
1335 case WM_MOUSEMOVE:
1336 if (infoPtr->bForward && infoPtr->hwndChild)
1337 PostMessageW(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1338 return PAGER_MouseMove (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1339
1340 case WM_LBUTTONDOWN:
1341 return PAGER_LButtonDown (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1342
1343 case WM_LBUTTONUP:
1344 return PAGER_LButtonUp (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1345
1346 case WM_ERASEBKGND:
1347 return PAGER_EraseBackground (infoPtr, (HDC)wParam);
1348
1349 case WM_TIMER:
1350 return PAGER_Timer (infoPtr, (INT)wParam);
1351
1352 case WM_NOTIFY:
1353 case WM_COMMAND:
1354 return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
1355
1356 default:
1357 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1358 }
1359 }
1360
1361
1362 VOID
1363 PAGER_Register (void)
1364 {
1365 WNDCLASSW wndClass;
1366
1367 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
1368 wndClass.style = CS_GLOBALCLASS;
1369 wndClass.lpfnWndProc = PAGER_WindowProc;
1370 wndClass.cbClsExtra = 0;
1371 wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1372 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1373 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
1374 wndClass.lpszClassName = WC_PAGESCROLLERW;
1375
1376 RegisterClassW (&wndClass);
1377 }
1378
1379
1380 VOID
1381 PAGER_Unregister (void)
1382 {
1383 UnregisterClassW (WC_PAGESCROLLERW, NULL);
1384 }