1 /* 2 * Header control 3 * 4 * Copyright 1998 Eric Kohl 5 * Copyright 2000 Eric Kohl for CodeWeavers 6 * Copyright 2003 Maxime Bellenge 7 * Copyright 2006 Mikolaj Zalewski 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 * 23 * TODO: 24 * - Imagelist support (completed?) 25 * - Hottrack support (completed?) 26 * - Filters support (HDS_FILTER, HDI_FILTER, HDM_*FILTER*, HDN_*FILTER*) 27 * - New Windows Vista features 28 */ 29 30 #include <stdarg.h> 31 #include <stdlib.h> 32 #include <string.h> 33 34 #include "windef.h" 35 #include "winbase.h" 36 #include "wine/unicode.h" 37 #include "wingdi.h" 38 #include "winuser.h" 39 #include "winnls.h" 40 #include "commctrl.h" 41 #include "comctl32.h" 42 #include "vssym32.h" 43 #include "uxtheme.h" 44 #include "wine/debug.h" 45 #include "wine/heap.h" 46 47 WINE_DEFAULT_DEBUG_CHANNEL(header); 48 49 typedef struct 50 { 51 INT cxy; 52 HBITMAP hbm; 53 LPWSTR pszText; 54 INT fmt; 55 LPARAM lParam; 56 INT iImage; 57 INT iOrder; /* see documentation of HD_ITEM */ 58 59 BOOL bDown; /* is item pressed? (used for drawing) */ 60 RECT rect; /* bounding rectangle of the item */ 61 DWORD callbackMask; /* HDI_* flags for items that are callback */ 62 } HEADER_ITEM; 63 64 65 typedef struct 66 { 67 HWND hwndSelf; /* Control window */ 68 HWND hwndNotify; /* Owner window to send notifications to */ 69 INT nNotifyFormat; /* format used for WM_NOTIFY messages */ 70 UINT uNumItem; /* number of items (columns) */ 71 INT nHeight; /* height of the header (pixels) */ 72 HFONT hFont; /* handle to the current font */ 73 HCURSOR hcurArrow; /* handle to the arrow cursor */ 74 HCURSOR hcurDivider; /* handle to a cursor (used over dividers) <-|-> */ 75 HCURSOR hcurDivopen; /* handle to a cursor (used over dividers) <-||-> */ 76 BOOL bCaptured; /* Is the mouse captured? */ 77 BOOL bPressed; /* Is a header item pressed (down)? */ 78 BOOL bDragging; /* Are we dragging an item? */ 79 BOOL bTracking; /* Is in tracking mode? */ 80 POINT ptLButtonDown; /* The point where the left button was pressed */ 81 DWORD dwStyle; /* the cached window GWL_STYLE */ 82 INT iMoveItem; /* index of tracked item. (Tracking mode) */ 83 INT xTrackOffset; /* distance between the right side of the tracked item and the cursor */ 84 INT xOldTrack; /* track offset (see above) after the last WM_MOUSEMOVE */ 85 INT iHotItem; /* index of hot item (cursor is over this item) */ 86 INT iHotDivider; /* index of the hot divider (used while dragging an item or by HDM_SETHOTDIVIDER) */ 87 INT iMargin; /* width of the margin that surrounds a bitmap */ 88 INT filter_change_timeout; /* change timeout set with HDM_SETFILTERCHANGETIMEOUT */ 89 90 HIMAGELIST himl; /* handle to an image list (may be 0) */ 91 HEADER_ITEM *items; /* pointer to array of HEADER_ITEM's */ 92 INT *order; /* array of item IDs indexed by order */ 93 BOOL bRectsValid; /* validity flag for bounding rectangles */ 94 } HEADER_INFO; 95 96 97 #define VERT_BORDER 4 98 #define DIVIDER_WIDTH 10 99 #define HOT_DIVIDER_WIDTH 2 100 #define MAX_HEADER_TEXT_LEN 260 101 #define HDN_UNICODE_OFFSET 20 102 #define HDN_FIRST_UNICODE (HDN_FIRST-HDN_UNICODE_OFFSET) 103 104 #define HDI_SUPPORTED_FIELDS (HDI_WIDTH|HDI_TEXT|HDI_FORMAT|HDI_LPARAM|HDI_BITMAP|HDI_IMAGE|HDI_ORDER) 105 #define HDI_UNSUPPORTED_FIELDS (HDI_FILTER) 106 #define HDI_UNKNOWN_FIELDS (~(HDI_SUPPORTED_FIELDS|HDI_UNSUPPORTED_FIELDS|HDI_DI_SETITEM)) 107 #define HDI_COMCTL32_4_0_FIELDS (HDI_WIDTH|HDI_TEXT|HDI_FORMAT|HDI_LPARAM|HDI_BITMAP) 108 109 110 static BOOL HEADER_PrepareCallbackItems(const HEADER_INFO *infoPtr, INT iItem, INT reqMask); 111 static void HEADER_FreeCallbackItems(HEADER_ITEM *lpItem); 112 static LRESULT HEADER_SendNotify(const HEADER_INFO *infoPtr, UINT code, NMHDR *hdr); 113 static LRESULT HEADER_SendCtrlCustomDraw(const HEADER_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, const RECT *rect); 114 115 static const WCHAR themeClass[] = {'H','e','a','d','e','r',0}; 116 117 static void HEADER_StoreHDItemInHeader(HEADER_ITEM *lpItem, UINT mask, const HDITEMW *phdi, BOOL fUnicode) 118 { 119 if (mask & HDI_UNSUPPORTED_FIELDS) 120 FIXME("unsupported header fields %x\n", (mask & HDI_UNSUPPORTED_FIELDS)); 121 122 if (mask & HDI_BITMAP) 123 lpItem->hbm = phdi->hbm; 124 125 if (mask & HDI_FORMAT) 126 lpItem->fmt = phdi->fmt; 127 128 if (mask & HDI_LPARAM) 129 lpItem->lParam = phdi->lParam; 130 131 if (mask & HDI_WIDTH) 132 lpItem->cxy = phdi->cxy; 133 134 if (mask & HDI_IMAGE) 135 { 136 lpItem->iImage = phdi->iImage; 137 if (phdi->iImage == I_IMAGECALLBACK) 138 lpItem->callbackMask |= HDI_IMAGE; 139 else 140 lpItem->callbackMask &= ~HDI_IMAGE; 141 } 142 143 if (mask & HDI_TEXT) 144 { 145 heap_free(lpItem->pszText); 146 lpItem->pszText = NULL; 147 148 if (phdi->pszText != LPSTR_TEXTCALLBACKW) /* covers != TEXTCALLBACKA too */ 149 { 150 static const WCHAR emptyString[] = {0}; 151 152 LPCWSTR pszText = (phdi->pszText != NULL ? phdi->pszText : emptyString); 153 if (fUnicode) 154 Str_SetPtrW(&lpItem->pszText, pszText); 155 else 156 Str_SetPtrAtoW(&lpItem->pszText, (LPCSTR)pszText); 157 lpItem->callbackMask &= ~HDI_TEXT; 158 } 159 else 160 { 161 lpItem->pszText = NULL; 162 lpItem->callbackMask |= HDI_TEXT; 163 } 164 } 165 } 166 167 static inline LRESULT 168 HEADER_IndexToOrder (const HEADER_INFO *infoPtr, INT iItem) 169 { 170 HEADER_ITEM *lpItem = &infoPtr->items[iItem]; 171 return lpItem->iOrder; 172 } 173 174 175 static INT 176 HEADER_OrderToIndex(const HEADER_INFO *infoPtr, INT iorder) 177 { 178 if ((iorder <0) || iorder >= infoPtr->uNumItem) 179 return iorder; 180 return infoPtr->order[iorder]; 181 } 182 183 static void 184 HEADER_ChangeItemOrder(const HEADER_INFO *infoPtr, INT iItem, INT iNewOrder) 185 { 186 HEADER_ITEM *lpItem = &infoPtr->items[iItem]; 187 INT i, nMin, nMax; 188 189 TRACE("%d: %d->%d\n", iItem, lpItem->iOrder, iNewOrder); 190 if (lpItem->iOrder < iNewOrder) 191 { 192 memmove(&infoPtr->order[lpItem->iOrder], 193 &infoPtr->order[lpItem->iOrder + 1], 194 (iNewOrder - lpItem->iOrder) * sizeof(INT)); 195 } 196 if (iNewOrder < lpItem->iOrder) 197 { 198 memmove(&infoPtr->order[iNewOrder + 1], 199 &infoPtr->order[iNewOrder], 200 (lpItem->iOrder - iNewOrder) * sizeof(INT)); 201 } 202 infoPtr->order[iNewOrder] = iItem; 203 nMin = min(lpItem->iOrder, iNewOrder); 204 nMax = max(lpItem->iOrder, iNewOrder); 205 for (i = nMin; i <= nMax; i++) 206 infoPtr->items[infoPtr->order[i]].iOrder = i; 207 } 208 209 /* Note: if iItem is the last item then this function returns infoPtr->uNumItem */ 210 static INT 211 HEADER_NextItem(const HEADER_INFO *infoPtr, INT iItem) 212 { 213 return HEADER_OrderToIndex(infoPtr, HEADER_IndexToOrder(infoPtr, iItem)+1); 214 } 215 216 static INT 217 HEADER_PrevItem(const HEADER_INFO *infoPtr, INT iItem) 218 { 219 return HEADER_OrderToIndex(infoPtr, HEADER_IndexToOrder(infoPtr, iItem)-1); 220 } 221 222 /* TRUE when item is not resizable with dividers, 223 note that valid index should be supplied */ 224 static inline BOOL 225 HEADER_IsItemFixed(const HEADER_INFO *infoPtr, INT iItem) 226 { 227 return (infoPtr->dwStyle & HDS_NOSIZING) || (infoPtr->items[iItem].fmt & HDF_FIXEDWIDTH); 228 } 229 230 static void 231 HEADER_SetItemBounds (HEADER_INFO *infoPtr) 232 { 233 HEADER_ITEM *phdi; 234 RECT rect; 235 unsigned int i; 236 int x; 237 238 infoPtr->bRectsValid = TRUE; 239 240 if (infoPtr->uNumItem == 0) 241 return; 242 243 GetClientRect (infoPtr->hwndSelf, &rect); 244 245 x = rect.left; 246 for (i = 0; i < infoPtr->uNumItem; i++) { 247 phdi = &infoPtr->items[HEADER_OrderToIndex(infoPtr,i)]; 248 phdi->rect.top = rect.top; 249 phdi->rect.bottom = rect.bottom; 250 phdi->rect.left = x; 251 phdi->rect.right = phdi->rect.left + ((phdi->cxy>0)?phdi->cxy:0); 252 x = phdi->rect.right; 253 } 254 } 255 256 static LRESULT 257 HEADER_Size (HEADER_INFO *infoPtr) 258 { 259 HEADER_SetItemBounds(infoPtr); 260 return 0; 261 } 262 263 static void HEADER_GetHotDividerRect(const HEADER_INFO *infoPtr, RECT *r) 264 { 265 INT iDivider = infoPtr->iHotDivider; 266 if (infoPtr->uNumItem > 0) 267 { 268 HEADER_ITEM *lpItem; 269 270 if (iDivider < infoPtr->uNumItem) 271 { 272 lpItem = &infoPtr->items[iDivider]; 273 r->left = lpItem->rect.left - HOT_DIVIDER_WIDTH/2; 274 r->right = lpItem->rect.left + HOT_DIVIDER_WIDTH/2; 275 } 276 else 277 { 278 lpItem = &infoPtr->items[HEADER_OrderToIndex(infoPtr, infoPtr->uNumItem-1)]; 279 r->left = lpItem->rect.right - HOT_DIVIDER_WIDTH/2; 280 r->right = lpItem->rect.right + HOT_DIVIDER_WIDTH/2; 281 } 282 r->top = lpItem->rect.top; 283 r->bottom = lpItem->rect.bottom; 284 } 285 else 286 { 287 RECT clientRect; 288 GetClientRect(infoPtr->hwndSelf, &clientRect); 289 *r = clientRect; 290 r->right = r->left + HOT_DIVIDER_WIDTH/2; 291 } 292 } 293 294 static void 295 HEADER_FillItemFrame(HEADER_INFO *infoPtr, HDC hdc, RECT *r, const HEADER_ITEM *item, BOOL hottrack) 296 { 297 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); 298 299 if (theme) { 300 int state = (item->bDown) ? HIS_PRESSED : (hottrack ? HIS_HOT : HIS_NORMAL); 301 DrawThemeBackground (theme, hdc, HP_HEADERITEM, state, r, NULL); 302 GetThemeBackgroundContentRect (theme, hdc, HP_HEADERITEM, state, r, r); 303 } 304 else 305 { 306 HBRUSH hbr = CreateSolidBrush(GetBkColor(hdc)); 307 FillRect(hdc, r, hbr); 308 DeleteObject(hbr); 309 } 310 } 311 312 static void 313 HEADER_DrawItemFrame(HEADER_INFO *infoPtr, HDC hdc, RECT *r, const HEADER_ITEM *item) 314 { 315 if (GetWindowTheme(infoPtr->hwndSelf)) return; 316 317 if (!(infoPtr->dwStyle & HDS_FLAT)) 318 { 319 if (infoPtr->dwStyle & HDS_BUTTONS) { 320 if (item->bDown) 321 DrawEdge (hdc, r, BDR_RAISEDOUTER, BF_RECT | BF_FLAT | BF_ADJUST); 322 else 323 DrawEdge (hdc, r, EDGE_RAISED, BF_RECT | BF_SOFT | BF_ADJUST); 324 } 325 else 326 DrawEdge (hdc, r, EDGE_ETCHED, BF_BOTTOM | BF_RIGHT | BF_ADJUST); 327 } 328 } 329 330 /* Create a region for the sort arrow with its bounding rect's top-left 331 co-ord x,y and its height h. */ 332 static HRGN create_sort_arrow( INT x, INT y, INT h, BOOL is_up ) 333 { 334 char buffer[256]; 335 RGNDATA *data = (RGNDATA *)buffer; 336 DWORD size = FIELD_OFFSET(RGNDATA, Buffer[h * sizeof(RECT)]); 337 INT i, yinc = 1; 338 HRGN rgn; 339 340 if (size > sizeof(buffer)) 341 { 342 data = heap_alloc( size ); 343 if (!data) return NULL; 344 } 345 data->rdh.dwSize = sizeof(data->rdh); 346 data->rdh.iType = RDH_RECTANGLES; 347 data->rdh.nCount = 0; 348 data->rdh.nRgnSize = h * sizeof(RECT); 349 350 if (!is_up) 351 { 352 y += h - 1; 353 yinc = -1; 354 } 355 356 x += h - 1; /* set x to the centre */ 357 358 for (i = 0; i < h; i++, y += yinc) 359 { 360 RECT *rect = (RECT *)data->Buffer + data->rdh.nCount; 361 rect->left = x - i; 362 rect->top = y; 363 rect->right = x + i + 1; 364 rect->bottom = y + 1; 365 data->rdh.nCount++; 366 } 367 rgn = ExtCreateRegion( NULL, size, data ); 368 if (data != (RGNDATA *)buffer) heap_free( data ); 369 return rgn; 370 } 371 372 static INT 373 HEADER_DrawItem (HEADER_INFO *infoPtr, HDC hdc, INT iItem, BOOL bHotTrack, LRESULT lCDFlags) 374 { 375 HEADER_ITEM *phdi = &infoPtr->items[iItem]; 376 RECT r; 377 INT oldBkMode; 378 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); 379 NMCUSTOMDRAW nmcd; 380 int state = 0; 381 382 TRACE("DrawItem(iItem %d bHotTrack %d unicode flag %d)\n", iItem, bHotTrack, (infoPtr->nNotifyFormat == NFR_UNICODE)); 383 384 r = phdi->rect; 385 if (r.right - r.left == 0) 386 return phdi->rect.right; 387 388 if (theme) 389 state = (phdi->bDown) ? HIS_PRESSED : (bHotTrack ? HIS_HOT : HIS_NORMAL); 390 391 /* Set the colors before sending NM_CUSTOMDRAW so that it can change them */ 392 SetTextColor(hdc, (bHotTrack && !theme) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT); 393 SetBkColor(hdc, comctl32_color.clr3dFace); 394 395 if (lCDFlags & CDRF_NOTIFYITEMDRAW && !(phdi->fmt & HDF_OWNERDRAW)) 396 { 397 LRESULT lCDItemFlags; 398 399 nmcd.dwDrawStage = CDDS_PREPAINT | CDDS_ITEM; 400 nmcd.hdc = hdc; 401 nmcd.dwItemSpec = iItem; 402 nmcd.rc = r; 403 nmcd.uItemState = phdi->bDown ? CDIS_SELECTED : 0; 404 nmcd.lItemlParam = phdi->lParam; 405 406 lCDItemFlags = HEADER_SendNotify(infoPtr, NM_CUSTOMDRAW, (NMHDR *)&nmcd); 407 if (lCDItemFlags & CDRF_SKIPDEFAULT) 408 return phdi->rect.right; 409 } 410 411 /* Fill background, owner could draw over it. */ 412 HEADER_FillItemFrame(infoPtr, hdc, &r, phdi, bHotTrack); 413 414 if (phdi->fmt & HDF_OWNERDRAW) 415 { 416 DRAWITEMSTRUCT dis; 417 BOOL ret; 418 419 dis.CtlType = ODT_HEADER; 420 dis.CtlID = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID); 421 dis.itemID = iItem; 422 dis.itemAction = ODA_DRAWENTIRE; 423 dis.itemState = phdi->bDown ? ODS_SELECTED : 0; 424 dis.hwndItem = infoPtr->hwndSelf; 425 dis.hDC = hdc; 426 dis.rcItem = phdi->rect; 427 dis.itemData = phdi->lParam; 428 oldBkMode = SetBkMode(hdc, TRANSPARENT); 429 ret = SendMessageW (infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); 430 if (oldBkMode != TRANSPARENT) 431 SetBkMode(hdc, oldBkMode); 432 433 if (!ret) 434 HEADER_FillItemFrame(infoPtr, hdc, &r, phdi, bHotTrack); 435 436 /* Edges are always drawn if we don't have attached theme. */ 437 HEADER_DrawItemFrame(infoPtr, hdc, &r, phdi); 438 /* If application processed WM_DRAWITEM we should skip label painting, 439 edges are drawn no matter what. */ 440 if (ret) return phdi->rect.right; 441 } 442 else 443 HEADER_DrawItemFrame(infoPtr, hdc, &r, phdi); 444 445 if (phdi->bDown) { 446 r.left += 2; 447 r.top += 2; 448 } 449 450 /* Now text and image */ 451 { 452 INT rw, rh; /* width and height of r */ 453 INT *x = NULL; /* x and ... */ 454 UINT *w = NULL; /* ... width of the pic (bmp or img) which is part of cnt */ 455 /* cnt,txt,img,bmp */ 456 INT cx, tx, ix, bx; 457 UINT cw, tw, iw, bw; 458 INT img_cx, img_cy; 459 INT sort_w, sort_x, sort_h; 460 BITMAP bmp; 461 462 HEADER_PrepareCallbackItems(infoPtr, iItem, HDI_TEXT|HDI_IMAGE); 463 cw = iw = bw = sort_w = sort_h = 0; 464 rw = r.right - r.left; 465 rh = r.bottom - r.top; 466 467 if (phdi->fmt & HDF_STRING) { 468 RECT textRect; 469 470 SetRectEmpty(&textRect); 471 472 if (theme) { 473 GetThemeTextExtent(theme, hdc, HP_HEADERITEM, state, phdi->pszText, -1, 474 DT_LEFT|DT_VCENTER|DT_SINGLELINE, NULL, &textRect); 475 } else { 476 DrawTextW (hdc, phdi->pszText, -1, 477 &textRect, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_CALCRECT); 478 } 479 cw = textRect.right - textRect.left + 2 * infoPtr->iMargin; 480 } 481 482 if (phdi->fmt & (HDF_SORTUP | HDF_SORTDOWN)) { 483 sort_h = MulDiv( infoPtr->nHeight - VERT_BORDER, 4, 13 ); 484 sort_w = 2 * sort_h - 1 + infoPtr->iMargin * 2; 485 cw += sort_w; 486 } else { /* sort arrows take precedent over images/bitmaps */ 487 if ((phdi->fmt & HDF_IMAGE) && ImageList_GetIconSize( infoPtr->himl, &img_cx, &img_cy )) { 488 iw = img_cx + 2 * infoPtr->iMargin; 489 x = &ix; 490 w = &iw; 491 } 492 493 if ((phdi->fmt & HDF_BITMAP) && (phdi->hbm)) { 494 GetObjectW (phdi->hbm, sizeof(BITMAP), &bmp); 495 bw = bmp.bmWidth + 2 * infoPtr->iMargin; 496 if (!iw) { 497 x = &bx; 498 w = &bw; 499 } 500 } 501 if (bw || iw) 502 cw += *w; 503 } 504 505 /* align cx using the unclipped cw */ 506 if ((phdi->fmt & HDF_JUSTIFYMASK) == HDF_LEFT) 507 cx = r.left; 508 else if ((phdi->fmt & HDF_JUSTIFYMASK) == HDF_CENTER) 509 cx = r.left + rw / 2 - cw / 2; 510 else /* HDF_RIGHT */ 511 cx = r.right - cw; 512 513 /* clip cx & cw */ 514 if (cx < r.left) 515 cx = r.left; 516 if (cx + cw > r.right) 517 cw = r.right - cx; 518 519 tx = cx + infoPtr->iMargin; 520 /* since cw might have changed we have to recalculate tw */ 521 tw = cw - infoPtr->iMargin * 2; 522 523 tw -= sort_w; 524 sort_x = cx + tw + infoPtr->iMargin * 3; 525 526 if (iw || bw) { 527 tw -= *w; 528 if (phdi->fmt & HDF_BITMAP_ON_RIGHT) { 529 /* put pic behind text */ 530 *x = cx + tw + infoPtr->iMargin * 3; 531 } else { 532 *x = cx + infoPtr->iMargin; 533 /* move text behind pic */ 534 tx += *w; 535 } 536 } 537 538 if (iw && bw) { 539 /* since we're done with the layout we can 540 now calculate the position of bmp which 541 has no influence on alignment and layout 542 because of img */ 543 if ((phdi->fmt & HDF_JUSTIFYMASK) == HDF_RIGHT) 544 bx = cx - bw + infoPtr->iMargin; 545 else 546 bx = cx + cw + infoPtr->iMargin; 547 } 548 549 if (sort_w || iw || bw) { 550 HDC hClipDC = GetDC(infoPtr->hwndSelf); 551 HRGN hClipRgn = CreateRectRgn(r.left, r.top, r.right, r.bottom); 552 SelectClipRgn(hClipDC, hClipRgn); 553 554 if (sort_w) { 555 HRGN arrow = create_sort_arrow( sort_x, r.top + (rh - sort_h) / 2, 556 sort_h, phdi->fmt & HDF_SORTUP ); 557 if (arrow) { 558 FillRgn( hClipDC, arrow, GetSysColorBrush( COLOR_GRAYTEXT ) ); 559 DeleteObject( arrow ); 560 } 561 } 562 563 if (bw) { 564 HDC hdcBitmap = CreateCompatibleDC (hClipDC); 565 SelectObject (hdcBitmap, phdi->hbm); 566 BitBlt (hClipDC, bx, r.top + (rh - bmp.bmHeight) / 2, 567 bmp.bmWidth, bmp.bmHeight, hdcBitmap, 0, 0, SRCCOPY); 568 DeleteDC (hdcBitmap); 569 } 570 571 if (iw) { 572 ImageList_DrawEx (infoPtr->himl, phdi->iImage, hClipDC, 573 ix, r.top + (rh - img_cy) / 2, 574 img_cx, img_cy, CLR_DEFAULT, CLR_DEFAULT, 0); 575 } 576 577 DeleteObject(hClipRgn); 578 ReleaseDC(infoPtr->hwndSelf, hClipDC); 579 } 580 581 if (((phdi->fmt & HDF_STRING) 582 || (!(phdi->fmt & (HDF_OWNERDRAW|HDF_STRING|HDF_BITMAP| 583 HDF_BITMAP_ON_RIGHT|HDF_IMAGE)))) /* no explicit format specified? */ 584 && (phdi->pszText)) { 585 oldBkMode = SetBkMode(hdc, TRANSPARENT); 586 r.left = tx; 587 r.right = tx + tw; 588 if (theme) { 589 DrawThemeText(theme, hdc, HP_HEADERITEM, state, phdi->pszText, 590 -1, DT_LEFT|DT_END_ELLIPSIS|DT_VCENTER|DT_SINGLELINE, 591 0, &r); 592 } else { 593 DrawTextW (hdc, phdi->pszText, -1, 594 &r, DT_LEFT|DT_END_ELLIPSIS|DT_VCENTER|DT_SINGLELINE); 595 } 596 if (oldBkMode != TRANSPARENT) 597 SetBkMode(hdc, oldBkMode); 598 } 599 HEADER_FreeCallbackItems(phdi); 600 } 601 602 return phdi->rect.right; 603 } 604 605 static void 606 HEADER_DrawHotDivider(const HEADER_INFO *infoPtr, HDC hdc) 607 { 608 HBRUSH brush; 609 RECT r; 610 611 HEADER_GetHotDividerRect(infoPtr, &r); 612 brush = CreateSolidBrush(comctl32_color.clrHighlight); 613 FillRect(hdc, &r, brush); 614 DeleteObject(brush); 615 } 616 617 static void 618 HEADER_Refresh (HEADER_INFO *infoPtr, HDC hdc) 619 { 620 HFONT hFont, hOldFont; 621 RECT rect, rcRest; 622 HBRUSH hbrBk; 623 UINT i; 624 INT x; 625 LRESULT lCDFlags; 626 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); 627 628 if (!infoPtr->bRectsValid) 629 HEADER_SetItemBounds(infoPtr); 630 631 /* get rect for the bar, adjusted for the border */ 632 GetClientRect (infoPtr->hwndSelf, &rect); 633 lCDFlags = HEADER_SendCtrlCustomDraw(infoPtr, CDDS_PREPAINT, hdc, &rect); 634 635 if (infoPtr->bDragging) 636 ImageList_DragShowNolock(FALSE); 637 638 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT); 639 hOldFont = SelectObject (hdc, hFont); 640 641 /* draw Background */ 642 if (infoPtr->uNumItem == 0 && theme == NULL) { 643 hbrBk = GetSysColorBrush(COLOR_3DFACE); 644 FillRect(hdc, &rect, hbrBk); 645 } 646 647 x = rect.left; 648 for (i = 0; x <= rect.right && i < infoPtr->uNumItem; i++) { 649 int idx = HEADER_OrderToIndex(infoPtr,i); 650 if (RectVisible(hdc, &infoPtr->items[idx].rect)) 651 HEADER_DrawItem(infoPtr, hdc, idx, infoPtr->iHotItem == idx, lCDFlags); 652 x = infoPtr->items[idx].rect.right; 653 } 654 655 rcRest = rect; 656 rcRest.left = x; 657 if ((x <= rect.right) && RectVisible(hdc, &rcRest) && (infoPtr->uNumItem > 0)) { 658 if (theme != NULL) { 659 DrawThemeBackground(theme, hdc, HP_HEADERITEM, HIS_NORMAL, &rcRest, NULL); 660 } 661 else if (infoPtr->dwStyle & HDS_FLAT) { 662 hbrBk = GetSysColorBrush(COLOR_3DFACE); 663 FillRect(hdc, &rcRest, hbrBk); 664 } 665 else 666 { 667 if (infoPtr->dwStyle & HDS_BUTTONS) 668 DrawEdge (hdc, &rcRest, EDGE_RAISED, BF_TOP|BF_LEFT|BF_BOTTOM|BF_SOFT|BF_MIDDLE); 669 else 670 DrawEdge (hdc, &rcRest, EDGE_ETCHED, BF_BOTTOM|BF_MIDDLE); 671 } 672 } 673 674 if (infoPtr->iHotDivider != -1) 675 HEADER_DrawHotDivider(infoPtr, hdc); 676 677 if (infoPtr->bDragging) 678 ImageList_DragShowNolock(TRUE); 679 SelectObject (hdc, hOldFont); 680 681 if (lCDFlags & CDRF_NOTIFYPOSTPAINT) 682 HEADER_SendCtrlCustomDraw(infoPtr, CDDS_POSTPAINT, hdc, &rect); 683 } 684 685 686 static void 687 HEADER_RefreshItem (HEADER_INFO *infoPtr, INT iItem) 688 { 689 if (!infoPtr->bRectsValid) 690 HEADER_SetItemBounds(infoPtr); 691 692 InvalidateRect(infoPtr->hwndSelf, &infoPtr->items[iItem].rect, FALSE); 693 } 694 695 696 static void 697 HEADER_InternalHitTest (const HEADER_INFO *infoPtr, const POINT *lpPt, UINT *pFlags, INT *pItem) 698 { 699 RECT rect, rcTest; 700 UINT iCount; 701 INT width; 702 BOOL bNoWidth; 703 704 GetClientRect (infoPtr->hwndSelf, &rect); 705 706 *pFlags = 0; 707 bNoWidth = FALSE; 708 if (PtInRect (&rect, *lpPt)) 709 { 710 if (infoPtr->uNumItem == 0) { 711 *pFlags |= HHT_NOWHERE; 712 *pItem = 1; 713 TRACE("NOWHERE\n"); 714 return; 715 } 716 else { 717 /* somewhere inside */ 718 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++) { 719 rect = infoPtr->items[iCount].rect; 720 width = rect.right - rect.left; 721 if (width == 0) { 722 bNoWidth = TRUE; 723 continue; 724 } 725 if (PtInRect (&rect, *lpPt)) { 726 if (width <= 2 * DIVIDER_WIDTH) { 727 *pFlags |= HHT_ONHEADER; 728 *pItem = iCount; 729 TRACE("ON HEADER %d\n", iCount); 730 return; 731 } 732 if (HEADER_IndexToOrder(infoPtr, iCount) > 0) { 733 rcTest = rect; 734 rcTest.right = rcTest.left + DIVIDER_WIDTH; 735 if (PtInRect (&rcTest, *lpPt)) { 736 if (HEADER_IsItemFixed(infoPtr, HEADER_PrevItem(infoPtr, iCount))) 737 { 738 *pFlags |= HHT_ONHEADER; 739 *pItem = iCount; 740 TRACE("ON HEADER %d\n", *pItem); 741 return; 742 } 743 if (bNoWidth) { 744 *pFlags |= HHT_ONDIVOPEN; 745 *pItem = HEADER_PrevItem(infoPtr, iCount); 746 TRACE("ON DIVOPEN %d\n", *pItem); 747 return; 748 } 749 else { 750 *pFlags |= HHT_ONDIVIDER; 751 *pItem = HEADER_PrevItem(infoPtr, iCount); 752 TRACE("ON DIVIDER %d\n", *pItem); 753 return; 754 } 755 } 756 } 757 rcTest = rect; 758 rcTest.left = rcTest.right - DIVIDER_WIDTH; 759 if (!HEADER_IsItemFixed(infoPtr, iCount) && PtInRect (&rcTest, *lpPt)) 760 { 761 *pFlags |= HHT_ONDIVIDER; 762 *pItem = iCount; 763 TRACE("ON DIVIDER %d\n", *pItem); 764 return; 765 } 766 767 *pFlags |= HHT_ONHEADER; 768 *pItem = iCount; 769 TRACE("ON HEADER %d\n", iCount); 770 return; 771 } 772 } 773 774 /* check for last divider part (on nowhere) */ 775 if (!HEADER_IsItemFixed(infoPtr, infoPtr->uNumItem - 1)) 776 { 777 rect = infoPtr->items[infoPtr->uNumItem-1].rect; 778 rect.left = rect.right; 779 rect.right += DIVIDER_WIDTH; 780 if (PtInRect (&rect, *lpPt)) { 781 if (bNoWidth) { 782 *pFlags |= HHT_ONDIVOPEN; 783 *pItem = infoPtr->uNumItem - 1; 784 TRACE("ON DIVOPEN %d\n", *pItem); 785 return; 786 } 787 else { 788 *pFlags |= HHT_ONDIVIDER; 789 *pItem = infoPtr->uNumItem - 1; 790 TRACE("ON DIVIDER %d\n", *pItem); 791 return; 792 } 793 } 794 } 795 796 *pFlags |= HHT_NOWHERE; 797 *pItem = 1; 798 TRACE("NOWHERE\n"); 799 return; 800 } 801 } 802 else { 803 if (lpPt->x < rect.left) { 804 TRACE("TO LEFT\n"); 805 *pFlags |= HHT_TOLEFT; 806 } 807 else if (lpPt->x > rect.right) { 808 TRACE("TO RIGHT\n"); 809 *pFlags |= HHT_TORIGHT; 810 } 811 812 if (lpPt->y < rect.top) { 813 TRACE("ABOVE\n"); 814 *pFlags |= HHT_ABOVE; 815 } 816 else if (lpPt->y > rect.bottom) { 817 TRACE("BELOW\n"); 818 *pFlags |= HHT_BELOW; 819 } 820 } 821 822 *pItem = 1; 823 TRACE("flags=0x%X\n", *pFlags); 824 return; 825 } 826 827 828 static void 829 HEADER_DrawTrackLine (const HEADER_INFO *infoPtr, HDC hdc, INT x) 830 { 831 RECT rect; 832 833 GetClientRect (infoPtr->hwndSelf, &rect); 834 PatBlt( hdc, x, rect.top, 1, rect.bottom - rect.top, DSTINVERT ); 835 } 836 837 /*** 838 * DESCRIPTION: 839 * Convert a HDITEM into the correct format (ANSI/Unicode) to send it in a notify 840 * 841 * PARAMETER(S): 842 * [I] infoPtr : the header that wants to send the notify 843 * [O] dest : The buffer to store the HDITEM for notify. It may be set to a HDITEMA of HDITEMW 844 * [I] src : The source HDITEM. It may be a HDITEMA or HDITEMW 845 * [I] fSourceUnicode : is src a HDITEMW or HDITEMA 846 * [O] ppvScratch : a pointer to a scratch buffer that needs to be freed after 847 * the HDITEM is no longer in use or NULL if none was needed 848 * 849 * NOTE: We depend on HDITEMA and HDITEMW having the same structure 850 */ 851 static void HEADER_CopyHDItemForNotify(const HEADER_INFO *infoPtr, HDITEMW *dest, 852 const HDITEMW *src, BOOL fSourceUnicode, LPVOID *ppvScratch) 853 { 854 *ppvScratch = NULL; 855 *dest = *src; 856 857 if (src->mask & HDI_TEXT && src->pszText != LPSTR_TEXTCALLBACKW) /* covers TEXTCALLBACKA as well */ 858 { 859 if (fSourceUnicode && infoPtr->nNotifyFormat != NFR_UNICODE) 860 { 861 dest->pszText = NULL; 862 Str_SetPtrWtoA((LPSTR *)&dest->pszText, src->pszText); 863 *ppvScratch = dest->pszText; 864 } 865 866 if (!fSourceUnicode && infoPtr->nNotifyFormat == NFR_UNICODE) 867 { 868 dest->pszText = NULL; 869 Str_SetPtrAtoW(&dest->pszText, (LPSTR)src->pszText); 870 *ppvScratch = dest->pszText; 871 } 872 } 873 } 874 875 static UINT HEADER_NotifyCodeWtoA(UINT code) 876 { 877 /* we use the fact that all the unicode messages are in HDN_FIRST_UNICODE..HDN_LAST*/ 878 if (code >= HDN_LAST && code <= HDN_FIRST_UNICODE) 879 return code + HDN_UNICODE_OFFSET; 880 else 881 return code; 882 } 883 884 static LRESULT 885 HEADER_SendNotify(const HEADER_INFO *infoPtr, UINT code, NMHDR *nmhdr) 886 { 887 nmhdr->hwndFrom = infoPtr->hwndSelf; 888 nmhdr->idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID); 889 nmhdr->code = code; 890 891 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, 892 nmhdr->idFrom, (LPARAM)nmhdr); 893 } 894 895 static BOOL 896 HEADER_SendSimpleNotify (const HEADER_INFO *infoPtr, UINT code) 897 { 898 NMHDR nmhdr; 899 return (BOOL)HEADER_SendNotify(infoPtr, code, &nmhdr); 900 } 901 902 static LRESULT 903 HEADER_SendCtrlCustomDraw(const HEADER_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, const RECT *rect) 904 { 905 NMCUSTOMDRAW nm; 906 nm.dwDrawStage = dwDrawStage; 907 nm.hdc = hdc; 908 nm.rc = *rect; 909 nm.dwItemSpec = 0; 910 nm.uItemState = 0; 911 nm.lItemlParam = 0; 912 913 return HEADER_SendNotify(infoPtr, NM_CUSTOMDRAW, (NMHDR *)&nm); 914 } 915 916 static BOOL 917 HEADER_SendNotifyWithHDItemT(const HEADER_INFO *infoPtr, UINT code, INT iItem, HDITEMW *lpItem) 918 { 919 NMHEADERW nmhdr; 920 921 if (infoPtr->nNotifyFormat != NFR_UNICODE) 922 code = HEADER_NotifyCodeWtoA(code); 923 nmhdr.iItem = iItem; 924 nmhdr.iButton = 0; 925 nmhdr.pitem = lpItem; 926 927 return (BOOL)HEADER_SendNotify(infoPtr, code, (NMHDR *)&nmhdr); 928 } 929 930 static BOOL 931 HEADER_SendNotifyWithIntFieldT(const HEADER_INFO *infoPtr, UINT code, INT iItem, INT mask, INT iValue) 932 { 933 HDITEMW nmitem; 934 935 /* copying only the iValue should be ok but to make the code more robust we copy everything */ 936 nmitem.cxy = infoPtr->items[iItem].cxy; 937 nmitem.hbm = infoPtr->items[iItem].hbm; 938 nmitem.pszText = NULL; 939 nmitem.cchTextMax = 0; 940 nmitem.fmt = infoPtr->items[iItem].fmt; 941 nmitem.lParam = infoPtr->items[iItem].lParam; 942 nmitem.iOrder = infoPtr->items[iItem].iOrder; 943 nmitem.iImage = infoPtr->items[iItem].iImage; 944 945 nmitem.mask = mask; 946 switch (mask) 947 { 948 case HDI_WIDTH: 949 nmitem.cxy = iValue; 950 break; 951 case HDI_ORDER: 952 nmitem.iOrder = iValue; 953 break; 954 default: 955 ERR("invalid mask value 0x%x\n", iValue); 956 } 957 958 return HEADER_SendNotifyWithHDItemT(infoPtr, code, iItem, &nmitem); 959 } 960 961 /** 962 * Prepare callback items 963 * depends on NMHDDISPINFOW having same structure as NMHDDISPINFOA 964 * (so we handle the two cases only doing a specific cast for pszText). 965 * Checks if any of the required fields is a callback. If this is the case sends a 966 * NMHDISPINFO notify to retrieve these items. The items are stored in the 967 * HEADER_ITEM pszText and iImage fields. They should be freed with 968 * HEADER_FreeCallbackItems. 969 * 970 * @param hwnd : hwnd header container handler 971 * @param iItem : the header item id 972 * @param reqMask : required fields. If any of them is callback this function will fetch it 973 * 974 * @return TRUE on success, else FALSE 975 */ 976 static BOOL 977 HEADER_PrepareCallbackItems(const HEADER_INFO *infoPtr, INT iItem, INT reqMask) 978 { 979 HEADER_ITEM *lpItem = &infoPtr->items[iItem]; 980 DWORD mask = reqMask & lpItem->callbackMask; 981 NMHDDISPINFOW dispInfo; 982 void *pvBuffer = NULL; 983 984 if (mask == 0) 985 return TRUE; 986 if (mask&HDI_TEXT && lpItem->pszText != NULL) 987 { 988 ERR("(): function called without a call to FreeCallbackItems\n"); 989 heap_free(lpItem->pszText); 990 lpItem->pszText = NULL; 991 } 992 993 memset(&dispInfo, 0, sizeof(NMHDDISPINFOW)); 994 dispInfo.hdr.hwndFrom = infoPtr->hwndSelf; 995 dispInfo.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID); 996 if (infoPtr->nNotifyFormat == NFR_UNICODE) 997 { 998 dispInfo.hdr.code = HDN_GETDISPINFOW; 999 if (mask & HDI_TEXT) 1000 pvBuffer = heap_alloc_zero(MAX_HEADER_TEXT_LEN * sizeof(WCHAR)); 1001 } 1002 else 1003 { 1004 dispInfo.hdr.code = HDN_GETDISPINFOA; 1005 if (mask & HDI_TEXT) 1006 pvBuffer = heap_alloc_zero(MAX_HEADER_TEXT_LEN * sizeof(CHAR)); 1007 } 1008 dispInfo.pszText = pvBuffer; 1009 dispInfo.cchTextMax = (pvBuffer!=NULL?MAX_HEADER_TEXT_LEN:0); 1010 dispInfo.iItem = iItem; 1011 dispInfo.mask = mask; 1012 dispInfo.lParam = lpItem->lParam; 1013 1014 TRACE("Sending HDN_GETDISPINFO%c\n", infoPtr->nNotifyFormat == NFR_UNICODE?'W':'A'); 1015 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, dispInfo.hdr.idFrom, (LPARAM)&dispInfo); 1016 1017 TRACE("SendMessage returns(mask:0x%x,str:%s,lParam:%p)\n", 1018 dispInfo.mask, 1019 (infoPtr->nNotifyFormat == NFR_UNICODE ? debugstr_w(dispInfo.pszText) : (LPSTR) dispInfo.pszText), 1020 (void*) dispInfo.lParam); 1021 1022 if (mask & HDI_IMAGE) 1023 lpItem->iImage = dispInfo.iImage; 1024 if (mask & HDI_TEXT) 1025 { 1026 if (infoPtr->nNotifyFormat == NFR_UNICODE) 1027 { 1028 lpItem->pszText = pvBuffer; 1029 1030 /* the user might have used his own buffer */ 1031 if (dispInfo.pszText != lpItem->pszText) 1032 Str_GetPtrW(dispInfo.pszText, lpItem->pszText, MAX_HEADER_TEXT_LEN); 1033 } 1034 else 1035 { 1036 Str_SetPtrAtoW(&lpItem->pszText, (LPSTR)dispInfo.pszText); 1037 heap_free(pvBuffer); 1038 } 1039 } 1040 1041 if (dispInfo.mask & HDI_DI_SETITEM) 1042 { 1043 /* make the items permanent */ 1044 lpItem->callbackMask &= ~dispInfo.mask; 1045 } 1046 1047 return TRUE; 1048 } 1049 1050 /*** 1051 * DESCRIPTION: 1052 * Free the items that might be allocated with HEADER_PrepareCallbackItems 1053 * 1054 * PARAMETER(S): 1055 * [I] lpItem : the item to free the data 1056 * 1057 */ 1058 static void 1059 HEADER_FreeCallbackItems(HEADER_ITEM *lpItem) 1060 { 1061 if (lpItem->callbackMask&HDI_TEXT) 1062 { 1063 heap_free(lpItem->pszText); 1064 lpItem->pszText = NULL; 1065 } 1066 1067 if (lpItem->callbackMask&HDI_IMAGE) 1068 lpItem->iImage = I_IMAGECALLBACK; 1069 } 1070 1071 static HIMAGELIST 1072 HEADER_CreateDragImage (HEADER_INFO *infoPtr, INT iItem) 1073 { 1074 HEADER_ITEM *lpItem; 1075 HIMAGELIST himl; 1076 HBITMAP hMemory, hOldBitmap; 1077 LRESULT lCDFlags; 1078 RECT rc; 1079 HDC hMemoryDC; 1080 HDC hDeviceDC; 1081 int height, width; 1082 HFONT hFont; 1083 1084 if (iItem >= infoPtr->uNumItem) 1085 return NULL; 1086 1087 if (!infoPtr->bRectsValid) 1088 HEADER_SetItemBounds(infoPtr); 1089 1090 lpItem = &infoPtr->items[iItem]; 1091 width = lpItem->rect.right - lpItem->rect.left; 1092 height = lpItem->rect.bottom - lpItem->rect.top; 1093 1094 hDeviceDC = GetDC(NULL); 1095 hMemoryDC = CreateCompatibleDC(hDeviceDC); 1096 hMemory = CreateCompatibleBitmap(hDeviceDC, width, height); 1097 ReleaseDC(NULL, hDeviceDC); 1098 hOldBitmap = SelectObject(hMemoryDC, hMemory); 1099 SetViewportOrgEx(hMemoryDC, -lpItem->rect.left, -lpItem->rect.top, NULL); 1100 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject(SYSTEM_FONT); 1101 SelectObject(hMemoryDC, hFont); 1102 1103 GetClientRect(infoPtr->hwndSelf, &rc); 1104 lCDFlags = HEADER_SendCtrlCustomDraw(infoPtr, CDDS_PREPAINT, hMemoryDC, &rc); 1105 HEADER_DrawItem(infoPtr, hMemoryDC, iItem, FALSE, lCDFlags); 1106 if (lCDFlags & CDRF_NOTIFYPOSTPAINT) 1107 HEADER_SendCtrlCustomDraw(infoPtr, CDDS_POSTPAINT, hMemoryDC, &rc); 1108 1109 hMemory = SelectObject(hMemoryDC, hOldBitmap); 1110 DeleteDC(hMemoryDC); 1111 1112 if (hMemory == NULL) /* if anything failed */ 1113 return NULL; 1114 1115 himl = ImageList_Create(width, height, ILC_COLORDDB, 1, 1); 1116 ImageList_Add(himl, hMemory, NULL); 1117 DeleteObject(hMemory); 1118 return himl; 1119 } 1120 1121 static LRESULT 1122 HEADER_SetHotDivider(HEADER_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 1123 { 1124 INT iDivider; 1125 RECT r; 1126 1127 if (wParam) 1128 { 1129 POINT pt; 1130 UINT flags; 1131 pt.x = (INT)(SHORT)LOWORD(lParam); 1132 pt.y = 0; 1133 HEADER_InternalHitTest (infoPtr, &pt, &flags, &iDivider); 1134 1135 if (flags & HHT_TOLEFT) 1136 iDivider = 0; 1137 else if (flags & HHT_NOWHERE || flags & HHT_TORIGHT) 1138 iDivider = infoPtr->uNumItem; 1139 else 1140 { 1141 HEADER_ITEM *lpItem = &infoPtr->items[iDivider]; 1142 if (pt.x > (lpItem->rect.left+lpItem->rect.right)/2) 1143 iDivider = HEADER_NextItem(infoPtr, iDivider); 1144 } 1145 } 1146 else 1147 iDivider = (INT)lParam; 1148 1149 /* Note; wParam==FALSE, lParam==-1 is valid and is used to clear the hot divider */ 1150 if (iDivider<-1 || iDivider>(int)infoPtr->uNumItem) 1151 return iDivider; 1152 1153 if (iDivider != infoPtr->iHotDivider) 1154 { 1155 if (infoPtr->iHotDivider != -1) 1156 { 1157 HEADER_GetHotDividerRect(infoPtr, &r); 1158 InvalidateRect(infoPtr->hwndSelf, &r, FALSE); 1159 } 1160 infoPtr->iHotDivider = iDivider; 1161 if (iDivider != -1) 1162 { 1163 HEADER_GetHotDividerRect(infoPtr, &r); 1164 InvalidateRect(infoPtr->hwndSelf, &r, FALSE); 1165 } 1166 } 1167 return iDivider; 1168 } 1169 1170 static LRESULT 1171 HEADER_DeleteItem (HEADER_INFO *infoPtr, INT iItem) 1172 { 1173 INT iOrder; 1174 UINT i; 1175 1176 TRACE("[iItem=%d]\n", iItem); 1177 1178 if ((iItem < 0) || (iItem >= (INT)infoPtr->uNumItem)) 1179 return FALSE; 1180 1181 for (i = 0; i < infoPtr->uNumItem; i++) 1182 TRACE("%d: order=%d, iOrder=%d, ->iOrder=%d\n", i, infoPtr->order[i], infoPtr->items[i].iOrder, infoPtr->items[infoPtr->order[i]].iOrder); 1183 1184 iOrder = infoPtr->items[iItem].iOrder; 1185 heap_free(infoPtr->items[iItem].pszText); 1186 1187 infoPtr->uNumItem--; 1188 memmove(&infoPtr->items[iItem], &infoPtr->items[iItem + 1], 1189 (infoPtr->uNumItem - iItem) * sizeof(HEADER_ITEM)); 1190 memmove(&infoPtr->order[iOrder], &infoPtr->order[iOrder + 1], 1191 (infoPtr->uNumItem - iOrder) * sizeof(INT)); 1192 infoPtr->items = heap_realloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem); 1193 infoPtr->order = heap_realloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem); 1194 1195 /* Correct the orders */ 1196 for (i = 0; i < infoPtr->uNumItem; i++) 1197 { 1198 if (infoPtr->order[i] > iItem) 1199 infoPtr->order[i]--; 1200 if (i >= iOrder) 1201 infoPtr->items[infoPtr->order[i]].iOrder = i; 1202 } 1203 for (i = 0; i < infoPtr->uNumItem; i++) 1204 TRACE("%d: order=%d, iOrder=%d, ->iOrder=%d\n", i, infoPtr->order[i], infoPtr->items[i].iOrder, infoPtr->items[infoPtr->order[i]].iOrder); 1205 1206 HEADER_SetItemBounds (infoPtr); 1207 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 1208 1209 return TRUE; 1210 } 1211 1212 1213 static LRESULT 1214 HEADER_GetImageList (const HEADER_INFO *infoPtr) 1215 { 1216 return (LRESULT)infoPtr->himl; 1217 } 1218 1219 1220 static LRESULT 1221 HEADER_GetItemT (const HEADER_INFO *infoPtr, INT nItem, LPHDITEMW phdi, BOOL bUnicode) 1222 { 1223 HEADER_ITEM *lpItem; 1224 UINT mask; 1225 1226 if (!phdi) 1227 return FALSE; 1228 1229 TRACE("[nItem=%d]\n", nItem); 1230 1231 mask = phdi->mask; 1232 if (mask == 0) 1233 return TRUE; 1234 1235 if ((nItem < 0) || (nItem >= (INT)infoPtr->uNumItem)) 1236 return FALSE; 1237 1238 if (mask & HDI_UNKNOWN_FIELDS) 1239 { 1240 TRACE("mask %x contains unknown fields. Using only comctl32 4.0 fields\n", mask); 1241 mask &= HDI_COMCTL32_4_0_FIELDS; 1242 } 1243 1244 lpItem = &infoPtr->items[nItem]; 1245 HEADER_PrepareCallbackItems(infoPtr, nItem, mask); 1246 1247 if (mask & HDI_BITMAP) 1248 phdi->hbm = lpItem->hbm; 1249 1250 if (mask & HDI_FORMAT) 1251 phdi->fmt = lpItem->fmt; 1252 1253 if (mask & HDI_WIDTH) 1254 phdi->cxy = lpItem->cxy; 1255 1256 if (mask & HDI_LPARAM) 1257 phdi->lParam = lpItem->lParam; 1258 1259 if (mask & HDI_IMAGE) 1260 phdi->iImage = lpItem->iImage; 1261 1262 if (mask & HDI_ORDER) 1263 phdi->iOrder = lpItem->iOrder; 1264 1265 if (mask & HDI_TEXT) 1266 { 1267 if (bUnicode) 1268 Str_GetPtrW (lpItem->pszText, phdi->pszText, phdi->cchTextMax); 1269 else 1270 Str_GetPtrWtoA (lpItem->pszText, (LPSTR)phdi->pszText, phdi->cchTextMax); 1271 } 1272 1273 HEADER_FreeCallbackItems(lpItem); 1274 return TRUE; 1275 } 1276 1277 1278 static inline LRESULT 1279 HEADER_GetItemCount (const HEADER_INFO *infoPtr) 1280 { 1281 return infoPtr->uNumItem; 1282 } 1283 1284 1285 static LRESULT 1286 HEADER_GetItemRect (const HEADER_INFO *infoPtr, INT iItem, LPRECT lpRect) 1287 { 1288 if ((iItem < 0) || (iItem >= (INT)infoPtr->uNumItem)) 1289 return FALSE; 1290 1291 lpRect->left = infoPtr->items[iItem].rect.left; 1292 lpRect->right = infoPtr->items[iItem].rect.right; 1293 lpRect->top = infoPtr->items[iItem].rect.top; 1294 lpRect->bottom = infoPtr->items[iItem].rect.bottom; 1295 1296 return TRUE; 1297 } 1298 1299 1300 static LRESULT 1301 HEADER_GetOrderArray(const HEADER_INFO *infoPtr, INT size, LPINT order) 1302 { 1303 if ((UINT)size <infoPtr->uNumItem) 1304 return FALSE; 1305 1306 memcpy(order, infoPtr->order, infoPtr->uNumItem * sizeof(INT)); 1307 return TRUE; 1308 } 1309 1310 /* Returns index of first duplicate 'value' from [0,to) range, 1311 or -1 if there isn't any */ 1312 static INT has_duplicate(const INT *array, INT to, INT value) 1313 { 1314 INT i; 1315 for(i = 0; i < to; i++) 1316 if (array[i] == value) return i; 1317 return -1; 1318 } 1319 1320 /* returns next available value from [0,max] not to duplicate in [0,to) */ 1321 static INT get_nextvalue(const INT *array, INT to, INT max) 1322 { 1323 INT i; 1324 for(i = 0; i < max; i++) 1325 if (has_duplicate(array, to, i) == -1) return i; 1326 return 0; 1327 } 1328 1329 static LRESULT 1330 HEADER_SetOrderArray(HEADER_INFO *infoPtr, INT size, const INT *order) 1331 { 1332 HEADER_ITEM *lpItem; 1333 INT i; 1334 1335 if ((UINT)size != infoPtr->uNumItem) 1336 return FALSE; 1337 1338 if (TRACE_ON(header)) 1339 { 1340 TRACE("count=%d, order array={", size); 1341 for (i = 0; i < size; i++) 1342 TRACE("%d%c", order[i], i != size-1 ? ',' : '}'); 1343 TRACE("\n"); 1344 } 1345 1346 for (i=0; i<size; i++) 1347 { 1348 if (order[i] >= size || order[i] < 0) 1349 /* on invalid index get next available */ 1350 /* FIXME: if i==0 array item is out of range behaviour is 1351 different, see tests */ 1352 infoPtr->order[i] = get_nextvalue(infoPtr->order, i, size); 1353 else 1354 { 1355 INT j, dup; 1356 1357 infoPtr->order[i] = order[i]; 1358 j = i; 1359 /* remove duplicates */ 1360 while ((dup = has_duplicate(infoPtr->order, j, order[j])) != -1) 1361 { 1362 INT next; 1363 1364 next = get_nextvalue(infoPtr->order, j, size); 1365 infoPtr->order[dup] = next; 1366 j--; 1367 } 1368 } 1369 } 1370 /* sync with item data */ 1371 for (i=0; i<size; i++) 1372 { 1373 lpItem = &infoPtr->items[infoPtr->order[i]]; 1374 lpItem->iOrder = i; 1375 } 1376 HEADER_SetItemBounds(infoPtr); 1377 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 1378 return TRUE; 1379 } 1380 1381 static inline LRESULT 1382 HEADER_GetUnicodeFormat (const HEADER_INFO *infoPtr) 1383 { 1384 return (infoPtr->nNotifyFormat == NFR_UNICODE); 1385 } 1386 1387 1388 static LRESULT 1389 HEADER_HitTest (const HEADER_INFO *infoPtr, LPHDHITTESTINFO phti) 1390 { 1391 UINT outside = HHT_NOWHERE | HHT_ABOVE | HHT_BELOW | HHT_TOLEFT | HHT_TORIGHT; 1392 1393 HEADER_InternalHitTest (infoPtr, &phti->pt, &phti->flags, &phti->iItem); 1394 1395 if (phti->flags & outside) 1396 return phti->iItem = -1; 1397 else 1398 return phti->iItem; 1399 } 1400 1401 1402 static LRESULT 1403 HEADER_InsertItemT (HEADER_INFO *infoPtr, INT nItem, const HDITEMW *phdi, BOOL bUnicode) 1404 { 1405 HEADER_ITEM *lpItem; 1406 INT iOrder; 1407 UINT i; 1408 UINT copyMask; 1409 1410 if ((phdi == NULL) || (nItem < 0) || (phdi->mask == 0)) 1411 return -1; 1412 1413 if (nItem > infoPtr->uNumItem) 1414 nItem = infoPtr->uNumItem; 1415 1416 iOrder = (phdi->mask & HDI_ORDER) ? phdi->iOrder : nItem; 1417 if (iOrder < 0) 1418 iOrder = 0; 1419 else if (infoPtr->uNumItem < iOrder) 1420 iOrder = infoPtr->uNumItem; 1421 1422 infoPtr->uNumItem++; 1423 infoPtr->items = heap_realloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem); 1424 infoPtr->order = heap_realloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem); 1425 1426 /* make space for the new item */ 1427 memmove(&infoPtr->items[nItem + 1], &infoPtr->items[nItem], 1428 (infoPtr->uNumItem - nItem - 1) * sizeof(HEADER_ITEM)); 1429 memmove(&infoPtr->order[iOrder + 1], &infoPtr->order[iOrder], 1430 (infoPtr->uNumItem - iOrder - 1) * sizeof(INT)); 1431 1432 /* update the order array */ 1433 infoPtr->order[iOrder] = nItem; 1434 for (i = 0; i < infoPtr->uNumItem; i++) 1435 { 1436 if (i != iOrder && infoPtr->order[i] >= nItem) 1437 infoPtr->order[i]++; 1438 infoPtr->items[infoPtr->order[i]].iOrder = i; 1439 } 1440 1441 lpItem = &infoPtr->items[nItem]; 1442 ZeroMemory(lpItem, sizeof(HEADER_ITEM)); 1443 /* cxy, fmt and lParam are copied even if not in the HDITEM mask */ 1444 copyMask = phdi->mask | HDI_WIDTH | HDI_FORMAT | HDI_LPARAM; 1445 HEADER_StoreHDItemInHeader(lpItem, copyMask, phdi, bUnicode); 1446 lpItem->iOrder = iOrder; 1447 1448 /* set automatically some format bits */ 1449 if (phdi->mask & HDI_TEXT) 1450 lpItem->fmt |= HDF_STRING; 1451 else 1452 lpItem->fmt &= ~HDF_STRING; 1453 1454 if (lpItem->hbm != NULL) 1455 lpItem->fmt |= HDF_BITMAP; 1456 else 1457 lpItem->fmt &= ~HDF_BITMAP; 1458 1459 if (phdi->mask & HDI_IMAGE) 1460 lpItem->fmt |= HDF_IMAGE; 1461 1462 HEADER_SetItemBounds (infoPtr); 1463 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 1464 1465 return nItem; 1466 } 1467 1468 1469 static LRESULT 1470 HEADER_Layout (HEADER_INFO *infoPtr, LPHDLAYOUT lpLayout) 1471 { 1472 lpLayout->pwpos->hwnd = infoPtr->hwndSelf; 1473 lpLayout->pwpos->hwndInsertAfter = 0; 1474 lpLayout->pwpos->x = lpLayout->prc->left; 1475 lpLayout->pwpos->y = lpLayout->prc->top; 1476 lpLayout->pwpos->cx = lpLayout->prc->right - lpLayout->prc->left; 1477 if (infoPtr->dwStyle & HDS_HIDDEN) 1478 lpLayout->pwpos->cy = 0; 1479 else { 1480 lpLayout->pwpos->cy = infoPtr->nHeight; 1481 lpLayout->prc->top += infoPtr->nHeight; 1482 } 1483 lpLayout->pwpos->flags = SWP_NOZORDER; 1484 1485 TRACE("Layout x=%d y=%d cx=%d cy=%d\n", 1486 lpLayout->pwpos->x, lpLayout->pwpos->y, 1487 lpLayout->pwpos->cx, lpLayout->pwpos->cy); 1488 1489 infoPtr->bRectsValid = FALSE; 1490 1491 return TRUE; 1492 } 1493 1494 1495 static LRESULT 1496 HEADER_SetImageList (HEADER_INFO *infoPtr, HIMAGELIST himl) 1497 { 1498 HIMAGELIST himlOld; 1499 1500 TRACE("(himl %p)\n", himl); 1501 himlOld = infoPtr->himl; 1502 infoPtr->himl = himl; 1503 1504 /* FIXME: Refresh needed??? */ 1505 1506 return (LRESULT)himlOld; 1507 } 1508 1509 1510 static LRESULT 1511 HEADER_GetBitmapMargin(const HEADER_INFO *infoPtr) 1512 { 1513 return infoPtr->iMargin; 1514 } 1515 1516 static LRESULT 1517 HEADER_SetBitmapMargin(HEADER_INFO *infoPtr, INT iMargin) 1518 { 1519 INT oldMargin = infoPtr->iMargin; 1520 1521 infoPtr->iMargin = iMargin; 1522 1523 return oldMargin; 1524 } 1525 1526 static LRESULT 1527 HEADER_SetItemT (HEADER_INFO *infoPtr, INT nItem, const HDITEMW *phdi, BOOL bUnicode) 1528 { 1529 HEADER_ITEM *lpItem; 1530 HDITEMW hdNotify; 1531 void *pvScratch; 1532 1533 if (phdi == NULL) 1534 return FALSE; 1535 if ((nItem < 0) || (nItem >= (INT)infoPtr->uNumItem)) 1536 return FALSE; 1537 1538 TRACE("[nItem=%d]\n", nItem); 1539 1540 HEADER_CopyHDItemForNotify(infoPtr, &hdNotify, phdi, bUnicode, &pvScratch); 1541 if (HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMCHANGINGW, nItem, &hdNotify)) 1542 { 1543 heap_free(pvScratch); 1544 return FALSE; 1545 } 1546 1547 lpItem = &infoPtr->items[nItem]; 1548 HEADER_StoreHDItemInHeader(lpItem, phdi->mask, phdi, bUnicode); 1549 1550 if (phdi->mask & HDI_ORDER) 1551 if (phdi->iOrder >= 0 && phdi->iOrder < infoPtr->uNumItem) 1552 HEADER_ChangeItemOrder(infoPtr, nItem, phdi->iOrder); 1553 1554 HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMCHANGEDW, nItem, &hdNotify); 1555 1556 HEADER_SetItemBounds (infoPtr); 1557 1558 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 1559 1560 heap_free(pvScratch); 1561 return TRUE; 1562 } 1563 1564 static inline LRESULT 1565 HEADER_SetUnicodeFormat (HEADER_INFO *infoPtr, WPARAM wParam) 1566 { 1567 BOOL bTemp = (infoPtr->nNotifyFormat == NFR_UNICODE); 1568 1569 infoPtr->nNotifyFormat = ((BOOL)wParam ? NFR_UNICODE : NFR_ANSI); 1570 1571 return bTemp; 1572 } 1573 1574 1575 static LRESULT 1576 HEADER_Create (HWND hwnd, const CREATESTRUCTW *lpcs) 1577 { 1578 HEADER_INFO *infoPtr; 1579 TEXTMETRICW tm; 1580 HFONT hOldFont; 1581 HDC hdc; 1582 1583 infoPtr = heap_alloc_zero (sizeof(*infoPtr)); 1584 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); 1585 1586 infoPtr->hwndSelf = hwnd; 1587 infoPtr->hwndNotify = lpcs->hwndParent; 1588 infoPtr->uNumItem = 0; 1589 infoPtr->hFont = 0; 1590 infoPtr->items = 0; 1591 infoPtr->order = 0; 1592 infoPtr->bRectsValid = FALSE; 1593 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW); 1594 infoPtr->hcurDivider = LoadCursorW (COMCTL32_hModule, MAKEINTRESOURCEW(IDC_DIVIDER)); 1595 infoPtr->hcurDivopen = LoadCursorW (COMCTL32_hModule, MAKEINTRESOURCEW(IDC_DIVIDEROPEN)); 1596 infoPtr->bPressed = FALSE; 1597 infoPtr->bTracking = FALSE; 1598 infoPtr->dwStyle = lpcs->style; 1599 infoPtr->iMoveItem = 0; 1600 infoPtr->himl = 0; 1601 infoPtr->iHotItem = -1; 1602 infoPtr->iHotDivider = -1; 1603 infoPtr->iMargin = 3*GetSystemMetrics(SM_CXEDGE); 1604 infoPtr->nNotifyFormat = 1605 SendMessageW (infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY); 1606 infoPtr->filter_change_timeout = 1000; 1607 1608 hdc = GetDC (0); 1609 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT)); 1610 GetTextMetricsW (hdc, &tm); 1611 infoPtr->nHeight = tm.tmHeight + VERT_BORDER; 1612 SelectObject (hdc, hOldFont); 1613 ReleaseDC (0, hdc); 1614 1615 OpenThemeData(hwnd, themeClass); 1616 1617 return 0; 1618 } 1619 1620 1621 static LRESULT 1622 HEADER_Destroy (HEADER_INFO *infoPtr) 1623 { 1624 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); 1625 CloseThemeData(theme); 1626 return 0; 1627 } 1628 1629 static LRESULT 1630 HEADER_NCDestroy (HEADER_INFO *infoPtr) 1631 { 1632 HEADER_ITEM *lpItem; 1633 INT nItem; 1634 1635 if (infoPtr->items) { 1636 lpItem = infoPtr->items; 1637 for (nItem = 0; nItem < infoPtr->uNumItem; nItem++, lpItem++) 1638 heap_free(lpItem->pszText); 1639 heap_free(infoPtr->items); 1640 } 1641 1642 heap_free(infoPtr->order); 1643 1644 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); 1645 heap_free(infoPtr); 1646 1647 return 0; 1648 } 1649 1650 1651 static inline LRESULT 1652 HEADER_GetFont (const HEADER_INFO *infoPtr) 1653 { 1654 return (LRESULT)infoPtr->hFont; 1655 } 1656 1657 1658 static BOOL 1659 HEADER_IsDragDistance(const HEADER_INFO *infoPtr, const POINT *pt) 1660 { 1661 /* Windows allows for a mouse movement before starting the drag. We use the 1662 * SM_CXDOUBLECLICK/SM_CYDOUBLECLICK as that distance. 1663 */ 1664 return (abs(infoPtr->ptLButtonDown.x - pt->x)>GetSystemMetrics(SM_CXDOUBLECLK) || 1665 abs(infoPtr->ptLButtonDown.y - pt->y)>GetSystemMetrics(SM_CYDOUBLECLK)); 1666 } 1667 1668 static LRESULT 1669 HEADER_LButtonDblClk (const HEADER_INFO *infoPtr, INT x, INT y) 1670 { 1671 POINT pt; 1672 UINT flags; 1673 INT nItem; 1674 1675 pt.x = x; 1676 pt.y = y; 1677 HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem); 1678 1679 if ((infoPtr->dwStyle & HDS_BUTTONS) && (flags == HHT_ONHEADER)) 1680 HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMDBLCLICKW, nItem, NULL); 1681 else if ((flags == HHT_ONDIVIDER) || (flags == HHT_ONDIVOPEN)) 1682 HEADER_SendNotifyWithHDItemT(infoPtr, HDN_DIVIDERDBLCLICKW, nItem, NULL); 1683 1684 return 0; 1685 } 1686 1687 1688 static LRESULT 1689 HEADER_LButtonDown (HEADER_INFO *infoPtr, INT x, INT y) 1690 { 1691 POINT pt; 1692 UINT flags; 1693 INT nItem; 1694 HDC hdc; 1695 1696 pt.x = x; 1697 pt.y = y; 1698 HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem); 1699 1700 if ((infoPtr->dwStyle & HDS_BUTTONS) && (flags == HHT_ONHEADER)) { 1701 SetCapture (infoPtr->hwndSelf); 1702 infoPtr->bCaptured = TRUE; 1703 infoPtr->bPressed = TRUE; 1704 infoPtr->bDragging = FALSE; 1705 infoPtr->iMoveItem = nItem; 1706 infoPtr->ptLButtonDown = pt; 1707 1708 infoPtr->items[nItem].bDown = TRUE; 1709 1710 /* Send WM_CUSTOMDRAW */ 1711 hdc = GetDC (infoPtr->hwndSelf); 1712 HEADER_RefreshItem (infoPtr, nItem); 1713 ReleaseDC (infoPtr->hwndSelf, hdc); 1714 1715 TRACE("Pressed item %d.\n", nItem); 1716 } 1717 else if ((flags == HHT_ONDIVIDER) || (flags == HHT_ONDIVOPEN)) { 1718 INT iCurrWidth = infoPtr->items[nItem].cxy; 1719 if (!HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_BEGINTRACKW, nItem, HDI_WIDTH, iCurrWidth)) 1720 { 1721 SetCapture (infoPtr->hwndSelf); 1722 infoPtr->bCaptured = TRUE; 1723 infoPtr->bTracking = TRUE; 1724 infoPtr->iMoveItem = nItem; 1725 infoPtr->xTrackOffset = infoPtr->items[nItem].rect.right - pt.x; 1726 1727 if (!(infoPtr->dwStyle & HDS_FULLDRAG)) { 1728 infoPtr->xOldTrack = infoPtr->items[nItem].rect.right; 1729 hdc = GetDC (infoPtr->hwndSelf); 1730 HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack); 1731 ReleaseDC (infoPtr->hwndSelf, hdc); 1732 } 1733 1734 TRACE("Begin tracking item %d.\n", nItem); 1735 } 1736 } 1737 1738 return 0; 1739 } 1740 1741 1742 static LRESULT 1743 HEADER_LButtonUp (HEADER_INFO *infoPtr, INT x, INT y) 1744 { 1745 POINT pt; 1746 UINT flags; 1747 INT nItem; 1748 HDC hdc; 1749 1750 pt.x = x; 1751 pt.y = y; 1752 HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem); 1753 1754 if (infoPtr->bPressed) { 1755 1756 infoPtr->items[infoPtr->iMoveItem].bDown = FALSE; 1757 1758 if (infoPtr->bDragging) 1759 { 1760 HEADER_ITEM *lpItem = &infoPtr->items[infoPtr->iMoveItem]; 1761 INT iNewOrder; 1762 1763 ImageList_DragShowNolock(FALSE); 1764 ImageList_EndDrag(); 1765 1766 if (infoPtr->iHotDivider == -1) 1767 iNewOrder = -1; 1768 else if (infoPtr->iHotDivider == infoPtr->uNumItem) 1769 iNewOrder = infoPtr->uNumItem-1; 1770 else 1771 { 1772 iNewOrder = HEADER_IndexToOrder(infoPtr, infoPtr->iHotDivider); 1773 if (iNewOrder > lpItem->iOrder) 1774 iNewOrder--; 1775 } 1776 1777 if (iNewOrder != -1 && 1778 !HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ENDDRAG, infoPtr->iMoveItem, HDI_ORDER, iNewOrder)) 1779 { 1780 HEADER_ChangeItemOrder(infoPtr, infoPtr->iMoveItem, iNewOrder); 1781 infoPtr->bRectsValid = FALSE; 1782 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 1783 } 1784 else 1785 InvalidateRect(infoPtr->hwndSelf, &infoPtr->items[infoPtr->iMoveItem].rect, FALSE); 1786 1787 infoPtr->bDragging = FALSE; 1788 HEADER_SetHotDivider(infoPtr, FALSE, -1); 1789 } 1790 else 1791 { 1792 hdc = GetDC (infoPtr->hwndSelf); 1793 HEADER_RefreshItem (infoPtr, infoPtr->iMoveItem); 1794 ReleaseDC (infoPtr->hwndSelf, hdc); 1795 1796 if (!(infoPtr->dwStyle & HDS_DRAGDROP) || !HEADER_IsDragDistance(infoPtr, &pt)) 1797 HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMCLICKW, infoPtr->iMoveItem, NULL); 1798 } 1799 1800 TRACE("Released item %d.\n", infoPtr->iMoveItem); 1801 infoPtr->bPressed = FALSE; 1802 } 1803 else if (infoPtr->bTracking) { 1804 INT iNewWidth = pt.x - infoPtr->items[infoPtr->iMoveItem].rect.left + infoPtr->xTrackOffset; 1805 if (iNewWidth < 0) 1806 iNewWidth = 0; 1807 TRACE("End tracking item %d.\n", infoPtr->iMoveItem); 1808 infoPtr->bTracking = FALSE; 1809 1810 HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ENDTRACKW, infoPtr->iMoveItem, HDI_WIDTH, iNewWidth); 1811 1812 if (!(infoPtr->dwStyle & HDS_FULLDRAG)) { 1813 hdc = GetDC (infoPtr->hwndSelf); 1814 HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack); 1815 ReleaseDC (infoPtr->hwndSelf, hdc); 1816 } 1817 1818 if (!HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGINGW, infoPtr->iMoveItem, HDI_WIDTH, iNewWidth)) 1819 { 1820 infoPtr->items[infoPtr->iMoveItem].cxy = iNewWidth; 1821 HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGEDW, infoPtr->iMoveItem, HDI_WIDTH, iNewWidth); 1822 } 1823 1824 HEADER_SetItemBounds (infoPtr); 1825 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 1826 } 1827 1828 if (infoPtr->bCaptured) { 1829 infoPtr->bCaptured = FALSE; 1830 ReleaseCapture (); 1831 HEADER_SendSimpleNotify (infoPtr, NM_RELEASEDCAPTURE); 1832 } 1833 1834 return 0; 1835 } 1836 1837 1838 static LRESULT 1839 HEADER_NotifyFormat (HEADER_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 1840 { 1841 switch (lParam) 1842 { 1843 case NF_QUERY: 1844 return infoPtr->nNotifyFormat; 1845 1846 case NF_REQUERY: 1847 infoPtr->nNotifyFormat = 1848 SendMessageW ((HWND)wParam, WM_NOTIFYFORMAT, 1849 (WPARAM)infoPtr->hwndSelf, NF_QUERY); 1850 return infoPtr->nNotifyFormat; 1851 } 1852 1853 return 0; 1854 } 1855 1856 static LRESULT 1857 HEADER_MouseLeave (HEADER_INFO *infoPtr) 1858 { 1859 /* Reset hot-tracked item when mouse leaves control. */ 1860 INT oldHotItem = infoPtr->iHotItem; 1861 HDC hdc = GetDC (infoPtr->hwndSelf); 1862 1863 infoPtr->iHotItem = -1; 1864 if (oldHotItem != -1) HEADER_RefreshItem (infoPtr, oldHotItem); 1865 ReleaseDC (infoPtr->hwndSelf, hdc); 1866 1867 return 0; 1868 } 1869 1870 1871 static LRESULT 1872 HEADER_MouseMove (HEADER_INFO *infoPtr, LPARAM lParam) 1873 { 1874 POINT pt; 1875 UINT flags; 1876 INT nItem, nWidth; 1877 HDC hdc; 1878 /* With theming, hottracking is always enabled */ 1879 BOOL hotTrackEnabled = 1880 ((infoPtr->dwStyle & HDS_BUTTONS) && (infoPtr->dwStyle & HDS_HOTTRACK)) 1881 || (GetWindowTheme (infoPtr->hwndSelf) != NULL); 1882 INT oldHotItem = infoPtr->iHotItem; 1883 1884 pt.x = (INT)(SHORT)LOWORD(lParam); 1885 pt.y = (INT)(SHORT)HIWORD(lParam); 1886 HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem); 1887 1888 if (hotTrackEnabled) { 1889 if (flags & (HHT_ONHEADER | HHT_ONDIVIDER | HHT_ONDIVOPEN)) 1890 infoPtr->iHotItem = nItem; 1891 else 1892 infoPtr->iHotItem = -1; 1893 } 1894 1895 if (infoPtr->bCaptured) { 1896 /* check if we should drag the header */ 1897 if (infoPtr->bPressed && !infoPtr->bDragging && (infoPtr->dwStyle & HDS_DRAGDROP) 1898 && HEADER_IsDragDistance(infoPtr, &pt)) 1899 { 1900 if (!HEADER_SendNotifyWithHDItemT(infoPtr, HDN_BEGINDRAG, infoPtr->iMoveItem, NULL)) 1901 { 1902 HIMAGELIST hDragItem = HEADER_CreateDragImage(infoPtr, infoPtr->iMoveItem); 1903 if (hDragItem != NULL) 1904 { 1905 HEADER_ITEM *lpItem = &infoPtr->items[infoPtr->iMoveItem]; 1906 TRACE("Starting item drag\n"); 1907 ImageList_BeginDrag(hDragItem, 0, pt.x - lpItem->rect.left, 0); 1908 ImageList_DragShowNolock(TRUE); 1909 ImageList_Destroy(hDragItem); 1910 infoPtr->bDragging = TRUE; 1911 } 1912 } 1913 } 1914 1915 if (infoPtr->bDragging) 1916 { 1917 POINT drag; 1918 drag.x = pt.x; 1919 drag.y = 0; 1920 ClientToScreen(infoPtr->hwndSelf, &drag); 1921 ImageList_DragMove(drag.x, drag.y); 1922 HEADER_SetHotDivider(infoPtr, TRUE, lParam); 1923 } 1924 1925 if (infoPtr->bPressed && !infoPtr->bDragging) { 1926 BOOL oldState = infoPtr->items[infoPtr->iMoveItem].bDown; 1927 if ((nItem == infoPtr->iMoveItem) && (flags == HHT_ONHEADER)) 1928 infoPtr->items[infoPtr->iMoveItem].bDown = TRUE; 1929 else 1930 infoPtr->items[infoPtr->iMoveItem].bDown = FALSE; 1931 if (oldState != infoPtr->items[infoPtr->iMoveItem].bDown) { 1932 hdc = GetDC (infoPtr->hwndSelf); 1933 HEADER_RefreshItem (infoPtr, infoPtr->iMoveItem); 1934 ReleaseDC (infoPtr->hwndSelf, hdc); 1935 } 1936 1937 TRACE("Moving pressed item %d.\n", infoPtr->iMoveItem); 1938 } 1939 else if (infoPtr->bTracking) { 1940 if (infoPtr->dwStyle & HDS_FULLDRAG) { 1941 HEADER_ITEM *lpItem = &infoPtr->items[infoPtr->iMoveItem]; 1942 nWidth = pt.x - lpItem->rect.left + infoPtr->xTrackOffset; 1943 if (!HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGINGW, infoPtr->iMoveItem, HDI_WIDTH, nWidth)) 1944 { 1945 INT nOldWidth = lpItem->rect.right - lpItem->rect.left; 1946 RECT rcClient; 1947 RECT rcScroll; 1948 1949 if (nWidth < 0) nWidth = 0; 1950 infoPtr->items[infoPtr->iMoveItem].cxy = nWidth; 1951 #ifdef __REACTOS__ 1952 InvalidateRect(infoPtr->hwndSelf, &lpItem->rect, FALSE); 1953 #endif 1954 HEADER_SetItemBounds(infoPtr); 1955 1956 GetClientRect(infoPtr->hwndSelf, &rcClient); 1957 rcScroll = rcClient; 1958 rcScroll.left = lpItem->rect.left + nOldWidth; 1959 #ifdef __REACTOS__ 1960 ScrollWindowEx(infoPtr->hwndSelf, nWidth - nOldWidth, 0, &rcScroll, &rcClient, 1961 NULL, NULL, SW_INVALIDATE); 1962 #else 1963 ScrollWindowEx(infoPtr->hwndSelf, nWidth - nOldWidth, 0, &rcScroll, &rcClient, NULL, NULL, 0); 1964 InvalidateRect(infoPtr->hwndSelf, &lpItem->rect, FALSE); 1965 #endif 1966 UpdateWindow(infoPtr->hwndSelf); 1967 1968 HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGEDW, infoPtr->iMoveItem, HDI_WIDTH, nWidth); 1969 } 1970 } 1971 else { 1972 INT iTrackWidth; 1973 hdc = GetDC (infoPtr->hwndSelf); 1974 HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack); 1975 infoPtr->xOldTrack = pt.x + infoPtr->xTrackOffset; 1976 if (infoPtr->xOldTrack < infoPtr->items[infoPtr->iMoveItem].rect.left) 1977 infoPtr->xOldTrack = infoPtr->items[infoPtr->iMoveItem].rect.left; 1978 HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack); 1979 ReleaseDC (infoPtr->hwndSelf, hdc); 1980 iTrackWidth = infoPtr->xOldTrack - infoPtr->items[infoPtr->iMoveItem].rect.left; 1981 /* FIXME: should stop tracking if HDN_TRACK returns TRUE */ 1982 HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_TRACKW, infoPtr->iMoveItem, HDI_WIDTH, iTrackWidth); 1983 } 1984 1985 TRACE("Tracking item %d.\n", infoPtr->iMoveItem); 1986 } 1987 } 1988 1989 if (hotTrackEnabled) { 1990 TRACKMOUSEEVENT tme; 1991 if (oldHotItem != infoPtr->iHotItem && !infoPtr->bDragging) { 1992 hdc = GetDC (infoPtr->hwndSelf); 1993 if (oldHotItem != -1) HEADER_RefreshItem (infoPtr, oldHotItem); 1994 if (infoPtr->iHotItem != -1) HEADER_RefreshItem (infoPtr, infoPtr->iHotItem); 1995 ReleaseDC (infoPtr->hwndSelf, hdc); 1996 } 1997 tme.cbSize = sizeof( tme ); 1998 tme.dwFlags = TME_LEAVE; 1999 tme.hwndTrack = infoPtr->hwndSelf; 2000 TrackMouseEvent( &tme ); 2001 } 2002 2003 return 0; 2004 } 2005 2006 2007 static LRESULT 2008 HEADER_Paint (HEADER_INFO *infoPtr, HDC hdcParam) 2009 { 2010 HDC hdc; 2011 PAINTSTRUCT ps; 2012 2013 hdc = hdcParam==0 ? BeginPaint (infoPtr->hwndSelf, &ps) : hdcParam; 2014 HEADER_Refresh (infoPtr, hdc); 2015 if(!hdcParam) 2016 EndPaint (infoPtr->hwndSelf, &ps); 2017 return 0; 2018 } 2019 2020 2021 static LRESULT 2022 HEADER_RButtonUp (HEADER_INFO *infoPtr, INT x, INT y) 2023 { 2024 BOOL bRet; 2025 POINT pt; 2026 2027 pt.x = x; 2028 pt.y = y; 2029 2030 /* Send a Notify message */ 2031 bRet = HEADER_SendSimpleNotify (infoPtr, NM_RCLICK); 2032 2033 /* Change to screen coordinate for WM_CONTEXTMENU */ 2034 ClientToScreen(infoPtr->hwndSelf, &pt); 2035 2036 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */ 2037 SendMessageW( infoPtr->hwndSelf, WM_CONTEXTMENU, (WPARAM) infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y)); 2038 2039 return bRet; 2040 } 2041 2042 2043 static LRESULT 2044 HEADER_SetCursor (HEADER_INFO *infoPtr, LPARAM lParam) 2045 { 2046 POINT pt; 2047 UINT flags; 2048 INT nItem; 2049 2050 TRACE("code=0x%X id=0x%X\n", LOWORD(lParam), HIWORD(lParam)); 2051 2052 GetCursorPos (&pt); 2053 ScreenToClient (infoPtr->hwndSelf, &pt); 2054 2055 HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem); 2056 2057 if (flags == HHT_ONDIVIDER) 2058 SetCursor (infoPtr->hcurDivider); 2059 else if (flags == HHT_ONDIVOPEN) 2060 SetCursor (infoPtr->hcurDivopen); 2061 else 2062 SetCursor (infoPtr->hcurArrow); 2063 2064 return 0; 2065 } 2066 2067 2068 static LRESULT 2069 HEADER_SetFont (HEADER_INFO *infoPtr, HFONT hFont, WORD Redraw) 2070 { 2071 TEXTMETRICW tm; 2072 HFONT hOldFont; 2073 HDC hdc; 2074 2075 infoPtr->hFont = hFont; 2076 2077 hdc = GetDC (0); 2078 hOldFont = SelectObject (hdc, infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT)); 2079 GetTextMetricsW (hdc, &tm); 2080 infoPtr->nHeight = tm.tmHeight + VERT_BORDER; 2081 SelectObject (hdc, hOldFont); 2082 ReleaseDC (0, hdc); 2083 2084 infoPtr->bRectsValid = FALSE; 2085 2086 if (Redraw) { 2087 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 2088 } 2089 2090 return 0; 2091 } 2092 2093 static LRESULT HEADER_SetRedraw(HEADER_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 2094 { 2095 /* ignoring the InvalidateRect calls is handled by user32. But some apps expect 2096 * that we invalidate the header and this has to be done manually */ 2097 LRESULT ret; 2098 2099 ret = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, wParam, lParam); 2100 if (wParam) 2101 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 2102 return ret; 2103 } 2104 2105 static INT HEADER_StyleChanged(HEADER_INFO *infoPtr, WPARAM wStyleType, 2106 const STYLESTRUCT *lpss) 2107 { 2108 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 2109 wStyleType, lpss->styleOld, lpss->styleNew); 2110 2111 if (wStyleType != GWL_STYLE) return 0; 2112 2113 infoPtr->dwStyle = lpss->styleNew; 2114 2115 return 0; 2116 } 2117 2118 /* Update the theme handle after a theme change */ 2119 static LRESULT HEADER_ThemeChanged(const HEADER_INFO *infoPtr) 2120 { 2121 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); 2122 CloseThemeData(theme); 2123 OpenThemeData(infoPtr->hwndSelf, themeClass); 2124 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); 2125 return 0; 2126 } 2127 2128 static INT HEADER_SetFilterChangeTimeout(HEADER_INFO *infoPtr, INT timeout) 2129 { 2130 INT old_timeout = infoPtr->filter_change_timeout; 2131 2132 if (timeout != 0) 2133 infoPtr->filter_change_timeout = timeout; 2134 return old_timeout; 2135 } 2136 2137 static LRESULT WINAPI 2138 HEADER_WindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 2139 { 2140 HEADER_INFO *infoPtr = (HEADER_INFO *)GetWindowLongPtrW(hwnd, 0); 2141 2142 TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx\n", hwnd, msg, wParam, lParam); 2143 if (!infoPtr && (msg != WM_CREATE)) 2144 return DefWindowProcW (hwnd, msg, wParam, lParam); 2145 switch (msg) { 2146 /* case HDM_CLEARFILTER: */ 2147 2148 case HDM_CREATEDRAGIMAGE: 2149 return (LRESULT)HEADER_CreateDragImage (infoPtr, (INT)wParam); 2150 2151 case HDM_DELETEITEM: 2152 return HEADER_DeleteItem (infoPtr, (INT)wParam); 2153 2154 /* case HDM_EDITFILTER: */ 2155 2156 case HDM_GETBITMAPMARGIN: 2157 return HEADER_GetBitmapMargin(infoPtr); 2158 2159 case HDM_GETIMAGELIST: 2160 return HEADER_GetImageList (infoPtr); 2161 2162 case HDM_GETITEMA: 2163 case HDM_GETITEMW: 2164 return HEADER_GetItemT (infoPtr, (INT)wParam, (LPHDITEMW)lParam, msg == HDM_GETITEMW); 2165 2166 case HDM_GETITEMCOUNT: 2167 return HEADER_GetItemCount (infoPtr); 2168 2169 case HDM_GETITEMRECT: 2170 return HEADER_GetItemRect (infoPtr, (INT)wParam, (LPRECT)lParam); 2171 2172 case HDM_GETORDERARRAY: 2173 return HEADER_GetOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); 2174 2175 case HDM_GETUNICODEFORMAT: 2176 return HEADER_GetUnicodeFormat (infoPtr); 2177 2178 case HDM_HITTEST: 2179 return HEADER_HitTest (infoPtr, (LPHDHITTESTINFO)lParam); 2180 2181 case HDM_INSERTITEMA: 2182 case HDM_INSERTITEMW: 2183 return HEADER_InsertItemT (infoPtr, (INT)wParam, (LPHDITEMW)lParam, msg == HDM_INSERTITEMW); 2184 2185 case HDM_LAYOUT: 2186 return HEADER_Layout (infoPtr, (LPHDLAYOUT)lParam); 2187 2188 case HDM_ORDERTOINDEX: 2189 return HEADER_OrderToIndex(infoPtr, (INT)wParam); 2190 2191 case HDM_SETBITMAPMARGIN: 2192 return HEADER_SetBitmapMargin(infoPtr, (INT)wParam); 2193 2194 case HDM_SETFILTERCHANGETIMEOUT: 2195 return HEADER_SetFilterChangeTimeout(infoPtr, (INT)lParam); 2196 2197 case HDM_SETHOTDIVIDER: 2198 return HEADER_SetHotDivider(infoPtr, wParam, lParam); 2199 2200 case HDM_SETIMAGELIST: 2201 return HEADER_SetImageList (infoPtr, (HIMAGELIST)lParam); 2202 2203 case HDM_SETITEMA: 2204 case HDM_SETITEMW: 2205 return HEADER_SetItemT (infoPtr, (INT)wParam, (LPHDITEMW)lParam, msg == HDM_SETITEMW); 2206 2207 case HDM_SETORDERARRAY: 2208 return HEADER_SetOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); 2209 2210 case HDM_SETUNICODEFORMAT: 2211 return HEADER_SetUnicodeFormat (infoPtr, wParam); 2212 2213 case WM_CREATE: 2214 return HEADER_Create (hwnd, (LPCREATESTRUCTW)lParam); 2215 2216 case WM_DESTROY: 2217 return HEADER_Destroy (infoPtr); 2218 2219 case WM_NCDESTROY: 2220 return HEADER_NCDestroy (infoPtr); 2221 2222 case WM_ERASEBKGND: 2223 return 1; 2224 2225 case WM_GETDLGCODE: 2226 return DLGC_WANTTAB | DLGC_WANTARROWS; 2227 2228 case WM_GETFONT: 2229 return HEADER_GetFont (infoPtr); 2230 2231 case WM_LBUTTONDBLCLK: 2232 return HEADER_LButtonDblClk (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 2233 2234 case WM_LBUTTONDOWN: 2235 return HEADER_LButtonDown (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 2236 2237 case WM_LBUTTONUP: 2238 return HEADER_LButtonUp (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 2239 2240 case WM_MOUSELEAVE: 2241 return HEADER_MouseLeave (infoPtr); 2242 2243 case WM_MOUSEMOVE: 2244 return HEADER_MouseMove (infoPtr, lParam); 2245 2246 case WM_NOTIFYFORMAT: 2247 return HEADER_NotifyFormat (infoPtr, wParam, lParam); 2248 2249 case WM_SIZE: 2250 return HEADER_Size (infoPtr); 2251 2252 case WM_THEMECHANGED: 2253 return HEADER_ThemeChanged (infoPtr); 2254 2255 case WM_PRINTCLIENT: 2256 case WM_PAINT: 2257 return HEADER_Paint (infoPtr, (HDC)wParam); 2258 2259 case WM_RBUTTONUP: 2260 return HEADER_RButtonUp (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 2261 2262 case WM_SETCURSOR: 2263 return HEADER_SetCursor (infoPtr, lParam); 2264 2265 case WM_SETFONT: 2266 return HEADER_SetFont (infoPtr, (HFONT)wParam, (WORD)lParam); 2267 2268 case WM_SETREDRAW: 2269 return HEADER_SetRedraw(infoPtr, wParam, lParam); 2270 2271 case WM_STYLECHANGED: 2272 return HEADER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam); 2273 2274 case WM_SYSCOLORCHANGE: 2275 COMCTL32_RefreshSysColors(); 2276 return 0; 2277 2278 default: 2279 if ((msg >= WM_USER) && (msg < WM_APP) && !COMCTL32_IsReflectedMessage(msg)) 2280 ERR("unknown msg %04x wp=%04lx lp=%08lx\n", 2281 msg, wParam, lParam ); 2282 return DefWindowProcW(hwnd, msg, wParam, lParam); 2283 } 2284 } 2285 2286 2287 VOID 2288 HEADER_Register (void) 2289 { 2290 WNDCLASSW wndClass; 2291 2292 ZeroMemory (&wndClass, sizeof(WNDCLASSW)); 2293 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS; 2294 wndClass.lpfnWndProc = HEADER_WindowProc; 2295 wndClass.cbClsExtra = 0; 2296 wndClass.cbWndExtra = sizeof(HEADER_INFO *); 2297 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); 2298 wndClass.lpszClassName = WC_HEADERW; 2299 2300 RegisterClassW (&wndClass); 2301 } 2302 2303 2304 VOID 2305 HEADER_Unregister (void) 2306 { 2307 UnregisterClassW (WC_HEADERW, NULL); 2308 } 2309