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