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