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