4 * Copyright 1998 Eric Kohl
5 * Copyright 2000 Eric Kohl for CodeWeavers
6 * Copyright 2003 Maxime Bellenge
7 * Copyright 2006 Mikolaj Zalewski
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.
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.
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
24 * - Imagelist support (completed?)
25 * - Hottrack support (completed?)
26 * - Filters support (HDS_FILTER, HDI_FILTER, HDM_*FILTER*, HDN_*FILTER*)
27 * - New Windows Vista features
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(header
);
55 INT iOrder
; /* see documentation of HD_ITEM */
57 BOOL bDown
; /* is item pressed? (used for drawing) */
58 RECT rect
; /* bounding rectangle of the item */
59 DWORD callbackMask
; /* HDI_* flags for items that are callback */
65 HWND hwndSelf
; /* Control window */
66 HWND hwndNotify
; /* Owner window to send notifications to */
67 INT nNotifyFormat
; /* format used for WM_NOTIFY messages */
68 UINT uNumItem
; /* number of items (columns) */
69 INT nHeight
; /* height of the header (pixels) */
70 HFONT hFont
; /* handle to the current font */
71 HCURSOR hcurArrow
; /* handle to the arrow cursor */
72 HCURSOR hcurDivider
; /* handle to a cursor (used over dividers) <-|-> */
73 HCURSOR hcurDivopen
; /* handle to a cursor (used over dividers) <-||-> */
74 BOOL bCaptured
; /* Is the mouse captured? */
75 BOOL bPressed
; /* Is a header item pressed (down)? */
76 BOOL bDragging
; /* Are we dragging an item? */
77 BOOL bTracking
; /* Is in tracking mode? */
78 POINT ptLButtonDown
; /* The point where the left button was pressed */
79 DWORD dwStyle
; /* the cached window GWL_STYLE */
80 INT iMoveItem
; /* index of tracked item. (Tracking mode) */
81 INT xTrackOffset
; /* distance between the right side of the tracked item and the cursor */
82 INT xOldTrack
; /* track offset (see above) after the last WM_MOUSEMOVE */
83 INT iHotItem
; /* index of hot item (cursor is over this item) */
84 INT iHotDivider
; /* index of the hot divider (used while dragging an item or by HDM_SETHOTDIVIDER) */
85 INT iMargin
; /* width of the margin that surrounds a bitmap */
86 INT filter_change_timeout
; /* change timeout set with HDM_SETFILTERCHANGETIMEOUT */
88 HIMAGELIST himl
; /* handle to an image list (may be 0) */
89 HEADER_ITEM
*items
; /* pointer to array of HEADER_ITEM's */
90 INT
*order
; /* array of item IDs indexed by order */
91 BOOL bRectsValid
; /* validity flag for bounding rectangles */
96 #define DIVIDER_WIDTH 10
97 #define HOT_DIVIDER_WIDTH 2
98 #define MAX_HEADER_TEXT_LEN 260
99 #define HDN_UNICODE_OFFSET 20
100 #define HDN_FIRST_UNICODE (HDN_FIRST-HDN_UNICODE_OFFSET)
102 #define HDI_SUPPORTED_FIELDS (HDI_WIDTH|HDI_TEXT|HDI_FORMAT|HDI_LPARAM|HDI_BITMAP|HDI_IMAGE|HDI_ORDER)
103 #define HDI_UNSUPPORTED_FIELDS (HDI_FILTER)
104 #define HDI_UNKNOWN_FIELDS (~(HDI_SUPPORTED_FIELDS|HDI_UNSUPPORTED_FIELDS|HDI_DI_SETITEM))
105 #define HDI_COMCTL32_4_0_FIELDS (HDI_WIDTH|HDI_TEXT|HDI_FORMAT|HDI_LPARAM|HDI_BITMAP)
108 static BOOL
HEADER_PrepareCallbackItems(const HEADER_INFO
*infoPtr
, INT iItem
, INT reqMask
);
109 static void HEADER_FreeCallbackItems(HEADER_ITEM
*lpItem
);
110 static LRESULT
HEADER_SendNotify(const HEADER_INFO
*infoPtr
, UINT code
, NMHDR
*hdr
);
111 static LRESULT
HEADER_SendCtrlCustomDraw(const HEADER_INFO
*infoPtr
, DWORD dwDrawStage
, HDC hdc
, const RECT
*rect
);
113 static const WCHAR themeClass
[] = L
"Header";
115 static void HEADER_StoreHDItemInHeader(HEADER_ITEM
*lpItem
, UINT mask
, const HDITEMW
*phdi
, BOOL fUnicode
)
117 if (mask
& HDI_UNSUPPORTED_FIELDS
)
118 FIXME("unsupported header fields %x\n", (mask
& HDI_UNSUPPORTED_FIELDS
));
120 if (mask
& HDI_BITMAP
)
121 lpItem
->hbm
= phdi
->hbm
;
123 if (mask
& HDI_FORMAT
)
124 lpItem
->fmt
= phdi
->fmt
;
126 if (mask
& HDI_LPARAM
)
127 lpItem
->lParam
= phdi
->lParam
;
129 if (mask
& HDI_WIDTH
)
130 lpItem
->cxy
= phdi
->cxy
;
132 if (mask
& HDI_IMAGE
)
134 lpItem
->iImage
= phdi
->iImage
;
135 if (phdi
->iImage
== I_IMAGECALLBACK
)
136 lpItem
->callbackMask
|= HDI_IMAGE
;
138 lpItem
->callbackMask
&= ~HDI_IMAGE
;
143 Free(lpItem
->pszText
);
144 lpItem
->pszText
= NULL
;
146 if (phdi
->pszText
!= LPSTR_TEXTCALLBACKW
) /* covers != TEXTCALLBACKA too */
148 const WCHAR
*pszText
= phdi
->pszText
!= NULL
? phdi
->pszText
: L
"";
150 Str_SetPtrW(&lpItem
->pszText
, pszText
);
152 Str_SetPtrAtoW(&lpItem
->pszText
, (LPCSTR
)pszText
);
153 lpItem
->callbackMask
&= ~HDI_TEXT
;
157 lpItem
->pszText
= NULL
;
158 lpItem
->callbackMask
|= HDI_TEXT
;
163 static inline LRESULT
164 HEADER_IndexToOrder (const HEADER_INFO
*infoPtr
, INT iItem
)
166 HEADER_ITEM
*lpItem
= &infoPtr
->items
[iItem
];
167 return lpItem
->iOrder
;
172 HEADER_OrderToIndex(const HEADER_INFO
*infoPtr
, INT iorder
)
174 if ((iorder
<0) || iorder
>= infoPtr
->uNumItem
)
176 return infoPtr
->order
[iorder
];
180 HEADER_ChangeItemOrder(const HEADER_INFO
*infoPtr
, INT iItem
, INT iNewOrder
)
182 HEADER_ITEM
*lpItem
= &infoPtr
->items
[iItem
];
185 TRACE("%d: %d->%d\n", iItem
, lpItem
->iOrder
, iNewOrder
);
186 if (lpItem
->iOrder
< iNewOrder
)
188 memmove(&infoPtr
->order
[lpItem
->iOrder
],
189 &infoPtr
->order
[lpItem
->iOrder
+ 1],
190 (iNewOrder
- lpItem
->iOrder
) * sizeof(INT
));
192 if (iNewOrder
< lpItem
->iOrder
)
194 memmove(&infoPtr
->order
[iNewOrder
+ 1],
195 &infoPtr
->order
[iNewOrder
],
196 (lpItem
->iOrder
- iNewOrder
) * sizeof(INT
));
198 infoPtr
->order
[iNewOrder
] = iItem
;
199 nMin
= min(lpItem
->iOrder
, iNewOrder
);
200 nMax
= max(lpItem
->iOrder
, iNewOrder
);
201 for (i
= nMin
; i
<= nMax
; i
++)
202 infoPtr
->items
[infoPtr
->order
[i
]].iOrder
= i
;
205 /* Note: if iItem is the last item then this function returns infoPtr->uNumItem */
207 HEADER_NextItem(const HEADER_INFO
*infoPtr
, INT iItem
)
209 return HEADER_OrderToIndex(infoPtr
, HEADER_IndexToOrder(infoPtr
, iItem
)+1);
213 HEADER_PrevItem(const HEADER_INFO
*infoPtr
, INT iItem
)
215 return HEADER_OrderToIndex(infoPtr
, HEADER_IndexToOrder(infoPtr
, iItem
)-1);
218 /* TRUE when item is not resizable with dividers,
219 note that valid index should be supplied */
221 HEADER_IsItemFixed(const HEADER_INFO
*infoPtr
, INT iItem
)
223 return (infoPtr
->dwStyle
& HDS_NOSIZING
) || (infoPtr
->items
[iItem
].fmt
& HDF_FIXEDWIDTH
);
227 HEADER_SetItemBounds (HEADER_INFO
*infoPtr
)
234 infoPtr
->bRectsValid
= TRUE
;
236 if (infoPtr
->uNumItem
== 0)
239 GetClientRect (infoPtr
->hwndSelf
, &rect
);
242 for (i
= 0; i
< infoPtr
->uNumItem
; i
++) {
243 phdi
= &infoPtr
->items
[HEADER_OrderToIndex(infoPtr
,i
)];
244 phdi
->rect
.top
= rect
.top
;
245 phdi
->rect
.bottom
= rect
.bottom
;
247 phdi
->rect
.right
= phdi
->rect
.left
+ ((phdi
->cxy
>0)?phdi
->cxy
:0);
248 x
= phdi
->rect
.right
;
253 HEADER_Size (HEADER_INFO
*infoPtr
)
255 HEADER_SetItemBounds(infoPtr
);
259 static void HEADER_GetHotDividerRect(const HEADER_INFO
*infoPtr
, RECT
*r
)
261 INT iDivider
= infoPtr
->iHotDivider
;
262 if (infoPtr
->uNumItem
> 0)
266 if (iDivider
< infoPtr
->uNumItem
)
268 lpItem
= &infoPtr
->items
[iDivider
];
269 r
->left
= lpItem
->rect
.left
- HOT_DIVIDER_WIDTH
/2;
270 r
->right
= lpItem
->rect
.left
+ HOT_DIVIDER_WIDTH
/2;
274 lpItem
= &infoPtr
->items
[HEADER_OrderToIndex(infoPtr
, infoPtr
->uNumItem
-1)];
275 r
->left
= lpItem
->rect
.right
- HOT_DIVIDER_WIDTH
/2;
276 r
->right
= lpItem
->rect
.right
+ HOT_DIVIDER_WIDTH
/2;
278 r
->top
= lpItem
->rect
.top
;
279 r
->bottom
= lpItem
->rect
.bottom
;
284 GetClientRect(infoPtr
->hwndSelf
, &clientRect
);
286 r
->right
= r
->left
+ HOT_DIVIDER_WIDTH
/2;
291 HEADER_FillItemFrame(HEADER_INFO
*infoPtr
, HDC hdc
, RECT
*r
, const HEADER_ITEM
*item
, BOOL hottrack
)
293 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
296 int state
= (item
->bDown
) ? HIS_PRESSED
: (hottrack
? HIS_HOT
: HIS_NORMAL
);
297 DrawThemeBackground (theme
, hdc
, HP_HEADERITEM
, state
, r
, NULL
);
298 GetThemeBackgroundContentRect (theme
, hdc
, HP_HEADERITEM
, state
, r
, r
);
302 HBRUSH hbr
= CreateSolidBrush(GetBkColor(hdc
));
303 FillRect(hdc
, r
, hbr
);
309 HEADER_DrawItemFrame(HEADER_INFO
*infoPtr
, HDC hdc
, RECT
*r
, const HEADER_ITEM
*item
)
311 if (GetWindowTheme(infoPtr
->hwndSelf
)) return;
313 if (!(infoPtr
->dwStyle
& HDS_FLAT
))
315 if (infoPtr
->dwStyle
& HDS_BUTTONS
) {
317 DrawEdge (hdc
, r
, BDR_RAISEDOUTER
, BF_RECT
| BF_FLAT
| BF_ADJUST
);
319 DrawEdge (hdc
, r
, EDGE_RAISED
, BF_RECT
| BF_SOFT
| BF_ADJUST
);
322 DrawEdge (hdc
, r
, EDGE_ETCHED
, BF_BOTTOM
| BF_RIGHT
| BF_ADJUST
);
326 /* Create a region for the sort arrow with its bounding rect's top-left
327 co-ord x,y and its height h. */
328 static HRGN
create_sort_arrow( INT x
, INT y
, INT h
, BOOL is_up
)
331 RGNDATA
*data
= (RGNDATA
*)buffer
;
332 DWORD size
= FIELD_OFFSET(RGNDATA
, Buffer
[h
* sizeof(RECT
)]);
336 if (size
> sizeof(buffer
))
338 data
= Alloc( size
);
339 if (!data
) return NULL
;
341 data
->rdh
.dwSize
= sizeof(data
->rdh
);
342 data
->rdh
.iType
= RDH_RECTANGLES
;
343 data
->rdh
.nCount
= 0;
344 data
->rdh
.nRgnSize
= h
* sizeof(RECT
);
352 x
+= h
- 1; /* set x to the centre */
354 for (i
= 0; i
< h
; i
++, y
+= yinc
)
356 RECT
*rect
= (RECT
*)data
->Buffer
+ data
->rdh
.nCount
;
359 rect
->right
= x
+ i
+ 1;
360 rect
->bottom
= y
+ 1;
363 rgn
= ExtCreateRegion( NULL
, size
, data
);
364 if (data
!= (RGNDATA
*)buffer
) Free( data
);
369 HEADER_DrawItem (HEADER_INFO
*infoPtr
, HDC hdc
, INT iItem
, BOOL bHotTrack
, LRESULT lCDFlags
)
371 HEADER_ITEM
*phdi
= &infoPtr
->items
[iItem
];
374 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
378 TRACE("DrawItem(iItem %d bHotTrack %d unicode flag %d)\n", iItem
, bHotTrack
, (infoPtr
->nNotifyFormat
== NFR_UNICODE
));
381 if (r
.right
- r
.left
== 0)
382 return phdi
->rect
.right
;
385 state
= (phdi
->bDown
) ? HIS_PRESSED
: (bHotTrack
? HIS_HOT
: HIS_NORMAL
);
387 /* Set the colors before sending NM_CUSTOMDRAW so that it can change them */
388 SetTextColor(hdc
, (bHotTrack
&& !theme
) ? comctl32_color
.clrHighlight
: comctl32_color
.clrBtnText
);
389 SetBkColor(hdc
, comctl32_color
.clr3dFace
);
391 if (lCDFlags
& CDRF_NOTIFYITEMDRAW
&& !(phdi
->fmt
& HDF_OWNERDRAW
))
393 LRESULT lCDItemFlags
;
395 nmcd
.dwDrawStage
= CDDS_PREPAINT
| CDDS_ITEM
;
397 nmcd
.dwItemSpec
= iItem
;
399 nmcd
.uItemState
= phdi
->bDown
? CDIS_SELECTED
: 0;
400 nmcd
.lItemlParam
= phdi
->lParam
;
402 lCDItemFlags
= HEADER_SendNotify(infoPtr
, NM_CUSTOMDRAW
, (NMHDR
*)&nmcd
);
403 if (lCDItemFlags
& CDRF_SKIPDEFAULT
)
404 return phdi
->rect
.right
;
407 /* Fill background, owner could draw over it. */
408 HEADER_FillItemFrame(infoPtr
, hdc
, &r
, phdi
, bHotTrack
);
410 if (phdi
->fmt
& HDF_OWNERDRAW
)
415 dis
.CtlType
= ODT_HEADER
;
416 dis
.CtlID
= GetWindowLongPtrW (infoPtr
->hwndSelf
, GWLP_ID
);
418 dis
.itemAction
= ODA_DRAWENTIRE
;
419 dis
.itemState
= phdi
->bDown
? ODS_SELECTED
: 0;
420 dis
.hwndItem
= infoPtr
->hwndSelf
;
422 dis
.rcItem
= phdi
->rect
;
423 dis
.itemData
= phdi
->lParam
;
424 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
425 ret
= SendMessageW (infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
426 if (oldBkMode
!= TRANSPARENT
)
427 SetBkMode(hdc
, oldBkMode
);
430 HEADER_FillItemFrame(infoPtr
, hdc
, &r
, phdi
, bHotTrack
);
432 /* Edges are always drawn if we don't have attached theme. */
433 HEADER_DrawItemFrame(infoPtr
, hdc
, &r
, phdi
);
434 /* If application processed WM_DRAWITEM we should skip label painting,
435 edges are drawn no matter what. */
436 if (ret
) return phdi
->rect
.right
;
439 HEADER_DrawItemFrame(infoPtr
, hdc
, &r
, phdi
);
446 /* Now text and image */
448 INT rw
, rh
; /* width and height of r */
449 INT
*x
= NULL
; /* x and ... */
450 UINT
*w
= NULL
; /* ... width of the pic (bmp or img) which is part of cnt */
451 /* cnt,txt,img,bmp */
455 INT sort_w
, sort_x
, sort_h
;
458 HEADER_PrepareCallbackItems(infoPtr
, iItem
, HDI_TEXT
|HDI_IMAGE
);
459 cw
= iw
= bw
= sort_w
= sort_h
= 0;
460 rw
= r
.right
- r
.left
;
461 rh
= r
.bottom
- r
.top
;
463 if (phdi
->fmt
& HDF_STRING
) {
466 SetRectEmpty(&textRect
);
469 GetThemeTextExtent(theme
, hdc
, HP_HEADERITEM
, state
, phdi
->pszText
, -1,
470 DT_LEFT
|DT_VCENTER
|DT_SINGLELINE
, NULL
, &textRect
);
472 DrawTextW (hdc
, phdi
->pszText
, -1,
473 &textRect
, DT_LEFT
|DT_VCENTER
|DT_SINGLELINE
|DT_CALCRECT
);
475 cw
= textRect
.right
- textRect
.left
+ 2 * infoPtr
->iMargin
;
478 if (phdi
->fmt
& (HDF_SORTUP
| HDF_SORTDOWN
)) {
479 sort_h
= MulDiv( infoPtr
->nHeight
- VERT_BORDER
, 4, 13 );
480 sort_w
= 2 * sort_h
- 1 + infoPtr
->iMargin
* 2;
482 } else { /* sort arrows take precedent over images/bitmaps */
483 if ((phdi
->fmt
& HDF_IMAGE
) && ImageList_GetIconSize( infoPtr
->himl
, &img_cx
, &img_cy
)) {
484 iw
= img_cx
+ 2 * infoPtr
->iMargin
;
489 if ((phdi
->fmt
& HDF_BITMAP
) && (phdi
->hbm
)) {
490 GetObjectW (phdi
->hbm
, sizeof(BITMAP
), &bmp
);
491 bw
= bmp
.bmWidth
+ 2 * infoPtr
->iMargin
;
501 /* align cx using the unclipped cw */
502 if ((phdi
->fmt
& HDF_JUSTIFYMASK
) == HDF_LEFT
)
504 else if ((phdi
->fmt
& HDF_JUSTIFYMASK
) == HDF_CENTER
)
505 cx
= r
.left
+ rw
/ 2 - cw
/ 2;
512 if (cx
+ cw
> r
.right
)
515 tx
= cx
+ infoPtr
->iMargin
;
516 /* since cw might have changed we have to recalculate tw */
517 tw
= cw
- infoPtr
->iMargin
* 2;
520 sort_x
= cx
+ tw
+ infoPtr
->iMargin
* 3;
524 if (phdi
->fmt
& HDF_BITMAP_ON_RIGHT
) {
525 /* put pic behind text */
526 *x
= cx
+ tw
+ infoPtr
->iMargin
* 3;
528 *x
= cx
+ infoPtr
->iMargin
;
529 /* move text behind pic */
535 /* since we're done with the layout we can
536 now calculate the position of bmp which
537 has no influence on alignment and layout
539 if ((phdi
->fmt
& HDF_JUSTIFYMASK
) == HDF_RIGHT
)
540 bx
= cx
- bw
+ infoPtr
->iMargin
;
542 bx
= cx
+ cw
+ infoPtr
->iMargin
;
545 if (sort_w
|| iw
|| bw
) {
546 HDC hClipDC
= GetDC(infoPtr
->hwndSelf
);
547 HRGN hClipRgn
= CreateRectRgn(r
.left
, r
.top
, r
.right
, r
.bottom
);
548 SelectClipRgn(hClipDC
, hClipRgn
);
551 HRGN arrow
= create_sort_arrow( sort_x
, r
.top
+ (rh
- sort_h
) / 2,
552 sort_h
, phdi
->fmt
& HDF_SORTUP
);
554 FillRgn( hClipDC
, arrow
, GetSysColorBrush( COLOR_GRAYTEXT
) );
555 DeleteObject( arrow
);
560 HDC hdcBitmap
= CreateCompatibleDC (hClipDC
);
561 SelectObject (hdcBitmap
, phdi
->hbm
);
562 BitBlt (hClipDC
, bx
, r
.top
+ (rh
- bmp
.bmHeight
) / 2,
563 bmp
.bmWidth
, bmp
.bmHeight
, hdcBitmap
, 0, 0, SRCCOPY
);
564 DeleteDC (hdcBitmap
);
568 ImageList_DrawEx (infoPtr
->himl
, phdi
->iImage
, hClipDC
,
569 ix
, r
.top
+ (rh
- img_cy
) / 2,
570 img_cx
, img_cy
, CLR_DEFAULT
, CLR_DEFAULT
, 0);
573 DeleteObject(hClipRgn
);
574 ReleaseDC(infoPtr
->hwndSelf
, hClipDC
);
577 if (((phdi
->fmt
& HDF_STRING
)
578 || (!(phdi
->fmt
& (HDF_OWNERDRAW
|HDF_STRING
|HDF_BITMAP
|
579 HDF_BITMAP_ON_RIGHT
|HDF_IMAGE
)))) /* no explicit format specified? */
580 && (phdi
->pszText
)) {
581 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
585 DrawThemeText(theme
, hdc
, HP_HEADERITEM
, state
, phdi
->pszText
,
586 -1, DT_LEFT
|DT_END_ELLIPSIS
|DT_VCENTER
|DT_SINGLELINE
,
589 DrawTextW (hdc
, phdi
->pszText
, -1,
590 &r
, DT_LEFT
|DT_END_ELLIPSIS
|DT_VCENTER
|DT_SINGLELINE
);
592 if (oldBkMode
!= TRANSPARENT
)
593 SetBkMode(hdc
, oldBkMode
);
595 HEADER_FreeCallbackItems(phdi
);
598 return phdi
->rect
.right
;
602 HEADER_DrawHotDivider(const HEADER_INFO
*infoPtr
, HDC hdc
)
607 HEADER_GetHotDividerRect(infoPtr
, &r
);
608 brush
= CreateSolidBrush(comctl32_color
.clrHighlight
);
609 FillRect(hdc
, &r
, brush
);
614 HEADER_Refresh (HEADER_INFO
*infoPtr
, HDC hdc
)
616 HFONT hFont
, hOldFont
;
622 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
624 if (!infoPtr
->bRectsValid
)
625 HEADER_SetItemBounds(infoPtr
);
627 /* get rect for the bar, adjusted for the border */
628 GetClientRect (infoPtr
->hwndSelf
, &rect
);
629 lCDFlags
= HEADER_SendCtrlCustomDraw(infoPtr
, CDDS_PREPAINT
, hdc
, &rect
);
631 if (infoPtr
->bDragging
)
632 ImageList_DragShowNolock(FALSE
);
634 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
635 hOldFont
= SelectObject (hdc
, hFont
);
637 /* draw Background */
638 if (infoPtr
->uNumItem
== 0 && theme
== NULL
) {
639 hbrBk
= GetSysColorBrush(COLOR_3DFACE
);
640 FillRect(hdc
, &rect
, hbrBk
);
644 for (i
= 0; x
<= rect
.right
&& i
< infoPtr
->uNumItem
; i
++) {
645 int idx
= HEADER_OrderToIndex(infoPtr
,i
);
646 if (RectVisible(hdc
, &infoPtr
->items
[idx
].rect
))
647 HEADER_DrawItem(infoPtr
, hdc
, idx
, infoPtr
->iHotItem
== idx
, lCDFlags
);
648 x
= infoPtr
->items
[idx
].rect
.right
;
653 if ((x
<= rect
.right
) && RectVisible(hdc
, &rcRest
) && (infoPtr
->uNumItem
> 0)) {
655 DrawThemeBackground(theme
, hdc
, HP_HEADERITEM
, HIS_NORMAL
, &rcRest
, NULL
);
657 else if (infoPtr
->dwStyle
& HDS_FLAT
) {
658 hbrBk
= GetSysColorBrush(COLOR_3DFACE
);
659 FillRect(hdc
, &rcRest
, hbrBk
);
663 if (infoPtr
->dwStyle
& HDS_BUTTONS
)
664 DrawEdge (hdc
, &rcRest
, EDGE_RAISED
, BF_TOP
|BF_LEFT
|BF_BOTTOM
|BF_SOFT
|BF_MIDDLE
);
666 DrawEdge (hdc
, &rcRest
, EDGE_ETCHED
, BF_BOTTOM
|BF_MIDDLE
);
670 if (infoPtr
->iHotDivider
!= -1)
671 HEADER_DrawHotDivider(infoPtr
, hdc
);
673 if (infoPtr
->bDragging
)
674 ImageList_DragShowNolock(TRUE
);
675 SelectObject (hdc
, hOldFont
);
677 if (lCDFlags
& CDRF_NOTIFYPOSTPAINT
)
678 HEADER_SendCtrlCustomDraw(infoPtr
, CDDS_POSTPAINT
, hdc
, &rect
);
683 HEADER_RefreshItem (HEADER_INFO
*infoPtr
, INT iItem
)
685 if (!infoPtr
->bRectsValid
)
686 HEADER_SetItemBounds(infoPtr
);
688 InvalidateRect(infoPtr
->hwndSelf
, &infoPtr
->items
[iItem
].rect
, FALSE
);
693 HEADER_InternalHitTest (const HEADER_INFO
*infoPtr
, const POINT
*lpPt
, UINT
*pFlags
, INT
*pItem
)
700 GetClientRect (infoPtr
->hwndSelf
, &rect
);
704 if (PtInRect (&rect
, *lpPt
))
706 if (infoPtr
->uNumItem
== 0) {
707 *pFlags
|= HHT_NOWHERE
;
713 /* somewhere inside */
714 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++) {
715 rect
= infoPtr
->items
[iCount
].rect
;
716 width
= rect
.right
- rect
.left
;
721 if (PtInRect (&rect
, *lpPt
)) {
722 if (width
<= 2 * DIVIDER_WIDTH
) {
723 *pFlags
|= HHT_ONHEADER
;
725 TRACE("ON HEADER %d\n", iCount
);
728 if (HEADER_IndexToOrder(infoPtr
, iCount
) > 0) {
730 rcTest
.right
= rcTest
.left
+ DIVIDER_WIDTH
;
731 if (PtInRect (&rcTest
, *lpPt
)) {
732 if (HEADER_IsItemFixed(infoPtr
, HEADER_PrevItem(infoPtr
, iCount
)))
734 *pFlags
|= HHT_ONHEADER
;
736 TRACE("ON HEADER %d\n", *pItem
);
740 *pFlags
|= HHT_ONDIVOPEN
;
741 *pItem
= HEADER_PrevItem(infoPtr
, iCount
);
742 TRACE("ON DIVOPEN %d\n", *pItem
);
746 *pFlags
|= HHT_ONDIVIDER
;
747 *pItem
= HEADER_PrevItem(infoPtr
, iCount
);
748 TRACE("ON DIVIDER %d\n", *pItem
);
754 rcTest
.left
= rcTest
.right
- DIVIDER_WIDTH
;
755 if (!HEADER_IsItemFixed(infoPtr
, iCount
) && PtInRect (&rcTest
, *lpPt
))
757 *pFlags
|= HHT_ONDIVIDER
;
759 TRACE("ON DIVIDER %d\n", *pItem
);
763 *pFlags
|= HHT_ONHEADER
;
765 TRACE("ON HEADER %d\n", iCount
);
770 /* check for last divider part (on nowhere) */
771 if (!HEADER_IsItemFixed(infoPtr
, infoPtr
->uNumItem
- 1))
773 rect
= infoPtr
->items
[infoPtr
->uNumItem
-1].rect
;
774 rect
.left
= rect
.right
;
775 rect
.right
+= DIVIDER_WIDTH
;
776 if (PtInRect (&rect
, *lpPt
)) {
778 *pFlags
|= HHT_ONDIVOPEN
;
779 *pItem
= infoPtr
->uNumItem
- 1;
780 TRACE("ON DIVOPEN %d\n", *pItem
);
784 *pFlags
|= HHT_ONDIVIDER
;
785 *pItem
= infoPtr
->uNumItem
- 1;
786 TRACE("ON DIVIDER %d\n", *pItem
);
792 *pFlags
|= HHT_NOWHERE
;
799 if (lpPt
->x
< rect
.left
) {
801 *pFlags
|= HHT_TOLEFT
;
803 else if (lpPt
->x
> rect
.right
) {
805 *pFlags
|= HHT_TORIGHT
;
808 if (lpPt
->y
< rect
.top
) {
810 *pFlags
|= HHT_ABOVE
;
812 else if (lpPt
->y
> rect
.bottom
) {
814 *pFlags
|= HHT_BELOW
;
819 TRACE("flags=0x%X\n", *pFlags
);
825 HEADER_DrawTrackLine (const HEADER_INFO
*infoPtr
, HDC hdc
, INT x
)
829 GetClientRect (infoPtr
->hwndSelf
, &rect
);
830 PatBlt( hdc
, x
, rect
.top
, 1, rect
.bottom
- rect
.top
, DSTINVERT
);
835 * Convert a HDITEM into the correct format (ANSI/Unicode) to send it in a notify
838 * [I] infoPtr : the header that wants to send the notify
839 * [O] dest : The buffer to store the HDITEM for notify. It may be set to a HDITEMA of HDITEMW
840 * [I] src : The source HDITEM. It may be a HDITEMA or HDITEMW
841 * [I] fSourceUnicode : is src a HDITEMW or HDITEMA
842 * [O] ppvScratch : a pointer to a scratch buffer that needs to be freed after
843 * the HDITEM is no longer in use or NULL if none was needed
845 * NOTE: We depend on HDITEMA and HDITEMW having the same structure
847 static void HEADER_CopyHDItemForNotify(const HEADER_INFO
*infoPtr
, HDITEMW
*dest
,
848 const HDITEMW
*src
, BOOL fSourceUnicode
, LPVOID
*ppvScratch
)
853 if (src
->mask
& HDI_TEXT
&& src
->pszText
!= LPSTR_TEXTCALLBACKW
) /* covers TEXTCALLBACKA as well */
855 if (fSourceUnicode
&& infoPtr
->nNotifyFormat
!= NFR_UNICODE
)
857 dest
->pszText
= NULL
;
858 Str_SetPtrWtoA((LPSTR
*)&dest
->pszText
, src
->pszText
);
859 *ppvScratch
= dest
->pszText
;
862 if (!fSourceUnicode
&& infoPtr
->nNotifyFormat
== NFR_UNICODE
)
864 dest
->pszText
= NULL
;
865 Str_SetPtrAtoW(&dest
->pszText
, (LPSTR
)src
->pszText
);
866 *ppvScratch
= dest
->pszText
;
871 static UINT
HEADER_NotifyCodeWtoA(UINT code
)
873 /* we use the fact that all the unicode messages are in HDN_FIRST_UNICODE..HDN_LAST*/
874 if (code
>= HDN_LAST
&& code
<= HDN_FIRST_UNICODE
)
875 return code
+ HDN_UNICODE_OFFSET
;
881 HEADER_SendNotify(const HEADER_INFO
*infoPtr
, UINT code
, NMHDR
*nmhdr
)
883 nmhdr
->hwndFrom
= infoPtr
->hwndSelf
;
884 nmhdr
->idFrom
= GetWindowLongPtrW (infoPtr
->hwndSelf
, GWLP_ID
);
887 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
888 nmhdr
->idFrom
, (LPARAM
)nmhdr
);
892 HEADER_SendSimpleNotify (const HEADER_INFO
*infoPtr
, UINT code
)
895 return (BOOL
)HEADER_SendNotify(infoPtr
, code
, &nmhdr
);
899 HEADER_SendCtrlCustomDraw(const HEADER_INFO
*infoPtr
, DWORD dwDrawStage
, HDC hdc
, const RECT
*rect
)
902 nm
.dwDrawStage
= dwDrawStage
;
909 return HEADER_SendNotify(infoPtr
, NM_CUSTOMDRAW
, (NMHDR
*)&nm
);
913 HEADER_SendNotifyWithHDItemT(const HEADER_INFO
*infoPtr
, UINT code
, INT iItem
, HDITEMW
*lpItem
)
917 if (infoPtr
->nNotifyFormat
!= NFR_UNICODE
)
918 code
= HEADER_NotifyCodeWtoA(code
);
921 nmhdr
.pitem
= lpItem
;
923 return (BOOL
)HEADER_SendNotify(infoPtr
, code
, (NMHDR
*)&nmhdr
);
927 HEADER_SendNotifyWithIntFieldT(const HEADER_INFO
*infoPtr
, UINT code
, INT iItem
, INT mask
, INT iValue
)
931 /* copying only the iValue should be ok but to make the code more robust we copy everything */
932 nmitem
.cxy
= infoPtr
->items
[iItem
].cxy
;
933 nmitem
.hbm
= infoPtr
->items
[iItem
].hbm
;
934 nmitem
.pszText
= NULL
;
935 nmitem
.cchTextMax
= 0;
936 nmitem
.fmt
= infoPtr
->items
[iItem
].fmt
;
937 nmitem
.lParam
= infoPtr
->items
[iItem
].lParam
;
938 nmitem
.iOrder
= infoPtr
->items
[iItem
].iOrder
;
939 nmitem
.iImage
= infoPtr
->items
[iItem
].iImage
;
948 nmitem
.iOrder
= iValue
;
951 ERR("invalid mask value 0x%x\n", iValue
);
954 return HEADER_SendNotifyWithHDItemT(infoPtr
, code
, iItem
, &nmitem
);
958 * Prepare callback items
959 * depends on NMHDDISPINFOW having same structure as NMHDDISPINFOA
960 * (so we handle the two cases only doing a specific cast for pszText).
961 * Checks if any of the required fields is a callback. If this is the case sends a
962 * NMHDISPINFO notify to retrieve these items. The items are stored in the
963 * HEADER_ITEM pszText and iImage fields. They should be Freed with
964 * HEADER_FreeCallbackItems.
966 * @param hwnd : hwnd header container handler
967 * @param iItem : the header item id
968 * @param reqMask : required fields. If any of them is callback this function will fetch it
970 * @return TRUE on success, else FALSE
973 HEADER_PrepareCallbackItems(const HEADER_INFO
*infoPtr
, INT iItem
, INT reqMask
)
975 HEADER_ITEM
*lpItem
= &infoPtr
->items
[iItem
];
976 DWORD mask
= reqMask
& lpItem
->callbackMask
;
977 NMHDDISPINFOW dispInfo
;
978 void *pvBuffer
= NULL
;
982 if (mask
&HDI_TEXT
&& lpItem
->pszText
!= NULL
)
984 ERR("(): function called without a call to FreeCallbackItems\n");
985 Free(lpItem
->pszText
);
986 lpItem
->pszText
= NULL
;
989 memset(&dispInfo
, 0, sizeof(NMHDDISPINFOW
));
990 dispInfo
.hdr
.hwndFrom
= infoPtr
->hwndSelf
;
991 dispInfo
.hdr
.idFrom
= GetWindowLongPtrW (infoPtr
->hwndSelf
, GWLP_ID
);
992 if (infoPtr
->nNotifyFormat
== NFR_UNICODE
)
994 dispInfo
.hdr
.code
= HDN_GETDISPINFOW
;
996 pvBuffer
= Alloc(MAX_HEADER_TEXT_LEN
* sizeof(WCHAR
));
1000 dispInfo
.hdr
.code
= HDN_GETDISPINFOA
;
1001 if (mask
& HDI_TEXT
)
1002 pvBuffer
= Alloc(MAX_HEADER_TEXT_LEN
* sizeof(CHAR
));
1004 dispInfo
.pszText
= pvBuffer
;
1005 dispInfo
.cchTextMax
= (pvBuffer
!=NULL
?MAX_HEADER_TEXT_LEN
:0);
1006 dispInfo
.iItem
= iItem
;
1007 dispInfo
.mask
= mask
;
1008 dispInfo
.lParam
= lpItem
->lParam
;
1010 TRACE("Sending HDN_GETDISPINFO%c\n", infoPtr
->nNotifyFormat
== NFR_UNICODE
?'W':'A');
1011 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, dispInfo
.hdr
.idFrom
, (LPARAM
)&dispInfo
);
1013 TRACE("SendMessage returns(mask:0x%x,str:%s,lParam:%p)\n",
1015 (infoPtr
->nNotifyFormat
== NFR_UNICODE
? debugstr_w(dispInfo
.pszText
) : (LPSTR
) dispInfo
.pszText
),
1016 (void*) dispInfo
.lParam
);
1018 if (mask
& HDI_IMAGE
)
1019 lpItem
->iImage
= dispInfo
.iImage
;
1020 if (mask
& HDI_TEXT
)
1022 if (infoPtr
->nNotifyFormat
== NFR_UNICODE
)
1024 lpItem
->pszText
= pvBuffer
;
1026 /* the user might have used his own buffer */
1027 if (dispInfo
.pszText
!= lpItem
->pszText
)
1028 Str_GetPtrW(dispInfo
.pszText
, lpItem
->pszText
, MAX_HEADER_TEXT_LEN
);
1032 Str_SetPtrAtoW(&lpItem
->pszText
, (LPSTR
)dispInfo
.pszText
);
1037 if (dispInfo
.mask
& HDI_DI_SETITEM
)
1039 /* make the items permanent */
1040 lpItem
->callbackMask
&= ~dispInfo
.mask
;
1048 * Free the items that might be allocated with HEADER_PrepareCallbackItems
1051 * [I] lpItem : the item to free the data
1055 HEADER_FreeCallbackItems(HEADER_ITEM
*lpItem
)
1057 if (lpItem
->callbackMask
&HDI_TEXT
)
1059 Free(lpItem
->pszText
);
1060 lpItem
->pszText
= NULL
;
1063 if (lpItem
->callbackMask
&HDI_IMAGE
)
1064 lpItem
->iImage
= I_IMAGECALLBACK
;
1068 HEADER_CreateDragImage (HEADER_INFO
*infoPtr
, INT iItem
)
1070 HEADER_ITEM
*lpItem
;
1072 HBITMAP hMemory
, hOldBitmap
;
1080 if (iItem
>= infoPtr
->uNumItem
)
1083 if (!infoPtr
->bRectsValid
)
1084 HEADER_SetItemBounds(infoPtr
);
1086 lpItem
= &infoPtr
->items
[iItem
];
1087 width
= lpItem
->rect
.right
- lpItem
->rect
.left
;
1088 height
= lpItem
->rect
.bottom
- lpItem
->rect
.top
;
1090 hDeviceDC
= GetDC(NULL
);
1091 hMemoryDC
= CreateCompatibleDC(hDeviceDC
);
1092 hMemory
= CreateCompatibleBitmap(hDeviceDC
, width
, height
);
1093 ReleaseDC(NULL
, hDeviceDC
);
1094 hOldBitmap
= SelectObject(hMemoryDC
, hMemory
);
1095 SetViewportOrgEx(hMemoryDC
, -lpItem
->rect
.left
, -lpItem
->rect
.top
, NULL
);
1096 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject(SYSTEM_FONT
);
1097 SelectObject(hMemoryDC
, hFont
);
1099 GetClientRect(infoPtr
->hwndSelf
, &rc
);
1100 lCDFlags
= HEADER_SendCtrlCustomDraw(infoPtr
, CDDS_PREPAINT
, hMemoryDC
, &rc
);
1101 HEADER_DrawItem(infoPtr
, hMemoryDC
, iItem
, FALSE
, lCDFlags
);
1102 if (lCDFlags
& CDRF_NOTIFYPOSTPAINT
)
1103 HEADER_SendCtrlCustomDraw(infoPtr
, CDDS_POSTPAINT
, hMemoryDC
, &rc
);
1105 hMemory
= SelectObject(hMemoryDC
, hOldBitmap
);
1106 DeleteDC(hMemoryDC
);
1108 if (hMemory
== NULL
) /* if anything failed */
1111 himl
= ImageList_Create(width
, height
, ILC_COLORDDB
, 1, 1);
1112 ImageList_Add(himl
, hMemory
, NULL
);
1113 DeleteObject(hMemory
);
1118 HEADER_SetHotDivider(HEADER_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
1127 pt
.x
= (INT
)(SHORT
)LOWORD(lParam
);
1129 HEADER_InternalHitTest (infoPtr
, &pt
, &flags
, &iDivider
);
1131 if (flags
& HHT_TOLEFT
)
1133 else if (flags
& HHT_NOWHERE
|| flags
& HHT_TORIGHT
)
1134 iDivider
= infoPtr
->uNumItem
;
1137 HEADER_ITEM
*lpItem
= &infoPtr
->items
[iDivider
];
1138 if (pt
.x
> (lpItem
->rect
.left
+lpItem
->rect
.right
)/2)
1139 iDivider
= HEADER_NextItem(infoPtr
, iDivider
);
1143 iDivider
= (INT
)lParam
;
1145 /* Note; wParam==FALSE, lParam==-1 is valid and is used to clear the hot divider */
1146 if (iDivider
<-1 || iDivider
>(int)infoPtr
->uNumItem
)
1149 if (iDivider
!= infoPtr
->iHotDivider
)
1151 if (infoPtr
->iHotDivider
!= -1)
1153 HEADER_GetHotDividerRect(infoPtr
, &r
);
1154 InvalidateRect(infoPtr
->hwndSelf
, &r
, FALSE
);
1156 infoPtr
->iHotDivider
= iDivider
;
1159 HEADER_GetHotDividerRect(infoPtr
, &r
);
1160 InvalidateRect(infoPtr
->hwndSelf
, &r
, FALSE
);
1167 HEADER_DeleteItem (HEADER_INFO
*infoPtr
, INT iItem
)
1172 TRACE("[iItem=%d]\n", iItem
);
1174 if ((iItem
< 0) || (iItem
>= (INT
)infoPtr
->uNumItem
))
1177 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1178 TRACE("%d: order=%d, iOrder=%d, ->iOrder=%d\n", i
, infoPtr
->order
[i
], infoPtr
->items
[i
].iOrder
, infoPtr
->items
[infoPtr
->order
[i
]].iOrder
);
1180 iOrder
= infoPtr
->items
[iItem
].iOrder
;
1181 Free(infoPtr
->items
[iItem
].pszText
);
1183 infoPtr
->uNumItem
--;
1184 memmove(&infoPtr
->items
[iItem
], &infoPtr
->items
[iItem
+ 1],
1185 (infoPtr
->uNumItem
- iItem
) * sizeof(HEADER_ITEM
));
1186 memmove(&infoPtr
->order
[iOrder
], &infoPtr
->order
[iOrder
+ 1],
1187 (infoPtr
->uNumItem
- iOrder
) * sizeof(INT
));
1188 infoPtr
->items
= ReAlloc(infoPtr
->items
, sizeof(HEADER_ITEM
) * infoPtr
->uNumItem
);
1189 infoPtr
->order
= ReAlloc(infoPtr
->order
, sizeof(INT
) * infoPtr
->uNumItem
);
1191 /* Correct the orders */
1192 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1194 if (infoPtr
->order
[i
] > iItem
)
1195 infoPtr
->order
[i
]--;
1197 infoPtr
->items
[infoPtr
->order
[i
]].iOrder
= i
;
1199 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1200 TRACE("%d: order=%d, iOrder=%d, ->iOrder=%d\n", i
, infoPtr
->order
[i
], infoPtr
->items
[i
].iOrder
, infoPtr
->items
[infoPtr
->order
[i
]].iOrder
);
1202 HEADER_SetItemBounds (infoPtr
);
1203 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1210 HEADER_GetImageList (const HEADER_INFO
*infoPtr
)
1212 return (LRESULT
)infoPtr
->himl
;
1217 HEADER_GetItemT (const HEADER_INFO
*infoPtr
, INT nItem
, LPHDITEMW phdi
, BOOL bUnicode
)
1219 HEADER_ITEM
*lpItem
;
1225 TRACE("[nItem=%d]\n", nItem
);
1231 if ((nItem
< 0) || (nItem
>= (INT
)infoPtr
->uNumItem
))
1234 if (mask
& HDI_UNKNOWN_FIELDS
)
1236 TRACE("mask %x contains unknown fields. Using only comctl32 4.0 fields\n", mask
);
1237 mask
&= HDI_COMCTL32_4_0_FIELDS
;
1240 lpItem
= &infoPtr
->items
[nItem
];
1241 HEADER_PrepareCallbackItems(infoPtr
, nItem
, mask
);
1243 if (mask
& HDI_BITMAP
)
1244 phdi
->hbm
= lpItem
->hbm
;
1246 if (mask
& HDI_FORMAT
)
1247 phdi
->fmt
= lpItem
->fmt
;
1249 if (mask
& HDI_WIDTH
)
1250 phdi
->cxy
= lpItem
->cxy
;
1252 if (mask
& HDI_LPARAM
)
1253 phdi
->lParam
= lpItem
->lParam
;
1255 if (mask
& HDI_IMAGE
)
1256 phdi
->iImage
= lpItem
->iImage
;
1258 if (mask
& HDI_ORDER
)
1259 phdi
->iOrder
= lpItem
->iOrder
;
1261 if (mask
& HDI_TEXT
)
1264 Str_GetPtrW (lpItem
->pszText
, phdi
->pszText
, phdi
->cchTextMax
);
1266 Str_GetPtrWtoA (lpItem
->pszText
, (LPSTR
)phdi
->pszText
, phdi
->cchTextMax
);
1269 HEADER_FreeCallbackItems(lpItem
);
1274 static inline LRESULT
1275 HEADER_GetItemCount (const HEADER_INFO
*infoPtr
)
1277 return infoPtr
->uNumItem
;
1282 HEADER_GetItemRect (const HEADER_INFO
*infoPtr
, INT iItem
, LPRECT lpRect
)
1284 if ((iItem
< 0) || (iItem
>= (INT
)infoPtr
->uNumItem
))
1287 lpRect
->left
= infoPtr
->items
[iItem
].rect
.left
;
1288 lpRect
->right
= infoPtr
->items
[iItem
].rect
.right
;
1289 lpRect
->top
= infoPtr
->items
[iItem
].rect
.top
;
1290 lpRect
->bottom
= infoPtr
->items
[iItem
].rect
.bottom
;
1297 HEADER_GetOrderArray(const HEADER_INFO
*infoPtr
, INT size
, LPINT order
)
1299 if ((UINT
)size
<infoPtr
->uNumItem
)
1302 memcpy(order
, infoPtr
->order
, infoPtr
->uNumItem
* sizeof(INT
));
1306 /* Returns index of first duplicate 'value' from [0,to) range,
1307 or -1 if there isn't any */
1308 static INT
has_duplicate(const INT
*array
, INT to
, INT value
)
1311 for(i
= 0; i
< to
; i
++)
1312 if (array
[i
] == value
) return i
;
1316 /* returns next available value from [0,max] not to duplicate in [0,to) */
1317 static INT
get_nextvalue(const INT
*array
, INT to
, INT max
)
1320 for(i
= 0; i
< max
; i
++)
1321 if (has_duplicate(array
, to
, i
) == -1) return i
;
1326 HEADER_SetOrderArray(HEADER_INFO
*infoPtr
, INT size
, const INT
*order
)
1328 HEADER_ITEM
*lpItem
;
1331 if ((UINT
)size
!= infoPtr
->uNumItem
)
1334 if (TRACE_ON(header
))
1336 TRACE("count=%d, order array={", size
);
1337 for (i
= 0; i
< size
; i
++)
1338 TRACE("%d%c", order
[i
], i
!= size
-1 ? ',' : '}');
1342 for (i
=0; i
<size
; i
++)
1344 if (order
[i
] >= size
|| order
[i
] < 0)
1345 /* on invalid index get next available */
1346 /* FIXME: if i==0 array item is out of range behaviour is
1347 different, see tests */
1348 infoPtr
->order
[i
] = get_nextvalue(infoPtr
->order
, i
, size
);
1353 infoPtr
->order
[i
] = order
[i
];
1355 /* remove duplicates */
1356 while ((dup
= has_duplicate(infoPtr
->order
, j
, order
[j
])) != -1)
1360 next
= get_nextvalue(infoPtr
->order
, j
, size
);
1361 infoPtr
->order
[dup
] = next
;
1366 /* sync with item data */
1367 for (i
=0; i
<size
; i
++)
1369 lpItem
= &infoPtr
->items
[infoPtr
->order
[i
]];
1372 HEADER_SetItemBounds(infoPtr
);
1373 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1377 static inline LRESULT
1378 HEADER_GetUnicodeFormat (const HEADER_INFO
*infoPtr
)
1380 return (infoPtr
->nNotifyFormat
== NFR_UNICODE
);
1385 HEADER_HitTest (const HEADER_INFO
*infoPtr
, LPHDHITTESTINFO phti
)
1387 UINT outside
= HHT_NOWHERE
| HHT_ABOVE
| HHT_BELOW
| HHT_TOLEFT
| HHT_TORIGHT
;
1389 HEADER_InternalHitTest (infoPtr
, &phti
->pt
, &phti
->flags
, &phti
->iItem
);
1391 if (phti
->flags
& outside
)
1392 return phti
->iItem
= -1;
1399 HEADER_InsertItemT (HEADER_INFO
*infoPtr
, INT nItem
, const HDITEMW
*phdi
, BOOL bUnicode
)
1401 HEADER_ITEM
*lpItem
;
1406 if ((phdi
== NULL
) || (nItem
< 0) || (phdi
->mask
== 0))
1409 if (nItem
> infoPtr
->uNumItem
)
1410 nItem
= infoPtr
->uNumItem
;
1412 iOrder
= (phdi
->mask
& HDI_ORDER
) ? phdi
->iOrder
: nItem
;
1415 else if (infoPtr
->uNumItem
< iOrder
)
1416 iOrder
= infoPtr
->uNumItem
;
1418 infoPtr
->uNumItem
++;
1419 infoPtr
->items
= ReAlloc(infoPtr
->items
, sizeof(HEADER_ITEM
) * infoPtr
->uNumItem
);
1420 infoPtr
->order
= ReAlloc(infoPtr
->order
, sizeof(INT
) * infoPtr
->uNumItem
);
1422 /* make space for the new item */
1423 memmove(&infoPtr
->items
[nItem
+ 1], &infoPtr
->items
[nItem
],
1424 (infoPtr
->uNumItem
- nItem
- 1) * sizeof(HEADER_ITEM
));
1425 memmove(&infoPtr
->order
[iOrder
+ 1], &infoPtr
->order
[iOrder
],
1426 (infoPtr
->uNumItem
- iOrder
- 1) * sizeof(INT
));
1428 /* update the order array */
1429 infoPtr
->order
[iOrder
] = nItem
;
1430 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1432 if (i
!= iOrder
&& infoPtr
->order
[i
] >= nItem
)
1433 infoPtr
->order
[i
]++;
1434 infoPtr
->items
[infoPtr
->order
[i
]].iOrder
= i
;
1437 lpItem
= &infoPtr
->items
[nItem
];
1438 ZeroMemory(lpItem
, sizeof(HEADER_ITEM
));
1439 /* cxy, fmt and lParam are copied even if not in the HDITEM mask */
1440 copyMask
= phdi
->mask
| HDI_WIDTH
| HDI_FORMAT
| HDI_LPARAM
;
1441 HEADER_StoreHDItemInHeader(lpItem
, copyMask
, phdi
, bUnicode
);
1442 lpItem
->iOrder
= iOrder
;
1444 /* set automatically some format bits */
1445 if (phdi
->mask
& HDI_TEXT
)
1446 lpItem
->fmt
|= HDF_STRING
;
1448 lpItem
->fmt
&= ~HDF_STRING
;
1450 if (lpItem
->hbm
!= NULL
)
1451 lpItem
->fmt
|= HDF_BITMAP
;
1453 lpItem
->fmt
&= ~HDF_BITMAP
;
1455 if (phdi
->mask
& HDI_IMAGE
)
1456 lpItem
->fmt
|= HDF_IMAGE
;
1458 HEADER_SetItemBounds (infoPtr
);
1459 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1466 HEADER_Layout (HEADER_INFO
*infoPtr
, LPHDLAYOUT lpLayout
)
1468 lpLayout
->pwpos
->hwndInsertAfter
= 0;
1469 lpLayout
->pwpos
->x
= lpLayout
->prc
->left
;
1470 lpLayout
->pwpos
->y
= lpLayout
->prc
->top
;
1471 lpLayout
->pwpos
->cx
= lpLayout
->prc
->right
- lpLayout
->prc
->left
;
1472 if (infoPtr
->dwStyle
& HDS_HIDDEN
)
1473 lpLayout
->pwpos
->cy
= 0;
1475 lpLayout
->pwpos
->cy
= infoPtr
->nHeight
;
1476 lpLayout
->prc
->top
+= infoPtr
->nHeight
;
1478 lpLayout
->pwpos
->flags
= SWP_NOZORDER
;
1480 TRACE("Layout x=%d y=%d cx=%d cy=%d\n",
1481 lpLayout
->pwpos
->x
, lpLayout
->pwpos
->y
,
1482 lpLayout
->pwpos
->cx
, lpLayout
->pwpos
->cy
);
1484 infoPtr
->bRectsValid
= FALSE
;
1491 HEADER_SetImageList (HEADER_INFO
*infoPtr
, HIMAGELIST himl
)
1495 TRACE("(himl %p)\n", himl
);
1496 himlOld
= infoPtr
->himl
;
1497 infoPtr
->himl
= himl
;
1499 /* FIXME: Refresh needed??? */
1501 return (LRESULT
)himlOld
;
1506 HEADER_GetBitmapMargin(const HEADER_INFO
*infoPtr
)
1508 return infoPtr
->iMargin
;
1512 HEADER_SetBitmapMargin(HEADER_INFO
*infoPtr
, INT iMargin
)
1514 INT oldMargin
= infoPtr
->iMargin
;
1516 infoPtr
->iMargin
= iMargin
;
1522 HEADER_SetItemT (HEADER_INFO
*infoPtr
, INT nItem
, const HDITEMW
*phdi
, BOOL bUnicode
)
1524 HEADER_ITEM
*lpItem
;
1530 if ((nItem
< 0) || (nItem
>= (INT
)infoPtr
->uNumItem
))
1533 TRACE("[nItem=%d]\n", nItem
);
1535 HEADER_CopyHDItemForNotify(infoPtr
, &hdNotify
, phdi
, bUnicode
, &pvScratch
);
1536 if (HEADER_SendNotifyWithHDItemT(infoPtr
, HDN_ITEMCHANGINGW
, nItem
, &hdNotify
))
1542 lpItem
= &infoPtr
->items
[nItem
];
1543 HEADER_StoreHDItemInHeader(lpItem
, phdi
->mask
, phdi
, bUnicode
);
1545 if (phdi
->mask
& HDI_ORDER
)
1546 if (phdi
->iOrder
>= 0 && phdi
->iOrder
< infoPtr
->uNumItem
)
1547 HEADER_ChangeItemOrder(infoPtr
, nItem
, phdi
->iOrder
);
1549 HEADER_SendNotifyWithHDItemT(infoPtr
, HDN_ITEMCHANGEDW
, nItem
, &hdNotify
);
1551 HEADER_SetItemBounds (infoPtr
);
1553 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1559 static inline LRESULT
1560 HEADER_SetUnicodeFormat (HEADER_INFO
*infoPtr
, WPARAM wParam
)
1562 BOOL bTemp
= (infoPtr
->nNotifyFormat
== NFR_UNICODE
);
1564 infoPtr
->nNotifyFormat
= ((BOOL
)wParam
? NFR_UNICODE
: NFR_ANSI
);
1571 HEADER_Create (HWND hwnd
, const CREATESTRUCTW
*lpcs
)
1573 HEADER_INFO
*infoPtr
;
1578 infoPtr
= Alloc(sizeof(*infoPtr
));
1579 SetWindowLongPtrW (hwnd
, 0, (DWORD_PTR
)infoPtr
);
1581 infoPtr
->hwndSelf
= hwnd
;
1582 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
1583 infoPtr
->uNumItem
= 0;
1587 infoPtr
->bRectsValid
= FALSE
;
1588 infoPtr
->hcurArrow
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
1589 infoPtr
->hcurDivider
= LoadCursorW (COMCTL32_hModule
, MAKEINTRESOURCEW(IDC_DIVIDER
));
1590 infoPtr
->hcurDivopen
= LoadCursorW (COMCTL32_hModule
, MAKEINTRESOURCEW(IDC_DIVIDEROPEN
));
1591 infoPtr
->bPressed
= FALSE
;
1592 infoPtr
->bTracking
= FALSE
;
1593 infoPtr
->dwStyle
= lpcs
->style
;
1594 infoPtr
->iMoveItem
= 0;
1596 infoPtr
->iHotItem
= -1;
1597 infoPtr
->iHotDivider
= -1;
1598 infoPtr
->iMargin
= 3*GetSystemMetrics(SM_CXEDGE
);
1599 infoPtr
->nNotifyFormat
=
1600 SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
, (WPARAM
)hwnd
, NF_QUERY
);
1601 infoPtr
->filter_change_timeout
= 1000;
1604 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
1605 GetTextMetricsW (hdc
, &tm
);
1606 infoPtr
->nHeight
= tm
.tmHeight
+ VERT_BORDER
;
1607 SelectObject (hdc
, hOldFont
);
1610 OpenThemeData(hwnd
, themeClass
);
1617 HEADER_Destroy (HEADER_INFO
*infoPtr
)
1619 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
1620 CloseThemeData(theme
);
1625 HEADER_NCDestroy (HEADER_INFO
*infoPtr
)
1627 HEADER_ITEM
*lpItem
;
1630 if (infoPtr
->items
) {
1631 lpItem
= infoPtr
->items
;
1632 for (nItem
= 0; nItem
< infoPtr
->uNumItem
; nItem
++, lpItem
++)
1633 Free(lpItem
->pszText
);
1634 Free(infoPtr
->items
);
1637 Free(infoPtr
->order
);
1639 SetWindowLongPtrW (infoPtr
->hwndSelf
, 0, 0);
1646 static inline LRESULT
1647 HEADER_GetFont (const HEADER_INFO
*infoPtr
)
1649 return (LRESULT
)infoPtr
->hFont
;
1654 HEADER_IsDragDistance(const HEADER_INFO
*infoPtr
, const POINT
*pt
)
1656 /* Windows allows for a mouse movement before starting the drag. We use the
1657 * SM_CXDOUBLECLICK/SM_CYDOUBLECLICK as that distance.
1659 return (abs(infoPtr
->ptLButtonDown
.x
- pt
->x
)>GetSystemMetrics(SM_CXDOUBLECLK
) ||
1660 abs(infoPtr
->ptLButtonDown
.y
- pt
->y
)>GetSystemMetrics(SM_CYDOUBLECLK
));
1664 HEADER_LButtonDblClk (const HEADER_INFO
*infoPtr
, INT x
, INT y
)
1672 HEADER_InternalHitTest (infoPtr
, &pt
, &flags
, &nItem
);
1674 if ((infoPtr
->dwStyle
& HDS_BUTTONS
) && (flags
== HHT_ONHEADER
))
1675 HEADER_SendNotifyWithHDItemT(infoPtr
, HDN_ITEMDBLCLICKW
, nItem
, NULL
);
1676 else if ((flags
== HHT_ONDIVIDER
) || (flags
== HHT_ONDIVOPEN
))
1677 HEADER_SendNotifyWithHDItemT(infoPtr
, HDN_DIVIDERDBLCLICKW
, nItem
, NULL
);
1684 HEADER_LButtonDown (HEADER_INFO
*infoPtr
, INT x
, INT y
)
1693 HEADER_InternalHitTest (infoPtr
, &pt
, &flags
, &nItem
);
1695 if ((infoPtr
->dwStyle
& HDS_BUTTONS
) && (flags
== HHT_ONHEADER
)) {
1696 SetCapture (infoPtr
->hwndSelf
);
1697 infoPtr
->bCaptured
= TRUE
;
1698 infoPtr
->bPressed
= TRUE
;
1699 infoPtr
->bDragging
= FALSE
;
1700 infoPtr
->iMoveItem
= nItem
;
1701 infoPtr
->ptLButtonDown
= pt
;
1703 infoPtr
->items
[nItem
].bDown
= TRUE
;
1705 /* Send WM_CUSTOMDRAW */
1706 hdc
= GetDC (infoPtr
->hwndSelf
);
1707 HEADER_RefreshItem (infoPtr
, nItem
);
1708 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1710 TRACE("Pressed item %d.\n", nItem
);
1712 else if ((flags
== HHT_ONDIVIDER
) || (flags
== HHT_ONDIVOPEN
)) {
1713 INT iCurrWidth
= infoPtr
->items
[nItem
].cxy
;
1714 if (!HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_BEGINTRACKW
, nItem
, HDI_WIDTH
, iCurrWidth
))
1716 SetCapture (infoPtr
->hwndSelf
);
1717 infoPtr
->bCaptured
= TRUE
;
1718 infoPtr
->bTracking
= TRUE
;
1719 infoPtr
->iMoveItem
= nItem
;
1720 infoPtr
->xTrackOffset
= infoPtr
->items
[nItem
].rect
.right
- pt
.x
;
1722 if (!(infoPtr
->dwStyle
& HDS_FULLDRAG
)) {
1723 infoPtr
->xOldTrack
= infoPtr
->items
[nItem
].rect
.right
;
1724 hdc
= GetDC (infoPtr
->hwndSelf
);
1725 HEADER_DrawTrackLine (infoPtr
, hdc
, infoPtr
->xOldTrack
);
1726 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1729 TRACE("Begin tracking item %d.\n", nItem
);
1738 HEADER_LButtonUp (HEADER_INFO
*infoPtr
, INT x
, INT y
)
1747 HEADER_InternalHitTest (infoPtr
, &pt
, &flags
, &nItem
);
1749 if (infoPtr
->bPressed
) {
1751 infoPtr
->items
[infoPtr
->iMoveItem
].bDown
= FALSE
;
1753 if (infoPtr
->bDragging
)
1755 HEADER_ITEM
*lpItem
= &infoPtr
->items
[infoPtr
->iMoveItem
];
1758 ImageList_DragShowNolock(FALSE
);
1759 ImageList_EndDrag();
1761 if (infoPtr
->iHotDivider
== -1)
1763 else if (infoPtr
->iHotDivider
== infoPtr
->uNumItem
)
1764 iNewOrder
= infoPtr
->uNumItem
-1;
1767 iNewOrder
= HEADER_IndexToOrder(infoPtr
, infoPtr
->iHotDivider
);
1768 if (iNewOrder
> lpItem
->iOrder
)
1772 if (iNewOrder
!= -1 &&
1773 !HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_ENDDRAG
, infoPtr
->iMoveItem
, HDI_ORDER
, iNewOrder
))
1775 HEADER_ChangeItemOrder(infoPtr
, infoPtr
->iMoveItem
, iNewOrder
);
1776 infoPtr
->bRectsValid
= FALSE
;
1777 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1780 InvalidateRect(infoPtr
->hwndSelf
, &infoPtr
->items
[infoPtr
->iMoveItem
].rect
, FALSE
);
1782 infoPtr
->bDragging
= FALSE
;
1783 HEADER_SetHotDivider(infoPtr
, FALSE
, -1);
1787 hdc
= GetDC (infoPtr
->hwndSelf
);
1788 HEADER_RefreshItem (infoPtr
, infoPtr
->iMoveItem
);
1789 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1791 if (!(infoPtr
->dwStyle
& HDS_DRAGDROP
) || !HEADER_IsDragDistance(infoPtr
, &pt
))
1792 HEADER_SendNotifyWithHDItemT(infoPtr
, HDN_ITEMCLICKW
, infoPtr
->iMoveItem
, NULL
);
1795 TRACE("Released item %d.\n", infoPtr
->iMoveItem
);
1796 infoPtr
->bPressed
= FALSE
;
1798 else if (infoPtr
->bTracking
) {
1799 INT iNewWidth
= pt
.x
- infoPtr
->items
[infoPtr
->iMoveItem
].rect
.left
+ infoPtr
->xTrackOffset
;
1802 TRACE("End tracking item %d.\n", infoPtr
->iMoveItem
);
1803 infoPtr
->bTracking
= FALSE
;
1805 HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_ENDTRACKW
, infoPtr
->iMoveItem
, HDI_WIDTH
, iNewWidth
);
1807 if (!(infoPtr
->dwStyle
& HDS_FULLDRAG
)) {
1808 hdc
= GetDC (infoPtr
->hwndSelf
);
1809 HEADER_DrawTrackLine (infoPtr
, hdc
, infoPtr
->xOldTrack
);
1810 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1813 if (!HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_ITEMCHANGINGW
, infoPtr
->iMoveItem
, HDI_WIDTH
, iNewWidth
))
1815 infoPtr
->items
[infoPtr
->iMoveItem
].cxy
= iNewWidth
;
1816 HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_ITEMCHANGEDW
, infoPtr
->iMoveItem
, HDI_WIDTH
, iNewWidth
);
1819 HEADER_SetItemBounds (infoPtr
);
1820 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
1823 if (infoPtr
->bCaptured
) {
1824 infoPtr
->bCaptured
= FALSE
;
1826 HEADER_SendSimpleNotify (infoPtr
, NM_RELEASEDCAPTURE
);
1834 HEADER_NotifyFormat (HEADER_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
1839 return infoPtr
->nNotifyFormat
;
1842 infoPtr
->nNotifyFormat
=
1843 SendMessageW ((HWND
)wParam
, WM_NOTIFYFORMAT
,
1844 (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
1845 return infoPtr
->nNotifyFormat
;
1852 HEADER_MouseLeave (HEADER_INFO
*infoPtr
)
1854 /* Reset hot-tracked item when mouse leaves control. */
1855 INT oldHotItem
= infoPtr
->iHotItem
;
1856 HDC hdc
= GetDC (infoPtr
->hwndSelf
);
1858 infoPtr
->iHotItem
= -1;
1859 if (oldHotItem
!= -1) HEADER_RefreshItem (infoPtr
, oldHotItem
);
1860 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1867 HEADER_MouseMove (HEADER_INFO
*infoPtr
, LPARAM lParam
)
1873 /* With theming, hottracking is always enabled */
1874 BOOL hotTrackEnabled
=
1875 ((infoPtr
->dwStyle
& HDS_BUTTONS
) && (infoPtr
->dwStyle
& HDS_HOTTRACK
))
1876 || (GetWindowTheme (infoPtr
->hwndSelf
) != NULL
);
1877 INT oldHotItem
= infoPtr
->iHotItem
;
1879 pt
.x
= (INT
)(SHORT
)LOWORD(lParam
);
1880 pt
.y
= (INT
)(SHORT
)HIWORD(lParam
);
1881 HEADER_InternalHitTest (infoPtr
, &pt
, &flags
, &nItem
);
1883 if (hotTrackEnabled
) {
1884 if (flags
& (HHT_ONHEADER
| HHT_ONDIVIDER
| HHT_ONDIVOPEN
))
1885 infoPtr
->iHotItem
= nItem
;
1887 infoPtr
->iHotItem
= -1;
1890 if (infoPtr
->bCaptured
) {
1891 /* check if we should drag the header */
1892 if (infoPtr
->bPressed
&& !infoPtr
->bDragging
&& (infoPtr
->dwStyle
& HDS_DRAGDROP
)
1893 && HEADER_IsDragDistance(infoPtr
, &pt
))
1895 if (!HEADER_SendNotifyWithHDItemT(infoPtr
, HDN_BEGINDRAG
, infoPtr
->iMoveItem
, NULL
))
1897 HIMAGELIST hDragItem
= HEADER_CreateDragImage(infoPtr
, infoPtr
->iMoveItem
);
1898 if (hDragItem
!= NULL
)
1900 HEADER_ITEM
*lpItem
= &infoPtr
->items
[infoPtr
->iMoveItem
];
1901 TRACE("Starting item drag\n");
1902 ImageList_BeginDrag(hDragItem
, 0, pt
.x
- lpItem
->rect
.left
, 0);
1903 ImageList_DragShowNolock(TRUE
);
1904 ImageList_Destroy(hDragItem
);
1905 infoPtr
->bDragging
= TRUE
;
1910 if (infoPtr
->bDragging
)
1915 ClientToScreen(infoPtr
->hwndSelf
, &drag
);
1916 ImageList_DragMove(drag
.x
, drag
.y
);
1917 HEADER_SetHotDivider(infoPtr
, TRUE
, lParam
);
1920 if (infoPtr
->bPressed
&& !infoPtr
->bDragging
) {
1921 BOOL oldState
= infoPtr
->items
[infoPtr
->iMoveItem
].bDown
;
1922 if ((nItem
== infoPtr
->iMoveItem
) && (flags
== HHT_ONHEADER
))
1923 infoPtr
->items
[infoPtr
->iMoveItem
].bDown
= TRUE
;
1925 infoPtr
->items
[infoPtr
->iMoveItem
].bDown
= FALSE
;
1926 if (oldState
!= infoPtr
->items
[infoPtr
->iMoveItem
].bDown
) {
1927 hdc
= GetDC (infoPtr
->hwndSelf
);
1928 HEADER_RefreshItem (infoPtr
, infoPtr
->iMoveItem
);
1929 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1932 TRACE("Moving pressed item %d.\n", infoPtr
->iMoveItem
);
1934 else if (infoPtr
->bTracking
) {
1935 if (infoPtr
->dwStyle
& HDS_FULLDRAG
) {
1936 HEADER_ITEM
*lpItem
= &infoPtr
->items
[infoPtr
->iMoveItem
];
1937 nWidth
= pt
.x
- lpItem
->rect
.left
+ infoPtr
->xTrackOffset
;
1938 if (!HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_ITEMCHANGINGW
, infoPtr
->iMoveItem
, HDI_WIDTH
, nWidth
))
1940 INT nOldWidth
= lpItem
->rect
.right
- lpItem
->rect
.left
;
1944 if (nWidth
< 0) nWidth
= 0;
1945 infoPtr
->items
[infoPtr
->iMoveItem
].cxy
= nWidth
;
1946 HEADER_SetItemBounds(infoPtr
);
1948 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
1949 rcScroll
= rcClient
;
1950 rcScroll
.left
= lpItem
->rect
.left
+ nOldWidth
;
1951 ScrollWindowEx(infoPtr
->hwndSelf
, nWidth
- nOldWidth
, 0, &rcScroll
, &rcClient
, NULL
, NULL
, 0);
1952 InvalidateRect(infoPtr
->hwndSelf
, &lpItem
->rect
, FALSE
);
1953 UpdateWindow(infoPtr
->hwndSelf
);
1955 HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_ITEMCHANGEDW
, infoPtr
->iMoveItem
, HDI_WIDTH
, nWidth
);
1960 hdc
= GetDC (infoPtr
->hwndSelf
);
1961 HEADER_DrawTrackLine (infoPtr
, hdc
, infoPtr
->xOldTrack
);
1962 infoPtr
->xOldTrack
= pt
.x
+ infoPtr
->xTrackOffset
;
1963 if (infoPtr
->xOldTrack
< infoPtr
->items
[infoPtr
->iMoveItem
].rect
.left
)
1964 infoPtr
->xOldTrack
= infoPtr
->items
[infoPtr
->iMoveItem
].rect
.left
;
1965 HEADER_DrawTrackLine (infoPtr
, hdc
, infoPtr
->xOldTrack
);
1966 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1967 iTrackWidth
= infoPtr
->xOldTrack
- infoPtr
->items
[infoPtr
->iMoveItem
].rect
.left
;
1968 /* FIXME: should stop tracking if HDN_TRACK returns TRUE */
1969 HEADER_SendNotifyWithIntFieldT(infoPtr
, HDN_TRACKW
, infoPtr
->iMoveItem
, HDI_WIDTH
, iTrackWidth
);
1972 TRACE("Tracking item %d.\n", infoPtr
->iMoveItem
);
1976 if (hotTrackEnabled
) {
1977 TRACKMOUSEEVENT tme
;
1978 if (oldHotItem
!= infoPtr
->iHotItem
&& !infoPtr
->bDragging
) {
1979 hdc
= GetDC (infoPtr
->hwndSelf
);
1980 if (oldHotItem
!= -1) HEADER_RefreshItem (infoPtr
, oldHotItem
);
1981 if (infoPtr
->iHotItem
!= -1) HEADER_RefreshItem (infoPtr
, infoPtr
->iHotItem
);
1982 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1984 tme
.cbSize
= sizeof( tme
);
1985 tme
.dwFlags
= TME_LEAVE
;
1986 tme
.hwndTrack
= infoPtr
->hwndSelf
;
1987 TrackMouseEvent( &tme
);
1995 HEADER_Paint (HEADER_INFO
*infoPtr
, HDC hdcParam
)
2000 hdc
= hdcParam
==0 ? BeginPaint (infoPtr
->hwndSelf
, &ps
) : hdcParam
;
2001 HEADER_Refresh (infoPtr
, hdc
);
2003 EndPaint (infoPtr
->hwndSelf
, &ps
);
2009 HEADER_RButtonUp (HEADER_INFO
*infoPtr
, INT x
, INT y
)
2017 /* Send a Notify message */
2018 bRet
= HEADER_SendSimpleNotify (infoPtr
, NM_RCLICK
);
2020 /* Change to screen coordinate for WM_CONTEXTMENU */
2021 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
2023 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
2024 SendMessageW( infoPtr
->hwndSelf
, WM_CONTEXTMENU
, (WPARAM
) infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
2031 HEADER_SetCursor (HEADER_INFO
*infoPtr
, LPARAM lParam
)
2037 TRACE("code=0x%X id=0x%X\n", LOWORD(lParam
), HIWORD(lParam
));
2040 ScreenToClient (infoPtr
->hwndSelf
, &pt
);
2042 HEADER_InternalHitTest (infoPtr
, &pt
, &flags
, &nItem
);
2044 if (flags
== HHT_ONDIVIDER
)
2045 SetCursor (infoPtr
->hcurDivider
);
2046 else if (flags
== HHT_ONDIVOPEN
)
2047 SetCursor (infoPtr
->hcurDivopen
);
2049 SetCursor (infoPtr
->hcurArrow
);
2056 HEADER_SetFont (HEADER_INFO
*infoPtr
, HFONT hFont
, WORD Redraw
)
2062 infoPtr
->hFont
= hFont
;
2065 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
));
2066 GetTextMetricsW (hdc
, &tm
);
2067 infoPtr
->nHeight
= tm
.tmHeight
+ VERT_BORDER
;
2068 SelectObject (hdc
, hOldFont
);
2071 infoPtr
->bRectsValid
= FALSE
;
2074 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
2080 static LRESULT
HEADER_SetRedraw(HEADER_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
2082 /* ignoring the InvalidateRect calls is handled by user32. But some apps expect
2083 * that we invalidate the header and this has to be done manually */
2086 ret
= DefWindowProcW(infoPtr
->hwndSelf
, WM_SETREDRAW
, wParam
, lParam
);
2088 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
2092 static INT
HEADER_StyleChanged(HEADER_INFO
*infoPtr
, WPARAM wStyleType
,
2093 const STYLESTRUCT
*lpss
)
2095 TRACE("styletype %Ix, styleOld %#lx, styleNew %#lx\n", wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
2097 if (wStyleType
!= GWL_STYLE
) return 0;
2099 infoPtr
->dwStyle
= lpss
->styleNew
;
2104 /* Update the theme handle after a theme change */
2105 static LRESULT
HEADER_ThemeChanged(const HEADER_INFO
*infoPtr
)
2107 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
2108 CloseThemeData(theme
);
2109 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
2110 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
2114 static INT
HEADER_SetFilterChangeTimeout(HEADER_INFO
*infoPtr
, INT timeout
)
2116 INT old_timeout
= infoPtr
->filter_change_timeout
;
2119 infoPtr
->filter_change_timeout
= timeout
;
2123 static LRESULT WINAPI
2124 HEADER_WindowProc (HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2126 HEADER_INFO
*infoPtr
= (HEADER_INFO
*)GetWindowLongPtrW(hwnd
, 0);
2128 TRACE("hwnd %p, msg %x, wparam %Ix, lParam %Ix\n", hwnd
, msg
, wParam
, lParam
);
2130 if (!infoPtr
&& (msg
!= WM_CREATE
))
2131 return DefWindowProcW (hwnd
, msg
, wParam
, lParam
);
2133 /* case HDM_CLEARFILTER: */
2135 case HDM_CREATEDRAGIMAGE
:
2136 return (LRESULT
)HEADER_CreateDragImage (infoPtr
, (INT
)wParam
);
2138 case HDM_DELETEITEM
:
2139 return HEADER_DeleteItem (infoPtr
, (INT
)wParam
);
2141 /* case HDM_EDITFILTER: */
2143 case HDM_GETBITMAPMARGIN
:
2144 return HEADER_GetBitmapMargin(infoPtr
);
2146 case HDM_GETIMAGELIST
:
2147 return HEADER_GetImageList (infoPtr
);
2151 return HEADER_GetItemT (infoPtr
, (INT
)wParam
, (LPHDITEMW
)lParam
, msg
== HDM_GETITEMW
);
2153 case HDM_GETITEMCOUNT
:
2154 return HEADER_GetItemCount (infoPtr
);
2156 case HDM_GETITEMRECT
:
2157 return HEADER_GetItemRect (infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
2159 case HDM_GETORDERARRAY
:
2160 return HEADER_GetOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
2162 case HDM_GETUNICODEFORMAT
:
2163 return HEADER_GetUnicodeFormat (infoPtr
);
2166 return HEADER_HitTest (infoPtr
, (LPHDHITTESTINFO
)lParam
);
2168 case HDM_INSERTITEMA
:
2169 case HDM_INSERTITEMW
:
2170 return HEADER_InsertItemT (infoPtr
, (INT
)wParam
, (LPHDITEMW
)lParam
, msg
== HDM_INSERTITEMW
);
2173 return HEADER_Layout (infoPtr
, (LPHDLAYOUT
)lParam
);
2175 case HDM_ORDERTOINDEX
:
2176 return HEADER_OrderToIndex(infoPtr
, (INT
)wParam
);
2178 case HDM_SETBITMAPMARGIN
:
2179 return HEADER_SetBitmapMargin(infoPtr
, (INT
)wParam
);
2181 case HDM_SETFILTERCHANGETIMEOUT
:
2182 return HEADER_SetFilterChangeTimeout(infoPtr
, (INT
)lParam
);
2184 case HDM_SETHOTDIVIDER
:
2185 return HEADER_SetHotDivider(infoPtr
, wParam
, lParam
);
2187 case HDM_SETIMAGELIST
:
2188 return HEADER_SetImageList (infoPtr
, (HIMAGELIST
)lParam
);
2192 return HEADER_SetItemT (infoPtr
, (INT
)wParam
, (LPHDITEMW
)lParam
, msg
== HDM_SETITEMW
);
2194 case HDM_SETORDERARRAY
:
2195 return HEADER_SetOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
2197 case HDM_SETUNICODEFORMAT
:
2198 return HEADER_SetUnicodeFormat (infoPtr
, wParam
);
2201 return HEADER_Create (hwnd
, (LPCREATESTRUCTW
)lParam
);
2204 return HEADER_Destroy (infoPtr
);
2207 return HEADER_NCDestroy (infoPtr
);
2213 return DLGC_WANTTAB
| DLGC_WANTARROWS
;
2216 return HEADER_GetFont (infoPtr
);
2218 case WM_LBUTTONDBLCLK
:
2219 return HEADER_LButtonDblClk (infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
2221 case WM_LBUTTONDOWN
:
2222 return HEADER_LButtonDown (infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
2225 return HEADER_LButtonUp (infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
2228 return HEADER_MouseLeave (infoPtr
);
2231 return HEADER_MouseMove (infoPtr
, lParam
);
2233 case WM_NOTIFYFORMAT
:
2234 return HEADER_NotifyFormat (infoPtr
, wParam
, lParam
);
2237 return HEADER_Size (infoPtr
);
2239 case WM_THEMECHANGED
:
2240 return HEADER_ThemeChanged (infoPtr
);
2242 case WM_PRINTCLIENT
:
2244 return HEADER_Paint (infoPtr
, (HDC
)wParam
);
2247 return HEADER_RButtonUp (infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
2250 return HEADER_SetCursor (infoPtr
, lParam
);
2253 return HEADER_SetFont (infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
2256 return HEADER_SetRedraw(infoPtr
, wParam
, lParam
);
2258 case WM_STYLECHANGED
:
2259 return HEADER_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
2261 case WM_SYSCOLORCHANGE
:
2262 COMCTL32_RefreshSysColors();
2266 if ((msg
>= WM_USER
) && (msg
< WM_APP
) && !COMCTL32_IsReflectedMessage(msg
))
2267 ERR("unknown msg %04x, wp %#Ix, lp %#Ix\n", msg
, wParam
, lParam
);
2268 return DefWindowProcW(hwnd
, msg
, wParam
, lParam
);
2274 HEADER_Register (void)
2278 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
2279 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
2280 wndClass
.lpfnWndProc
= HEADER_WindowProc
;
2281 wndClass
.cbClsExtra
= 0;
2282 wndClass
.cbWndExtra
= sizeof(HEADER_INFO
*);
2283 wndClass
.hCursor
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
2284 wndClass
.lpszClassName
= WC_HEADERW
;
2286 RegisterClassW (&wndClass
);
2291 HEADER_Unregister (void)
2293 UnregisterClassW (WC_HEADERW
, NULL
);