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