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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "commctrl.h" 63 #include "comctl32.h" 64 #include "wine/debug.h" 65 #include "wine/heap.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 BOOL bUnicode; /* send notifications in Unicode */ 75 DWORD dwStyle; /* styles for this control */ 76 COLORREF clrBk; /* background color */ 77 INT nBorder; /* border size for the control */ 78 INT nButtonSize;/* size of the pager btns */ 79 INT nPos; /* scroll position */ 80 INT nWidth; /* from child wnd's response to PGN_CALCSIZE */ 81 INT nHeight; /* from child wnd's response to PGN_CALCSIZE */ 82 BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */ 83 BOOL bCapture; /* we have captured the mouse */ 84 INT TLbtnState; /* state of top or left btn */ 85 INT BRbtnState; /* state of bottom or right btn */ 86 INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */ 87 WCHAR *pwszBuffer;/* text buffer for converted notifications */ 88 INT nBufferSize;/* size of the above buffer */ 89 } PAGER_INFO; 90 91 #define TIMERID1 1 92 #define TIMERID2 2 93 #define INITIAL_DELAY 500 94 #define REPEAT_DELAY 50 95 96 /* Text field conversion behavior flags for PAGER_SendConvertedNotify() */ 97 enum conversion_flags 98 { 99 /* Convert Unicode text to ANSI for parent before sending. If not set, do nothing */ 100 CONVERT_SEND = 0x01, 101 /* Convert ANSI text from parent back to Unicode for children */ 102 CONVERT_RECEIVE = 0x02, 103 /* Send empty text to parent if text is NULL. Original text pointer still remains NULL */ 104 SEND_EMPTY_IF_NULL = 0x04, 105 /* Set text to null after parent received the notification if the required mask is not set before sending notification */ 106 SET_NULL_IF_NO_MASK = 0x08, 107 /* Zero out the text buffer before sending it to parent */ 108 ZERO_SEND = 0x10 109 }; 110 111 static void 112 PAGER_GetButtonRects(const PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords) 113 { 114 RECT rcWindow; 115 GetWindowRect (infoPtr->hwndSelf, &rcWindow); 116 117 if (bClientCoords) 118 MapWindowPoints( 0, infoPtr->hwndSelf, (POINT *)&rcWindow, 2 ); 119 else 120 OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top); 121 122 *prcTopLeft = *prcBottomRight = rcWindow; 123 if (infoPtr->dwStyle & PGS_HORZ) 124 { 125 prcTopLeft->right = prcTopLeft->left + infoPtr->nButtonSize; 126 prcBottomRight->left = prcBottomRight->right - infoPtr->nButtonSize; 127 } 128 else 129 { 130 prcTopLeft->bottom = prcTopLeft->top + infoPtr->nButtonSize; 131 prcBottomRight->top = prcBottomRight->bottom - infoPtr->nButtonSize; 132 } 133 } 134 135 static void 136 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT rc, 137 BOOL horz, BOOL topLeft, INT btnState) 138 { 139 UINT flags; 140 141 TRACE("rc = %s, btnState = %d\n", wine_dbgstr_rect(&rc), btnState); 142 143 if (btnState == PGF_INVISIBLE) 144 return; 145 146 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0)) 147 return; 148 149 if (horz) 150 flags = topLeft ? DFCS_SCROLLLEFT : DFCS_SCROLLRIGHT; 151 else 152 flags = topLeft ? DFCS_SCROLLUP : DFCS_SCROLLDOWN; 153 154 switch (btnState) 155 { 156 case PGF_HOT: 157 break; 158 case PGF_NORMAL: 159 flags |= DFCS_FLAT; 160 break; 161 case PGF_DEPRESSED: 162 flags |= DFCS_PUSHED; 163 break; 164 case PGF_GRAYED: 165 flags |= DFCS_INACTIVE | DFCS_FLAT; 166 break; 167 } 168 DrawFrameControl( hdc, &rc, DFC_SCROLL, flags ); 169 } 170 171 /* << PAGER_GetDropTarget >> */ 172 173 static inline LRESULT 174 PAGER_ForwardMouse (PAGER_INFO* infoPtr, BOOL bFwd) 175 { 176 TRACE("[%p]\n", infoPtr->hwndSelf); 177 178 infoPtr->bForward = bFwd; 179 180 return 0; 181 } 182 183 static inline LRESULT 184 PAGER_GetButtonState (const PAGER_INFO* infoPtr, INT btn) 185 { 186 LRESULT btnState = PGF_INVISIBLE; 187 TRACE("[%p]\n", infoPtr->hwndSelf); 188 189 if (btn == PGB_TOPORLEFT) 190 btnState = infoPtr->TLbtnState; 191 else if (btn == PGB_BOTTOMORRIGHT) 192 btnState = infoPtr->BRbtnState; 193 194 return btnState; 195 } 196 197 198 static inline INT 199 PAGER_GetPos(const PAGER_INFO *infoPtr) 200 { 201 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nPos); 202 return infoPtr->nPos; 203 } 204 205 static inline INT 206 PAGER_GetButtonSize(const PAGER_INFO *infoPtr) 207 { 208 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize); 209 return infoPtr->nButtonSize; 210 } 211 212 static inline INT 213 PAGER_GetBorder(const PAGER_INFO *infoPtr) 214 { 215 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nBorder); 216 return infoPtr->nBorder; 217 } 218 219 static inline COLORREF 220 PAGER_GetBkColor(const PAGER_INFO *infoPtr) 221 { 222 TRACE("[%p] returns %06x\n", infoPtr->hwndSelf, infoPtr->clrBk); 223 return infoPtr->clrBk; 224 } 225 226 static void 227 PAGER_CalcSize( PAGER_INFO *infoPtr ) 228 { 229 NMPGCALCSIZE nmpgcs; 230 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE)); 231 nmpgcs.hdr.hwndFrom = infoPtr->hwndSelf; 232 nmpgcs.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID); 233 nmpgcs.hdr.code = PGN_CALCSIZE; 234 nmpgcs.dwFlag = (infoPtr->dwStyle & PGS_HORZ) ? PGF_CALCWIDTH : PGF_CALCHEIGHT; 235 nmpgcs.iWidth = infoPtr->nWidth; 236 nmpgcs.iHeight = infoPtr->nHeight; 237 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs); 238 239 if (infoPtr->dwStyle & PGS_HORZ) 240 infoPtr->nWidth = nmpgcs.iWidth; 241 else 242 infoPtr->nHeight = nmpgcs.iHeight; 243 244 TRACE("[%p] PGN_CALCSIZE returns %dx%d\n", infoPtr->hwndSelf, nmpgcs.iWidth, nmpgcs.iHeight ); 245 } 246 247 static void 248 PAGER_PositionChildWnd(PAGER_INFO* infoPtr) 249 { 250 if (infoPtr->hwndChild) 251 { 252 RECT rcClient; 253 int nPos = infoPtr->nPos; 254 255 /* compensate for a grayed btn, which will soon become invisible */ 256 if (infoPtr->TLbtnState == PGF_GRAYED) 257 nPos += infoPtr->nButtonSize; 258 259 GetClientRect(infoPtr->hwndSelf, &rcClient); 260 261 if (infoPtr->dwStyle & PGS_HORZ) 262 { 263 int wndSize = max(0, rcClient.right - rcClient.left); 264 if (infoPtr->nWidth < wndSize) 265 infoPtr->nWidth = wndSize; 266 267 TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf, 268 infoPtr->nWidth, infoPtr->nHeight, 269 -nPos, 0); 270 SetWindowPos(infoPtr->hwndChild, HWND_TOP, 271 -nPos, 0, 272 infoPtr->nWidth, infoPtr->nHeight, 0); 273 } 274 else 275 { 276 int wndSize = max(0, rcClient.bottom - rcClient.top); 277 if (infoPtr->nHeight < wndSize) 278 infoPtr->nHeight = wndSize; 279 280 TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf, 281 infoPtr->nWidth, infoPtr->nHeight, 282 0, -nPos); 283 SetWindowPos(infoPtr->hwndChild, HWND_TOP, 284 0, -nPos, 285 infoPtr->nWidth, infoPtr->nHeight, 0); 286 } 287 288 InvalidateRect(infoPtr->hwndChild, NULL, TRUE); 289 } 290 } 291 292 static INT 293 PAGER_GetScrollRange(PAGER_INFO* infoPtr, BOOL calc_size) 294 { 295 INT scrollRange = 0; 296 297 if (infoPtr->hwndChild) 298 { 299 INT wndSize, childSize; 300 RECT wndRect; 301 GetWindowRect(infoPtr->hwndSelf, &wndRect); 302 303 if (calc_size) 304 PAGER_CalcSize(infoPtr); 305 if (infoPtr->dwStyle & PGS_HORZ) 306 { 307 wndSize = wndRect.right - wndRect.left; 308 childSize = infoPtr->nWidth; 309 } 310 else 311 { 312 wndSize = wndRect.bottom - wndRect.top; 313 childSize = infoPtr->nHeight; 314 } 315 316 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize); 317 if (childSize > wndSize) 318 scrollRange = childSize - wndSize + infoPtr->nButtonSize; 319 } 320 321 TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange); 322 return scrollRange; 323 } 324 325 static void 326 PAGER_UpdateBtns(PAGER_INFO *infoPtr, INT scrollRange, BOOL hideGrayBtns) 327 { 328 BOOL resizeClient; 329 BOOL repaintBtns; 330 INT oldTLbtnState = infoPtr->TLbtnState; 331 INT oldBRbtnState = infoPtr->BRbtnState; 332 POINT pt; 333 RECT rcTopLeft, rcBottomRight; 334 335 /* get button rects */ 336 PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE); 337 338 GetCursorPos(&pt); 339 ScreenToClient( infoPtr->hwndSelf, &pt ); 340 341 /* update states based on scroll position */ 342 if (infoPtr->nPos > 0) 343 { 344 if (infoPtr->TLbtnState == PGF_INVISIBLE || infoPtr->TLbtnState == PGF_GRAYED) 345 infoPtr->TLbtnState = PGF_NORMAL; 346 } 347 else if (!hideGrayBtns && PtInRect(&rcTopLeft, pt)) 348 infoPtr->TLbtnState = PGF_GRAYED; 349 else 350 infoPtr->TLbtnState = PGF_INVISIBLE; 351 352 if (scrollRange <= 0) 353 { 354 infoPtr->TLbtnState = PGF_INVISIBLE; 355 infoPtr->BRbtnState = PGF_INVISIBLE; 356 } 357 else if (infoPtr->nPos < scrollRange) 358 { 359 if (infoPtr->BRbtnState == PGF_INVISIBLE || infoPtr->BRbtnState == PGF_GRAYED) 360 infoPtr->BRbtnState = PGF_NORMAL; 361 } 362 else if (!hideGrayBtns && PtInRect(&rcBottomRight, pt)) 363 infoPtr->BRbtnState = PGF_GRAYED; 364 else 365 infoPtr->BRbtnState = PGF_INVISIBLE; 366 367 /* only need to resize when entering or leaving PGF_INVISIBLE state */ 368 resizeClient = 369 ((oldTLbtnState == PGF_INVISIBLE) != (infoPtr->TLbtnState == PGF_INVISIBLE)) || 370 ((oldBRbtnState == PGF_INVISIBLE) != (infoPtr->BRbtnState == PGF_INVISIBLE)); 371 /* initiate NCCalcSize to resize client wnd if necessary */ 372 if (resizeClient) 373 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, 374 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | 375 SWP_NOZORDER | SWP_NOACTIVATE); 376 377 /* repaint when changing any state */ 378 repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) || 379 (oldBRbtnState != infoPtr->BRbtnState); 380 if (repaintBtns) 381 SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0); 382 } 383 384 static LRESULT 385 PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress, BOOL calc_size) 386 { 387 INT scrollRange = PAGER_GetScrollRange(infoPtr, calc_size); 388 INT oldPos = infoPtr->nPos; 389 390 if ((scrollRange <= 0) || (newPos < 0)) 391 infoPtr->nPos = 0; 392 else if (newPos > scrollRange) 393 infoPtr->nPos = scrollRange; 394 else 395 infoPtr->nPos = newPos; 396 397 TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr->hwndSelf, infoPtr->nPos, oldPos); 398 399 if (infoPtr->nPos != oldPos) 400 { 401 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */ 402 PAGER_UpdateBtns(infoPtr, scrollRange, !fromBtnPress); 403 PAGER_PositionChildWnd(infoPtr); 404 } 405 406 return 0; 407 } 408 409 /****************************************************************** 410 * For the PGM_RECALCSIZE message (but not the other uses in * 411 * this module), the native control does only the following: * 412 * * 413 * if (some condition) * 414 * PostMessageW(hwnd, EM_FMTLINES, 0, 0); * 415 * return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0); * 416 * * 417 * When we figure out what the "some condition" is we will * 418 * implement that for the message processing. * 419 ******************************************************************/ 420 421 static LRESULT 422 PAGER_RecalcSize(PAGER_INFO *infoPtr) 423 { 424 TRACE("[%p]\n", infoPtr->hwndSelf); 425 426 if (infoPtr->hwndChild) 427 { 428 INT scrollRange = PAGER_GetScrollRange(infoPtr, TRUE); 429 430 if (scrollRange <= 0) 431 { 432 infoPtr->nPos = -1; 433 PAGER_SetPos(infoPtr, 0, FALSE, TRUE); 434 } 435 else 436 PAGER_PositionChildWnd(infoPtr); 437 } 438 439 return 1; 440 } 441 442 443 static COLORREF 444 PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk) 445 { 446 COLORREF clrTemp = infoPtr->clrBk; 447 448 infoPtr->clrBk = clrBk; 449 TRACE("[%p] %06x\n", infoPtr->hwndSelf, infoPtr->clrBk); 450 451 /* the native control seems to do things this way */ 452 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, 453 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | 454 SWP_NOZORDER | SWP_NOACTIVATE); 455 456 RedrawWindow(infoPtr->hwndSelf, 0, 0, RDW_ERASE | RDW_INVALIDATE); 457 458 return clrTemp; 459 } 460 461 462 static INT 463 PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder) 464 { 465 INT nTemp = infoPtr->nBorder; 466 467 infoPtr->nBorder = iBorder; 468 TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nBorder); 469 470 PAGER_RecalcSize(infoPtr); 471 472 return nTemp; 473 } 474 475 476 static INT 477 PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize) 478 { 479 INT nTemp = infoPtr->nButtonSize; 480 481 infoPtr->nButtonSize = iButtonSize; 482 TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize); 483 484 PAGER_RecalcSize(infoPtr); 485 486 return nTemp; 487 } 488 489 490 static LRESULT 491 PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild) 492 { 493 infoPtr->hwndChild = IsWindow (hwndChild) ? hwndChild : 0; 494 495 if (infoPtr->hwndChild) 496 { 497 TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild); 498 499 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, 500 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); 501 502 infoPtr->nPos = -1; 503 PAGER_SetPos(infoPtr, 0, FALSE, FALSE); 504 } 505 506 return 0; 507 } 508 509 static void 510 PAGER_Scroll(PAGER_INFO* infoPtr, INT dir) 511 { 512 NMPGSCROLL nmpgScroll; 513 RECT rcWnd; 514 515 if (infoPtr->hwndChild) 516 { 517 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL)); 518 nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf; 519 nmpgScroll.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID); 520 nmpgScroll.hdr.code = PGN_SCROLL; 521 522 GetWindowRect(infoPtr->hwndSelf, &rcWnd); 523 GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent); 524 nmpgScroll.iXpos = nmpgScroll.iYpos = 0; 525 nmpgScroll.iDir = dir; 526 527 if (infoPtr->dwStyle & PGS_HORZ) 528 { 529 nmpgScroll.iScroll = rcWnd.right - rcWnd.left; 530 nmpgScroll.iXpos = infoPtr->nPos; 531 } 532 else 533 { 534 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top; 535 nmpgScroll.iYpos = infoPtr->nPos; 536 } 537 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize; 538 539 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll); 540 541 TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr->hwndSelf, nmpgScroll.iScroll); 542 543 if (nmpgScroll.iScroll > 0) 544 { 545 infoPtr->direction = dir; 546 547 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP) 548 PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE, TRUE); 549 else 550 PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE, TRUE); 551 } 552 else 553 infoPtr->direction = -1; 554 } 555 } 556 557 static LRESULT 558 PAGER_FmtLines(const PAGER_INFO *infoPtr) 559 { 560 /* initiate NCCalcSize to resize client wnd and get size */ 561 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, 562 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | 563 SWP_NOZORDER | SWP_NOACTIVATE); 564 565 SetWindowPos(infoPtr->hwndChild, 0, 566 0,0,infoPtr->nWidth,infoPtr->nHeight, 567 0); 568 569 return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0); 570 } 571 572 static LRESULT 573 PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs) 574 { 575 PAGER_INFO *infoPtr; 576 INT ret; 577 578 /* allocate memory for info structure */ 579 infoPtr = heap_alloc_zero (sizeof(*infoPtr)); 580 if (!infoPtr) return -1; 581 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); 582 583 /* set default settings */ 584 infoPtr->hwndSelf = hwnd; 585 infoPtr->hwndChild = NULL; 586 infoPtr->hwndNotify = lpcs->hwndParent; 587 infoPtr->dwStyle = lpcs->style; 588 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE); 589 infoPtr->nBorder = 0; 590 infoPtr->nButtonSize = 12; 591 infoPtr->nPos = 0; 592 infoPtr->nWidth = 0; 593 infoPtr->nHeight = 0; 594 infoPtr->bForward = FALSE; 595 infoPtr->bCapture = FALSE; 596 infoPtr->TLbtnState = PGF_INVISIBLE; 597 infoPtr->BRbtnState = PGF_INVISIBLE; 598 infoPtr->direction = -1; 599 600 if (infoPtr->dwStyle & PGS_DRAGNDROP) 601 FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf); 602 603 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY); 604 infoPtr->bUnicode = (ret == NFR_UNICODE); 605 606 return 0; 607 } 608 609 610 static LRESULT 611 PAGER_Destroy (PAGER_INFO *infoPtr) 612 { 613 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); 614 heap_free (infoPtr->pwszBuffer); 615 heap_free (infoPtr); 616 return 0; 617 } 618 619 static LRESULT 620 PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect) 621 { 622 RECT rcChild, rcWindow; 623 624 /* 625 * lpRect points to a RECT struct. On entry, the struct 626 * contains the proposed wnd rectangle for the window. 627 * On exit, the struct should contain the screen 628 * coordinates of the corresponding window's client area. 629 */ 630 631 DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect); 632 633 TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect)); 634 635 GetWindowRect (infoPtr->hwndChild, &rcChild); 636 MapWindowPoints (0, infoPtr->hwndSelf, (LPPOINT)&rcChild, 2); /* FIXME: RECT != 2 POINTS */ 637 GetWindowRect (infoPtr->hwndSelf, &rcWindow); 638 639 infoPtr->nWidth = lpRect->right - lpRect->left; 640 infoPtr->nHeight = lpRect->bottom - lpRect->top; 641 PAGER_CalcSize( infoPtr ); 642 643 if (infoPtr->dwStyle & PGS_HORZ) 644 { 645 if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right)) 646 lpRect->left += infoPtr->nButtonSize; 647 if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left)) 648 lpRect->right -= infoPtr->nButtonSize; 649 } 650 else 651 { 652 if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom)) 653 lpRect->top += infoPtr->nButtonSize; 654 if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top)) 655 lpRect->bottom -= infoPtr->nButtonSize; 656 } 657 658 TRACE("nPos=%d, nHeight=%d, window=%s\n", infoPtr->nPos, infoPtr->nHeight, wine_dbgstr_rect(&rcWindow)); 659 TRACE("[%p] client rect set to %s BtnState[%d,%d]\n", infoPtr->hwndSelf, wine_dbgstr_rect(lpRect), 660 infoPtr->TLbtnState, infoPtr->BRbtnState); 661 662 return 0; 663 } 664 665 static LRESULT 666 PAGER_NCPaint (const PAGER_INFO* infoPtr, HRGN hRgn) 667 { 668 RECT rcBottomRight, rcTopLeft; 669 HDC hdc; 670 671 if (infoPtr->dwStyle & WS_MINIMIZE) 672 return 0; 673 674 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0); 675 676 if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW))) 677 return 0; 678 679 PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE); 680 681 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft, 682 infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState); 683 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight, 684 infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState); 685 686 ReleaseDC( infoPtr->hwndSelf, hdc ); 687 return 0; 688 } 689 690 static INT 691 PAGER_HitTest (const PAGER_INFO* infoPtr, const POINT * pt) 692 { 693 RECT clientRect, rcTopLeft, rcBottomRight; 694 POINT ptWindow; 695 696 GetClientRect (infoPtr->hwndSelf, &clientRect); 697 698 if (PtInRect(&clientRect, *pt)) 699 { 700 TRACE("child\n"); 701 return -1; 702 } 703 704 ptWindow = *pt; 705 PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE); 706 707 if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow)) 708 { 709 TRACE("PGB_TOPORLEFT\n"); 710 return PGB_TOPORLEFT; 711 } 712 else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow)) 713 { 714 TRACE("PGB_BOTTOMORRIGHT\n"); 715 return PGB_BOTTOMORRIGHT; 716 } 717 718 TRACE("nowhere\n"); 719 return -1; 720 } 721 722 static LRESULT 723 PAGER_NCHitTest (const PAGER_INFO* infoPtr, INT x, INT y) 724 { 725 POINT pt; 726 INT nHit; 727 728 pt.x = x; 729 pt.y = y; 730 731 ScreenToClient (infoPtr->hwndSelf, &pt); 732 nHit = PAGER_HitTest(infoPtr, &pt); 733 734 return (nHit < 0) ? HTTRANSPARENT : HTCLIENT; 735 } 736 737 static LRESULT 738 PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y) 739 { 740 POINT clpt, pt; 741 RECT wnrect; 742 BOOL topLeft = FALSE; 743 INT btnstate = 0; 744 INT hit; 745 HDC hdc; 746 747 pt.x = x; 748 pt.y = y; 749 750 TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y); 751 ClientToScreen(infoPtr->hwndSelf, &pt); 752 GetWindowRect(infoPtr->hwndSelf, &wnrect); 753 if (PtInRect(&wnrect, pt)) { 754 RECT topleft, bottomright, *rect = NULL; 755 756 PAGER_GetButtonRects(infoPtr, &topleft, &bottomright, FALSE); 757 758 clpt = pt; 759 MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1); 760 hit = PAGER_HitTest(infoPtr, &clpt); 761 if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL)) 762 { 763 topLeft = TRUE; 764 rect = &topleft; 765 infoPtr->TLbtnState = PGF_HOT; 766 btnstate = infoPtr->TLbtnState; 767 } 768 else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL)) 769 { 770 topLeft = FALSE; 771 rect = &bottomright; 772 infoPtr->BRbtnState = PGF_HOT; 773 btnstate = infoPtr->BRbtnState; 774 } 775 776 /* If in one of the buttons the capture and draw buttons */ 777 if (rect) 778 { 779 TRACE("[%p] draw btn (%s), Capture %s, style %08x\n", 780 infoPtr->hwndSelf, wine_dbgstr_rect(rect), 781 (infoPtr->bCapture) ? "TRUE" : "FALSE", 782 infoPtr->dwStyle); 783 if (!infoPtr->bCapture) 784 { 785 TRACE("[%p] SetCapture\n", infoPtr->hwndSelf); 786 SetCapture(infoPtr->hwndSelf); 787 infoPtr->bCapture = TRUE; 788 } 789 if (infoPtr->dwStyle & PGS_AUTOSCROLL) 790 SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0); 791 hdc = GetWindowDC(infoPtr->hwndSelf); 792 /* OffsetRect(wnrect, 0 | 1, 0 | 1) */ 793 PAGER_DrawButton(hdc, infoPtr->clrBk, *rect, 794 infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate); 795 ReleaseDC(infoPtr->hwndSelf, hdc); 796 return 0; 797 } 798 } 799 800 /* If we think we are captured, then do release */ 801 if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf)) 802 { 803 NMHDR nmhdr; 804 805 infoPtr->bCapture = FALSE; 806 807 if (GetCapture() == infoPtr->hwndSelf) 808 { 809 ReleaseCapture(); 810 811 if (infoPtr->TLbtnState == PGF_GRAYED) 812 { 813 infoPtr->TLbtnState = PGF_INVISIBLE; 814 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, 815 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | 816 SWP_NOZORDER | SWP_NOACTIVATE); 817 } 818 else if (infoPtr->TLbtnState == PGF_HOT) 819 { 820 infoPtr->TLbtnState = PGF_NORMAL; 821 /* FIXME: just invalidate button rect */ 822 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); 823 } 824 825 if (infoPtr->BRbtnState == PGF_GRAYED) 826 { 827 infoPtr->BRbtnState = PGF_INVISIBLE; 828 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, 829 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | 830 SWP_NOZORDER | SWP_NOACTIVATE); 831 } 832 else if (infoPtr->BRbtnState == PGF_HOT) 833 { 834 infoPtr->BRbtnState = PGF_NORMAL; 835 /* FIXME: just invalidate button rect */ 836 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); 837 } 838 839 /* Notify parent of released mouse capture */ 840 memset(&nmhdr, 0, sizeof(NMHDR)); 841 nmhdr.hwndFrom = infoPtr->hwndSelf; 842 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 843 nmhdr.code = NM_RELEASEDCAPTURE; 844 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr); 845 } 846 if (IsWindow(infoPtr->hwndSelf)) 847 KillTimer(infoPtr->hwndSelf, TIMERID1); 848 } 849 return 0; 850 } 851 852 static LRESULT 853 PAGER_LButtonDown (PAGER_INFO* infoPtr, INT keys, INT x, INT y) 854 { 855 BOOL repaintBtns = FALSE; 856 POINT pt; 857 INT hit; 858 859 pt.x = x; 860 pt.y = y; 861 862 TRACE("[%p] at (%d,%d)\n", infoPtr->hwndSelf, x, y); 863 864 hit = PAGER_HitTest(infoPtr, &pt); 865 866 /* put btn in DEPRESSED state */ 867 if (hit == PGB_TOPORLEFT) 868 { 869 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED; 870 infoPtr->TLbtnState = PGF_DEPRESSED; 871 SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0); 872 } 873 else if (hit == PGB_BOTTOMORRIGHT) 874 { 875 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED; 876 infoPtr->BRbtnState = PGF_DEPRESSED; 877 SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0); 878 } 879 880 if (repaintBtns) 881 SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0); 882 883 switch(hit) 884 { 885 case PGB_TOPORLEFT: 886 if (infoPtr->dwStyle & PGS_HORZ) 887 { 888 TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr->hwndSelf); 889 PAGER_Scroll(infoPtr, PGF_SCROLLLEFT); 890 } 891 else 892 { 893 TRACE("[%p] PGF_SCROLLUP\n", infoPtr->hwndSelf); 894 PAGER_Scroll(infoPtr, PGF_SCROLLUP); 895 } 896 break; 897 case PGB_BOTTOMORRIGHT: 898 if (infoPtr->dwStyle & PGS_HORZ) 899 { 900 TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr->hwndSelf); 901 PAGER_Scroll(infoPtr, PGF_SCROLLRIGHT); 902 } 903 else 904 { 905 TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr->hwndSelf); 906 PAGER_Scroll(infoPtr, PGF_SCROLLDOWN); 907 } 908 break; 909 default: 910 break; 911 } 912 913 return 0; 914 } 915 916 static LRESULT 917 PAGER_LButtonUp (PAGER_INFO* infoPtr, INT keys, INT x, INT y) 918 { 919 TRACE("[%p]\n", infoPtr->hwndSelf); 920 921 KillTimer (infoPtr->hwndSelf, TIMERID1); 922 KillTimer (infoPtr->hwndSelf, TIMERID2); 923 924 /* make PRESSED btns NORMAL but don't hide gray btns */ 925 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED)) 926 infoPtr->TLbtnState = PGF_NORMAL; 927 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED)) 928 infoPtr->BRbtnState = PGF_NORMAL; 929 930 return 0; 931 } 932 933 static LRESULT 934 PAGER_Timer (PAGER_INFO* infoPtr, INT nTimerId) 935 { 936 INT dir; 937 938 /* if initial timer, kill it and start the repeat timer */ 939 if (nTimerId == TIMERID1) { 940 if (infoPtr->TLbtnState == PGF_HOT) 941 dir = (infoPtr->dwStyle & PGS_HORZ) ? 942 PGF_SCROLLLEFT : PGF_SCROLLUP; 943 else 944 dir = (infoPtr->dwStyle & PGS_HORZ) ? 945 PGF_SCROLLRIGHT : PGF_SCROLLDOWN; 946 TRACE("[%p] TIMERID1: style=%08x, dir=%d\n", 947 infoPtr->hwndSelf, infoPtr->dwStyle, dir); 948 KillTimer(infoPtr->hwndSelf, TIMERID1); 949 SetTimer(infoPtr->hwndSelf, TIMERID1, REPEAT_DELAY, 0); 950 if (infoPtr->dwStyle & PGS_AUTOSCROLL) { 951 PAGER_Scroll(infoPtr, dir); 952 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, 953 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | 954 SWP_NOZORDER | SWP_NOACTIVATE); 955 } 956 return 0; 957 958 } 959 960 TRACE("[%p] TIMERID2: dir=%d\n", infoPtr->hwndSelf, infoPtr->direction); 961 KillTimer(infoPtr->hwndSelf, TIMERID2); 962 if (infoPtr->direction > 0) { 963 PAGER_Scroll(infoPtr, infoPtr->direction); 964 SetTimer(infoPtr->hwndSelf, TIMERID2, REPEAT_DELAY, 0); 965 } 966 return 0; 967 } 968 969 static LRESULT 970 PAGER_EraseBackground (const PAGER_INFO* infoPtr, HDC hdc) 971 { 972 POINT pt, ptorig; 973 HWND parent; 974 LRESULT ret; 975 976 pt.x = 0; 977 pt.y = 0; 978 parent = GetParent(infoPtr->hwndSelf); 979 MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1); 980 OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig); 981 ret = SendMessageW (parent, WM_ERASEBKGND, (WPARAM)hdc, 0); 982 SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0); 983 984 return ret; 985 } 986 987 988 static LRESULT 989 PAGER_Size (PAGER_INFO* infoPtr, INT type, INT x, INT y) 990 { 991 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */ 992 993 TRACE("[%p] %d,%d\n", infoPtr->hwndSelf, x, y); 994 995 if (infoPtr->dwStyle & PGS_HORZ) 996 infoPtr->nHeight = y; 997 else 998 infoPtr->nWidth = x; 999 1000 return PAGER_RecalcSize(infoPtr); 1001 } 1002 1003 1004 static LRESULT 1005 PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss) 1006 { 1007 DWORD oldStyle = infoPtr->dwStyle; 1008 1009 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 1010 wStyleType, lpss->styleOld, lpss->styleNew); 1011 1012 if (wStyleType != GWL_STYLE) return 0; 1013 1014 infoPtr->dwStyle = lpss->styleNew; 1015 1016 if ((oldStyle ^ lpss->styleNew) & (PGS_HORZ | PGS_VERT)) 1017 { 1018 PAGER_RecalcSize(infoPtr); 1019 } 1020 1021 return 0; 1022 } 1023 1024 static LRESULT PAGER_NotifyFormat(PAGER_INFO *infoPtr, INT command) 1025 { 1026 INT ret; 1027 switch (command) 1028 { 1029 case NF_REQUERY: 1030 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY); 1031 infoPtr->bUnicode = (ret == NFR_UNICODE); 1032 return ret; 1033 case NF_QUERY: 1034 /* Pager always wants Unicode notifications from children */ 1035 return NFR_UNICODE; 1036 default: 1037 return 0; 1038 } 1039 } 1040 1041 static UINT PAGER_GetAnsiNtfCode(UINT code) 1042 { 1043 switch (code) 1044 { 1045 /* ComboxBoxEx */ 1046 case CBEN_DRAGBEGINW: return CBEN_DRAGBEGINA; 1047 case CBEN_ENDEDITW: return CBEN_ENDEDITA; 1048 case CBEN_GETDISPINFOW: return CBEN_GETDISPINFOA; 1049 /* Date and Time Picker */ 1050 case DTN_FORMATW: return DTN_FORMATA; 1051 case DTN_FORMATQUERYW: return DTN_FORMATQUERYA; 1052 case DTN_USERSTRINGW: return DTN_USERSTRINGA; 1053 case DTN_WMKEYDOWNW: return DTN_WMKEYDOWNA; 1054 /* Header */ 1055 case HDN_BEGINTRACKW: return HDN_BEGINTRACKA; 1056 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA; 1057 case HDN_ENDTRACKW: return HDN_ENDTRACKA; 1058 case HDN_GETDISPINFOW: return HDN_GETDISPINFOA; 1059 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA; 1060 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA; 1061 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA; 1062 case HDN_ITEMDBLCLICKW: return HDN_ITEMDBLCLICKA; 1063 case HDN_TRACKW: return HDN_TRACKA; 1064 /* List View */ 1065 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA; 1066 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA; 1067 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA; 1068 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA; 1069 case LVN_INCREMENTALSEARCHW: return LVN_INCREMENTALSEARCHA; 1070 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA; 1071 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA; 1072 /* Toolbar */ 1073 case TBN_GETBUTTONINFOW: return TBN_GETBUTTONINFOA; 1074 case TBN_GETINFOTIPW: return TBN_GETINFOTIPA; 1075 /* Tooltip */ 1076 case TTN_GETDISPINFOW: return TTN_GETDISPINFOA; 1077 /* Tree View */ 1078 case TVN_BEGINDRAGW: return TVN_BEGINDRAGA; 1079 case TVN_BEGINLABELEDITW: return TVN_BEGINLABELEDITA; 1080 case TVN_BEGINRDRAGW: return TVN_BEGINRDRAGA; 1081 case TVN_DELETEITEMW: return TVN_DELETEITEMA; 1082 case TVN_ENDLABELEDITW: return TVN_ENDLABELEDITA; 1083 case TVN_GETDISPINFOW: return TVN_GETDISPINFOA; 1084 case TVN_GETINFOTIPW: return TVN_GETINFOTIPA; 1085 case TVN_ITEMEXPANDEDW: return TVN_ITEMEXPANDEDA; 1086 case TVN_ITEMEXPANDINGW: return TVN_ITEMEXPANDINGA; 1087 case TVN_SELCHANGEDW: return TVN_SELCHANGEDA; 1088 case TVN_SELCHANGINGW: return TVN_SELCHANGINGA; 1089 case TVN_SETDISPINFOW: return TVN_SETDISPINFOA; 1090 } 1091 return code; 1092 } 1093 1094 static BOOL PAGER_AdjustBuffer(PAGER_INFO *infoPtr, INT size) 1095 { 1096 if (!infoPtr->pwszBuffer) 1097 infoPtr->pwszBuffer = heap_alloc(size); 1098 else if (infoPtr->nBufferSize < size) 1099 infoPtr->pwszBuffer = heap_realloc(infoPtr->pwszBuffer, size); 1100 1101 if (!infoPtr->pwszBuffer) return FALSE; 1102 if (infoPtr->nBufferSize < size) infoPtr->nBufferSize = size; 1103 1104 return TRUE; 1105 } 1106 1107 /* Convert text to Unicode and return the original text address */ 1108 static WCHAR *PAGER_ConvertText(WCHAR **text) 1109 { 1110 WCHAR *oldText = *text; 1111 *text = NULL; 1112 Str_SetPtrWtoA((CHAR **)text, oldText); 1113 return oldText; 1114 } 1115 1116 static void PAGER_RestoreText(WCHAR **text, WCHAR *oldText) 1117 { 1118 if (!oldText) return; 1119 1120 Free(*text); 1121 *text = oldText; 1122 } 1123 1124 static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, UINT *mask, UINT requiredMask, WCHAR **text, 1125 INT *textMax, DWORD flags) 1126 { 1127 CHAR *sendBuffer = NULL; 1128 CHAR *receiveBuffer; 1129 INT bufferSize; 1130 WCHAR *oldText; 1131 INT oldTextMax; 1132 LRESULT ret = NO_ERROR; 1133 1134 oldText = *text; 1135 oldTextMax = textMax ? *textMax : 0; 1136 1137 hdr->code = PAGER_GetAnsiNtfCode(hdr->code); 1138 1139 if (mask && !(*mask & requiredMask)) 1140 { 1141 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1142 if (flags & SET_NULL_IF_NO_MASK) oldText = NULL; 1143 goto done; 1144 } 1145 1146 if (oldTextMax < 0) goto done; 1147 1148 if ((*text && flags & (CONVERT_SEND | ZERO_SEND)) || (!*text && flags & SEND_EMPTY_IF_NULL)) 1149 { 1150 bufferSize = textMax ? *textMax : lstrlenW(*text) + 1; 1151 sendBuffer = heap_alloc_zero(bufferSize); 1152 if (!sendBuffer) goto done; 1153 if (!(flags & ZERO_SEND)) WideCharToMultiByte(CP_ACP, 0, *text, -1, sendBuffer, bufferSize, NULL, FALSE); 1154 *text = (WCHAR *)sendBuffer; 1155 } 1156 1157 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1158 1159 if (*text && oldText && (flags & CONVERT_RECEIVE)) 1160 { 1161 /* MultiByteToWideChar requires that source and destination are not the same buffer */ 1162 if (*text == oldText) 1163 { 1164 bufferSize = lstrlenA((CHAR *)*text) + 1; 1165 receiveBuffer = heap_alloc(bufferSize); 1166 if (!receiveBuffer) goto done; 1167 memcpy(receiveBuffer, *text, bufferSize); 1168 MultiByteToWideChar(CP_ACP, 0, receiveBuffer, bufferSize, oldText, oldTextMax); 1169 heap_free(receiveBuffer); 1170 } 1171 else 1172 MultiByteToWideChar(CP_ACP, 0, (CHAR *)*text, -1, oldText, oldTextMax); 1173 } 1174 1175 done: 1176 heap_free(sendBuffer); 1177 *text = oldText; 1178 return ret; 1179 } 1180 1181 static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr) 1182 { 1183 LRESULT ret; 1184 1185 if (infoPtr->bUnicode) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1186 1187 switch (hdr->code) 1188 { 1189 /* ComboBoxEx */ 1190 case CBEN_GETDISPINFOW: 1191 { 1192 NMCOMBOBOXEXW *nmcbe = (NMCOMBOBOXEXW *)hdr; 1193 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmcbe->ceItem.mask, CBEIF_TEXT, &nmcbe->ceItem.pszText, 1194 &nmcbe->ceItem.cchTextMax, ZERO_SEND | SET_NULL_IF_NO_MASK | CONVERT_RECEIVE); 1195 } 1196 case CBEN_DRAGBEGINW: 1197 { 1198 NMCBEDRAGBEGINW *nmdbW = (NMCBEDRAGBEGINW *)hdr; 1199 NMCBEDRAGBEGINA nmdbA = {{0}}; 1200 nmdbA.hdr.code = PAGER_GetAnsiNtfCode(nmdbW->hdr.code); 1201 nmdbA.hdr.hwndFrom = nmdbW->hdr.hwndFrom; 1202 nmdbA.hdr.idFrom = nmdbW->hdr.idFrom; 1203 nmdbA.iItemid = nmdbW->iItemid; 1204 WideCharToMultiByte(CP_ACP, 0, nmdbW->szText, ARRAY_SIZE(nmdbW->szText), nmdbA.szText, ARRAY_SIZE(nmdbA.szText), 1205 NULL, FALSE); 1206 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmdbA); 1207 } 1208 case CBEN_ENDEDITW: 1209 { 1210 NMCBEENDEDITW *nmedW = (NMCBEENDEDITW *)hdr; 1211 NMCBEENDEDITA nmedA = {{0}}; 1212 nmedA.hdr.code = PAGER_GetAnsiNtfCode(nmedW->hdr.code); 1213 nmedA.hdr.hwndFrom = nmedW->hdr.hwndFrom; 1214 nmedA.hdr.idFrom = nmedW->hdr.idFrom; 1215 nmedA.fChanged = nmedW->fChanged; 1216 nmedA.iNewSelection = nmedW->iNewSelection; 1217 nmedA.iWhy = nmedW->iWhy; 1218 WideCharToMultiByte(CP_ACP, 0, nmedW->szText, ARRAY_SIZE(nmedW->szText), nmedA.szText, ARRAY_SIZE(nmedA.szText), 1219 NULL, FALSE); 1220 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmedA); 1221 } 1222 /* Date and Time Picker */ 1223 case DTN_FORMATW: 1224 { 1225 NMDATETIMEFORMATW *nmdtf = (NMDATETIMEFORMATW *)hdr; 1226 WCHAR *oldFormat; 1227 INT textLength; 1228 1229 hdr->code = PAGER_GetAnsiNtfCode(hdr->code); 1230 oldFormat = PAGER_ConvertText((WCHAR **)&nmdtf->pszFormat); 1231 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)nmdtf); 1232 PAGER_RestoreText((WCHAR **)&nmdtf->pszFormat, oldFormat); 1233 1234 if (nmdtf->pszDisplay) 1235 { 1236 textLength = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)nmdtf->pszDisplay, -1, 0, 0); 1237 if (!PAGER_AdjustBuffer(infoPtr, textLength * sizeof(WCHAR))) return ret; 1238 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)nmdtf->pszDisplay, -1, infoPtr->pwszBuffer, textLength); 1239 if (nmdtf->pszDisplay != nmdtf->szDisplay) 1240 nmdtf->pszDisplay = infoPtr->pwszBuffer; 1241 else 1242 { 1243 textLength = min(textLength, ARRAY_SIZE(nmdtf->szDisplay)); 1244 memcpy(nmdtf->szDisplay, infoPtr->pwszBuffer, textLength * sizeof(WCHAR)); 1245 } 1246 } 1247 1248 return ret; 1249 } 1250 case DTN_FORMATQUERYW: 1251 { 1252 NMDATETIMEFORMATQUERYW *nmdtfq = (NMDATETIMEFORMATQUERYW *)hdr; 1253 return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdtfq->pszFormat, NULL, CONVERT_SEND); 1254 } 1255 case DTN_WMKEYDOWNW: 1256 { 1257 NMDATETIMEWMKEYDOWNW *nmdtkd = (NMDATETIMEWMKEYDOWNW *)hdr; 1258 return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdtkd->pszFormat, NULL, CONVERT_SEND); 1259 } 1260 case DTN_USERSTRINGW: 1261 { 1262 NMDATETIMESTRINGW *nmdts = (NMDATETIMESTRINGW *)hdr; 1263 return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdts->pszUserString, NULL, CONVERT_SEND); 1264 } 1265 /* Header */ 1266 case HDN_BEGINTRACKW: 1267 case HDN_DIVIDERDBLCLICKW: 1268 case HDN_ENDTRACKW: 1269 case HDN_ITEMCHANGEDW: 1270 case HDN_ITEMCHANGINGW: 1271 case HDN_ITEMCLICKW: 1272 case HDN_ITEMDBLCLICKW: 1273 case HDN_TRACKW: 1274 { 1275 NMHEADERW *nmh = (NMHEADERW *)hdr; 1276 WCHAR *oldText = NULL, *oldFilterText = NULL; 1277 HD_TEXTFILTERW *tf = NULL; 1278 1279 hdr->code = PAGER_GetAnsiNtfCode(hdr->code); 1280 1281 if (!nmh->pitem) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1282 if (nmh->pitem->mask & HDI_TEXT) oldText = PAGER_ConvertText(&nmh->pitem->pszText); 1283 if ((nmh->pitem->mask & HDI_FILTER) && (nmh->pitem->type == HDFT_ISSTRING) && nmh->pitem->pvFilter) 1284 { 1285 tf = (HD_TEXTFILTERW *)nmh->pitem->pvFilter; 1286 oldFilterText = PAGER_ConvertText(&tf->pszText); 1287 } 1288 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1289 PAGER_RestoreText(&nmh->pitem->pszText, oldText); 1290 if (tf) PAGER_RestoreText(&tf->pszText, oldFilterText); 1291 return ret; 1292 } 1293 case HDN_GETDISPINFOW: 1294 { 1295 NMHDDISPINFOW *nmhddi = (NMHDDISPINFOW *)hdr; 1296 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmhddi->mask, HDI_TEXT, &nmhddi->pszText, &nmhddi->cchTextMax, 1297 SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE); 1298 } 1299 /* List View */ 1300 case LVN_BEGINLABELEDITW: 1301 case LVN_ENDLABELEDITW: 1302 case LVN_SETDISPINFOW: 1303 { 1304 NMLVDISPINFOW *nmlvdi = (NMLVDISPINFOW *)hdr; 1305 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvdi->item.mask, LVIF_TEXT, &nmlvdi->item.pszText, 1306 &nmlvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE); 1307 } 1308 case LVN_GETDISPINFOW: 1309 { 1310 NMLVDISPINFOW *nmlvdi = (NMLVDISPINFOW *)hdr; 1311 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvdi->item.mask, LVIF_TEXT, &nmlvdi->item.pszText, 1312 &nmlvdi->item.cchTextMax, CONVERT_RECEIVE); 1313 } 1314 case LVN_GETINFOTIPW: 1315 { 1316 NMLVGETINFOTIPW *nmlvgit = (NMLVGETINFOTIPW *)hdr; 1317 return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmlvgit->pszText, &nmlvgit->cchTextMax, 1318 CONVERT_SEND | CONVERT_RECEIVE); 1319 } 1320 case LVN_INCREMENTALSEARCHW: 1321 case LVN_ODFINDITEMW: 1322 { 1323 NMLVFINDITEMW *nmlvfi = (NMLVFINDITEMW *)hdr; 1324 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvfi->lvfi.flags, LVFI_STRING | LVFI_SUBSTRING, 1325 (WCHAR **)&nmlvfi->lvfi.psz, NULL, CONVERT_SEND); 1326 } 1327 /* Toolbar */ 1328 case TBN_GETBUTTONINFOW: 1329 { 1330 NMTOOLBARW *nmtb = (NMTOOLBARW *)hdr; 1331 return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtb->pszText, &nmtb->cchText, 1332 SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE); 1333 } 1334 case TBN_GETINFOTIPW: 1335 { 1336 NMTBGETINFOTIPW *nmtbgit = (NMTBGETINFOTIPW *)hdr; 1337 return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtbgit->pszText, &nmtbgit->cchTextMax, CONVERT_RECEIVE); 1338 } 1339 /* Tooltip */ 1340 case TTN_GETDISPINFOW: 1341 { 1342 NMTTDISPINFOW *nmttdiW = (NMTTDISPINFOW *)hdr; 1343 NMTTDISPINFOA nmttdiA = {{0}}; 1344 INT size; 1345 1346 nmttdiA.hdr.code = PAGER_GetAnsiNtfCode(nmttdiW->hdr.code); 1347 nmttdiA.hdr.hwndFrom = nmttdiW->hdr.hwndFrom; 1348 nmttdiA.hdr.idFrom = nmttdiW->hdr.idFrom; 1349 nmttdiA.hinst = nmttdiW->hinst; 1350 nmttdiA.uFlags = nmttdiW->uFlags; 1351 nmttdiA.lParam = nmttdiW->lParam; 1352 nmttdiA.lpszText = nmttdiA.szText; 1353 WideCharToMultiByte(CP_ACP, 0, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText), nmttdiA.szText, 1354 ARRAY_SIZE(nmttdiA.szText), NULL, FALSE); 1355 1356 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmttdiA); 1357 1358 nmttdiW->hinst = nmttdiA.hinst; 1359 nmttdiW->uFlags = nmttdiA.uFlags; 1360 nmttdiW->lParam = nmttdiA.lParam; 1361 1362 MultiByteToWideChar(CP_ACP, 0, nmttdiA.szText, ARRAY_SIZE(nmttdiA.szText), nmttdiW->szText, 1363 ARRAY_SIZE(nmttdiW->szText)); 1364 if (!nmttdiA.lpszText) 1365 nmttdiW->lpszText = nmttdiW->szText; 1366 else if (!IS_INTRESOURCE(nmttdiA.lpszText)) 1367 { 1368 size = MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, 0, 0); 1369 if (size > ARRAY_SIZE(nmttdiW->szText)) 1370 { 1371 if (!PAGER_AdjustBuffer(infoPtr, size * sizeof(WCHAR))) return ret; 1372 MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, infoPtr->pwszBuffer, size); 1373 nmttdiW->lpszText = infoPtr->pwszBuffer; 1374 /* Override content in szText */ 1375 memcpy(nmttdiW->szText, nmttdiW->lpszText, min(sizeof(nmttdiW->szText), size * sizeof(WCHAR))); 1376 } 1377 else 1378 { 1379 MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText)); 1380 nmttdiW->lpszText = nmttdiW->szText; 1381 } 1382 } 1383 else 1384 { 1385 nmttdiW->szText[0] = 0; 1386 nmttdiW->lpszText = (WCHAR *)nmttdiA.lpszText; 1387 } 1388 1389 return ret; 1390 } 1391 /* Tree View */ 1392 case TVN_BEGINDRAGW: 1393 case TVN_BEGINRDRAGW: 1394 case TVN_ITEMEXPANDEDW: 1395 case TVN_ITEMEXPANDINGW: 1396 { 1397 NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr; 1398 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtv->itemNew.mask, TVIF_TEXT, &nmtv->itemNew.pszText, NULL, 1399 CONVERT_SEND); 1400 } 1401 case TVN_DELETEITEMW: 1402 { 1403 NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr; 1404 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtv->itemOld.mask, TVIF_TEXT, &nmtv->itemOld.pszText, NULL, 1405 CONVERT_SEND); 1406 } 1407 case TVN_BEGINLABELEDITW: 1408 case TVN_ENDLABELEDITW: 1409 { 1410 NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr; 1411 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText, 1412 &nmtvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE); 1413 } 1414 case TVN_SELCHANGINGW: 1415 case TVN_SELCHANGEDW: 1416 { 1417 NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr; 1418 WCHAR *oldItemOldText = NULL; 1419 WCHAR *oldItemNewText = NULL; 1420 1421 hdr->code = PAGER_GetAnsiNtfCode(hdr->code); 1422 1423 if (!((nmtv->itemNew.mask | nmtv->itemOld.mask) & TVIF_TEXT)) 1424 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1425 1426 if (nmtv->itemOld.mask & TVIF_TEXT) oldItemOldText = PAGER_ConvertText(&nmtv->itemOld.pszText); 1427 if (nmtv->itemNew.mask & TVIF_TEXT) oldItemNewText = PAGER_ConvertText(&nmtv->itemNew.pszText); 1428 1429 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1430 PAGER_RestoreText(&nmtv->itemOld.pszText, oldItemOldText); 1431 PAGER_RestoreText(&nmtv->itemNew.pszText, oldItemNewText); 1432 return ret; 1433 } 1434 case TVN_GETDISPINFOW: 1435 { 1436 NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr; 1437 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText, 1438 &nmtvdi->item.cchTextMax, ZERO_SEND | CONVERT_RECEIVE); 1439 } 1440 case TVN_SETDISPINFOW: 1441 { 1442 NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr; 1443 return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText, 1444 &nmtvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE); 1445 } 1446 case TVN_GETINFOTIPW: 1447 { 1448 NMTVGETINFOTIPW *nmtvgit = (NMTVGETINFOTIPW *)hdr; 1449 return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtvgit->pszText, &nmtvgit->cchTextMax, CONVERT_RECEIVE); 1450 } 1451 } 1452 /* Other notifications, no need to convert */ 1453 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); 1454 } 1455 1456 static LRESULT WINAPI 1457 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1458 { 1459 PAGER_INFO *infoPtr = (PAGER_INFO *)GetWindowLongPtrW(hwnd, 0); 1460 1461 TRACE("(%p, %#x, %#lx, %#lx)\n", hwnd, uMsg, wParam, lParam); 1462 1463 if (!infoPtr && (uMsg != WM_CREATE)) 1464 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 1465 1466 switch (uMsg) 1467 { 1468 case EM_FMTLINES: 1469 return PAGER_FmtLines(infoPtr); 1470 1471 case PGM_FORWARDMOUSE: 1472 return PAGER_ForwardMouse (infoPtr, (BOOL)wParam); 1473 1474 case PGM_GETBKCOLOR: 1475 return PAGER_GetBkColor(infoPtr); 1476 1477 case PGM_GETBORDER: 1478 return PAGER_GetBorder(infoPtr); 1479 1480 case PGM_GETBUTTONSIZE: 1481 return PAGER_GetButtonSize(infoPtr); 1482 1483 case PGM_GETPOS: 1484 return PAGER_GetPos(infoPtr); 1485 1486 case PGM_GETBUTTONSTATE: 1487 return PAGER_GetButtonState (infoPtr, (INT)lParam); 1488 1489 /* case PGM_GETDROPTARGET: */ 1490 1491 case PGM_RECALCSIZE: 1492 return PAGER_RecalcSize(infoPtr); 1493 1494 case PGM_SETBKCOLOR: 1495 return PAGER_SetBkColor (infoPtr, (COLORREF)lParam); 1496 1497 case PGM_SETBORDER: 1498 return PAGER_SetBorder (infoPtr, (INT)lParam); 1499 1500 case PGM_SETBUTTONSIZE: 1501 return PAGER_SetButtonSize (infoPtr, (INT)lParam); 1502 1503 case PGM_SETCHILD: 1504 return PAGER_SetChild (infoPtr, (HWND)lParam); 1505 1506 case PGM_SETPOS: 1507 return PAGER_SetPos(infoPtr, (INT)lParam, FALSE, TRUE); 1508 1509 case WM_CREATE: 1510 return PAGER_Create (hwnd, (LPCREATESTRUCTW)lParam); 1511 1512 case WM_DESTROY: 1513 return PAGER_Destroy (infoPtr); 1514 1515 case WM_SIZE: 1516 return PAGER_Size (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam)); 1517 1518 case WM_NCPAINT: 1519 return PAGER_NCPaint (infoPtr, (HRGN)wParam); 1520 1521 case WM_STYLECHANGED: 1522 return PAGER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam); 1523 1524 case WM_NCCALCSIZE: 1525 return PAGER_NCCalcSize (infoPtr, wParam, (LPRECT)lParam); 1526 1527 case WM_NCHITTEST: 1528 return PAGER_NCHitTest (infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam)); 1529 1530 case WM_MOUSEMOVE: 1531 if (infoPtr->bForward && infoPtr->hwndChild) 1532 PostMessageW(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam); 1533 return PAGER_MouseMove (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam)); 1534 1535 case WM_LBUTTONDOWN: 1536 return PAGER_LButtonDown (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam)); 1537 1538 case WM_LBUTTONUP: 1539 return PAGER_LButtonUp (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam)); 1540 1541 case WM_ERASEBKGND: 1542 return PAGER_EraseBackground (infoPtr, (HDC)wParam); 1543 1544 case WM_TIMER: 1545 return PAGER_Timer (infoPtr, (INT)wParam); 1546 1547 case WM_NOTIFYFORMAT: 1548 return PAGER_NotifyFormat (infoPtr, lParam); 1549 1550 case WM_NOTIFY: 1551 return PAGER_Notify (infoPtr, (NMHDR *)lParam); 1552 1553 case WM_COMMAND: 1554 return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam); 1555 1556 default: 1557 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 1558 } 1559 } 1560 1561 1562 VOID 1563 PAGER_Register (void) 1564 { 1565 WNDCLASSW wndClass; 1566 1567 ZeroMemory (&wndClass, sizeof(WNDCLASSW)); 1568 wndClass.style = CS_GLOBALCLASS; 1569 wndClass.lpfnWndProc = PAGER_WindowProc; 1570 wndClass.cbClsExtra = 0; 1571 wndClass.cbWndExtra = sizeof(PAGER_INFO *); 1572 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); 1573 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 1574 wndClass.lpszClassName = WC_PAGESCROLLERW; 1575 1576 RegisterClassW (&wndClass); 1577 } 1578 1579 1580 VOID 1581 PAGER_Unregister (void) 1582 { 1583 UnregisterClassW (WC_PAGESCROLLERW, NULL); 1584 } 1585