Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/comctl32/listview.c
5965 views
1
/*
2
* Listview control
3
*
4
* Copyright 1998, 1999 Eric Kohl
5
* Copyright 1999 Luc Tourangeau
6
* Copyright 2000 Jason Mawdsley
7
* Copyright 2001 CodeWeavers Inc.
8
* Copyright 2002 Dimitrie O. Paun
9
* Copyright 2009-2015 Nikolay Sivov
10
* Copyright 2009 Owen Rudge for CodeWeavers
11
* Copyright 2012-2013 Daniel Jelinski
12
*
13
* This library is free software; you can redistribute it and/or
14
* modify it under the terms of the GNU Lesser General Public
15
* License as published by the Free Software Foundation; either
16
* version 2.1 of the License, or (at your option) any later version.
17
*
18
* This library is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
* Lesser General Public License for more details.
22
*
23
* You should have received a copy of the GNU Lesser General Public
24
* License along with this library; if not, write to the Free Software
25
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26
*
27
* TODO:
28
*
29
* Default Message Processing
30
* -- WM_CREATE: create the icon and small icon image lists at this point only if
31
* the LVS_SHAREIMAGELISTS style is not specified.
32
* -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
33
* or small icon and the LVS_AUTOARRANGE style is specified.
34
* -- WM_TIMER
35
* -- WM_WININICHANGE
36
*
37
* Features
38
* -- Hot item handling, mouse hovering
39
* -- Workareas support
40
* -- Tilemode support
41
* -- Groups support
42
*
43
* Bugs
44
* -- Expand large item in ICON mode when the cursor is flying over the icon or text.
45
* -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
46
* -- LVA_SNAPTOGRID not implemented
47
* -- LISTVIEW_ApproximateViewRect partially implemented
48
* -- LISTVIEW_StyleChanged doesn't handle some changes too well
49
*
50
* Speedups
51
* -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
52
* linear in the number of items in the list, and this is
53
* unacceptable for large lists.
54
* -- if list is sorted by item text LISTVIEW_InsertItemT could use
55
* binary search to calculate item index (e.g. DPA_Search()).
56
* This requires sorted state to be reliably tracked in item modifiers.
57
* -- we should keep an ordered array of coordinates in iconic mode.
58
* This would allow framing items (iterator_frameditems),
59
* and finding the nearest item (LVFI_NEARESTXY) a lot more efficiently.
60
*
61
* Flags
62
* -- LVIF_COLUMNS
63
* -- LVIF_GROUPID
64
*
65
* States
66
* -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
67
* -- LVIS_DROPHILITED
68
*
69
* Styles
70
* -- LVS_NOLABELWRAP
71
* -- LVS_NOSCROLL (see Q137520)
72
* -- LVS_ALIGNTOP
73
*
74
* Extended Styles
75
* -- LVS_EX_BORDERSELECT
76
* -- LVS_EX_FLATSB
77
* -- LVS_EX_INFOTIP
78
* -- LVS_EX_LABELTIP
79
* -- LVS_EX_MULTIWORKAREAS
80
* -- LVS_EX_REGIONAL
81
* -- LVS_EX_SIMPLESELECT
82
* -- LVS_EX_TWOCLICKACTIVATE
83
* -- LVS_EX_UNDERLINECOLD
84
* -- LVS_EX_UNDERLINEHOT
85
*
86
* Notifications:
87
* -- LVN_BEGINSCROLL, LVN_ENDSCROLL
88
* -- LVN_GETINFOTIP
89
* -- LVN_HOTTRACK
90
* -- LVN_SETDISPINFO
91
*
92
* Messages:
93
* -- LVM_ENABLEGROUPVIEW
94
* -- LVM_GETBKIMAGE
95
* -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
96
* -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
97
* -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
98
* -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
99
* -- LVM_GETINSERTMARKRECT
100
* -- LVM_GETNUMBEROFWORKAREAS
101
* -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
102
* -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
103
* -- LVM_GETTILEINFO, LVM_SETTILEINFO
104
* -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
105
* -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
106
* -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
107
* -- LVM_INSERTGROUPSORTED
108
* -- LVM_INSERTMARKHITTEST
109
* -- LVM_ISGROUPVIEWENABLED
110
* -- LVM_MOVEGROUP
111
* -- LVM_MOVEITEMTOGROUP
112
* -- LVM_SETINFOTIP
113
* -- LVM_SETTILEWIDTH
114
* -- LVM_SORTGROUPS
115
*
116
* Macros:
117
* -- ListView_GetHoverTime, ListView_SetHoverTime
118
* -- ListView_GetISearchString
119
* -- ListView_GetNumberOfWorkAreas
120
* -- ListView_GetWorkAreas, ListView_SetWorkAreas
121
*
122
* Functions:
123
* -- LVGroupComparE
124
*/
125
126
#include <assert.h>
127
#include <ctype.h>
128
#include <string.h>
129
#include <stdlib.h>
130
#include <stdarg.h>
131
#include <stdio.h>
132
133
#include "windef.h"
134
#include "winbase.h"
135
#include "winnt.h"
136
#include "wingdi.h"
137
#include "winuser.h"
138
#include "winnls.h"
139
#include "commctrl.h"
140
#include "comctl32.h"
141
#include "shlwapi.h"
142
143
#include "wine/debug.h"
144
145
WINE_DEFAULT_DEBUG_CHANNEL(listview);
146
147
typedef struct tagCOLUMN_INFO
148
{
149
RECT rcHeader; /* tracks the header's rectangle */
150
INT fmt; /* same as LVCOLUMN.fmt */
151
INT cxMin;
152
} COLUMN_INFO;
153
154
typedef struct tagITEMHDR
155
{
156
LPWSTR pszText;
157
INT iImage;
158
} ITEMHDR, *LPITEMHDR;
159
160
typedef struct tagSUBITEM_INFO
161
{
162
ITEMHDR hdr;
163
INT iSubItem;
164
} SUBITEM_INFO;
165
166
typedef struct tagITEM_ID ITEM_ID;
167
168
typedef struct tagITEM_INFO
169
{
170
ITEMHDR hdr;
171
UINT state;
172
LPARAM lParam;
173
INT iIndent;
174
ITEM_ID *id;
175
} ITEM_INFO;
176
177
struct tagITEM_ID
178
{
179
UINT id; /* item id */
180
HDPA item; /* link to item data */
181
};
182
183
typedef struct tagRANGE
184
{
185
INT lower;
186
INT upper;
187
} RANGE;
188
189
typedef struct tagRANGES
190
{
191
HDPA hdpa;
192
} *RANGES;
193
194
typedef struct tagITERATOR
195
{
196
INT nItem;
197
INT nSpecial;
198
RANGE range;
199
RANGES ranges;
200
INT index;
201
} ITERATOR;
202
203
typedef struct tagDELAYED_ITEM_EDIT
204
{
205
BOOL fEnabled;
206
INT iItem;
207
} DELAYED_ITEM_EDIT;
208
209
enum notification_mask
210
{
211
NOTIFY_MASK_ITEM_CHANGE = 0x1,
212
NOTIFY_MASK_END_LABEL_EDIT = 0x2,
213
NOTIFY_MASK_UNMASK_ALL = 0xffffffff
214
};
215
216
typedef struct tagLISTVIEW_INFO
217
{
218
/* control window */
219
HWND hwndSelf;
220
RECT rcList; /* This rectangle is really the window
221
* client rectangle possibly reduced by the
222
* horizontal scroll bar and/or header - see
223
* LISTVIEW_UpdateSize. This rectangle offset
224
* by the LISTVIEW_GetOrigin value is in
225
* client coordinates */
226
227
/* notification window */
228
SHORT notifyFormat;
229
HWND hwndNotify;
230
DWORD notify_mask;
231
UINT uCallbackMask;
232
233
/* tooltips */
234
HWND hwndToolTip;
235
236
/* items */
237
INT nItemCount; /* the number of items in the list */
238
HDPA hdpaItems; /* array ITEM_INFO pointers */
239
HDPA hdpaItemIds; /* array of ITEM_ID pointers */
240
HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
241
HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
242
RANGES selectionRanges;
243
INT nSelectionMark; /* item to start next multiselection from */
244
INT nHotItem;
245
246
/* columns */
247
HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
248
BOOL colRectsDirty; /* trigger column rectangles requery from header */
249
INT selected_column; /* index for LVM_SETSELECTEDCOLUMN/LVM_GETSELECTEDCOLUMN */
250
251
/* item metrics */
252
BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
253
INT nItemHeight;
254
INT nItemWidth;
255
256
/* style */
257
DWORD dwStyle; /* the cached window GWL_STYLE */
258
DWORD dwLvExStyle; /* extended listview style */
259
DWORD uView; /* current view available through LVM_[G,S]ETVIEW */
260
261
/* edit item */
262
HWND hwndEdit;
263
WNDPROC EditWndProc;
264
INT nEditLabelItem;
265
DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
266
267
/* icons */
268
HIMAGELIST himlNormal;
269
HIMAGELIST himlSmall;
270
HIMAGELIST himlState;
271
SIZE iconSize;
272
BOOL autoSpacing;
273
SIZE iconSpacing;
274
SIZE iconStateSize;
275
POINT currIconPos; /* this is the position next icon will be placed */
276
277
/* header */
278
HWND hwndHeader;
279
INT xTrackLine; /* The x coefficient of the track line or -1 if none */
280
281
/* marquee selection */
282
BOOL bMarqueeSelect; /* marquee selection/highlight underway */
283
BOOL bScrolling;
284
RECT marqueeRect; /* absolute coordinates of marquee selection */
285
RECT marqueeDrawRect; /* relative coordinates for drawing marquee */
286
POINT marqueeOrigin; /* absolute coordinates of marquee click origin */
287
288
/* focus drawing */
289
BOOL bFocus; /* control has focus */
290
INT nFocusedItem;
291
RECT rcFocus; /* focus bounds */
292
293
/* colors */
294
HBRUSH hBkBrush;
295
COLORREF clrBk;
296
COLORREF clrText;
297
COLORREF clrTextBk;
298
HBITMAP hBkBitmap;
299
300
/* font */
301
HFONT hDefaultFont;
302
HFONT hFont;
303
INT ntmHeight; /* Some cached metrics of the font used */
304
INT ntmMaxCharWidth; /* by the listview to draw items */
305
INT nEllipsisWidth;
306
307
/* mouse operation */
308
BOOL bLButtonDown;
309
BOOL bDragging;
310
POINT ptClickPos; /* point where the user clicked */
311
INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
312
DWORD dwHoverTime;
313
HCURSOR hHotCursor;
314
INT cWheelRemainder;
315
316
/* keyboard operation */
317
DWORD lastKeyPressTimestamp;
318
WPARAM charCode;
319
INT nSearchParamLength;
320
WCHAR szSearchParam[ MAX_PATH ];
321
322
/* painting */
323
BOOL bIsDrawing; /* Drawing in progress */
324
INT nMeasureItemHeight; /* WM_MEASUREITEM result */
325
BOOL redraw; /* WM_SETREDRAW switch */
326
327
/* misc */
328
INT iVersion; /* CCM_[G,S]ETVERSION */
329
} LISTVIEW_INFO;
330
331
/*
332
* constants
333
*/
334
/* How many we debug buffer to allocate */
335
#define DEBUG_BUFFERS 20
336
/* The size of a single debug buffer */
337
#define DEBUG_BUFFER_SIZE 256
338
339
/* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
340
#define SB_INTERNAL -1
341
342
/* maximum size of a label */
343
#define DISP_TEXT_SIZE 260
344
345
/* padding for items in list and small icon display modes */
346
#define WIDTH_PADDING 12
347
348
/* padding for items in list, report and small icon display modes */
349
#define HEIGHT_PADDING 1
350
351
/* offset of items in report display mode */
352
#define REPORT_MARGINX 2
353
354
/* padding for icon in large icon display mode
355
* ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
356
* that HITTEST will see.
357
* ICON_TOP_PADDING_HITABLE - spacing between above and icon.
358
* ICON_TOP_PADDING - sum of the two above.
359
* ICON_BOTTOM_PADDING - between bottom of icon and top of text
360
* LABEL_HOR_PADDING - between text and sides of box
361
* LABEL_VERT_PADDING - between bottom of text and end of box
362
*
363
* ICON_LR_PADDING - additional width above icon size.
364
* ICON_LR_HALF - half of the above value
365
*/
366
#define ICON_TOP_PADDING_NOTHITABLE 2
367
#define ICON_TOP_PADDING_HITABLE 2
368
#define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
369
#define ICON_BOTTOM_PADDING 4
370
#define LABEL_HOR_PADDING 5
371
#define LABEL_VERT_PADDING 7
372
#define ICON_LR_PADDING 16
373
#define ICON_LR_HALF (ICON_LR_PADDING/2)
374
375
/* default label width for items in list and small icon display modes */
376
#define DEFAULT_LABEL_WIDTH 40
377
/* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
378
#define MAX_EMPTYTEXT_SELECT_WIDTH 80
379
380
/* default column width for items in list display mode */
381
#define DEFAULT_COLUMN_WIDTH 128
382
383
/* Size of "line" scroll for V & H scrolls */
384
#define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
385
386
/* Padding between image and label */
387
#define IMAGE_PADDING 2
388
389
/* Padding behind the label */
390
#define TRAILING_LABEL_PADDING 12
391
#define TRAILING_HEADER_PADDING 11
392
393
/* Border for the icon caption */
394
#define CAPTION_BORDER 2
395
396
/* Standard DrawText flags */
397
#define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
398
#define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
399
#define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
400
401
/* Image index from state */
402
#define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
403
404
/* The time in milliseconds to reset the search in the list */
405
#define KEY_DELAY 450
406
407
/* Dump the LISTVIEW_INFO structure to the debug channel */
408
#define LISTVIEW_DUMP(iP) do { \
409
TRACE("hwndSelf=%p, clrBk=%#lx, clrText=%#lx, clrTextBk=%#lx, ItemHeight=%d, ItemWidth=%d, Style=%#lx\n", \
410
iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
411
iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
412
TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=%#lx, Focus=%d\n", \
413
iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
414
iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
415
TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
416
iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
417
iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
418
TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
419
} while(0)
420
421
enum key_state
422
{
423
SHIFT_KEY = 0x1,
424
CTRL_KEY = 0x2,
425
SPACE_KEY = 0x4,
426
};
427
428
/*
429
* forward declarations
430
*/
431
static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
432
static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
433
static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
434
static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
435
static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
436
static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
437
static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
438
static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
439
static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
440
static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
441
static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, DWORD);
442
static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
443
static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
444
static VOID LISTVIEW_SetOwnerDataState(LISTVIEW_INFO *, INT, INT, const LVITEMW *);
445
static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT);
446
static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT);
447
static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
448
static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
449
static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
450
static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL);
451
static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *, INT, INT);
452
453
/******** Text handling functions *************************************/
454
455
/* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
456
* text string. The string may be ANSI or Unicode, in which case
457
* the boolean isW tells us the type of the string.
458
*
459
* The name of the function tell what type of strings it expects:
460
* W: Unicode, T: ANSI/Unicode - function of isW
461
*/
462
463
static inline BOOL is_text(LPCWSTR text)
464
{
465
return text != NULL && text != LPSTR_TEXTCALLBACKW;
466
}
467
468
static inline int textlenT(LPCWSTR text, BOOL isW)
469
{
470
return !is_text(text) ? 0 :
471
isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
472
}
473
474
static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
475
{
476
if (isDestW)
477
if (isSrcW) lstrcpynW(dest, src, max);
478
else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
479
else
480
if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
481
else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
482
}
483
484
static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
485
{
486
LPWSTR wstr = (LPWSTR)text;
487
488
if (!isW && is_text(text))
489
{
490
INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
491
wstr = Alloc(len * sizeof(WCHAR));
492
if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
493
}
494
TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
495
return wstr;
496
}
497
498
static inline void textfreeT(LPWSTR wstr, BOOL isW)
499
{
500
if (!isW && is_text(wstr)) Free (wstr);
501
}
502
503
/*
504
* dest is a pointer to a Unicode string
505
* src is a pointer to a string (Unicode if isW, ANSI if !isW)
506
*/
507
static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
508
{
509
BOOL bResult = TRUE;
510
511
if (src == LPSTR_TEXTCALLBACKW)
512
{
513
if (is_text(*dest)) Free(*dest);
514
*dest = LPSTR_TEXTCALLBACKW;
515
}
516
else
517
{
518
LPWSTR pszText = textdupTtoW(src, isW);
519
if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
520
bResult = Str_SetPtrW(dest, pszText);
521
textfreeT(pszText, isW);
522
}
523
return bResult;
524
}
525
526
/*
527
* compares a Unicode to a Unicode/ANSI text string
528
*/
529
static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
530
{
531
if (!aw) return bt ? -1 : 0;
532
if (!bt) return 1;
533
if (aw == LPSTR_TEXTCALLBACKW)
534
return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
535
if (bt != LPSTR_TEXTCALLBACKW)
536
{
537
LPWSTR bw = textdupTtoW(bt, isW);
538
int r = bw ? lstrcmpW(aw, bw) : 1;
539
textfreeT(bw, isW);
540
return r;
541
}
542
543
return 1;
544
}
545
546
/******** Debugging functions *****************************************/
547
548
static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
549
{
550
if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
551
return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
552
}
553
554
static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
555
{
556
if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
557
n = min(textlenT(text, isW), n);
558
return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
559
}
560
561
static char* debug_getbuf(void)
562
{
563
static int index = 0;
564
static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
565
return buffers[index++ % DEBUG_BUFFERS];
566
}
567
568
static inline const char* debugrange(const RANGE *lprng)
569
{
570
if (!lprng) return "(null)";
571
return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
572
}
573
574
static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
575
{
576
char* buf = debug_getbuf(), *text = buf;
577
int len, size = DEBUG_BUFFER_SIZE;
578
579
if (pScrollInfo == NULL) return "(null)";
580
len = snprintf(buf, size, "{cbSize=%u, ", pScrollInfo->cbSize);
581
if (len == -1) goto end;
582
buf += len; size -= len;
583
if (pScrollInfo->fMask & SIF_RANGE)
584
len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
585
else len = 0;
586
if (len == -1) goto end;
587
buf += len; size -= len;
588
if (pScrollInfo->fMask & SIF_PAGE)
589
len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
590
else len = 0;
591
if (len == -1) goto end;
592
buf += len; size -= len;
593
if (pScrollInfo->fMask & SIF_POS)
594
len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
595
else len = 0;
596
if (len == -1) goto end;
597
buf += len; size -= len;
598
if (pScrollInfo->fMask & SIF_TRACKPOS)
599
len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
600
else len = 0;
601
if (len == -1) goto end;
602
buf += len;
603
goto undo;
604
end:
605
buf = text + strlen(text);
606
undo:
607
if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
608
return text;
609
}
610
611
static const char* debugnmlistview(const NMLISTVIEW *plvnm)
612
{
613
if (!plvnm) return "(null)";
614
return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
615
" uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%Id",
616
plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
617
plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
618
}
619
620
static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
621
{
622
char* buf = debug_getbuf(), *text = buf;
623
int len, size = DEBUG_BUFFER_SIZE;
624
625
if (lpLVItem == NULL) return "(null)";
626
len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
627
if (len == -1) goto end;
628
buf += len; size -= len;
629
if (lpLVItem->mask & LVIF_STATE)
630
len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
631
else len = 0;
632
if (len == -1) goto end;
633
buf += len; size -= len;
634
if (lpLVItem->mask & LVIF_TEXT)
635
len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
636
else len = 0;
637
if (len == -1) goto end;
638
buf += len; size -= len;
639
if (lpLVItem->mask & LVIF_IMAGE)
640
len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
641
else len = 0;
642
if (len == -1) goto end;
643
buf += len; size -= len;
644
if (lpLVItem->mask & LVIF_PARAM)
645
len = snprintf(buf, size, "lParam=%Ix, ", lpLVItem->lParam);
646
else len = 0;
647
if (len == -1) goto end;
648
buf += len; size -= len;
649
if (lpLVItem->mask & LVIF_INDENT)
650
len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
651
else len = 0;
652
if (len == -1) goto end;
653
buf += len;
654
goto undo;
655
end:
656
buf = text + strlen(text);
657
undo:
658
if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
659
return text;
660
}
661
662
static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
663
{
664
char* buf = debug_getbuf(), *text = buf;
665
int len, size = DEBUG_BUFFER_SIZE;
666
667
if (lpColumn == NULL) return "(null)";
668
len = snprintf(buf, size, "{");
669
if (len == -1) goto end;
670
buf += len; size -= len;
671
if (lpColumn->mask & LVCF_SUBITEM)
672
len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
673
else len = 0;
674
if (len == -1) goto end;
675
buf += len; size -= len;
676
if (lpColumn->mask & LVCF_FMT)
677
len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
678
else len = 0;
679
if (len == -1) goto end;
680
buf += len; size -= len;
681
if (lpColumn->mask & LVCF_WIDTH)
682
len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
683
else len = 0;
684
if (len == -1) goto end;
685
buf += len; size -= len;
686
if (lpColumn->mask & LVCF_TEXT)
687
len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
688
else len = 0;
689
if (len == -1) goto end;
690
buf += len; size -= len;
691
if (lpColumn->mask & LVCF_IMAGE)
692
len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
693
else len = 0;
694
if (len == -1) goto end;
695
buf += len; size -= len;
696
if (lpColumn->mask & LVCF_ORDER)
697
len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
698
else len = 0;
699
if (len == -1) goto end;
700
buf += len;
701
goto undo;
702
end:
703
buf = text + strlen(text);
704
undo:
705
if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
706
return text;
707
}
708
709
static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
710
{
711
if (!lpht) return "(null)";
712
713
return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
714
wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
715
}
716
717
/* Return the corresponding text for a given scroll value */
718
static inline LPCSTR debugscrollcode(int nScrollCode)
719
{
720
switch(nScrollCode)
721
{
722
case SB_LINELEFT: return "SB_LINELEFT";
723
case SB_LINERIGHT: return "SB_LINERIGHT";
724
case SB_PAGELEFT: return "SB_PAGELEFT";
725
case SB_PAGERIGHT: return "SB_PAGERIGHT";
726
case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
727
case SB_THUMBTRACK: return "SB_THUMBTRACK";
728
case SB_ENDSCROLL: return "SB_ENDSCROLL";
729
case SB_INTERNAL: return "SB_INTERNAL";
730
default: return "unknown";
731
}
732
}
733
734
735
/******** Notification functions ************************************/
736
737
static int get_ansi_notification(UINT unicodeNotificationCode)
738
{
739
switch (unicodeNotificationCode)
740
{
741
case LVN_BEGINLABELEDITA:
742
case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
743
case LVN_ENDLABELEDITA:
744
case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
745
case LVN_GETDISPINFOA:
746
case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
747
case LVN_SETDISPINFOA:
748
case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
749
case LVN_ODFINDITEMA:
750
case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
751
case LVN_GETINFOTIPA:
752
case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
753
/* header forwards */
754
case HDN_TRACKA:
755
case HDN_TRACKW: return HDN_TRACKA;
756
case HDN_ENDTRACKA:
757
case HDN_ENDTRACKW: return HDN_ENDTRACKA;
758
case HDN_BEGINDRAG: return HDN_BEGINDRAG;
759
case HDN_ENDDRAG: return HDN_ENDDRAG;
760
case HDN_ITEMCHANGINGA:
761
case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
762
case HDN_ITEMCHANGEDA:
763
case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
764
case HDN_ITEMCLICKA:
765
case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
766
case HDN_DIVIDERDBLCLICKA:
767
case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
768
default: break;
769
}
770
FIXME("unknown notification %x\n", unicodeNotificationCode);
771
return unicodeNotificationCode;
772
}
773
774
/* forwards header notifications to listview parent */
775
static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, NMHEADERW *lpnmhW)
776
{
777
LPCWSTR text = NULL, filter = NULL;
778
LRESULT ret;
779
NMHEADERA *lpnmh = (NMHEADERA*) lpnmhW;
780
781
/* on unicode format exit earlier */
782
if (infoPtr->notifyFormat == NFR_UNICODE)
783
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
784
(LPARAM)lpnmh);
785
786
/* header always supplies unicode notifications,
787
all we have to do is to convert strings to ANSI */
788
if (lpnmh->pitem)
789
{
790
/* convert item text */
791
if (lpnmh->pitem->mask & HDI_TEXT)
792
{
793
text = (LPCWSTR)lpnmh->pitem->pszText;
794
lpnmh->pitem->pszText = NULL;
795
Str_SetPtrWtoA(&lpnmh->pitem->pszText, text);
796
}
797
/* convert filter text */
798
if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
799
lpnmh->pitem->pvFilter)
800
{
801
filter = (LPCWSTR)((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText;
802
((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = NULL;
803
Str_SetPtrWtoA(&((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText, filter);
804
}
805
}
806
lpnmh->hdr.code = get_ansi_notification(lpnmh->hdr.code);
807
808
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
809
(LPARAM)lpnmh);
810
811
/* cleanup */
812
if(text)
813
{
814
Free(lpnmh->pitem->pszText);
815
lpnmh->pitem->pszText = (LPSTR)text;
816
}
817
if(filter)
818
{
819
Free(((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText);
820
((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = (LPSTR)filter;
821
}
822
823
return ret;
824
}
825
826
static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
827
{
828
LRESULT result;
829
830
TRACE("(code=%d)\n", code);
831
832
pnmh->hwndFrom = infoPtr->hwndSelf;
833
pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
834
pnmh->code = code;
835
result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
836
837
TRACE(" <= %Id\n", result);
838
839
return result;
840
}
841
842
static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
843
{
844
NMHDR nmh;
845
HWND hwnd = infoPtr->hwndSelf;
846
notify_hdr(infoPtr, code, &nmh);
847
return IsWindow(hwnd);
848
}
849
850
static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
851
{
852
NMITEMACTIVATE nmia;
853
LVITEMW item;
854
855
nmia.uNewState = 0;
856
nmia.uOldState = 0;
857
nmia.uChanged = 0;
858
nmia.uKeyFlags = 0;
859
860
item.mask = LVIF_PARAM|LVIF_STATE;
861
item.iItem = htInfo->iItem;
862
item.iSubItem = 0;
863
item.stateMask = (UINT)-1;
864
if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
865
nmia.lParam = item.lParam;
866
nmia.uOldState = item.state;
867
nmia.uNewState = item.state | LVIS_ACTIVATING;
868
nmia.uChanged = LVIF_STATE;
869
}
870
871
nmia.iItem = htInfo->iItem;
872
nmia.iSubItem = htInfo->iSubItem;
873
nmia.ptAction = htInfo->pt;
874
875
if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
876
if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
877
if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
878
879
notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
880
}
881
882
static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
883
{
884
TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
885
return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
886
}
887
888
/* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
889
static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
890
{
891
NMITEMACTIVATE nmia;
892
LVITEMW item;
893
HWND hwnd = infoPtr->hwndSelf;
894
LRESULT ret;
895
896
TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
897
ZeroMemory(&nmia, sizeof(nmia));
898
nmia.iItem = lvht->iItem;
899
nmia.iSubItem = lvht->iSubItem;
900
nmia.ptAction = lvht->pt;
901
item.mask = LVIF_PARAM;
902
item.iItem = lvht->iItem;
903
item.iSubItem = 0;
904
if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
905
ret = notify_hdr(infoPtr, code, (NMHDR*)&nmia);
906
return IsWindow(hwnd) && (code == NM_RCLICK ? !ret : TRUE);
907
}
908
909
static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
910
{
911
NMLISTVIEW nmlv;
912
LVITEMW item;
913
HWND hwnd = infoPtr->hwndSelf;
914
915
ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
916
nmlv.iItem = nItem;
917
item.mask = LVIF_PARAM;
918
item.iItem = nItem;
919
item.iSubItem = 0;
920
if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
921
notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
922
return IsWindow(hwnd);
923
}
924
925
/*
926
Send notification. depends on dispinfoW having same
927
structure as dispinfoA.
928
infoPtr : listview struct
929
code : *Unicode* notification code
930
pdi : dispinfo structure (can be unicode or ansi)
931
isW : TRUE if dispinfo is Unicode
932
*/
933
static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT code, LPNMLVDISPINFOW pdi, BOOL isW)
934
{
935
INT length = 0, ret_length;
936
LPWSTR buffer = NULL, ret_text;
937
BOOL return_ansi = FALSE;
938
BOOL return_unicode = FALSE;
939
BOOL ret;
940
941
if ((pdi->item.mask & LVIF_TEXT) && is_text(pdi->item.pszText))
942
{
943
return_unicode = ( isW && infoPtr->notifyFormat == NFR_ANSI);
944
return_ansi = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
945
}
946
947
ret_length = pdi->item.cchTextMax;
948
ret_text = pdi->item.pszText;
949
950
if (return_unicode || return_ansi)
951
{
952
if (code != LVN_GETDISPINFOW)
953
{
954
length = return_ansi ?
955
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
956
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
957
}
958
else
959
{
960
length = pdi->item.cchTextMax;
961
*pdi->item.pszText = 0; /* make sure we don't process garbage */
962
}
963
964
buffer = Alloc( length * (return_ansi ? sizeof(WCHAR) : sizeof(CHAR)) );
965
if (!buffer) return FALSE;
966
967
if (return_ansi)
968
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
969
buffer, length);
970
else
971
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
972
length, NULL, NULL);
973
974
pdi->item.pszText = buffer;
975
pdi->item.cchTextMax = length;
976
}
977
978
if (infoPtr->notifyFormat == NFR_ANSI)
979
code = get_ansi_notification(code);
980
981
TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
982
ret = notify_hdr(infoPtr, code, &pdi->hdr);
983
TRACE(" resulting code=%d\n", pdi->hdr.code);
984
985
if (return_ansi || return_unicode)
986
{
987
if (return_ansi && (pdi->hdr.code == LVN_GETDISPINFOA))
988
{
989
strcpy((char*)ret_text, (char*)pdi->item.pszText);
990
}
991
else if (return_unicode && (pdi->hdr.code == LVN_GETDISPINFOW))
992
{
993
lstrcpyW(ret_text, pdi->item.pszText);
994
}
995
else if (return_ansi) /* note : pointer can be changed by app ! */
996
{
997
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) ret_text,
998
ret_length, NULL, NULL);
999
}
1000
else
1001
MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
1002
ret_text, ret_length);
1003
1004
pdi->item.pszText = ret_text; /* restores our buffer */
1005
pdi->item.cchTextMax = ret_length;
1006
1007
Free(buffer);
1008
return ret;
1009
}
1010
1011
/* if dispinfo holder changed notification code then convert */
1012
if (!isW && (pdi->hdr.code == LVN_GETDISPINFOW) && (pdi->item.mask & LVIF_TEXT))
1013
{
1014
length = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
1015
1016
buffer = Alloc(length * sizeof(CHAR));
1017
if (!buffer) return FALSE;
1018
1019
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
1020
ret_length, NULL, NULL);
1021
1022
strcpy((LPSTR)pdi->item.pszText, (LPSTR)buffer);
1023
Free(buffer);
1024
}
1025
1026
return ret;
1027
}
1028
1029
static int notify_odfinditem(const LISTVIEW_INFO *infoPtr, NMLVFINDITEMW *nmlv)
1030
{
1031
NMLVFINDITEMA nmlva;
1032
char *str = NULL;
1033
int len, ret;
1034
1035
if (infoPtr->notifyFormat == NFR_UNICODE)
1036
return notify_hdr(infoPtr, LVN_ODFINDITEMW, &nmlv->hdr);
1037
1038
/* A/W layout is the same, the only difference is string encoding. */
1039
memcpy(&nmlva, nmlv, sizeof(nmlva));
1040
nmlva.lvfi.psz = NULL;
1041
1042
if (nmlv->lvfi.psz)
1043
{
1044
len = WideCharToMultiByte(CP_ACP, 0, nmlv->lvfi.psz, -1, NULL, 0, NULL, NULL);
1045
str = Alloc(len);
1046
if (!str) return 0;
1047
WideCharToMultiByte(CP_ACP, 0, nmlv->lvfi.psz, -1, str, len, NULL, NULL);
1048
nmlva.lvfi.psz = str;
1049
}
1050
1051
ret = notify_hdr(infoPtr, LVN_ODFINDITEMA, &nmlva.hdr);
1052
Free(str);
1053
1054
return ret;
1055
}
1056
1057
static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
1058
const RECT *rcBounds, const LVITEMW *lplvItem)
1059
{
1060
ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
1061
lpnmlvcd->nmcd.hdc = hdc;
1062
lpnmlvcd->nmcd.rc = *rcBounds;
1063
lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
1064
lpnmlvcd->clrText = infoPtr->clrText;
1065
if (!lplvItem) return;
1066
lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
1067
lpnmlvcd->iSubItem = lplvItem->iSubItem;
1068
if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
1069
if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
1070
if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
1071
lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
1072
}
1073
1074
static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
1075
{
1076
BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
1077
DWORD result;
1078
1079
lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
1080
if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
1081
if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
1082
if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
1083
result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
1084
if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
1085
return result;
1086
}
1087
1088
static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, const NMLVCUSTOMDRAW *cd, BOOL SubItem)
1089
{
1090
COLORREF backcolor, textcolor;
1091
1092
backcolor = cd->clrTextBk;
1093
textcolor = cd->clrText;
1094
1095
/* apparently, for selected items, we have to override the returned values */
1096
if (!SubItem)
1097
{
1098
if (cd->nmcd.uItemState & CDIS_SELECTED)
1099
{
1100
if (infoPtr->bFocus)
1101
{
1102
backcolor = comctl32_color.clrHighlight;
1103
textcolor = comctl32_color.clrHighlightText;
1104
}
1105
else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1106
{
1107
backcolor = comctl32_color.clr3dFace;
1108
textcolor = comctl32_color.clrBtnText;
1109
}
1110
}
1111
}
1112
1113
if (backcolor == CLR_DEFAULT)
1114
backcolor = comctl32_color.clrWindow;
1115
if (textcolor == CLR_DEFAULT)
1116
textcolor = comctl32_color.clrWindowText;
1117
1118
/* Set the text attributes */
1119
SetBkColor(hdc, backcolor);
1120
SetTextColor(hdc, textcolor);
1121
}
1122
1123
static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1124
{
1125
return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1126
}
1127
1128
/* returns TRUE when repaint needed, FALSE otherwise */
1129
static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1130
{
1131
MEASUREITEMSTRUCT mis;
1132
mis.CtlType = ODT_LISTVIEW;
1133
mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1134
mis.itemID = -1;
1135
mis.itemWidth = 0;
1136
mis.itemData = 0;
1137
mis.itemHeight= infoPtr->nItemHeight;
1138
SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1139
if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1140
{
1141
infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1142
return TRUE;
1143
}
1144
return FALSE;
1145
}
1146
1147
/******** Item iterator functions **********************************/
1148
1149
static RANGES ranges_create(int count);
1150
static void ranges_destroy(RANGES ranges);
1151
static BOOL ranges_add(RANGES ranges, RANGE range);
1152
static BOOL ranges_del(RANGES ranges, RANGE range);
1153
static void ranges_dump(RANGES ranges);
1154
1155
static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1156
{
1157
RANGE range = { nItem, nItem + 1 };
1158
1159
return ranges_add(ranges, range);
1160
}
1161
1162
static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1163
{
1164
RANGE range = { nItem, nItem + 1 };
1165
1166
return ranges_del(ranges, range);
1167
}
1168
1169
/***
1170
* ITERATOR DOCUMENTATION
1171
*
1172
* The iterator functions allow for easy, and convenient iteration
1173
* over items of interest in the list. Typically, you create an
1174
* iterator, use it, and destroy it, as such:
1175
* ITERATOR i;
1176
*
1177
* iterator_xxxitems(&i, ...);
1178
* while (iterator_{prev,next}(&i)
1179
* {
1180
* //code which uses i.nItem
1181
* }
1182
* iterator_destroy(&i);
1183
*
1184
* where xxx is either: framed, or visible.
1185
* Note that it is important that the code destroys the iterator
1186
* after it's done with it, as the creation of the iterator may
1187
* allocate memory, which thus needs to be freed.
1188
*
1189
* You can iterate both forwards, and backwards through the list,
1190
* by using iterator_next or iterator_prev respectively.
1191
*
1192
* Lower numbered items are draw on top of higher number items in
1193
* LVS_ICON, and LVS_SMALLICON (which are the only modes where
1194
* items may overlap). So, to test items, you should use
1195
* iterator_next
1196
* which lists the items top to bottom (in Z-order).
1197
* For drawing items, you should use
1198
* iterator_prev
1199
* which lists the items bottom to top (in Z-order).
1200
* If you keep iterating over the items after the end-of-items
1201
* marker (-1) is returned, the iterator will start from the
1202
* beginning. Typically, you don't need to test for -1,
1203
* because iterator_{next,prev} will return TRUE if more items
1204
* are to be iterated over, or FALSE otherwise.
1205
*
1206
* Note: the iterator is defined to be bidirectional. That is,
1207
* any number of prev followed by any number of next, or
1208
* five versa, should leave the iterator at the same item:
1209
* prev * n, next * n = next * n, prev * n
1210
*
1211
* The iterator has a notion of an out-of-order, special item,
1212
* which sits at the start of the list. This is used in
1213
* LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1214
* which needs to be first, as it may overlap other items.
1215
*
1216
* The code is a bit messy because we have:
1217
* - a special item to deal with
1218
* - simple range, or composite range
1219
* - empty range.
1220
* If you find bugs, or want to add features, please make sure you
1221
* always check/modify *both* iterator_prev, and iterator_next.
1222
*/
1223
1224
/****
1225
* This function iterates through the items in increasing order,
1226
* but prefixed by the special item, then -1. That is:
1227
* special, 1, 2, 3, ..., n, -1.
1228
* Each item is listed only once.
1229
*/
1230
static inline BOOL iterator_next(ITERATOR* i)
1231
{
1232
if (i->nItem == -1)
1233
{
1234
i->nItem = i->nSpecial;
1235
if (i->nItem != -1) return TRUE;
1236
}
1237
if (i->nItem == i->nSpecial)
1238
{
1239
if (i->ranges) i->index = 0;
1240
goto pickarange;
1241
}
1242
1243
i->nItem++;
1244
testitem:
1245
if (i->nItem == i->nSpecial) i->nItem++;
1246
if (i->nItem < i->range.upper) return TRUE;
1247
1248
pickarange:
1249
if (i->ranges)
1250
{
1251
if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1252
i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1253
else goto end;
1254
}
1255
else if (i->nItem >= i->range.upper) goto end;
1256
1257
i->nItem = i->range.lower;
1258
if (i->nItem >= 0) goto testitem;
1259
end:
1260
i->nItem = -1;
1261
return FALSE;
1262
}
1263
1264
/****
1265
* This function iterates through the items in decreasing order,
1266
* followed by the special item, then -1. That is:
1267
* n, n-1, ..., 3, 2, 1, special, -1.
1268
* Each item is listed only once.
1269
*/
1270
static inline BOOL iterator_prev(ITERATOR* i)
1271
{
1272
BOOL start = FALSE;
1273
1274
if (i->nItem == -1)
1275
{
1276
start = TRUE;
1277
if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1278
goto pickarange;
1279
}
1280
if (i->nItem == i->nSpecial)
1281
{
1282
i->nItem = -1;
1283
return FALSE;
1284
}
1285
1286
testitem:
1287
i->nItem--;
1288
if (i->nItem == i->nSpecial) i->nItem--;
1289
if (i->nItem >= i->range.lower) return TRUE;
1290
1291
pickarange:
1292
if (i->ranges)
1293
{
1294
if (i->index > 0)
1295
i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1296
else goto end;
1297
}
1298
else if (!start && i->nItem < i->range.lower) goto end;
1299
1300
i->nItem = i->range.upper;
1301
if (i->nItem > 0) goto testitem;
1302
end:
1303
return (i->nItem = i->nSpecial) != -1;
1304
}
1305
1306
static RANGE iterator_range(const ITERATOR *i)
1307
{
1308
RANGE range;
1309
1310
if (!i->ranges) return i->range;
1311
1312
if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1313
{
1314
range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1315
range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1316
}
1317
else range.lower = range.upper = 0;
1318
1319
return range;
1320
}
1321
1322
/***
1323
* Releases resources associated with this iterator.
1324
*/
1325
static inline void iterator_destroy(const ITERATOR *i)
1326
{
1327
ranges_destroy(i->ranges);
1328
}
1329
1330
/***
1331
* Create an empty iterator.
1332
*/
1333
static inline void iterator_empty(ITERATOR* i)
1334
{
1335
ZeroMemory(i, sizeof(*i));
1336
i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1337
}
1338
1339
/***
1340
* Create an iterator over a range.
1341
*/
1342
static inline void iterator_rangeitems(ITERATOR* i, RANGE range)
1343
{
1344
iterator_empty(i);
1345
i->range = range;
1346
}
1347
1348
/***
1349
* Create an iterator over a bunch of ranges.
1350
* Please note that the iterator will take ownership of the ranges,
1351
* and will free them upon destruction.
1352
*/
1353
static inline void iterator_rangesitems(ITERATOR* i, RANGES ranges)
1354
{
1355
iterator_empty(i);
1356
i->ranges = ranges;
1357
}
1358
1359
/***
1360
* Creates an iterator over the items which intersect frame.
1361
* Uses absolute coordinates rather than compensating for the current offset.
1362
*/
1363
static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame)
1364
{
1365
RECT rcItem, rcTemp;
1366
RANGES ranges;
1367
1368
TRACE("(frame=%s)\n", wine_dbgstr_rect(frame));
1369
1370
/* in case we fail, we want to return an empty iterator */
1371
iterator_empty(i);
1372
1373
if (infoPtr->nItemCount == 0)
1374
return TRUE;
1375
1376
if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1377
{
1378
INT nItem;
1379
1380
if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1381
{
1382
LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1383
if (IntersectRect(&rcTemp, &rcItem, frame))
1384
i->nSpecial = infoPtr->nFocusedItem;
1385
}
1386
if (!(ranges = ranges_create(50))) return FALSE;
1387
iterator_rangesitems(i, ranges);
1388
/* to do better here, we need to have PosX, and PosY sorted */
1389
TRACE("building icon ranges:\n");
1390
for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1391
{
1392
rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1393
rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1394
rcItem.right = rcItem.left + infoPtr->nItemWidth;
1395
rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1396
if (IntersectRect(&rcTemp, &rcItem, frame))
1397
ranges_additem(i->ranges, nItem);
1398
}
1399
return TRUE;
1400
}
1401
else if (infoPtr->uView == LV_VIEW_DETAILS)
1402
{
1403
RANGE range;
1404
1405
if (frame->left >= infoPtr->nItemWidth) return TRUE;
1406
if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1407
1408
range.lower = max(frame->top / infoPtr->nItemHeight, 0);
1409
range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1410
if (range.upper <= range.lower) return TRUE;
1411
iterator_rangeitems(i, range);
1412
TRACE(" report=%s\n", debugrange(&i->range));
1413
}
1414
else
1415
{
1416
INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1417
INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0);
1418
INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1419
INT nFirstCol;
1420
INT nLastCol;
1421
INT lower;
1422
RANGE item_range;
1423
INT nCol;
1424
1425
if (infoPtr->nItemWidth)
1426
{
1427
nFirstCol = max(frame->left / infoPtr->nItemWidth, 0);
1428
nLastCol = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1429
}
1430
else
1431
{
1432
nFirstCol = max(frame->left, 0);
1433
nLastCol = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1434
}
1435
1436
lower = nFirstCol * nPerCol + nFirstRow;
1437
1438
TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1439
nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1440
1441
if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1442
1443
if (!(ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
1444
iterator_rangesitems(i, ranges);
1445
TRACE("building list ranges:\n");
1446
for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1447
{
1448
item_range.lower = nCol * nPerCol + nFirstRow;
1449
if(item_range.lower >= infoPtr->nItemCount) break;
1450
item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1451
TRACE(" list=%s\n", debugrange(&item_range));
1452
ranges_add(i->ranges, item_range);
1453
}
1454
}
1455
1456
return TRUE;
1457
}
1458
1459
/***
1460
* Creates an iterator over the items which intersect lprc.
1461
*/
1462
static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1463
{
1464
RECT frame = *lprc;
1465
POINT Origin;
1466
1467
TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1468
1469
LISTVIEW_GetOrigin(infoPtr, &Origin);
1470
OffsetRect(&frame, -Origin.x, -Origin.y);
1471
1472
return iterator_frameditems_absolute(i, infoPtr, &frame);
1473
}
1474
1475
/***
1476
* Creates an iterator over the items which intersect the visible region of hdc.
1477
*/
1478
static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1479
{
1480
POINT Origin, Position;
1481
RECT rcItem, rcClip;
1482
INT rgntype;
1483
1484
rgntype = GetClipBox(hdc, &rcClip);
1485
if (rgntype == NULLREGION)
1486
{
1487
iterator_empty(i);
1488
return TRUE;
1489
}
1490
if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1491
if (rgntype == SIMPLEREGION) return TRUE;
1492
1493
/* first deal with the special item */
1494
if (i->nSpecial != -1)
1495
{
1496
LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1497
if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1498
}
1499
1500
/* if we can't deal with the region, we'll just go with the simple range */
1501
LISTVIEW_GetOrigin(infoPtr, &Origin);
1502
TRACE("building visible range:\n");
1503
if (!i->ranges && i->range.lower < i->range.upper)
1504
{
1505
if (!(i->ranges = ranges_create(50))) return TRUE;
1506
if (!ranges_add(i->ranges, i->range))
1507
{
1508
ranges_destroy(i->ranges);
1509
i->ranges = 0;
1510
return TRUE;
1511
}
1512
}
1513
1514
/* now delete the invisible items from the list */
1515
while(iterator_next(i))
1516
{
1517
LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1518
rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x;
1519
rcItem.top = Position.y + Origin.y;
1520
rcItem.right = rcItem.left + infoPtr->nItemWidth;
1521
rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1522
if (!RectVisible(hdc, &rcItem))
1523
ranges_delitem(i->ranges, i->nItem);
1524
}
1525
/* the iterator should restart on the next iterator_next */
1526
TRACE("done\n");
1527
1528
return TRUE;
1529
}
1530
1531
/* Remove common elements from two iterators */
1532
/* Passed iterators have to point on the first elements */
1533
static BOOL iterator_remove_common_items(ITERATOR *iter1, ITERATOR *iter2)
1534
{
1535
if(!iter1->ranges || !iter2->ranges) {
1536
int lower, upper;
1537
1538
if(iter1->ranges || iter2->ranges ||
1539
(iter1->range.lower<iter2->range.lower && iter1->range.upper>iter2->range.upper) ||
1540
(iter1->range.lower>iter2->range.lower && iter1->range.upper<iter2->range.upper)) {
1541
ERR("result is not a one range iterator\n");
1542
return FALSE;
1543
}
1544
1545
if(iter1->range.lower==-1 || iter2->range.lower==-1)
1546
return TRUE;
1547
1548
lower = iter1->range.lower;
1549
upper = iter1->range.upper;
1550
1551
if(lower < iter2->range.lower)
1552
iter1->range.upper = iter2->range.lower;
1553
else if(upper > iter2->range.upper)
1554
iter1->range.lower = iter2->range.upper;
1555
else
1556
iter1->range.lower = iter1->range.upper = -1;
1557
1558
if(iter2->range.lower < lower)
1559
iter2->range.upper = lower;
1560
else if(iter2->range.upper > upper)
1561
iter2->range.lower = upper;
1562
else
1563
iter2->range.lower = iter2->range.upper = -1;
1564
1565
return TRUE;
1566
}
1567
1568
iterator_next(iter1);
1569
iterator_next(iter2);
1570
1571
while(1) {
1572
if(iter1->nItem==-1 || iter2->nItem==-1)
1573
break;
1574
1575
if(iter1->nItem == iter2->nItem) {
1576
int delete = iter1->nItem;
1577
1578
iterator_prev(iter1);
1579
iterator_prev(iter2);
1580
ranges_delitem(iter1->ranges, delete);
1581
ranges_delitem(iter2->ranges, delete);
1582
iterator_next(iter1);
1583
iterator_next(iter2);
1584
} else if(iter1->nItem > iter2->nItem)
1585
iterator_next(iter2);
1586
else
1587
iterator_next(iter1);
1588
}
1589
1590
iter1->nItem = iter1->range.lower = iter1->range.upper = -1;
1591
iter2->nItem = iter2->range.lower = iter2->range.upper = -1;
1592
return TRUE;
1593
}
1594
1595
/******** Misc helper functions ************************************/
1596
1597
static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1598
WPARAM wParam, LPARAM lParam, BOOL isW)
1599
{
1600
if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1601
else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1602
}
1603
1604
static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1605
{
1606
return (infoPtr->dwStyle & LVS_AUTOARRANGE) &&
1607
(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1608
}
1609
1610
static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1611
{
1612
DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1613
if(state == 1 || state == 2)
1614
{
1615
LVITEMW lvitem;
1616
state ^= 3;
1617
lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1618
lvitem.stateMask = LVIS_STATEIMAGEMASK;
1619
LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1620
}
1621
}
1622
1623
/* this should be called after window style got updated,
1624
it used to reset view state to match current window style */
1625
static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1626
{
1627
switch (infoPtr->dwStyle & LVS_TYPEMASK)
1628
{
1629
case LVS_ICON:
1630
infoPtr->uView = LV_VIEW_ICON;
1631
break;
1632
case LVS_REPORT:
1633
infoPtr->uView = LV_VIEW_DETAILS;
1634
break;
1635
case LVS_SMALLICON:
1636
infoPtr->uView = LV_VIEW_SMALLICON;
1637
break;
1638
case LVS_LIST:
1639
infoPtr->uView = LV_VIEW_LIST;
1640
}
1641
}
1642
1643
/* computes next item id value */
1644
static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1645
{
1646
INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1647
1648
if (count > 0)
1649
{
1650
ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1651
return lpID->id + 1;
1652
}
1653
return 0;
1654
}
1655
1656
/******** Internal API functions ************************************/
1657
1658
static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1659
{
1660
static COLUMN_INFO mainItem;
1661
1662
if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1663
assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1664
1665
/* update cached column rectangles */
1666
if (infoPtr->colRectsDirty)
1667
{
1668
COLUMN_INFO *info;
1669
LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr;
1670
INT i;
1671
1672
for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) {
1673
info = DPA_GetPtr(infoPtr->hdpaColumns, i);
1674
SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader);
1675
}
1676
Ptr->colRectsDirty = FALSE;
1677
}
1678
1679
return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1680
}
1681
1682
static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1683
{
1684
DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1685
HINSTANCE hInst;
1686
1687
if (infoPtr->hwndHeader) return 0;
1688
1689
TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1690
1691
/* setup creation flags */
1692
dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1693
dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1694
1695
hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1696
1697
/* create header */
1698
infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1699
0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1700
if (!infoPtr->hwndHeader) return -1;
1701
1702
/* set header unicode format */
1703
SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1704
1705
/* set header font */
1706
SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, TRUE);
1707
1708
/* set header image list */
1709
if (infoPtr->himlSmall)
1710
SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)infoPtr->himlSmall);
1711
1712
LISTVIEW_UpdateSize(infoPtr);
1713
1714
return 0;
1715
}
1716
1717
static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1718
{
1719
*lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1720
}
1721
1722
static inline BOOL LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO *infoPtr)
1723
{
1724
return (infoPtr->uView == LV_VIEW_DETAILS ||
1725
infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) &&
1726
!(infoPtr->dwStyle & LVS_NOCOLUMNHEADER);
1727
}
1728
1729
static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1730
{
1731
return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1732
}
1733
1734
/* used to handle collapse main item column case */
1735
static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1736
{
1737
return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1738
DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1739
}
1740
1741
/* Listview invalidation functions: use _only_ these functions to invalidate */
1742
1743
static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1744
{
1745
return infoPtr->redraw;
1746
}
1747
1748
static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1749
{
1750
if(!is_redrawing(infoPtr)) return;
1751
TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1752
InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1753
}
1754
1755
static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1756
{
1757
RECT rcBox;
1758
1759
if (!is_redrawing(infoPtr) || nItem < 0 || nItem >= infoPtr->nItemCount)
1760
return;
1761
1762
LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1763
LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1764
}
1765
1766
static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1767
{
1768
POINT Origin, Position;
1769
RECT rcBox;
1770
1771
if(!is_redrawing(infoPtr)) return;
1772
assert (infoPtr->uView == LV_VIEW_DETAILS);
1773
LISTVIEW_GetOrigin(infoPtr, &Origin);
1774
LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1775
LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1776
rcBox.top = 0;
1777
rcBox.bottom = infoPtr->nItemHeight;
1778
OffsetRect(&rcBox, Origin.x, Origin.y + Position.y);
1779
LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1780
}
1781
1782
static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1783
{
1784
LISTVIEW_InvalidateRect(infoPtr, NULL);
1785
}
1786
1787
static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1788
{
1789
RECT rcCol;
1790
1791
if(!is_redrawing(infoPtr)) return;
1792
LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1793
rcCol.top = infoPtr->rcList.top;
1794
rcCol.bottom = infoPtr->rcList.bottom;
1795
LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1796
}
1797
1798
/***
1799
* DESCRIPTION:
1800
* Retrieves the number of items that can fit vertically in the client area.
1801
*
1802
* PARAMETER(S):
1803
* [I] infoPtr : valid pointer to the listview structure
1804
*
1805
* RETURN:
1806
* Number of items per row.
1807
*/
1808
static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1809
{
1810
INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1811
1812
return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1813
}
1814
1815
/***
1816
* DESCRIPTION:
1817
* Retrieves the number of items that can fit horizontally in the client
1818
* area.
1819
*
1820
* PARAMETER(S):
1821
* [I] infoPtr : valid pointer to the listview structure
1822
*
1823
* RETURN:
1824
* Number of items per column.
1825
*/
1826
static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1827
{
1828
INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1829
1830
return infoPtr->nItemHeight ? max(nListHeight / infoPtr->nItemHeight, 1) : 0;
1831
}
1832
1833
1834
/*************************************************************************
1835
* LISTVIEW_ProcessLetterKeys
1836
*
1837
* Processes keyboard messages generated by pressing the letter keys
1838
* on the keyboard.
1839
* What this does is perform a case insensitive search from the
1840
* current position with the following quirks:
1841
* - If two chars or more are pressed in quick succession we search
1842
* for the corresponding string (e.g. 'abc').
1843
* - If there is a delay we wipe away the current search string and
1844
* restart with just that char.
1845
* - If the user keeps pressing the same character, whether slowly or
1846
* fast, so that the search string is entirely composed of this
1847
* character ('aaaaa' for instance), then we search for first item
1848
* that starting with that character.
1849
* - If the user types the above character in quick succession, then
1850
* we must also search for the corresponding string ('aaaaa'), and
1851
* go to that string if there is a match.
1852
*
1853
* PARAMETERS
1854
* [I] hwnd : handle to the window
1855
* [I] charCode : the character code, the actual character
1856
* [I] keyData : key data
1857
*
1858
* RETURNS
1859
*
1860
* Zero.
1861
*
1862
* BUGS
1863
*
1864
* - The current implementation has a list of characters it will
1865
* accept and it ignores everything else. In particular it will
1866
* ignore accentuated characters which seems to match what
1867
* Windows does. But I'm not sure it makes sense to follow
1868
* Windows there.
1869
* - We don't sound a beep when the search fails.
1870
*
1871
* SEE ALSO
1872
*
1873
* TREEVIEW_ProcessLetterKeys
1874
*/
1875
static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1876
{
1877
WCHAR buffer[MAX_PATH];
1878
DWORD prevTime;
1879
LVITEMW item;
1880
int startidx;
1881
INT nItem;
1882
INT diff;
1883
1884
/* simple parameter checking */
1885
if (!charCode || !keyData || infoPtr->nItemCount == 0) return 0;
1886
1887
/* only allow the valid WM_CHARs through */
1888
if (!iswalnum(charCode) &&
1889
charCode != '.' && charCode != '`' && charCode != '!' &&
1890
charCode != '@' && charCode != '#' && charCode != '$' &&
1891
charCode != '%' && charCode != '^' && charCode != '&' &&
1892
charCode != '*' && charCode != '(' && charCode != ')' &&
1893
charCode != '-' && charCode != '_' && charCode != '+' &&
1894
charCode != '=' && charCode != '\\'&& charCode != ']' &&
1895
charCode != '}' && charCode != '[' && charCode != '{' &&
1896
charCode != '/' && charCode != '?' && charCode != '>' &&
1897
charCode != '<' && charCode != ',' && charCode != '~')
1898
return 0;
1899
1900
/* update the search parameters */
1901
prevTime = infoPtr->lastKeyPressTimestamp;
1902
infoPtr->lastKeyPressTimestamp = GetTickCount();
1903
diff = infoPtr->lastKeyPressTimestamp - prevTime;
1904
1905
if (diff >= 0 && diff < KEY_DELAY)
1906
{
1907
if (infoPtr->nSearchParamLength < MAX_PATH - 1)
1908
infoPtr->szSearchParam[infoPtr->nSearchParamLength++] = charCode;
1909
1910
if (infoPtr->charCode != charCode)
1911
infoPtr->charCode = charCode = 0;
1912
}
1913
else
1914
{
1915
infoPtr->charCode = charCode;
1916
infoPtr->szSearchParam[0] = charCode;
1917
infoPtr->nSearchParamLength = 1;
1918
}
1919
1920
/* should start from next after focused item, so next item that matches
1921
will be selected, if there isn't any and focused matches it will be selected
1922
on second search stage from beginning of the list */
1923
if (infoPtr->nFocusedItem >= 0 && infoPtr->nItemCount > 1)
1924
{
1925
/* with some accumulated search data available start with current focus, otherwise
1926
it's excluded from search */
1927
startidx = infoPtr->nSearchParamLength > 1 ? infoPtr->nFocusedItem : infoPtr->nFocusedItem + 1;
1928
if (startidx == infoPtr->nItemCount) startidx = 0;
1929
}
1930
else
1931
startidx = 0;
1932
1933
/* let application handle this for virtual listview */
1934
if (infoPtr->dwStyle & LVS_OWNERDATA)
1935
{
1936
NMLVFINDITEMW nmlv;
1937
1938
memset(&nmlv.lvfi, 0, sizeof(nmlv.lvfi));
1939
nmlv.lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1940
nmlv.lvfi.psz = infoPtr->szSearchParam;
1941
nmlv.iStart = startidx;
1942
1943
infoPtr->szSearchParam[infoPtr->nSearchParamLength] = 0;
1944
1945
nItem = notify_odfinditem(infoPtr, &nmlv);
1946
}
1947
else
1948
{
1949
int i = startidx, endidx;
1950
1951
/* and search from the current position */
1952
nItem = -1;
1953
endidx = infoPtr->nItemCount;
1954
1955
/* first search in [startidx, endidx), on failure continue in [0, startidx) */
1956
while (1)
1957
{
1958
/* start from first item if not found with >= startidx */
1959
if (i == infoPtr->nItemCount && startidx > 0)
1960
{
1961
endidx = startidx;
1962
startidx = 0;
1963
}
1964
1965
for (i = startidx; i < endidx; i++)
1966
{
1967
/* retrieve text */
1968
item.mask = LVIF_TEXT;
1969
item.iItem = i;
1970
item.iSubItem = 0;
1971
item.pszText = buffer;
1972
item.cchTextMax = MAX_PATH;
1973
if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1974
1975
if (!wcsnicmp(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength))
1976
{
1977
nItem = i;
1978
break;
1979
}
1980
/* this is used to find first char match when search string is not available yet,
1981
otherwise every WM_CHAR will search to next item by first char, ignoring that we're
1982
already waiting for user to complete a string */
1983
else if (nItem == -1 && infoPtr->nSearchParamLength == 1 && !wcsnicmp(item.pszText, infoPtr->szSearchParam, 1))
1984
{
1985
/* this would work but we must keep looking for a longer match */
1986
nItem = i;
1987
}
1988
}
1989
1990
if ( nItem != -1 || /* found something */
1991
endidx != infoPtr->nItemCount || /* second search done */
1992
(startidx == 0 && endidx == infoPtr->nItemCount) /* full range for first search */ )
1993
break;
1994
};
1995
}
1996
1997
if (nItem != -1)
1998
LISTVIEW_KeySelection(infoPtr, nItem, 0);
1999
2000
return 0;
2001
}
2002
2003
/*************************************************************************
2004
* LISTVIEW_UpdateHeaderSize [Internal]
2005
*
2006
* Function to resize the header control
2007
*
2008
* PARAMS
2009
* [I] hwnd : handle to a window
2010
* [I] nNewScrollPos : scroll pos to set
2011
*
2012
* RETURNS
2013
* None.
2014
*/
2015
static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
2016
{
2017
RECT winRect;
2018
POINT point[2];
2019
2020
TRACE("nNewScrollPos=%d\n", nNewScrollPos);
2021
2022
if (!infoPtr->hwndHeader) return;
2023
2024
GetWindowRect(infoPtr->hwndHeader, &winRect);
2025
point[0].x = winRect.left;
2026
point[0].y = winRect.top;
2027
point[1].x = winRect.right;
2028
point[1].y = winRect.bottom;
2029
2030
MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
2031
point[0].x = -nNewScrollPos;
2032
point[1].x += nNewScrollPos;
2033
2034
SetWindowPos(infoPtr->hwndHeader,0,
2035
point[0].x,point[0].y,point[1].x,point[1].y,
2036
(infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
2037
SWP_NOZORDER | SWP_NOACTIVATE);
2038
}
2039
2040
static INT LISTVIEW_UpdateHScroll(LISTVIEW_INFO *infoPtr)
2041
{
2042
SCROLLINFO horzInfo;
2043
INT dx;
2044
2045
ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
2046
horzInfo.cbSize = sizeof(SCROLLINFO);
2047
horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
2048
2049
/* for now, we'll set info.nMax to the _count_, and adjust it later */
2050
if (infoPtr->uView == LV_VIEW_LIST)
2051
{
2052
INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
2053
horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
2054
2055
/* scroll by at least one column per page */
2056
if(horzInfo.nPage < infoPtr->nItemWidth)
2057
horzInfo.nPage = infoPtr->nItemWidth;
2058
2059
if (infoPtr->nItemWidth)
2060
horzInfo.nPage /= infoPtr->nItemWidth;
2061
}
2062
else if (infoPtr->uView == LV_VIEW_DETAILS)
2063
{
2064
horzInfo.nMax = infoPtr->nItemWidth;
2065
}
2066
else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2067
{
2068
RECT rcView;
2069
2070
if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
2071
}
2072
2073
if (LISTVIEW_IsHeaderEnabled(infoPtr))
2074
{
2075
if (DPA_GetPtrCount(infoPtr->hdpaColumns))
2076
{
2077
RECT rcHeader;
2078
INT index;
2079
2080
index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2081
DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2082
2083
LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2084
horzInfo.nMax = rcHeader.right;
2085
TRACE("horzInfo.nMax=%d\n", horzInfo.nMax);
2086
}
2087
}
2088
2089
horzInfo.fMask = SIF_RANGE | SIF_PAGE;
2090
horzInfo.nMax = max(horzInfo.nMax - 1, 0);
2091
dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
2092
dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
2093
TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
2094
2095
/* Update the Header Control */
2096
if (infoPtr->hwndHeader)
2097
{
2098
horzInfo.fMask = SIF_POS;
2099
GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
2100
LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
2101
}
2102
2103
LISTVIEW_UpdateSize(infoPtr);
2104
return dx;
2105
}
2106
2107
static INT LISTVIEW_UpdateVScroll(LISTVIEW_INFO *infoPtr)
2108
{
2109
SCROLLINFO vertInfo;
2110
INT dy;
2111
2112
ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
2113
vertInfo.cbSize = sizeof(SCROLLINFO);
2114
vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
2115
2116
if (infoPtr->uView == LV_VIEW_DETAILS)
2117
{
2118
vertInfo.nMax = infoPtr->nItemCount;
2119
2120
/* scroll by at least one page */
2121
if(vertInfo.nPage < infoPtr->nItemHeight)
2122
vertInfo.nPage = infoPtr->nItemHeight;
2123
2124
if (infoPtr->nItemHeight > 0)
2125
vertInfo.nPage /= infoPtr->nItemHeight;
2126
}
2127
else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2128
{
2129
RECT rcView;
2130
2131
if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
2132
}
2133
2134
vertInfo.fMask = SIF_RANGE | SIF_PAGE;
2135
vertInfo.nMax = max(vertInfo.nMax - 1, 0);
2136
dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
2137
dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
2138
TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
2139
2140
LISTVIEW_UpdateSize(infoPtr);
2141
return dy;
2142
}
2143
2144
/***
2145
* DESCRIPTION:
2146
* Update the scrollbars. This function should be called whenever
2147
* the content, size or view changes.
2148
*
2149
* PARAMETER(S):
2150
* [I] infoPtr : valid pointer to the listview structure
2151
*
2152
* RETURN:
2153
* None
2154
*/
2155
static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
2156
{
2157
INT dx, dy, pass;
2158
2159
if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
2160
2161
/* Setting the horizontal scroll can change the listview size
2162
* (and potentially everything else) so we need to recompute
2163
* everything again for the vertical scroll and vice-versa
2164
*/
2165
for (dx = 0, dy = 0, pass = 0; pass <= 1; pass++)
2166
{
2167
dx += LISTVIEW_UpdateHScroll(infoPtr);
2168
dy += LISTVIEW_UpdateVScroll(infoPtr);
2169
}
2170
2171
/* Change of the range may have changed the scroll pos. If so move the content */
2172
if (dx != 0 || dy != 0)
2173
{
2174
RECT listRect;
2175
listRect = infoPtr->rcList;
2176
ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
2177
SW_ERASE | SW_INVALIDATE);
2178
}
2179
}
2180
2181
2182
/***
2183
* DESCRIPTION:
2184
* Shows/hides the focus rectangle.
2185
*
2186
* PARAMETER(S):
2187
* [I] infoPtr : valid pointer to the listview structure
2188
* [I] fShow : TRUE to show the focus, FALSE to hide it.
2189
*
2190
* RETURN:
2191
* None
2192
*/
2193
static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
2194
{
2195
HDC hdc;
2196
2197
TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
2198
2199
if (infoPtr->nFocusedItem < 0) return;
2200
2201
/* we need some gymnastics in ICON mode to handle large items */
2202
if (infoPtr->uView == LV_VIEW_ICON)
2203
{
2204
RECT rcBox;
2205
2206
LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
2207
if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
2208
{
2209
LISTVIEW_InvalidateRect(infoPtr, &rcBox);
2210
return;
2211
}
2212
}
2213
2214
if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
2215
2216
/* for some reason, owner draw should work only in report mode */
2217
if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
2218
{
2219
DRAWITEMSTRUCT dis;
2220
LVITEMW item;
2221
2222
HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2223
HFONT hOldFont = SelectObject(hdc, hFont);
2224
2225
item.iItem = infoPtr->nFocusedItem;
2226
item.iSubItem = 0;
2227
item.mask = LVIF_PARAM;
2228
if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
2229
2230
ZeroMemory(&dis, sizeof(dis));
2231
dis.CtlType = ODT_LISTVIEW;
2232
dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
2233
dis.itemID = item.iItem;
2234
dis.itemAction = ODA_FOCUS;
2235
if (fShow) dis.itemState |= ODS_FOCUS;
2236
dis.hwndItem = infoPtr->hwndSelf;
2237
dis.hDC = hdc;
2238
LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
2239
dis.itemData = item.lParam;
2240
2241
SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
2242
2243
SelectObject(hdc, hOldFont);
2244
}
2245
else
2246
LISTVIEW_InvalidateItem(infoPtr, infoPtr->nFocusedItem);
2247
2248
done:
2249
ReleaseDC(infoPtr->hwndSelf, hdc);
2250
}
2251
2252
/***
2253
* Invalidates all visible selected items.
2254
*/
2255
static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
2256
{
2257
ITERATOR i;
2258
2259
iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
2260
while(iterator_next(&i))
2261
{
2262
if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
2263
LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2264
}
2265
iterator_destroy(&i);
2266
}
2267
2268
2269
/***
2270
* DESCRIPTION: [INTERNAL]
2271
* Computes an item's (left,top) corner, relative to rcView.
2272
* That is, the position has NOT been made relative to the Origin.
2273
* This is deliberate, to avoid computing the Origin over, and
2274
* over again, when this function is called in a loop. Instead,
2275
* one can factor the computation of the Origin before the loop,
2276
* and offset the value returned by this function, on every iteration.
2277
*
2278
* PARAMETER(S):
2279
* [I] infoPtr : valid pointer to the listview structure
2280
* [I] nItem : item number
2281
* [O] lpptOrig : item top, left corner
2282
*
2283
* RETURN:
2284
* None.
2285
*/
2286
static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2287
{
2288
assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2289
2290
if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2291
{
2292
lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2293
lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2294
}
2295
else if (infoPtr->uView == LV_VIEW_LIST)
2296
{
2297
INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2298
lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2299
lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2300
}
2301
else /* LV_VIEW_DETAILS */
2302
{
2303
lpptPosition->x = REPORT_MARGINX;
2304
/* item is always at zero indexed column */
2305
if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2306
lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2307
lpptPosition->y = nItem * infoPtr->nItemHeight;
2308
}
2309
}
2310
2311
/***
2312
* DESCRIPTION: [INTERNAL]
2313
* Compute the rectangles of an item. This is to localize all
2314
* the computations in one place. If you are not interested in some
2315
* of these values, simply pass in a NULL -- the function is smart
2316
* enough to compute only what's necessary. The function computes
2317
* the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2318
* one, the BOX rectangle. This rectangle is very cheap to compute,
2319
* and is guaranteed to contain all the other rectangles. Computing
2320
* the ICON rect is also cheap, but all the others are potentially
2321
* expensive. This gives an easy and effective optimization when
2322
* searching (like point inclusion, or rectangle intersection):
2323
* first test against the BOX, and if TRUE, test against the desired
2324
* rectangle.
2325
* If the function does not have all the necessary information
2326
* to computed the requested rectangles, will crash with a
2327
* failed assertion. This is done so we catch all programming
2328
* errors, given that the function is called only from our code.
2329
*
2330
* We have the following 'special' meanings for a few fields:
2331
* * If LVIS_FOCUSED is set, we assume the item has the focus
2332
* This is important in ICON mode, where it might get a larger
2333
* then usual rectangle
2334
*
2335
* Please note that subitem support works only in REPORT mode.
2336
*
2337
* PARAMETER(S):
2338
* [I] infoPtr : valid pointer to the listview structure
2339
* [I] lpLVItem : item to compute the measures for
2340
* [O] lprcBox : ptr to Box rectangle
2341
* Same as LVM_GETITEMRECT with LVIR_BOUNDS
2342
* [0] lprcSelectBox : ptr to select box rectangle
2343
* Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2344
* [O] lprcIcon : ptr to Icon rectangle
2345
* Same as LVM_GETITEMRECT with LVIR_ICON
2346
* [O] lprcStateIcon: ptr to State Icon rectangle
2347
* [O] lprcLabel : ptr to Label rectangle
2348
* Same as LVM_GETITEMRECT with LVIR_LABEL
2349
*
2350
* RETURN:
2351
* None.
2352
*/
2353
static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2354
LPRECT lprcBox, LPRECT lprcSelectBox,
2355
LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2356
{
2357
BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2358
RECT Box, SelectBox, Icon, Label;
2359
COLUMN_INFO *lpColumnInfo = NULL;
2360
SIZE labelSize = { 0, 0 };
2361
2362
TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2363
2364
/* Be smart and try to figure out the minimum we have to do */
2365
if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2366
if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2367
{
2368
assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2369
if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2370
}
2371
if (lprcSelectBox) doSelectBox = TRUE;
2372
if (lprcLabel) doLabel = TRUE;
2373
if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2374
if (doSelectBox)
2375
{
2376
doIcon = TRUE;
2377
doLabel = TRUE;
2378
}
2379
2380
/************************************************************/
2381
/* compute the box rectangle (it should be cheap to do) */
2382
/************************************************************/
2383
if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2384
lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2385
2386
if (lpLVItem->iSubItem)
2387
{
2388
Box = lpColumnInfo->rcHeader;
2389
}
2390
else
2391
{
2392
Box.left = 0;
2393
Box.right = infoPtr->nItemWidth;
2394
}
2395
Box.top = 0;
2396
Box.bottom = infoPtr->nItemHeight;
2397
2398
/******************************************************************/
2399
/* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2400
/******************************************************************/
2401
if (doIcon)
2402
{
2403
LONG state_width = 0;
2404
2405
if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2406
state_width = infoPtr->iconStateSize.cx;
2407
2408
if (infoPtr->uView == LV_VIEW_ICON)
2409
{
2410
Icon.left = Box.left + state_width;
2411
if (infoPtr->himlNormal)
2412
Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2413
Icon.top = Box.top + ICON_TOP_PADDING;
2414
Icon.right = Icon.left;
2415
Icon.bottom = Icon.top;
2416
if (infoPtr->himlNormal)
2417
{
2418
Icon.right += infoPtr->iconSize.cx;
2419
Icon.bottom += infoPtr->iconSize.cy;
2420
}
2421
}
2422
else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2423
{
2424
Icon.left = Box.left + state_width;
2425
2426
if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2427
{
2428
/* we need the indent in report mode */
2429
assert(lpLVItem->mask & LVIF_INDENT);
2430
Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2431
}
2432
2433
Icon.top = Box.top;
2434
Icon.right = Icon.left;
2435
if (infoPtr->himlSmall &&
2436
(!lpColumnInfo || lpLVItem->iSubItem == 0 ||
2437
((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2438
Icon.right += infoPtr->iconSize.cx;
2439
Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2440
}
2441
if(lprcIcon) *lprcIcon = Icon;
2442
TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2443
2444
/* TODO: is this correct? */
2445
if (lprcStateIcon)
2446
{
2447
lprcStateIcon->left = Icon.left - state_width;
2448
lprcStateIcon->right = Icon.left;
2449
lprcStateIcon->top = Icon.top;
2450
lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2451
TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2452
}
2453
}
2454
else Icon.right = 0;
2455
2456
/************************************************************/
2457
/* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2458
/************************************************************/
2459
if (doLabel)
2460
{
2461
/* calculate how far to the right can the label stretch */
2462
Label.right = Box.right;
2463
if (infoPtr->uView == LV_VIEW_DETAILS)
2464
{
2465
if (lpLVItem->iSubItem == 0)
2466
{
2467
/* we need a zero based rect here */
2468
Label = lpColumnInfo->rcHeader;
2469
OffsetRect(&Label, -Label.left, 0);
2470
}
2471
}
2472
2473
if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2474
{
2475
labelSize.cx = infoPtr->nItemWidth;
2476
labelSize.cy = infoPtr->nItemHeight;
2477
goto calc_label;
2478
}
2479
2480
/* we need the text in non owner draw mode */
2481
assert(lpLVItem->mask & LVIF_TEXT);
2482
if (is_text(lpLVItem->pszText))
2483
{
2484
HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2485
HDC hdc = GetDC(infoPtr->hwndSelf);
2486
HFONT hOldFont = SelectObject(hdc, hFont);
2487
UINT uFormat;
2488
RECT rcText;
2489
2490
/* compute rough rectangle where the label will go */
2491
SetRectEmpty(&rcText);
2492
rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2493
rcText.bottom = infoPtr->nItemHeight;
2494
if (infoPtr->uView == LV_VIEW_ICON)
2495
rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2496
2497
/* now figure out the flags */
2498
if (infoPtr->uView == LV_VIEW_ICON)
2499
uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2500
else
2501
uFormat = LV_SL_DT_FLAGS;
2502
2503
DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2504
2505
if (rcText.right != rcText.left)
2506
labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2507
2508
labelSize.cy = rcText.bottom - rcText.top;
2509
2510
SelectObject(hdc, hOldFont);
2511
ReleaseDC(infoPtr->hwndSelf, hdc);
2512
}
2513
2514
calc_label:
2515
if (infoPtr->uView == LV_VIEW_ICON)
2516
{
2517
Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2518
Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2519
infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2520
Label.right = Label.left + labelSize.cx;
2521
Label.bottom = Label.top + infoPtr->nItemHeight;
2522
if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2523
{
2524
labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2525
labelSize.cy /= infoPtr->ntmHeight;
2526
labelSize.cy = max(labelSize.cy, 1);
2527
labelSize.cy *= infoPtr->ntmHeight;
2528
}
2529
Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2530
}
2531
else if (infoPtr->uView == LV_VIEW_DETAILS)
2532
{
2533
Label.left = Icon.right;
2534
Label.top = Box.top;
2535
Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right :
2536
lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
2537
Label.bottom = Label.top + infoPtr->nItemHeight;
2538
}
2539
else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2540
{
2541
Label.left = Icon.right;
2542
Label.top = Box.top;
2543
Label.right = min(Label.left + labelSize.cx, Label.right);
2544
Label.bottom = Label.top + infoPtr->nItemHeight;
2545
}
2546
2547
if (lprcLabel) *lprcLabel = Label;
2548
TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2549
}
2550
2551
/************************************************************/
2552
/* compute SELECT bounding box */
2553
/************************************************************/
2554
if (doSelectBox)
2555
{
2556
if (infoPtr->uView == LV_VIEW_DETAILS)
2557
{
2558
SelectBox.left = Icon.left;
2559
SelectBox.top = Box.top;
2560
SelectBox.bottom = Box.bottom;
2561
2562
if (labelSize.cx)
2563
SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2564
else
2565
SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right);
2566
}
2567
else
2568
{
2569
UnionRect(&SelectBox, &Icon, &Label);
2570
}
2571
if (lprcSelectBox) *lprcSelectBox = SelectBox;
2572
TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2573
}
2574
2575
/* Fix the Box if necessary */
2576
if (lprcBox)
2577
{
2578
if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2579
else *lprcBox = Box;
2580
}
2581
TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2582
}
2583
2584
/***
2585
* DESCRIPTION: [INTERNAL]
2586
*
2587
* PARAMETER(S):
2588
* [I] infoPtr : valid pointer to the listview structure
2589
* [I] nItem : item number
2590
* [O] lprcBox : ptr to Box rectangle
2591
*
2592
* RETURN:
2593
* None.
2594
*/
2595
static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2596
{
2597
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2598
POINT Position, Origin;
2599
LVITEMW lvItem;
2600
2601
LISTVIEW_GetOrigin(infoPtr, &Origin);
2602
LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2603
2604
/* Be smart and try to figure out the minimum we have to do */
2605
lvItem.mask = 0;
2606
if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2607
lvItem.mask |= LVIF_TEXT;
2608
lvItem.iItem = nItem;
2609
lvItem.iSubItem = 0;
2610
lvItem.pszText = szDispText;
2611
lvItem.cchTextMax = DISP_TEXT_SIZE;
2612
if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2613
if (infoPtr->uView == LV_VIEW_ICON)
2614
{
2615
lvItem.mask |= LVIF_STATE;
2616
lvItem.stateMask = LVIS_FOCUSED;
2617
lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2618
}
2619
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2620
2621
if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2622
SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2623
{
2624
OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2625
}
2626
else
2627
OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2628
}
2629
2630
/* LISTVIEW_MapIdToIndex helper */
2631
static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2632
{
2633
ITEM_ID *id1 = (ITEM_ID*)p1;
2634
ITEM_ID *id2 = (ITEM_ID*)p2;
2635
2636
if (id1->id == id2->id) return 0;
2637
2638
return (id1->id < id2->id) ? -1 : 1;
2639
}
2640
2641
/***
2642
* DESCRIPTION:
2643
* Returns the item index for id specified.
2644
*
2645
* PARAMETER(S):
2646
* [I] infoPtr : valid pointer to the listview structure
2647
* [I] iID : item id to get index for
2648
*
2649
* RETURN:
2650
* Item index, or -1 on failure.
2651
*/
2652
static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2653
{
2654
ITEM_ID ID;
2655
INT index;
2656
2657
TRACE("iID=%d\n", iID);
2658
2659
if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2660
if (infoPtr->nItemCount == 0) return -1;
2661
2662
ID.id = iID;
2663
index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, MapIdSearchCompare, 0, DPAS_SORTED);
2664
2665
if (index != -1)
2666
{
2667
ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2668
return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2669
}
2670
2671
return -1;
2672
}
2673
2674
/***
2675
* DESCRIPTION:
2676
* Returns the item id for index given.
2677
*
2678
* PARAMETER(S):
2679
* [I] infoPtr : valid pointer to the listview structure
2680
* [I] iItem : item index to get id for
2681
*
2682
* RETURN:
2683
* Item id.
2684
*/
2685
static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2686
{
2687
ITEM_INFO *lpItem;
2688
HDPA hdpaSubItems;
2689
2690
TRACE("iItem=%d\n", iItem);
2691
2692
if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2693
if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2694
2695
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2696
lpItem = DPA_GetPtr(hdpaSubItems, 0);
2697
2698
return lpItem->id->id;
2699
}
2700
2701
/***
2702
* DESCRIPTION:
2703
* Returns the current icon position, and advances it along the top.
2704
* The returned position is not offset by Origin.
2705
*
2706
* PARAMETER(S):
2707
* [I] infoPtr : valid pointer to the listview structure
2708
* [O] lpPos : will get the current icon position
2709
*
2710
* RETURN:
2711
* None
2712
*/
2713
static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2714
{
2715
INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2716
2717
*lpPos = infoPtr->currIconPos;
2718
2719
infoPtr->currIconPos.x += infoPtr->nItemWidth;
2720
if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2721
2722
infoPtr->currIconPos.x = 0;
2723
infoPtr->currIconPos.y += infoPtr->nItemHeight;
2724
}
2725
2726
2727
/***
2728
* DESCRIPTION:
2729
* Returns the current icon position, and advances it down the left edge.
2730
* The returned position is not offset by Origin.
2731
*
2732
* PARAMETER(S):
2733
* [I] infoPtr : valid pointer to the listview structure
2734
* [O] lpPos : will get the current icon position
2735
*
2736
* RETURN:
2737
* None
2738
*/
2739
static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2740
{
2741
INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2742
2743
*lpPos = infoPtr->currIconPos;
2744
2745
infoPtr->currIconPos.y += infoPtr->nItemHeight;
2746
if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2747
2748
infoPtr->currIconPos.x += infoPtr->nItemWidth;
2749
infoPtr->currIconPos.y = 0;
2750
}
2751
2752
2753
/***
2754
* DESCRIPTION:
2755
* Moves an icon to the specified position.
2756
* It takes care of invalidating the item, etc.
2757
*
2758
* PARAMETER(S):
2759
* [I] infoPtr : valid pointer to the listview structure
2760
* [I] nItem : the item to move
2761
* [I] lpPos : the new icon position
2762
* [I] isNew : flags the item as being new
2763
*
2764
* RETURN:
2765
* Success: TRUE
2766
* Failure: FALSE
2767
*/
2768
static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2769
{
2770
POINT old;
2771
2772
if (!isNew)
2773
{
2774
old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2775
old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2776
2777
if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2778
LISTVIEW_InvalidateItem(infoPtr, nItem);
2779
}
2780
2781
/* Allocating a POINTER for every item is too resource intensive,
2782
* so we'll keep the (x,y) in different arrays */
2783
if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2784
if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2785
2786
LISTVIEW_InvalidateItem(infoPtr, nItem);
2787
2788
return TRUE;
2789
}
2790
2791
/***
2792
* DESCRIPTION:
2793
* Arranges listview items in icon display mode.
2794
*
2795
* PARAMETER(S):
2796
* [I] infoPtr : valid pointer to the listview structure
2797
* [I] nAlignCode : alignment code
2798
*
2799
* RETURN:
2800
* SUCCESS : TRUE
2801
* FAILURE : FALSE
2802
*/
2803
static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2804
{
2805
void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2806
POINT pos;
2807
INT i;
2808
2809
if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2810
2811
TRACE("nAlignCode=%d\n", nAlignCode);
2812
2813
if (nAlignCode == LVA_DEFAULT)
2814
{
2815
if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2816
else nAlignCode = LVA_ALIGNTOP;
2817
}
2818
2819
switch (nAlignCode)
2820
{
2821
case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2822
case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2823
case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2824
default: return FALSE;
2825
}
2826
2827
infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2828
for (i = 0; i < infoPtr->nItemCount; i++)
2829
{
2830
next_pos(infoPtr, &pos);
2831
LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2832
}
2833
2834
return TRUE;
2835
}
2836
2837
/***
2838
* DESCRIPTION:
2839
* Retrieves the bounding rectangle of all the items, not offset by Origin.
2840
* For LVS_REPORT always returns empty rectangle.
2841
*
2842
* PARAMETER(S):
2843
* [I] infoPtr : valid pointer to the listview structure
2844
* [O] lprcView : bounding rectangle
2845
*
2846
* RETURN:
2847
* SUCCESS : TRUE
2848
* FAILURE : FALSE
2849
*/
2850
static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2851
{
2852
INT i, x, y;
2853
2854
SetRectEmpty(lprcView);
2855
2856
switch (infoPtr->uView)
2857
{
2858
case LV_VIEW_ICON:
2859
case LV_VIEW_SMALLICON:
2860
for (i = 0; i < infoPtr->nItemCount; i++)
2861
{
2862
x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2863
y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2864
lprcView->right = max(lprcView->right, x);
2865
lprcView->bottom = max(lprcView->bottom, y);
2866
}
2867
if (infoPtr->nItemCount > 0)
2868
{
2869
lprcView->right += infoPtr->nItemWidth;
2870
lprcView->bottom += infoPtr->nItemHeight;
2871
}
2872
break;
2873
2874
case LV_VIEW_LIST:
2875
y = LISTVIEW_GetCountPerColumn(infoPtr);
2876
x = infoPtr->nItemCount / y;
2877
if (infoPtr->nItemCount % y) x++;
2878
lprcView->right = x * infoPtr->nItemWidth;
2879
lprcView->bottom = y * infoPtr->nItemHeight;
2880
break;
2881
}
2882
}
2883
2884
/***
2885
* DESCRIPTION:
2886
* Retrieves the bounding rectangle of all the items.
2887
*
2888
* PARAMETER(S):
2889
* [I] infoPtr : valid pointer to the listview structure
2890
* [O] lprcView : bounding rectangle
2891
*
2892
* RETURN:
2893
* SUCCESS : TRUE
2894
* FAILURE : FALSE
2895
*/
2896
static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2897
{
2898
POINT ptOrigin;
2899
2900
TRACE("(lprcView=%p)\n", lprcView);
2901
2902
if (!lprcView) return FALSE;
2903
2904
LISTVIEW_GetAreaRect(infoPtr, lprcView);
2905
2906
if (infoPtr->uView != LV_VIEW_DETAILS)
2907
{
2908
LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2909
OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2910
}
2911
2912
TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2913
2914
return TRUE;
2915
}
2916
2917
/***
2918
* DESCRIPTION:
2919
* Retrieves the subitem pointer associated with the subitem index.
2920
*
2921
* PARAMETER(S):
2922
* [I] hdpaSubItems : DPA handle for a specific item
2923
* [I] nSubItem : index of subitem
2924
*
2925
* RETURN:
2926
* SUCCESS : subitem pointer
2927
* FAILURE : NULL
2928
*/
2929
static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2930
{
2931
SUBITEM_INFO *lpSubItem;
2932
INT i;
2933
2934
/* we should binary search here if need be */
2935
for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2936
{
2937
lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2938
if (lpSubItem->iSubItem == nSubItem)
2939
return lpSubItem;
2940
}
2941
2942
return NULL;
2943
}
2944
2945
2946
/***
2947
* DESCRIPTION:
2948
* Calculates the desired item width.
2949
*
2950
* PARAMETER(S):
2951
* [I] infoPtr : valid pointer to the listview structure
2952
*
2953
* RETURN:
2954
* The desired item width.
2955
*/
2956
static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2957
{
2958
INT nItemWidth = 0;
2959
2960
TRACE("view %ld\n", infoPtr->uView);
2961
2962
if (infoPtr->uView == LV_VIEW_ICON)
2963
nItemWidth = infoPtr->iconSpacing.cx;
2964
else if (infoPtr->uView == LV_VIEW_DETAILS)
2965
{
2966
if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2967
{
2968
RECT rcHeader;
2969
INT index;
2970
2971
index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2972
DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2973
2974
LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2975
nItemWidth = rcHeader.right;
2976
}
2977
}
2978
else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2979
{
2980
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2981
LVITEMW lvItem;
2982
INT i;
2983
2984
lvItem.mask = LVIF_TEXT;
2985
lvItem.iSubItem = 0;
2986
2987
for (i = 0; i < infoPtr->nItemCount; i++)
2988
{
2989
lvItem.iItem = i;
2990
lvItem.pszText = szDispText;
2991
lvItem.cchTextMax = DISP_TEXT_SIZE;
2992
if (LISTVIEW_GetItemW(infoPtr, &lvItem))
2993
nItemWidth = max(LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE),
2994
nItemWidth);
2995
}
2996
2997
if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2998
if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2999
3000
nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
3001
}
3002
3003
return nItemWidth;
3004
}
3005
3006
/***
3007
* DESCRIPTION:
3008
* Calculates the desired item height.
3009
*
3010
* PARAMETER(S):
3011
* [I] infoPtr : valid pointer to the listview structure
3012
*
3013
* RETURN:
3014
* The desired item height.
3015
*/
3016
static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
3017
{
3018
INT nItemHeight;
3019
3020
TRACE("view %ld\n", infoPtr->uView);
3021
3022
if (infoPtr->uView == LV_VIEW_ICON)
3023
nItemHeight = infoPtr->iconSpacing.cy;
3024
else
3025
{
3026
nItemHeight = infoPtr->ntmHeight;
3027
if (infoPtr->himlState)
3028
nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
3029
if (infoPtr->himlSmall)
3030
nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
3031
nItemHeight += HEIGHT_PADDING;
3032
if (infoPtr->nMeasureItemHeight > 0)
3033
nItemHeight = infoPtr->nMeasureItemHeight;
3034
}
3035
3036
return max(nItemHeight, 1);
3037
}
3038
3039
/***
3040
* DESCRIPTION:
3041
* Updates the width, and height of an item.
3042
*
3043
* PARAMETER(S):
3044
* [I] infoPtr : valid pointer to the listview structure
3045
*
3046
* RETURN:
3047
* None.
3048
*/
3049
static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
3050
{
3051
infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
3052
infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
3053
}
3054
3055
3056
/***
3057
* DESCRIPTION:
3058
* Retrieves and saves important text metrics info for the current
3059
* Listview font.
3060
*
3061
* PARAMETER(S):
3062
* [I] infoPtr : valid pointer to the listview structure
3063
*
3064
*/
3065
static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
3066
{
3067
HDC hdc = GetDC(infoPtr->hwndSelf);
3068
HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
3069
HFONT hOldFont = SelectObject(hdc, hFont);
3070
TEXTMETRICW tm;
3071
SIZE sz;
3072
3073
if (GetTextMetricsW(hdc, &tm))
3074
{
3075
infoPtr->ntmHeight = tm.tmHeight;
3076
infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
3077
}
3078
3079
if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
3080
infoPtr->nEllipsisWidth = sz.cx;
3081
3082
SelectObject(hdc, hOldFont);
3083
ReleaseDC(infoPtr->hwndSelf, hdc);
3084
3085
TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
3086
}
3087
3088
/***
3089
* DESCRIPTION:
3090
* A compare function for ranges
3091
*
3092
* PARAMETER(S)
3093
* [I] range1 : pointer to range 1;
3094
* [I] range2 : pointer to range 2;
3095
* [I] flags : flags
3096
*
3097
* RETURNS:
3098
* > 0 : if range 1 > range 2
3099
* < 0 : if range 2 > range 1
3100
* = 0 : if range intersects range 2
3101
*/
3102
static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
3103
{
3104
INT cmp;
3105
3106
if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
3107
cmp = -1;
3108
else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
3109
cmp = 1;
3110
else
3111
cmp = 0;
3112
3113
TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
3114
3115
return cmp;
3116
}
3117
3118
#define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__)
3119
3120
static void ranges_assert(RANGES ranges, LPCSTR desc, const char *file, int line)
3121
{
3122
INT i;
3123
RANGE *prev, *curr;
3124
3125
TRACE("*** Checking %s:%d:%s ***\n", file, line, desc);
3126
assert (ranges);
3127
assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
3128
ranges_dump(ranges);
3129
if (DPA_GetPtrCount(ranges->hdpa) > 0)
3130
{
3131
prev = DPA_GetPtr(ranges->hdpa, 0);
3132
assert (prev->lower >= 0 && prev->lower < prev->upper);
3133
for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
3134
{
3135
curr = DPA_GetPtr(ranges->hdpa, i);
3136
assert (prev->upper <= curr->lower);
3137
assert (curr->lower < curr->upper);
3138
prev = curr;
3139
}
3140
}
3141
TRACE("--- Done checking---\n");
3142
}
3143
3144
static RANGES ranges_create(int count)
3145
{
3146
RANGES ranges = Alloc(sizeof(*ranges));
3147
if (!ranges) return NULL;
3148
ranges->hdpa = DPA_Create(count);
3149
if (ranges->hdpa) return ranges;
3150
Free(ranges);
3151
return NULL;
3152
}
3153
3154
static void ranges_clear(RANGES ranges)
3155
{
3156
INT i;
3157
3158
for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3159
Free(DPA_GetPtr(ranges->hdpa, i));
3160
DPA_DeleteAllPtrs(ranges->hdpa);
3161
}
3162
3163
3164
static void ranges_destroy(RANGES ranges)
3165
{
3166
if (!ranges) return;
3167
ranges_clear(ranges);
3168
DPA_Destroy(ranges->hdpa);
3169
Free(ranges);
3170
}
3171
3172
static RANGES ranges_clone(RANGES ranges)
3173
{
3174
RANGES clone;
3175
INT i;
3176
3177
if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
3178
3179
for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3180
{
3181
RANGE *newrng = Alloc(sizeof(*newrng));
3182
if (!newrng) goto fail;
3183
*newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
3184
if (!DPA_SetPtr(clone->hdpa, i, newrng))
3185
{
3186
Free(newrng);
3187
goto fail;
3188
}
3189
}
3190
return clone;
3191
3192
fail:
3193
TRACE ("clone failed\n");
3194
ranges_destroy(clone);
3195
return NULL;
3196
}
3197
3198
static RANGES ranges_diff(RANGES ranges, RANGES sub)
3199
{
3200
INT i;
3201
3202
for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
3203
ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
3204
3205
return ranges;
3206
}
3207
3208
static void ranges_dump(RANGES ranges)
3209
{
3210
INT i;
3211
3212
for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3213
TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
3214
}
3215
3216
static inline BOOL ranges_contain(RANGES ranges, INT nItem)
3217
{
3218
RANGE srchrng = { nItem, nItem + 1 };
3219
3220
TRACE("(nItem=%d)\n", nItem);
3221
ranges_check(ranges, "before contain");
3222
return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
3223
}
3224
3225
static INT ranges_itemcount(RANGES ranges)
3226
{
3227
INT i, count = 0;
3228
3229
for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3230
{
3231
RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
3232
count += sel->upper - sel->lower;
3233
}
3234
3235
return count;
3236
}
3237
3238
static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
3239
{
3240
RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
3241
INT index;
3242
3243
index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3244
if (index == -1) return TRUE;
3245
3246
for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
3247
{
3248
chkrng = DPA_GetPtr(ranges->hdpa, index);
3249
if (chkrng->lower >= nItem)
3250
chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
3251
if (chkrng->upper > nItem)
3252
chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
3253
}
3254
return TRUE;
3255
}
3256
3257
static BOOL ranges_add(RANGES ranges, RANGE range)
3258
{
3259
RANGE srchrgn;
3260
INT index;
3261
3262
TRACE("(%s)\n", debugrange(&range));
3263
ranges_check(ranges, "before add");
3264
3265
/* try find overlapping regions first */
3266
srchrgn.lower = range.lower - 1;
3267
srchrgn.upper = range.upper + 1;
3268
index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
3269
3270
if (index == -1)
3271
{
3272
RANGE *newrgn;
3273
3274
TRACE("Adding new range\n");
3275
3276
/* create the brand new range to insert */
3277
newrgn = Alloc(sizeof(*newrgn));
3278
if(!newrgn) goto fail;
3279
*newrgn = range;
3280
3281
/* figure out where to insert it */
3282
index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3283
TRACE("index=%d\n", index);
3284
if (index == -1) index = 0;
3285
3286
/* and get it over with */
3287
if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3288
{
3289
Free(newrgn);
3290
goto fail;
3291
}
3292
}
3293
else
3294
{
3295
RANGE *chkrgn, *mrgrgn;
3296
INT fromindex, mergeindex;
3297
3298
chkrgn = DPA_GetPtr(ranges->hdpa, index);
3299
TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3300
3301
chkrgn->lower = min(range.lower, chkrgn->lower);
3302
chkrgn->upper = max(range.upper, chkrgn->upper);
3303
3304
TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3305
3306
/* merge now common ranges */
3307
fromindex = 0;
3308
srchrgn.lower = chkrgn->lower - 1;
3309
srchrgn.upper = chkrgn->upper + 1;
3310
3311
do
3312
{
3313
mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3314
if (mergeindex == -1) break;
3315
if (mergeindex == index)
3316
{
3317
fromindex = index + 1;
3318
continue;
3319
}
3320
3321
TRACE("Merge with index %i\n", mergeindex);
3322
3323
mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3324
chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3325
chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3326
Free(mrgrgn);
3327
DPA_DeletePtr(ranges->hdpa, mergeindex);
3328
if (mergeindex < index) index --;
3329
} while(1);
3330
}
3331
3332
ranges_check(ranges, "after add");
3333
return TRUE;
3334
3335
fail:
3336
ranges_check(ranges, "failed add");
3337
return FALSE;
3338
}
3339
3340
static BOOL ranges_del(RANGES ranges, RANGE range)
3341
{
3342
RANGE *chkrgn;
3343
INT index;
3344
3345
TRACE("(%s)\n", debugrange(&range));
3346
ranges_check(ranges, "before del");
3347
3348
/* we don't use DPAS_SORTED here, since we need *
3349
* to find the first overlapping range */
3350
index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3351
while(index != -1)
3352
{
3353
chkrgn = DPA_GetPtr(ranges->hdpa, index);
3354
3355
TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
3356
3357
/* case 1: Same range */
3358
if ( (chkrgn->upper == range.upper) &&
3359
(chkrgn->lower == range.lower) )
3360
{
3361
DPA_DeletePtr(ranges->hdpa, index);
3362
Free(chkrgn);
3363
break;
3364
}
3365
/* case 2: engulf */
3366
else if ( (chkrgn->upper <= range.upper) &&
3367
(chkrgn->lower >= range.lower) )
3368
{
3369
DPA_DeletePtr(ranges->hdpa, index);
3370
Free(chkrgn);
3371
}
3372
/* case 3: overlap upper */
3373
else if ( (chkrgn->upper <= range.upper) &&
3374
(chkrgn->lower < range.lower) )
3375
{
3376
chkrgn->upper = range.lower;
3377
}
3378
/* case 4: overlap lower */
3379
else if ( (chkrgn->upper > range.upper) &&
3380
(chkrgn->lower >= range.lower) )
3381
{
3382
chkrgn->lower = range.upper;
3383
break;
3384
}
3385
/* case 5: fully internal */
3386
else
3387
{
3388
RANGE *newrgn;
3389
3390
if (!(newrgn = Alloc(sizeof(*newrgn)))) goto fail;
3391
newrgn->lower = chkrgn->lower;
3392
newrgn->upper = range.lower;
3393
chkrgn->lower = range.upper;
3394
if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3395
{
3396
Free(newrgn);
3397
goto fail;
3398
}
3399
break;
3400
}
3401
3402
index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3403
}
3404
3405
ranges_check(ranges, "after del");
3406
return TRUE;
3407
3408
fail:
3409
ranges_check(ranges, "failed del");
3410
return FALSE;
3411
}
3412
3413
/***
3414
* DESCRIPTION:
3415
* Removes all selection ranges
3416
*
3417
* Parameters(s):
3418
* [I] infoPtr : valid pointer to the listview structure
3419
* [I] toSkip : item range to skip removing the selection
3420
*
3421
* RETURNS:
3422
* SUCCESS : TRUE
3423
* FAILURE : FALSE
3424
*/
3425
static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3426
{
3427
LVITEMW lvItem;
3428
ITERATOR i;
3429
RANGES clone;
3430
3431
TRACE("()\n");
3432
3433
lvItem.state = 0;
3434
lvItem.stateMask = LVIS_SELECTED;
3435
3436
/* Only send one deselect all (-1) notification for LVS_OWNERDATA style */
3437
if (infoPtr->dwStyle & LVS_OWNERDATA)
3438
{
3439
LISTVIEW_SetItemState(infoPtr, -1, &lvItem);
3440
return TRUE;
3441
}
3442
3443
/* need to clone the DPA because callbacks can change it */
3444
if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3445
iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3446
while(iterator_next(&i))
3447
LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3448
/* note that the iterator destructor will free the cloned range */
3449
iterator_destroy(&i);
3450
3451
return TRUE;
3452
}
3453
3454
static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3455
{
3456
RANGES toSkip;
3457
3458
if (!(toSkip = ranges_create(1))) return FALSE;
3459
if (nItem != -1) ranges_additem(toSkip, nItem);
3460
LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3461
ranges_destroy(toSkip);
3462
return TRUE;
3463
}
3464
3465
static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3466
{
3467
return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3468
}
3469
3470
/***
3471
* DESCRIPTION:
3472
* Retrieves the number of items that are marked as selected.
3473
*
3474
* PARAMETER(S):
3475
* [I] infoPtr : valid pointer to the listview structure
3476
*
3477
* RETURN:
3478
* Number of items selected.
3479
*/
3480
static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3481
{
3482
INT nSelectedCount = 0;
3483
3484
if (infoPtr->uCallbackMask & LVIS_SELECTED)
3485
{
3486
INT i;
3487
for (i = 0; i < infoPtr->nItemCount; i++)
3488
{
3489
if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3490
nSelectedCount++;
3491
}
3492
}
3493
else
3494
nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3495
3496
TRACE("nSelectedCount=%d\n", nSelectedCount);
3497
return nSelectedCount;
3498
}
3499
3500
/***
3501
* DESCRIPTION:
3502
* Manages the item focus.
3503
*
3504
* PARAMETER(S):
3505
* [I] infoPtr : valid pointer to the listview structure
3506
* [I] nItem : item index
3507
*
3508
* RETURN:
3509
* TRUE : focused item changed
3510
* FALSE : focused item has NOT changed
3511
*/
3512
static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3513
{
3514
INT oldFocus = infoPtr->nFocusedItem;
3515
LVITEMW lvItem;
3516
3517
if (nItem == infoPtr->nFocusedItem) return FALSE;
3518
3519
lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3520
lvItem.stateMask = LVIS_FOCUSED;
3521
LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3522
3523
return oldFocus != infoPtr->nFocusedItem;
3524
}
3525
3526
static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3527
{
3528
if (nShiftItem < nItem) return nShiftItem;
3529
3530
if (nShiftItem > nItem) return nShiftItem + direction;
3531
3532
if (direction > 0) return nShiftItem + direction;
3533
3534
return min(nShiftItem, infoPtr->nItemCount - 1);
3535
}
3536
3537
/* This function updates focus index.
3538
3539
Parameters:
3540
focus : current focus index
3541
item : index of item to be added/removed
3542
direction : add/remove flag
3543
*/
3544
static void LISTVIEW_ShiftFocus(LISTVIEW_INFO *infoPtr, INT focus, INT item, INT direction)
3545
{
3546
DWORD old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
3547
3548
infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
3549
focus = shift_item(infoPtr, focus, item, direction);
3550
if (focus != infoPtr->nFocusedItem)
3551
LISTVIEW_SetItemFocus(infoPtr, focus);
3552
infoPtr->notify_mask |= old_mask;
3553
}
3554
3555
/**
3556
* DESCRIPTION:
3557
* Updates the various indices after an item has been inserted or deleted.
3558
*
3559
* PARAMETER(S):
3560
* [I] infoPtr : valid pointer to the listview structure
3561
* [I] nItem : item index
3562
* [I] direction : Direction of shift, +1 or -1.
3563
*
3564
* RETURN:
3565
* None
3566
*/
3567
static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3568
{
3569
TRACE("Shifting %i, %i steps\n", nItem, direction);
3570
3571
ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3572
assert(abs(direction) == 1);
3573
infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3574
3575
/* But we are not supposed to modify nHotItem! */
3576
}
3577
3578
/**
3579
* DESCRIPTION:
3580
* Adds a block of selections.
3581
*
3582
* PARAMETER(S):
3583
* [I] infoPtr : valid pointer to the listview structure
3584
* [I] nItem : item index
3585
*
3586
* RETURN:
3587
* Whether the window is still valid.
3588
*/
3589
static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3590
{
3591
INT nFirst = min(infoPtr->nSelectionMark, nItem);
3592
INT nLast = max(infoPtr->nSelectionMark, nItem);
3593
HWND hwndSelf = infoPtr->hwndSelf;
3594
DWORD old_mask;
3595
LVITEMW item;
3596
INT i;
3597
3598
/* Temporarily disable change notification
3599
* If the control is LVS_OWNERDATA, we need to send
3600
* only one LVN_ODSTATECHANGED notification.
3601
* See MSDN documentation for LVN_ITEMCHANGED.
3602
*/
3603
old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
3604
if (infoPtr->dwStyle & LVS_OWNERDATA)
3605
infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
3606
3607
if (nFirst == -1) nFirst = nItem;
3608
3609
item.state = LVIS_SELECTED;
3610
item.stateMask = LVIS_SELECTED;
3611
3612
for (i = nFirst; i <= nLast; i++)
3613
LISTVIEW_SetItemState(infoPtr,i,&item);
3614
3615
if (infoPtr->dwStyle & LVS_OWNERDATA)
3616
LISTVIEW_SetOwnerDataState(infoPtr, nFirst, nLast, &item);
3617
3618
if (!IsWindow(hwndSelf))
3619
return FALSE;
3620
infoPtr->notify_mask |= old_mask;
3621
return TRUE;
3622
}
3623
3624
3625
/***
3626
* DESCRIPTION:
3627
* Sets a single group selection.
3628
*
3629
* PARAMETER(S):
3630
* [I] infoPtr : valid pointer to the listview structure
3631
* [I] nItem : item index
3632
*
3633
* RETURN:
3634
* None
3635
*/
3636
static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3637
{
3638
INT nFirst = -1, nLast = -1;
3639
RANGES selection;
3640
DWORD old_mask;
3641
LVITEMW item;
3642
ITERATOR i;
3643
3644
if (!(selection = ranges_create(100))) return;
3645
3646
item.state = LVIS_SELECTED;
3647
item.stateMask = LVIS_SELECTED;
3648
3649
if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3650
{
3651
if (infoPtr->nSelectionMark == -1)
3652
{
3653
infoPtr->nSelectionMark = nItem;
3654
ranges_additem(selection, nItem);
3655
}
3656
else
3657
{
3658
RANGE sel;
3659
3660
sel.lower = min(infoPtr->nSelectionMark, nItem);
3661
sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3662
ranges_add(selection, sel);
3663
}
3664
}
3665
else
3666
{
3667
RECT rcItem, rcSel, rcSelMark;
3668
POINT ptItem;
3669
3670
rcItem.left = LVIR_BOUNDS;
3671
if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) {
3672
ranges_destroy (selection);
3673
return;
3674
}
3675
rcSelMark.left = LVIR_BOUNDS;
3676
if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) {
3677
ranges_destroy (selection);
3678
return;
3679
}
3680
UnionRect(&rcSel, &rcItem, &rcSelMark);
3681
iterator_frameditems(&i, infoPtr, &rcSel);
3682
while(iterator_next(&i))
3683
{
3684
LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3685
if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3686
}
3687
iterator_destroy(&i);
3688
}
3689
3690
/* Disable per item notifications on LVS_OWNERDATA style */
3691
old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
3692
if (infoPtr->dwStyle & LVS_OWNERDATA)
3693
infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
3694
3695
LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3696
3697
iterator_rangesitems(&i, selection);
3698
while(iterator_next(&i))
3699
{
3700
/* Find the range for LVN_ODSTATECHANGED */
3701
if (nFirst == -1)
3702
nFirst = i.nItem;
3703
nLast = i.nItem;
3704
LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3705
}
3706
/* this will also destroy the selection */
3707
iterator_destroy(&i);
3708
3709
if (infoPtr->dwStyle & LVS_OWNERDATA)
3710
LISTVIEW_SetOwnerDataState(infoPtr, nFirst, nLast, &item);
3711
3712
infoPtr->notify_mask |= old_mask;
3713
LISTVIEW_SetItemFocus(infoPtr, nItem);
3714
}
3715
3716
/***
3717
* DESCRIPTION:
3718
* Sets a single selection.
3719
*
3720
* PARAMETER(S):
3721
* [I] infoPtr : valid pointer to the listview structure
3722
* [I] nItem : item index
3723
*
3724
* RETURN:
3725
* None
3726
*/
3727
static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3728
{
3729
LVITEMW lvItem;
3730
3731
TRACE("nItem=%d\n", nItem);
3732
3733
LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3734
3735
lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3736
lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3737
LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3738
3739
infoPtr->nSelectionMark = nItem;
3740
}
3741
3742
/* Change item selection with key input. */
3743
static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, DWORD keys)
3744
{
3745
WORD wShift = !!(keys & SHIFT_KEY);
3746
WORD wCtrl = !!(keys & CTRL_KEY);
3747
BOOL bResult = FALSE;
3748
3749
TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3750
if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3751
{
3752
bResult = TRUE;
3753
3754
if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3755
LISTVIEW_SetSelection(infoPtr, nItem);
3756
else
3757
{
3758
if (wShift)
3759
LISTVIEW_SetGroupSelection(infoPtr, nItem);
3760
else if (wCtrl)
3761
{
3762
LVITEMW lvItem;
3763
lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3764
lvItem.stateMask = LVIS_SELECTED;
3765
if (keys & SPACE_KEY)
3766
{
3767
LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3768
if (lvItem.state & LVIS_SELECTED)
3769
infoPtr->nSelectionMark = nItem;
3770
}
3771
bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3772
}
3773
}
3774
LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3775
}
3776
3777
UpdateWindow(infoPtr->hwndSelf); /* update client area */
3778
return bResult;
3779
}
3780
3781
static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3782
{
3783
LVHITTESTINFO lvHitTestInfo;
3784
3785
ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3786
lvHitTestInfo.pt.x = pt.x;
3787
lvHitTestInfo.pt.y = pt.y;
3788
3789
LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3790
3791
lpLVItem->mask = LVIF_PARAM;
3792
lpLVItem->iItem = lvHitTestInfo.iItem;
3793
lpLVItem->iSubItem = 0;
3794
3795
return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3796
}
3797
3798
static inline BOOL LISTVIEW_IsHotTracking(const LISTVIEW_INFO *infoPtr)
3799
{
3800
return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3801
(infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3802
(infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3803
}
3804
3805
/***
3806
* DESCRIPTION:
3807
* Called when the mouse is being actively tracked and has hovered for a specified
3808
* amount of time
3809
*
3810
* PARAMETER(S):
3811
* [I] infoPtr : valid pointer to the listview structure
3812
* [I] fwKeys : key indicator
3813
* [I] x,y : mouse position
3814
*
3815
* RETURN:
3816
* 0 if the message was processed, non-zero if there was an error
3817
*
3818
* INFO:
3819
* LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3820
* over the item for a certain period of time.
3821
*
3822
*/
3823
static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, INT x, INT y)
3824
{
3825
NMHDR hdr;
3826
3827
if (notify_hdr(infoPtr, NM_HOVER, &hdr)) return 0;
3828
3829
if (LISTVIEW_IsHotTracking(infoPtr))
3830
{
3831
LVITEMW item;
3832
POINT pt;
3833
3834
pt.x = x;
3835
pt.y = y;
3836
3837
if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3838
LISTVIEW_SetSelection(infoPtr, item.iItem);
3839
3840
SetFocus(infoPtr->hwndSelf);
3841
}
3842
3843
return 0;
3844
}
3845
3846
#define SCROLL_LEFT 0x1
3847
#define SCROLL_RIGHT 0x2
3848
#define SCROLL_UP 0x4
3849
#define SCROLL_DOWN 0x8
3850
3851
/***
3852
* DESCRIPTION:
3853
* Utility routine to draw and highlight items within a marquee selection rectangle.
3854
*
3855
* PARAMETER(S):
3856
* [I] infoPtr : valid pointer to the listview structure
3857
* [I] coords_orig : original co-ordinates of the cursor
3858
* [I] coords_offs : offsetted coordinates of the cursor
3859
* [I] offset : offset amount
3860
* [I] scroll : Bitmask of which directions we should scroll, if at all
3861
*
3862
* RETURN:
3863
* None.
3864
*/
3865
static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, const POINT *coords_orig,
3866
INT scroll)
3867
{
3868
BOOL controlDown = FALSE;
3869
LVITEMW item;
3870
ITERATOR old_elems, new_elems;
3871
RECT rect;
3872
POINT coords_offs, offset;
3873
3874
/* Ensure coordinates are within client bounds */
3875
coords_offs.x = max(min(coords_orig->x, infoPtr->rcList.right), 0);
3876
coords_offs.y = max(min(coords_orig->y, infoPtr->rcList.bottom), 0);
3877
3878
/* Get offset */
3879
LISTVIEW_GetOrigin(infoPtr, &offset);
3880
3881
/* Offset coordinates by the appropriate amount */
3882
coords_offs.x -= offset.x;
3883
coords_offs.y -= offset.y;
3884
3885
if (coords_offs.x > infoPtr->marqueeOrigin.x)
3886
{
3887
rect.left = infoPtr->marqueeOrigin.x;
3888
rect.right = coords_offs.x;
3889
}
3890
else
3891
{
3892
rect.left = coords_offs.x;
3893
rect.right = infoPtr->marqueeOrigin.x;
3894
}
3895
3896
if (coords_offs.y > infoPtr->marqueeOrigin.y)
3897
{
3898
rect.top = infoPtr->marqueeOrigin.y;
3899
rect.bottom = coords_offs.y;
3900
}
3901
else
3902
{
3903
rect.top = coords_offs.y;
3904
rect.bottom = infoPtr->marqueeOrigin.y;
3905
}
3906
3907
/* Cancel out the old marquee rectangle and draw the new one */
3908
LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3909
3910
/* Scroll by the appropriate distance if applicable - speed up scrolling as
3911
the cursor is further away */
3912
3913
if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0))
3914
LISTVIEW_Scroll(infoPtr, coords_orig->x, 0);
3915
3916
if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right))
3917
LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0);
3918
3919
if ((scroll & SCROLL_UP) && (coords_orig->y <= 0))
3920
LISTVIEW_Scroll(infoPtr, 0, coords_orig->y);
3921
3922
if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom))
3923
LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom));
3924
3925
iterator_frameditems_absolute(&old_elems, infoPtr, &infoPtr->marqueeRect);
3926
3927
infoPtr->marqueeRect = rect;
3928
infoPtr->marqueeDrawRect = rect;
3929
OffsetRect(&infoPtr->marqueeDrawRect, offset.x, offset.y);
3930
3931
iterator_frameditems_absolute(&new_elems, infoPtr, &infoPtr->marqueeRect);
3932
iterator_remove_common_items(&old_elems, &new_elems);
3933
3934
/* Iterate over no longer selected items */
3935
while (iterator_next(&old_elems))
3936
{
3937
if (old_elems.nItem > -1)
3938
{
3939
if (LISTVIEW_GetItemState(infoPtr, old_elems.nItem, LVIS_SELECTED) == LVIS_SELECTED)
3940
item.state = 0;
3941
else
3942
item.state = LVIS_SELECTED;
3943
3944
item.stateMask = LVIS_SELECTED;
3945
3946
LISTVIEW_SetItemState(infoPtr, old_elems.nItem, &item);
3947
}
3948
}
3949
iterator_destroy(&old_elems);
3950
3951
3952
/* Iterate over newly selected items */
3953
if (GetKeyState(VK_CONTROL) & 0x8000)
3954
controlDown = TRUE;
3955
3956
while (iterator_next(&new_elems))
3957
{
3958
if (new_elems.nItem > -1)
3959
{
3960
/* If CTRL is pressed, invert. If not, always select the item. */
3961
if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, new_elems.nItem, LVIS_SELECTED)))
3962
item.state = 0;
3963
else
3964
item.state = LVIS_SELECTED;
3965
3966
item.stateMask = LVIS_SELECTED;
3967
3968
LISTVIEW_SetItemState(infoPtr, new_elems.nItem, &item);
3969
}
3970
}
3971
iterator_destroy(&new_elems);
3972
3973
LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3974
}
3975
3976
/***
3977
* DESCRIPTION:
3978
* Called when we are in a marquee selection that involves scrolling the listview (ie,
3979
* the cursor is outside the bounds of the client area). This is a TIMERPROC.
3980
*
3981
* PARAMETER(S):
3982
* [I] hwnd : Handle to the listview
3983
* [I] uMsg : WM_TIMER (ignored)
3984
* [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3985
* [I] dwTimer : The elapsed time (ignored)
3986
*
3987
* RETURN:
3988
* None.
3989
*/
3990
static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
3991
{
3992
LISTVIEW_INFO *infoPtr;
3993
SCROLLINFO scrollInfo;
3994
POINT coords;
3995
INT scroll = 0;
3996
3997
infoPtr = (LISTVIEW_INFO *) idEvent;
3998
3999
if (!infoPtr)
4000
return;
4001
4002
/* Get the current cursor position and convert to client coordinates */
4003
GetCursorPos(&coords);
4004
ScreenToClient(hWnd, &coords);
4005
4006
scrollInfo.cbSize = sizeof(SCROLLINFO);
4007
scrollInfo.fMask = SIF_ALL;
4008
4009
/* Work out in which directions we can scroll */
4010
if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4011
{
4012
if (scrollInfo.nPos != scrollInfo.nMin)
4013
scroll |= SCROLL_UP;
4014
4015
if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
4016
scroll |= SCROLL_DOWN;
4017
}
4018
4019
if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4020
{
4021
if (scrollInfo.nPos != scrollInfo.nMin)
4022
scroll |= SCROLL_LEFT;
4023
4024
if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
4025
scroll |= SCROLL_RIGHT;
4026
}
4027
4028
if (((coords.x <= 0) && (scroll & SCROLL_LEFT)) ||
4029
((coords.y <= 0) && (scroll & SCROLL_UP)) ||
4030
((coords.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) ||
4031
((coords.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN)))
4032
{
4033
LISTVIEW_MarqueeHighlight(infoPtr, &coords, scroll);
4034
}
4035
}
4036
4037
/***
4038
* DESCRIPTION:
4039
* Called whenever WM_MOUSEMOVE is received.
4040
*
4041
* PARAMETER(S):
4042
* [I] infoPtr : valid pointer to the listview structure
4043
* [I] fwKeys : key indicator
4044
* [I] x,y : mouse position
4045
*
4046
* RETURN:
4047
* 0 if the message is processed, non-zero if there was an error
4048
*/
4049
static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
4050
{
4051
LVHITTESTINFO ht;
4052
RECT rect;
4053
POINT pt;
4054
4055
pt.x = x;
4056
pt.y = y;
4057
4058
if (!(fwKeys & MK_LBUTTON))
4059
infoPtr->bLButtonDown = FALSE;
4060
4061
if (infoPtr->bLButtonDown)
4062
{
4063
rect.left = rect.right = infoPtr->ptClickPos.x;
4064
rect.top = rect.bottom = infoPtr->ptClickPos.y;
4065
4066
InflateRect(&rect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
4067
4068
if (infoPtr->bMarqueeSelect)
4069
{
4070
/* Enable the timer if we're going outside our bounds, in case the user doesn't
4071
move the mouse again */
4072
4073
if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) ||
4074
(y >= infoPtr->rcList.bottom))
4075
{
4076
if (!infoPtr->bScrolling)
4077
{
4078
infoPtr->bScrolling = TRUE;
4079
SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer);
4080
}
4081
}
4082
else
4083
{
4084
infoPtr->bScrolling = FALSE;
4085
KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
4086
}
4087
4088
LISTVIEW_MarqueeHighlight(infoPtr, &pt, 0);
4089
return 0;
4090
}
4091
4092
ht.pt = pt;
4093
LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
4094
4095
/* reset item marker */
4096
if (infoPtr->nLButtonDownItem != ht.iItem)
4097
infoPtr->nLButtonDownItem = -1;
4098
4099
if (!PtInRect(&rect, pt))
4100
{
4101
/* this path covers the following:
4102
1. WM_LBUTTONDOWN over selected item (sets focus on it)
4103
2. change focus with keys
4104
3. move mouse over item from step 1 selects it and moves focus on it */
4105
if (infoPtr->nLButtonDownItem != -1 &&
4106
!LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
4107
{
4108
LVITEMW lvItem;
4109
4110
lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
4111
lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
4112
4113
LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
4114
infoPtr->nLButtonDownItem = -1;
4115
}
4116
4117
if (!infoPtr->bDragging)
4118
{
4119
ht.pt = infoPtr->ptClickPos;
4120
LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
4121
4122
/* If the click is outside the range of an item, begin a
4123
highlight. If not, begin an item drag. */
4124
if (ht.iItem == -1)
4125
{
4126
NMHDR hdr;
4127
4128
/* If we're allowing multiple selections, send notification.
4129
If return value is non-zero, cancel. */
4130
if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0))
4131
{
4132
/* Store the absolute coordinates of the click */
4133
POINT offset;
4134
LISTVIEW_GetOrigin(infoPtr, &offset);
4135
4136
infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x;
4137
infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y;
4138
4139
/* Begin selection and capture mouse */
4140
infoPtr->bMarqueeSelect = TRUE;
4141
infoPtr->marqueeRect = rect;
4142
SetCapture(infoPtr->hwndSelf);
4143
}
4144
}
4145
else
4146
{
4147
NMLISTVIEW nmlv;
4148
4149
ZeroMemory(&nmlv, sizeof(nmlv));
4150
nmlv.iItem = ht.iItem;
4151
nmlv.ptAction = infoPtr->ptClickPos;
4152
4153
notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
4154
infoPtr->bDragging = TRUE;
4155
}
4156
}
4157
4158
return 0;
4159
}
4160
}
4161
4162
/* see if we are supposed to be tracking mouse hovering */
4163
if (LISTVIEW_IsHotTracking(infoPtr)) {
4164
TRACKMOUSEEVENT trackinfo;
4165
NMLISTVIEW nmlv = { 0 };
4166
DWORD flags;
4167
4168
trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
4169
trackinfo.dwFlags = TME_QUERY;
4170
4171
/* see if we are already tracking this hwnd */
4172
_TrackMouseEvent(&trackinfo);
4173
4174
flags = TME_LEAVE;
4175
if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
4176
flags |= TME_HOVER;
4177
4178
if((trackinfo.dwFlags & flags) != flags || trackinfo.hwndTrack != infoPtr->hwndSelf) {
4179
trackinfo.dwFlags = flags;
4180
trackinfo.dwHoverTime = infoPtr->dwHoverTime;
4181
trackinfo.hwndTrack = infoPtr->hwndSelf;
4182
4183
/* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4184
_TrackMouseEvent(&trackinfo);
4185
}
4186
4187
ht.pt = pt;
4188
LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
4189
4190
nmlv.iItem = ht.iItem;
4191
nmlv.iSubItem = ht.iSubItem;
4192
nmlv.ptAction = pt;
4193
4194
notify_listview(infoPtr, LVN_HOTTRACK, &nmlv);
4195
}
4196
4197
return 0;
4198
}
4199
4200
4201
/***
4202
* Tests whether the item is assignable to a list with style lStyle
4203
*/
4204
static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
4205
{
4206
if ( (lpLVItem->mask & LVIF_TEXT) &&
4207
(lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
4208
(lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
4209
4210
return TRUE;
4211
}
4212
4213
4214
/***
4215
* DESCRIPTION:
4216
* Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
4217
*
4218
* PARAMETER(S):
4219
* [I] infoPtr : valid pointer to the listview structure
4220
* [I] lpLVItem : valid pointer to new item attributes
4221
* [I] isNew : the item being set is being inserted
4222
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4223
* [O] bChanged : will be set to TRUE if the item really changed
4224
*
4225
* RETURN:
4226
* SUCCESS : TRUE
4227
* FAILURE : FALSE
4228
*/
4229
static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
4230
{
4231
ITEM_INFO *lpItem;
4232
NMLISTVIEW nmlv;
4233
UINT uChanged = 0;
4234
LVITEMW item;
4235
/* stateMask is ignored for LVM_INSERTITEM */
4236
UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
4237
BOOL send_change_notification;
4238
4239
TRACE("()\n");
4240
4241
assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
4242
4243
if (lpLVItem->mask == 0) return TRUE;
4244
4245
if (infoPtr->dwStyle & LVS_OWNERDATA)
4246
{
4247
/* a virtual listview only stores selection and focus */
4248
if (lpLVItem->mask & ~LVIF_STATE)
4249
return FALSE;
4250
lpItem = NULL;
4251
}
4252
else
4253
{
4254
HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4255
lpItem = DPA_GetPtr(hdpaSubItems, 0);
4256
assert (lpItem);
4257
}
4258
4259
/* we need to get the lParam and state of the item */
4260
item.iItem = lpLVItem->iItem;
4261
item.iSubItem = lpLVItem->iSubItem;
4262
item.mask = LVIF_STATE | LVIF_PARAM;
4263
item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
4264
4265
item.state = 0;
4266
item.lParam = 0;
4267
if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
4268
4269
TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
4270
/* determine what fields will change */
4271
if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
4272
uChanged |= LVIF_STATE;
4273
4274
if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
4275
uChanged |= LVIF_IMAGE;
4276
4277
if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
4278
uChanged |= LVIF_PARAM;
4279
4280
if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
4281
uChanged |= LVIF_INDENT;
4282
4283
if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
4284
uChanged |= LVIF_TEXT;
4285
4286
TRACE("change mask=0x%x\n", uChanged);
4287
4288
memset(&nmlv, 0, sizeof(NMLISTVIEW));
4289
nmlv.iItem = lpLVItem->iItem;
4290
if (lpLVItem->mask & LVIF_STATE)
4291
{
4292
nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
4293
nmlv.uOldState = item.state;
4294
}
4295
nmlv.uChanged = isNew ? LVIF_STATE : (uChanged ? uChanged : lpLVItem->mask);
4296
nmlv.lParam = item.lParam;
4297
4298
/* Send change notification if the item is not being inserted, or inserted (selected|focused),
4299
and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
4300
are enabled. Even if nothing really changed we still need to send this,
4301
in this case uChanged mask is just set to passed item mask. */
4302
4303
send_change_notification = !isNew;
4304
send_change_notification |= (uChanged & LVIF_STATE) && (lpLVItem->state & (LVIS_FOCUSED | LVIS_SELECTED));
4305
send_change_notification &= !!(infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE);
4306
4307
if (lpItem && send_change_notification)
4308
{
4309
HWND hwndSelf = infoPtr->hwndSelf;
4310
4311
if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
4312
return FALSE;
4313
if (!IsWindow(hwndSelf))
4314
return FALSE;
4315
}
4316
4317
/* When item is inserted we need to shift existing focus index if new item has lower index. */
4318
if (isNew && (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) &&
4319
/* this means we won't hit a focus change path later */
4320
((uChanged & LVIF_STATE) == 0 || (!(lpLVItem->state & LVIS_FOCUSED) && (infoPtr->nFocusedItem != lpLVItem->iItem))))
4321
{
4322
if (infoPtr->nFocusedItem != -1 && (lpLVItem->iItem <= infoPtr->nFocusedItem))
4323
infoPtr->nFocusedItem++;
4324
}
4325
4326
if (!uChanged) return TRUE;
4327
*bChanged = TRUE;
4328
4329
/* copy information */
4330
if (lpLVItem->mask & LVIF_TEXT)
4331
textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
4332
4333
if (lpLVItem->mask & LVIF_IMAGE)
4334
lpItem->hdr.iImage = lpLVItem->iImage;
4335
4336
if (lpLVItem->mask & LVIF_PARAM)
4337
lpItem->lParam = lpLVItem->lParam;
4338
4339
if (lpLVItem->mask & LVIF_INDENT)
4340
lpItem->iIndent = lpLVItem->iIndent;
4341
4342
if (uChanged & LVIF_STATE)
4343
{
4344
if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
4345
{
4346
lpItem->state &= ~stateMask;
4347
lpItem->state |= (lpLVItem->state & stateMask);
4348
}
4349
if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
4350
{
4351
if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
4352
ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
4353
}
4354
else if (stateMask & LVIS_SELECTED)
4355
{
4356
ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
4357
}
4358
/* If we are asked to change focus, and we manage it, do it.
4359
It's important to have all new item data stored at this point,
4360
because changing existing focus could result in a redrawing operation,
4361
which in turn could ask for disp data, application should see all data
4362
for inserted item when processing LVN_GETDISPINFO.
4363
4364
The way this works application will see nested item change notifications -
4365
changed item notifications interrupted by ones from item losing focus. */
4366
if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
4367
{
4368
if (lpLVItem->state & LVIS_FOCUSED)
4369
{
4370
/* update selection mark */
4371
if (infoPtr->nFocusedItem == -1 && infoPtr->nSelectionMark == -1)
4372
infoPtr->nSelectionMark = lpLVItem->iItem;
4373
4374
if (infoPtr->nFocusedItem != -1)
4375
{
4376
/* remove current focus */
4377
item.mask = LVIF_STATE;
4378
item.state = 0;
4379
item.stateMask = LVIS_FOCUSED;
4380
4381
/* recurse with redrawing an item */
4382
LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
4383
}
4384
4385
infoPtr->nFocusedItem = lpLVItem->iItem;
4386
LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
4387
}
4388
else if (infoPtr->nFocusedItem == lpLVItem->iItem)
4389
{
4390
infoPtr->nFocusedItem = -1;
4391
}
4392
}
4393
}
4394
4395
if (send_change_notification)
4396
{
4397
if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
4398
notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
4399
}
4400
4401
return TRUE;
4402
}
4403
4404
/***
4405
* DESCRIPTION:
4406
* Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4407
*
4408
* PARAMETER(S):
4409
* [I] infoPtr : valid pointer to the listview structure
4410
* [I] lpLVItem : valid pointer to new subitem attributes
4411
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4412
* [O] bChanged : will be set to TRUE if the item really changed
4413
*
4414
* RETURN:
4415
* SUCCESS : TRUE
4416
* FAILURE : FALSE
4417
*/
4418
static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
4419
{
4420
HDPA hdpaSubItems;
4421
SUBITEM_INFO *lpSubItem;
4422
4423
/* we do not support subitems for virtual listviews */
4424
if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
4425
4426
/* set subitem only if column is present */
4427
if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4428
4429
/* First do some sanity checks */
4430
/* The LVIF_STATE flag is valid for subitems, but does not appear to be
4431
particularly useful. We currently do not actually do anything with
4432
the flag on subitems.
4433
*/
4434
if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_DI_SETITEM)) return FALSE;
4435
if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
4436
4437
/* get the subitem structure, and create it if not there */
4438
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4439
assert (hdpaSubItems);
4440
4441
lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4442
if (!lpSubItem)
4443
{
4444
SUBITEM_INFO *tmpSubItem;
4445
INT i;
4446
4447
lpSubItem = Alloc(sizeof(*lpSubItem));
4448
if (!lpSubItem) return FALSE;
4449
/* we could binary search here, if need be...*/
4450
for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4451
{
4452
tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
4453
if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
4454
}
4455
if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
4456
{
4457
Free(lpSubItem);
4458
return FALSE;
4459
}
4460
lpSubItem->iSubItem = lpLVItem->iSubItem;
4461
lpSubItem->hdr.iImage = I_IMAGECALLBACK;
4462
*bChanged = TRUE;
4463
}
4464
4465
if ((lpLVItem->mask & LVIF_IMAGE) && (lpSubItem->hdr.iImage != lpLVItem->iImage))
4466
{
4467
lpSubItem->hdr.iImage = lpLVItem->iImage;
4468
*bChanged = TRUE;
4469
}
4470
4471
if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
4472
{
4473
textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
4474
*bChanged = TRUE;
4475
}
4476
4477
return TRUE;
4478
}
4479
4480
/***
4481
* DESCRIPTION:
4482
* Sets item attributes.
4483
*
4484
* PARAMETER(S):
4485
* [I] infoPtr : valid pointer to the listview structure
4486
* [I] lpLVItem : new item attributes
4487
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4488
*
4489
* RETURN:
4490
* SUCCESS : TRUE
4491
* FAILURE : FALSE
4492
*/
4493
static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
4494
{
4495
HWND hwndSelf = infoPtr->hwndSelf;
4496
LPWSTR pszText = NULL;
4497
BOOL bResult, bChanged = FALSE;
4498
RECT oldItemArea;
4499
4500
TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4501
4502
if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4503
return FALSE;
4504
4505
/* Store old item area */
4506
LISTVIEW_GetItemBox(infoPtr, lpLVItem->iItem, &oldItemArea);
4507
4508
/* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4509
if ((lpLVItem->mask & LVIF_TEXT) && is_text(lpLVItem->pszText))
4510
{
4511
pszText = lpLVItem->pszText;
4512
lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
4513
}
4514
4515
/* actually set the fields */
4516
if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
4517
4518
if (lpLVItem->iSubItem)
4519
bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
4520
else
4521
bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
4522
if (!IsWindow(hwndSelf))
4523
return FALSE;
4524
4525
/* redraw item, if necessary */
4526
if (bChanged && !infoPtr->bIsDrawing)
4527
{
4528
/* this little optimization eliminates some nasty flicker */
4529
if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
4530
!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
4531
lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
4532
LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
4533
else
4534
{
4535
LISTVIEW_InvalidateRect(infoPtr, &oldItemArea);
4536
LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
4537
}
4538
}
4539
/* restore text */
4540
if (pszText)
4541
{
4542
textfreeT(lpLVItem->pszText, isW);
4543
lpLVItem->pszText = pszText;
4544
}
4545
4546
return bResult;
4547
}
4548
4549
/***
4550
* DESCRIPTION:
4551
* Retrieves the index of the item at coordinate (0, 0) of the client area.
4552
*
4553
* PARAMETER(S):
4554
* [I] infoPtr : valid pointer to the listview structure
4555
*
4556
* RETURN:
4557
* item index
4558
*/
4559
static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
4560
{
4561
INT nItem = 0;
4562
SCROLLINFO scrollInfo;
4563
4564
scrollInfo.cbSize = sizeof(SCROLLINFO);
4565
scrollInfo.fMask = SIF_POS;
4566
4567
if (infoPtr->uView == LV_VIEW_LIST)
4568
{
4569
if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4570
nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
4571
}
4572
else if (infoPtr->uView == LV_VIEW_DETAILS)
4573
{
4574
if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4575
nItem = scrollInfo.nPos;
4576
}
4577
else
4578
{
4579
if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4580
nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
4581
}
4582
4583
TRACE("nItem=%d\n", nItem);
4584
4585
return nItem;
4586
}
4587
4588
static void LISTVIEW_DrawBackgroundBitmap(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4589
{
4590
HDC mem_hdc;
4591
4592
if (!infoPtr->hBkBitmap)
4593
return;
4594
4595
TRACE("(hdc=%p, lprcBox=%s, hBkBitmap=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBitmap);
4596
4597
mem_hdc = CreateCompatibleDC(hdc);
4598
SelectObject(mem_hdc, infoPtr->hBkBitmap);
4599
BitBlt(hdc, lprcBox->left, lprcBox->top, lprcBox->right - lprcBox->left,
4600
lprcBox->bottom - lprcBox->top, mem_hdc, lprcBox->left, lprcBox->top, SRCCOPY);
4601
DeleteDC(mem_hdc);
4602
}
4603
4604
/***
4605
* DESCRIPTION:
4606
* Erases the background of the given rectangle
4607
*
4608
* PARAMETER(S):
4609
* [I] infoPtr : valid pointer to the listview structure
4610
* [I] hdc : device context handle
4611
* [I] lprcBox : clipping rectangle
4612
*
4613
* RETURN:
4614
* Success: TRUE
4615
* Failure: FALSE
4616
*/
4617
static BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4618
{
4619
if (infoPtr->hBkBrush)
4620
{
4621
TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
4622
4623
FillRect(hdc, lprcBox, infoPtr->hBkBrush);
4624
}
4625
4626
LISTVIEW_DrawBackgroundBitmap(infoPtr, hdc, lprcBox);
4627
return TRUE;
4628
}
4629
4630
/* Draw main item or subitem */
4631
static void LISTVIEW_DrawItemPart(LISTVIEW_INFO *infoPtr, LVITEMW *item, const NMLVCUSTOMDRAW *nmlvcd, const POINT *pos)
4632
{
4633
RECT rcSelect, rcLabel, rcBox, rcStateIcon, rcIcon;
4634
const RECT *background;
4635
HIMAGELIST himl;
4636
UINT format;
4637
RECT *focus;
4638
4639
/* now check if we need to update the focus rectangle */
4640
focus = infoPtr->bFocus && (item->state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4641
if (!focus) item->state &= ~LVIS_FOCUSED;
4642
4643
LISTVIEW_GetItemMetrics(infoPtr, item, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
4644
OffsetRect(&rcBox, pos->x, pos->y);
4645
OffsetRect(&rcSelect, pos->x, pos->y);
4646
OffsetRect(&rcIcon, pos->x, pos->y);
4647
OffsetRect(&rcStateIcon, pos->x, pos->y);
4648
OffsetRect(&rcLabel, pos->x, pos->y);
4649
TRACE("%d: box=%s, select=%s, icon=%s. label=%s\n", item->iSubItem,
4650
wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
4651
wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
4652
4653
/* FIXME: temporary hack */
4654
rcSelect.left = rcLabel.left;
4655
4656
if (infoPtr->uView == LV_VIEW_DETAILS && item->iSubItem == 0)
4657
{
4658
if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4659
OffsetRect(&rcSelect, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4660
OffsetRect(&rcIcon, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4661
OffsetRect(&rcStateIcon, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4662
OffsetRect(&rcLabel, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4663
}
4664
4665
/* in icon mode, the label rect is really what we want to draw the
4666
* background for */
4667
/* in detail mode, we want to paint background for label rect when
4668
* item is not selected or listview has full row select; otherwise paint
4669
* background for text only */
4670
if ( infoPtr->uView == LV_VIEW_ICON ||
4671
(infoPtr->uView == LV_VIEW_DETAILS && (!(item->state & LVIS_SELECTED) ||
4672
(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
4673
background = &rcLabel;
4674
else
4675
background = &rcSelect;
4676
4677
if (nmlvcd->clrTextBk != CLR_NONE)
4678
ExtTextOutW(nmlvcd->nmcd.hdc, background->left, background->top, ETO_OPAQUE, background, NULL, 0, NULL);
4679
4680
if (item->state & LVIS_FOCUSED)
4681
{
4682
if (infoPtr->uView == LV_VIEW_DETAILS)
4683
{
4684
if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4685
{
4686
/* we have to update left focus bound too if item isn't in leftmost column
4687
and reduce right box bound */
4688
if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4689
{
4690
INT leftmost;
4691
4692
if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
4693
{
4694
INT Originx = pos->x - LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left;
4695
INT rightmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4696
DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4697
4698
rcBox.right = LISTVIEW_GetColumnInfo(infoPtr, rightmost)->rcHeader.right + Originx;
4699
rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx;
4700
}
4701
}
4702
rcSelect.right = rcBox.right;
4703
}
4704
infoPtr->rcFocus = rcSelect;
4705
}
4706
else
4707
infoPtr->rcFocus = rcLabel;
4708
}
4709
4710
/* state icons */
4711
if (infoPtr->himlState && STATEIMAGEINDEX(item->state) && (item->iSubItem == 0))
4712
{
4713
UINT stateimage = STATEIMAGEINDEX(item->state);
4714
if (stateimage)
4715
{
4716
TRACE("stateimage=%d\n", stateimage);
4717
ImageList_Draw(infoPtr->himlState, stateimage-1, nmlvcd->nmcd.hdc, rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4718
}
4719
}
4720
4721
/* item icons */
4722
himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4723
if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon))
4724
{
4725
UINT style;
4726
4727
TRACE("iImage=%d\n", item->iImage);
4728
4729
if (item->state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
4730
style = ILD_SELECTED;
4731
else
4732
style = ILD_NORMAL;
4733
4734
ImageList_DrawEx(himl, item->iImage, nmlvcd->nmcd.hdc, rcIcon.left, rcIcon.top,
4735
rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk,
4736
item->state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
4737
style | (item->state & LVIS_OVERLAYMASK));
4738
}
4739
4740
/* Don't bother painting item being edited */
4741
if (infoPtr->hwndEdit && item->iItem == infoPtr->nEditLabelItem && item->iSubItem == 0) return;
4742
4743
/* figure out the text drawing flags */
4744
format = (infoPtr->uView == LV_VIEW_ICON ? (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4745
if (infoPtr->uView == LV_VIEW_ICON)
4746
format = (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4747
else if (item->iSubItem)
4748
{
4749
switch (LISTVIEW_GetColumnInfo(infoPtr, item->iSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4750
{
4751
case LVCFMT_RIGHT: format |= DT_RIGHT; break;
4752
case LVCFMT_CENTER: format |= DT_CENTER; break;
4753
default: format |= DT_LEFT;
4754
}
4755
}
4756
if (!(format & (DT_RIGHT | DT_CENTER)))
4757
{
4758
if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4759
else rcLabel.left += LABEL_HOR_PADDING;
4760
}
4761
else if (format & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4762
4763
/* for GRIDLINES reduce the bottom so the text formats correctly */
4764
if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4765
rcLabel.bottom--;
4766
4767
DrawTextW(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format);
4768
}
4769
4770
/***
4771
* DESCRIPTION:
4772
* Draws an item.
4773
*
4774
* PARAMETER(S):
4775
* [I] infoPtr : valid pointer to the listview structure
4776
* [I] hdc : device context handle
4777
* [I] nItem : item index
4778
* [I] nSubItem : subitem index
4779
* [I] pos : item position in client coordinates
4780
* [I] cdmode : custom draw mode
4781
*
4782
* RETURN:
4783
* Success: TRUE
4784
* Failure: FALSE
4785
*/
4786
static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERATOR *subitems, POINT pos, DWORD cdmode)
4787
{
4788
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4789
static WCHAR callbackW[] = L"(callback)";
4790
DWORD cdsubitemmode = CDRF_DODEFAULT;
4791
RECT *focus, rcBox;
4792
NMLVCUSTOMDRAW nmlvcd;
4793
LVITEMW lvItem;
4794
4795
TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc, nItem, subitems, wine_dbgstr_point(&pos));
4796
4797
/* get information needed for drawing the item */
4798
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
4799
if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
4800
lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
4801
lvItem.iItem = nItem;
4802
lvItem.iSubItem = 0;
4803
lvItem.state = 0;
4804
lvItem.lParam = 0;
4805
lvItem.cchTextMax = DISP_TEXT_SIZE;
4806
lvItem.pszText = szDispText;
4807
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4808
if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
4809
TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4810
4811
/* now check if we need to update the focus rectangle */
4812
focus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4813
if (!focus) lvItem.state &= ~LVIS_FOCUSED;
4814
4815
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, NULL, NULL, NULL);
4816
OffsetRect(&rcBox, pos.x, pos.y);
4817
4818
/* Full custom draw stage sequence looks like this:
4819
4820
LV_VIEW_DETAILS:
4821
4822
- CDDS_ITEMPREPAINT
4823
- CDDS_ITEMPREPAINT|CDDS_SUBITEM | => sent n times, where n is number of subitems,
4824
CDDS_ITEMPOSTPAINT|CDDS_SUBITEM | including item itself
4825
- CDDS_ITEMPOSTPAINT
4826
4827
other styles:
4828
4829
- CDDS_ITEMPREPAINT
4830
- CDDS_ITEMPOSTPAINT
4831
*/
4832
4833
/* fill in the custom draw structure */
4834
customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
4835
if (cdmode & CDRF_NOTIFYITEMDRAW)
4836
cdsubitemmode = notify_customdraw(infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
4837
if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4838
4839
if (subitems)
4840
{
4841
while (iterator_next(subitems))
4842
{
4843
DWORD subitemstage = CDRF_DODEFAULT;
4844
4845
/* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
4846
if (subitems->nItem)
4847
{
4848
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_INDENT;
4849
lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
4850
lvItem.iItem = nItem;
4851
lvItem.iSubItem = subitems->nItem;
4852
lvItem.state = 0;
4853
lvItem.lParam = 0;
4854
lvItem.cchTextMax = DISP_TEXT_SIZE;
4855
lvItem.pszText = szDispText;
4856
szDispText[0] = 0;
4857
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4858
if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4859
lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4860
if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
4861
TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4862
4863
/* update custom draw data */
4864
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &nmlvcd.nmcd.rc, NULL, NULL, NULL, NULL);
4865
OffsetRect(&nmlvcd.nmcd.rc, pos.x, pos.y);
4866
nmlvcd.iSubItem = subitems->nItem;
4867
}
4868
4869
if (cdsubitemmode & CDRF_NOTIFYSUBITEMDRAW)
4870
subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
4871
4872
if (subitems->nItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
4873
prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4874
else if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4875
prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
4876
4877
if (!(subitemstage & CDRF_SKIPDEFAULT))
4878
LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
4879
4880
if (subitemstage & CDRF_NOTIFYPOSTPAINT)
4881
subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &nmlvcd);
4882
}
4883
}
4884
else
4885
{
4886
prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4887
LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
4888
}
4889
4890
postpaint:
4891
if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4892
{
4893
nmlvcd.iSubItem = 0;
4894
notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
4895
}
4896
4897
return TRUE;
4898
}
4899
4900
/***
4901
* DESCRIPTION:
4902
* Draws listview items when in owner draw mode.
4903
*
4904
* PARAMETER(S):
4905
* [I] infoPtr : valid pointer to the listview structure
4906
* [I] hdc : device context handle
4907
*
4908
* RETURN:
4909
* None
4910
*/
4911
static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4912
{
4913
UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4914
DWORD cditemmode = CDRF_DODEFAULT;
4915
NMLVCUSTOMDRAW nmlvcd;
4916
POINT Origin, Position;
4917
DRAWITEMSTRUCT dis;
4918
LVITEMW item;
4919
4920
TRACE("()\n");
4921
4922
ZeroMemory(&dis, sizeof(dis));
4923
4924
/* Get scroll info once before loop */
4925
LISTVIEW_GetOrigin(infoPtr, &Origin);
4926
4927
/* iterate through the invalidated rows */
4928
while(iterator_next(i))
4929
{
4930
item.iItem = i->nItem;
4931
item.iSubItem = 0;
4932
item.mask = LVIF_PARAM | LVIF_STATE;
4933
item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4934
if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4935
4936
dis.CtlType = ODT_LISTVIEW;
4937
dis.CtlID = uID;
4938
dis.itemID = item.iItem;
4939
dis.itemAction = ODA_DRAWENTIRE;
4940
dis.itemState = 0;
4941
if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4942
if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4943
dis.hwndItem = infoPtr->hwndSelf;
4944
dis.hDC = hdc;
4945
LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4946
dis.rcItem.left = Position.x + Origin.x;
4947
dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4948
dis.rcItem.top = Position.y + Origin.y;
4949
dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4950
dis.itemData = item.lParam;
4951
4952
TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4953
4954
/*
4955
* Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4956
* structure for the rest. of the paint cycle
4957
*/
4958
customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4959
if (cdmode & CDRF_NOTIFYITEMDRAW)
4960
cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4961
4962
if (!(cditemmode & CDRF_SKIPDEFAULT))
4963
{
4964
prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4965
SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4966
}
4967
4968
if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4969
notify_postpaint(infoPtr, &nmlvcd);
4970
}
4971
}
4972
4973
/***
4974
* DESCRIPTION:
4975
* Draws listview items when in report display mode.
4976
*
4977
* PARAMETER(S):
4978
* [I] infoPtr : valid pointer to the listview structure
4979
* [I] hdc : device context handle
4980
* [I] cdmode : custom draw mode
4981
*
4982
* RETURN:
4983
* None
4984
*/
4985
static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4986
{
4987
INT rgntype;
4988
RECT rcClip, rcItem;
4989
POINT Origin;
4990
RANGES colRanges;
4991
INT col;
4992
ITERATOR j;
4993
4994
TRACE("()\n");
4995
4996
/* figure out what to draw */
4997
rgntype = GetClipBox(hdc, &rcClip);
4998
if (rgntype == NULLREGION) return;
4999
5000
/* Get scroll info once before loop */
5001
LISTVIEW_GetOrigin(infoPtr, &Origin);
5002
5003
colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
5004
5005
/* narrow down the columns we need to paint */
5006
for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
5007
{
5008
INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
5009
5010
LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5011
if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
5012
ranges_additem(colRanges, index);
5013
}
5014
iterator_rangesitems(&j, colRanges);
5015
5016
/* in full row select, we _have_ to draw the main item */
5017
if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
5018
j.nSpecial = 0;
5019
5020
/* iterate through the invalidated rows */
5021
while(iterator_next(i))
5022
{
5023
RANGES subitems;
5024
POINT Position;
5025
ITERATOR k;
5026
5027
SelectObject(hdc, infoPtr->hFont);
5028
LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
5029
Position.x = Origin.x;
5030
Position.y += Origin.y;
5031
5032
subitems = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
5033
5034
/* iterate through the invalidated columns */
5035
while(iterator_next(&j))
5036
{
5037
LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
5038
5039
if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
5040
{
5041
rcItem.top = 0;
5042
rcItem.bottom = infoPtr->nItemHeight;
5043
OffsetRect(&rcItem, Origin.x, Position.y);
5044
if (!RectVisible(hdc, &rcItem)) continue;
5045
}
5046
5047
ranges_additem(subitems, j.nItem);
5048
}
5049
5050
iterator_rangesitems(&k, subitems);
5051
LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, &k, Position, cdmode);
5052
iterator_destroy(&k);
5053
}
5054
iterator_destroy(&j);
5055
}
5056
5057
/***
5058
* DESCRIPTION:
5059
* Draws the gridlines if necessary when in report display mode.
5060
*
5061
* PARAMETER(S):
5062
* [I] infoPtr : valid pointer to the listview structure
5063
* [I] hdc : device context handle
5064
*
5065
* RETURN:
5066
* None
5067
*/
5068
static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
5069
{
5070
INT rgntype;
5071
INT y, itemheight;
5072
INT col, index;
5073
HPEN hPen, hOldPen;
5074
RECT rcClip, rcItem = {0};
5075
POINT Origin;
5076
RANGES colRanges;
5077
ITERATOR j;
5078
BOOL rmost = FALSE;
5079
5080
TRACE("()\n");
5081
5082
/* figure out what to draw */
5083
rgntype = GetClipBox(hdc, &rcClip);
5084
if (rgntype == NULLREGION) return;
5085
5086
/* Get scroll info once before loop */
5087
LISTVIEW_GetOrigin(infoPtr, &Origin);
5088
5089
colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
5090
5091
/* narrow down the columns we need to paint */
5092
for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
5093
{
5094
index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
5095
5096
LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5097
if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
5098
ranges_additem(colRanges, index);
5099
}
5100
5101
/* is right most vertical line visible? */
5102
if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
5103
{
5104
index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
5105
LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5106
rmost = (rcItem.right + Origin.x < rcClip.right);
5107
}
5108
5109
if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
5110
{
5111
hOldPen = SelectObject ( hdc, hPen );
5112
5113
/* draw the vertical lines for the columns */
5114
iterator_rangesitems(&j, colRanges);
5115
while(iterator_next(&j))
5116
{
5117
LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
5118
if (rcItem.left == 0) continue; /* skip leftmost column */
5119
rcItem.left += Origin.x;
5120
rcItem.right += Origin.x;
5121
rcItem.top = infoPtr->rcList.top;
5122
rcItem.bottom = infoPtr->rcList.bottom;
5123
TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
5124
MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
5125
LineTo (hdc, rcItem.left, rcItem.bottom);
5126
}
5127
iterator_destroy(&j);
5128
/* draw rightmost grid line if visible */
5129
if (rmost)
5130
{
5131
index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
5132
DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
5133
LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5134
5135
rcItem.right += Origin.x;
5136
5137
MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
5138
LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
5139
}
5140
5141
/* draw the horizontal lines for the rows */
5142
itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
5143
rcItem.left = infoPtr->rcList.left;
5144
rcItem.right = infoPtr->rcList.right;
5145
for(y = Origin.y > 1 ? Origin.y - 1 : itemheight - 1 + Origin.y % itemheight; y<=infoPtr->rcList.bottom; y+=itemheight)
5146
{
5147
rcItem.bottom = rcItem.top = y;
5148
TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
5149
MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
5150
LineTo (hdc, rcItem.right, rcItem.top);
5151
}
5152
5153
SelectObject( hdc, hOldPen );
5154
DeleteObject( hPen );
5155
}
5156
else
5157
ranges_destroy(colRanges);
5158
}
5159
5160
/***
5161
* DESCRIPTION:
5162
* Draws listview items when in list display mode.
5163
*
5164
* PARAMETER(S):
5165
* [I] infoPtr : valid pointer to the listview structure
5166
* [I] hdc : device context handle
5167
* [I] cdmode : custom draw mode
5168
*
5169
* RETURN:
5170
* None
5171
*/
5172
static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
5173
{
5174
POINT Origin, Position;
5175
5176
/* Get scroll info once before loop */
5177
LISTVIEW_GetOrigin(infoPtr, &Origin);
5178
5179
while(iterator_prev(i))
5180
{
5181
SelectObject(hdc, infoPtr->hFont);
5182
LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
5183
Position.x += Origin.x;
5184
Position.y += Origin.y;
5185
5186
LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, NULL, Position, cdmode);
5187
}
5188
}
5189
5190
5191
/***
5192
* DESCRIPTION:
5193
* Draws listview items.
5194
*
5195
* PARAMETER(S):
5196
* [I] infoPtr : valid pointer to the listview structure
5197
* [I] hdc : device context handle
5198
* [I] prcErase : rect to be erased before refresh (may be NULL)
5199
*
5200
* RETURN:
5201
* NoneX
5202
*/
5203
static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
5204
{
5205
COLORREF oldTextColor = 0, oldBkColor = 0;
5206
NMLVCUSTOMDRAW nmlvcd;
5207
HFONT hOldFont = 0;
5208
DWORD cdmode;
5209
INT oldBkMode = 0;
5210
RECT rcClient;
5211
ITERATOR i;
5212
HDC hdcOrig = hdc;
5213
HBITMAP hbmp = NULL;
5214
RANGE range;
5215
5216
LISTVIEW_DUMP(infoPtr);
5217
5218
if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
5219
TRACE("double buffering\n");
5220
5221
hdc = CreateCompatibleDC(hdcOrig);
5222
if (!hdc) {
5223
ERR("Failed to create DC for backbuffer\n");
5224
return;
5225
}
5226
hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
5227
infoPtr->rcList.bottom);
5228
if (!hbmp) {
5229
ERR("Failed to create bitmap for backbuffer\n");
5230
DeleteDC(hdc);
5231
return;
5232
}
5233
5234
SelectObject(hdc, hbmp);
5235
SelectObject(hdc, infoPtr->hFont);
5236
5237
if(GetClipBox(hdcOrig, &rcClient))
5238
IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
5239
} else {
5240
/* Save dc values we're gonna trash while drawing
5241
* FIXME: Should be done in LISTVIEW_DrawItem() */
5242
hOldFont = SelectObject(hdc, infoPtr->hFont);
5243
oldBkMode = GetBkMode(hdc);
5244
oldBkColor = GetBkColor(hdc);
5245
oldTextColor = GetTextColor(hdc);
5246
}
5247
5248
infoPtr->bIsDrawing = TRUE;
5249
5250
if (prcErase) {
5251
LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
5252
} else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
5253
/* If no erasing was done (usually because RedrawWindow was called
5254
* with RDW_INVALIDATE only) we need to copy the old contents into
5255
* the backbuffer before continuing. */
5256
BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
5257
infoPtr->rcList.right - infoPtr->rcList.left,
5258
infoPtr->rcList.bottom - infoPtr->rcList.top,
5259
hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5260
}
5261
5262
SetBkMode(hdc, TRANSPARENT);
5263
GetClientRect(infoPtr->hwndSelf, &rcClient);
5264
customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
5265
cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
5266
if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
5267
5268
/* nothing to draw */
5269
if(infoPtr->nItemCount == 0) goto enddraw;
5270
5271
/* figure out what we need to draw */
5272
iterator_visibleitems(&i, infoPtr, hdc);
5273
range = iterator_range(&i);
5274
5275
/* send cache hint notification */
5276
if (infoPtr->dwStyle & LVS_OWNERDATA)
5277
{
5278
NMLVCACHEHINT nmlv;
5279
5280
ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
5281
nmlv.iFrom = range.lower;
5282
nmlv.iTo = range.upper - 1;
5283
notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
5284
}
5285
5286
if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
5287
LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
5288
else
5289
{
5290
if (infoPtr->uView == LV_VIEW_DETAILS)
5291
LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
5292
else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
5293
LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
5294
5295
/* if we have a focus rect and it's visible, draw it */
5296
if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
5297
(range.upper - 1) >= infoPtr->nFocusedItem)
5298
LISTVIEW_DrawFocusRect(infoPtr, hdc);
5299
}
5300
iterator_destroy(&i);
5301
5302
enddraw:
5303
/* For LVS_EX_GRIDLINES go and draw lines */
5304
/* This includes the case where there were *no* items */
5305
if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
5306
LISTVIEW_RefreshReportGrid(infoPtr, hdc);
5307
5308
/* Draw marquee rectangle if appropriate */
5309
if (infoPtr->bMarqueeSelect)
5310
DrawFocusRect(hdc, &infoPtr->marqueeDrawRect);
5311
5312
if (cdmode & CDRF_NOTIFYPOSTPAINT)
5313
notify_postpaint(infoPtr, &nmlvcd);
5314
5315
if(hbmp) {
5316
BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
5317
infoPtr->rcList.right - infoPtr->rcList.left,
5318
infoPtr->rcList.bottom - infoPtr->rcList.top,
5319
hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5320
5321
DeleteObject(hbmp);
5322
DeleteDC(hdc);
5323
} else {
5324
SelectObject(hdc, hOldFont);
5325
SetBkMode(hdc, oldBkMode);
5326
SetBkColor(hdc, oldBkColor);
5327
SetTextColor(hdc, oldTextColor);
5328
}
5329
5330
infoPtr->bIsDrawing = FALSE;
5331
}
5332
5333
5334
/***
5335
* DESCRIPTION:
5336
* Calculates the approximate width and height of a given number of items.
5337
*
5338
* PARAMETER(S):
5339
* [I] infoPtr : valid pointer to the listview structure
5340
* [I] nItemCount : number of items
5341
* [I] wWidth : width
5342
* [I] wHeight : height
5343
*
5344
* RETURN:
5345
* Returns a DWORD. The width in the low word and the height in high word.
5346
*/
5347
static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
5348
WORD wWidth, WORD wHeight)
5349
{
5350
DWORD dwViewRect = 0;
5351
5352
if (nItemCount == -1)
5353
nItemCount = infoPtr->nItemCount;
5354
5355
if (infoPtr->uView == LV_VIEW_LIST)
5356
{
5357
INT nItemCountPerColumn = 1;
5358
INT nColumnCount = 0;
5359
5360
if (wHeight == 0xFFFF)
5361
{
5362
/* use current height */
5363
wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5364
}
5365
5366
if (wHeight < infoPtr->nItemHeight)
5367
wHeight = infoPtr->nItemHeight;
5368
5369
if (nItemCount > 0)
5370
{
5371
if (infoPtr->nItemHeight > 0)
5372
{
5373
nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
5374
if (nItemCountPerColumn == 0)
5375
nItemCountPerColumn = 1;
5376
5377
if (nItemCount % nItemCountPerColumn != 0)
5378
nColumnCount = nItemCount / nItemCountPerColumn;
5379
else
5380
nColumnCount = nItemCount / nItemCountPerColumn + 1;
5381
}
5382
}
5383
5384
/* Microsoft padding magic */
5385
wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
5386
wWidth = nColumnCount * infoPtr->nItemWidth + 2;
5387
5388
dwViewRect = MAKELONG(wWidth, wHeight);
5389
}
5390
else if (infoPtr->uView == LV_VIEW_DETAILS)
5391
{
5392
RECT rcBox;
5393
5394
if (infoPtr->nItemCount > 0)
5395
{
5396
LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
5397
wWidth = rcBox.right - rcBox.left;
5398
wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
5399
}
5400
else
5401
{
5402
/* use current height and width */
5403
if (wHeight == 0xffff)
5404
wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5405
if (wWidth == 0xffff)
5406
wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5407
}
5408
5409
dwViewRect = MAKELONG(wWidth, wHeight);
5410
}
5411
else if (infoPtr->uView == LV_VIEW_ICON)
5412
{
5413
UINT rows,cols;
5414
UINT nItemWidth;
5415
UINT nItemHeight;
5416
5417
nItemWidth = infoPtr->iconSpacing.cx;
5418
nItemHeight = infoPtr->iconSpacing.cy;
5419
5420
if (wWidth == 0xffff)
5421
wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5422
5423
if (wWidth < nItemWidth)
5424
wWidth = nItemWidth;
5425
5426
cols = wWidth / nItemWidth;
5427
if (cols > nItemCount)
5428
cols = nItemCount;
5429
if (cols < 1)
5430
cols = 1;
5431
5432
if (nItemCount)
5433
{
5434
rows = nItemCount / cols;
5435
if (nItemCount % cols)
5436
rows++;
5437
}
5438
else
5439
rows = 0;
5440
5441
wHeight = (nItemHeight * rows)+2;
5442
wWidth = (nItemWidth * cols)+2;
5443
5444
dwViewRect = MAKELONG(wWidth, wHeight);
5445
}
5446
else if (infoPtr->uView == LV_VIEW_SMALLICON)
5447
FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5448
5449
return dwViewRect;
5450
}
5451
5452
/***
5453
* DESCRIPTION:
5454
* Cancel edit label with saving item text.
5455
*
5456
* PARAMETER(S):
5457
* [I] infoPtr : valid pointer to the listview structure
5458
*
5459
* RETURN:
5460
* Always returns TRUE.
5461
*/
5462
static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr)
5463
{
5464
if (infoPtr->hwndEdit)
5465
{
5466
/* handle value will be lost after LISTVIEW_EndEditLabelT */
5467
HWND edit = infoPtr->hwndEdit;
5468
5469
LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
5470
SendMessageW(edit, WM_CLOSE, 0, 0);
5471
}
5472
5473
return TRUE;
5474
}
5475
5476
/***
5477
* DESCRIPTION:
5478
* Create a drag image list for the specified item.
5479
*
5480
* PARAMETER(S):
5481
* [I] infoPtr : valid pointer to the listview structure
5482
* [I] iItem : index of item
5483
* [O] lppt : Upper-left corner of the image
5484
*
5485
* RETURN:
5486
* Returns a handle to the image list if successful, NULL otherwise.
5487
*/
5488
static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
5489
{
5490
RECT rcItem;
5491
SIZE size;
5492
POINT pos;
5493
HDC hdc, hdcOrig;
5494
HBITMAP hbmp, hOldbmp;
5495
HFONT hOldFont;
5496
HIMAGELIST dragList = 0;
5497
TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
5498
5499
if (iItem < 0 || iItem >= infoPtr->nItemCount || !lppt)
5500
return 0;
5501
5502
rcItem.left = LVIR_BOUNDS;
5503
if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
5504
return 0;
5505
5506
lppt->x = rcItem.left;
5507
lppt->y = rcItem.top;
5508
5509
size.cx = rcItem.right - rcItem.left;
5510
size.cy = rcItem.bottom - rcItem.top;
5511
5512
hdcOrig = GetDC(infoPtr->hwndSelf);
5513
hdc = CreateCompatibleDC(hdcOrig);
5514
hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
5515
hOldbmp = SelectObject(hdc, hbmp);
5516
hOldFont = SelectObject(hdc, infoPtr->hFont);
5517
5518
SetRect(&rcItem, 0, 0, size.cx, size.cy);
5519
FillRect(hdc, &rcItem, infoPtr->hBkBrush);
5520
5521
pos.x = pos.y = 0;
5522
if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, NULL, pos, CDRF_DODEFAULT))
5523
{
5524
dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
5525
SelectObject(hdc, hOldbmp);
5526
ImageList_Add(dragList, hbmp, 0);
5527
}
5528
else
5529
SelectObject(hdc, hOldbmp);
5530
5531
SelectObject(hdc, hOldFont);
5532
DeleteObject(hbmp);
5533
DeleteDC(hdc);
5534
ReleaseDC(infoPtr->hwndSelf, hdcOrig);
5535
5536
TRACE("ret=%p\n", dragList);
5537
5538
return dragList;
5539
}
5540
5541
5542
/***
5543
* DESCRIPTION:
5544
* Removes all listview items and subitems.
5545
*
5546
* PARAMETER(S):
5547
* [I] infoPtr : valid pointer to the listview structure
5548
*
5549
* RETURN:
5550
* SUCCESS : TRUE
5551
* FAILURE : FALSE
5552
*/
5553
static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
5554
{
5555
HDPA hdpaSubItems = NULL;
5556
BOOL suppress = FALSE;
5557
ITEMHDR *hdrItem;
5558
ITEM_INFO *lpItem;
5559
ITEM_ID *lpID;
5560
INT i, j;
5561
5562
TRACE("()\n");
5563
5564
/* we do it directly, to avoid notifications */
5565
ranges_clear(infoPtr->selectionRanges);
5566
infoPtr->nSelectionMark = -1;
5567
infoPtr->nFocusedItem = -1;
5568
SetRectEmpty(&infoPtr->rcFocus);
5569
/* But we are supposed to leave nHotItem as is! */
5570
5571
/* send LVN_DELETEALLITEMS notification */
5572
if (!(infoPtr->dwStyle & LVS_OWNERDATA) || !destroy)
5573
{
5574
NMLISTVIEW nmlv;
5575
5576
memset(&nmlv, 0, sizeof(NMLISTVIEW));
5577
nmlv.iItem = -1;
5578
suppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
5579
}
5580
5581
for (i = infoPtr->nItemCount - 1; i >= 0; i--)
5582
{
5583
if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5584
{
5585
/* send LVN_DELETEITEM notification, if not suppressed
5586
and if it is not a virtual listview */
5587
if (!suppress) notify_deleteitem(infoPtr, i);
5588
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
5589
lpItem = DPA_GetPtr(hdpaSubItems, 0);
5590
/* free id struct */
5591
j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5592
lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j);
5593
DPA_DeletePtr(infoPtr->hdpaItemIds, j);
5594
Free(lpID);
5595
/* both item and subitem start with ITEMHDR header */
5596
for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
5597
{
5598
hdrItem = DPA_GetPtr(hdpaSubItems, j);
5599
if (is_text(hdrItem->pszText)) Free(hdrItem->pszText);
5600
Free(hdrItem);
5601
}
5602
DPA_Destroy(hdpaSubItems);
5603
DPA_DeletePtr(infoPtr->hdpaItems, i);
5604
}
5605
DPA_DeletePtr(infoPtr->hdpaPosX, i);
5606
DPA_DeletePtr(infoPtr->hdpaPosY, i);
5607
infoPtr->nItemCount --;
5608
}
5609
5610
if (!destroy)
5611
{
5612
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5613
LISTVIEW_UpdateScroll(infoPtr);
5614
}
5615
LISTVIEW_InvalidateList(infoPtr);
5616
infoPtr->bNoItemMetrics = TRUE;
5617
5618
if (!destroy)
5619
{
5620
NotifyWinEvent( EVENT_OBJECT_REORDER, infoPtr->hwndSelf, OBJID_CLIENT, 0 );
5621
NotifyWinEvent( EVENT_OBJECT_DESTROY, infoPtr->hwndSelf, OBJID_CLIENT, 0 );
5622
NotifyWinEvent( EVENT_OBJECT_CREATE, infoPtr->hwndSelf, OBJID_CLIENT, 0 );
5623
}
5624
5625
return TRUE;
5626
}
5627
5628
/***
5629
* DESCRIPTION:
5630
* Scrolls, and updates the columns, when a column is changing width.
5631
*
5632
* PARAMETER(S):
5633
* [I] infoPtr : valid pointer to the listview structure
5634
* [I] nColumn : column to scroll
5635
* [I] dx : amount of scroll, in pixels
5636
*
5637
* RETURN:
5638
* None.
5639
*/
5640
static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
5641
{
5642
COLUMN_INFO *lpColumnInfo;
5643
RECT rcOld, rcCol;
5644
POINT ptOrigin;
5645
INT nCol;
5646
HDITEMW hdi;
5647
5648
if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
5649
lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
5650
rcCol = lpColumnInfo->rcHeader;
5651
if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5652
rcCol.left = rcCol.right;
5653
5654
/* adjust the other columns */
5655
hdi.mask = HDI_ORDER;
5656
if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
5657
{
5658
INT nOrder = hdi.iOrder;
5659
for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
5660
{
5661
hdi.mask = HDI_ORDER;
5662
SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi);
5663
if (hdi.iOrder >= nOrder) {
5664
lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
5665
lpColumnInfo->rcHeader.left += dx;
5666
lpColumnInfo->rcHeader.right += dx;
5667
}
5668
}
5669
}
5670
5671
/* do not update screen if not in report mode */
5672
if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
5673
5674
/* Need to reset the item width when inserting a new column */
5675
infoPtr->nItemWidth += dx;
5676
5677
LISTVIEW_UpdateScroll(infoPtr);
5678
LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
5679
5680
/* scroll to cover the deleted column, and invalidate for redraw */
5681
rcOld = infoPtr->rcList;
5682
rcOld.left = ptOrigin.x + rcCol.left + dx;
5683
ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5684
}
5685
5686
/***
5687
* DESCRIPTION:
5688
* Removes a column from the listview control.
5689
*
5690
* PARAMETER(S):
5691
* [I] infoPtr : valid pointer to the listview structure
5692
* [I] nColumn : column index
5693
*
5694
* RETURN:
5695
* SUCCESS : TRUE
5696
* FAILURE : FALSE
5697
*/
5698
static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
5699
{
5700
RECT rcCol;
5701
5702
TRACE("nColumn=%d\n", nColumn);
5703
5704
if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5705
return FALSE;
5706
5707
/* While the MSDN specifically says that column zero should not be deleted,
5708
what actually happens is that the column itself is deleted but no items or subitems
5709
are removed.
5710
*/
5711
5712
LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
5713
5714
if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
5715
return FALSE;
5716
5717
Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
5718
DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
5719
5720
if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
5721
{
5722
SUBITEM_INFO *lpSubItem, *lpDelItem;
5723
HDPA hdpaSubItems;
5724
INT nItem, nSubItem, i;
5725
5726
for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5727
{
5728
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
5729
nSubItem = 0;
5730
lpDelItem = 0;
5731
for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
5732
{
5733
lpSubItem = DPA_GetPtr(hdpaSubItems, i);
5734
if (lpSubItem->iSubItem == nColumn)
5735
{
5736
nSubItem = i;
5737
lpDelItem = lpSubItem;
5738
}
5739
else if (lpSubItem->iSubItem > nColumn)
5740
{
5741
lpSubItem->iSubItem--;
5742
}
5743
}
5744
5745
/* if we found our subitem, zap it */
5746
if (nSubItem > 0)
5747
{
5748
/* free string */
5749
if (is_text(lpDelItem->hdr.pszText))
5750
Free(lpDelItem->hdr.pszText);
5751
5752
/* free item */
5753
Free(lpDelItem);
5754
5755
/* free dpa memory */
5756
DPA_DeletePtr(hdpaSubItems, nSubItem);
5757
}
5758
}
5759
}
5760
5761
/* update the other column info */
5762
if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
5763
LISTVIEW_InvalidateList(infoPtr);
5764
else
5765
LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
5766
LISTVIEW_UpdateItemSize(infoPtr);
5767
5768
return TRUE;
5769
}
5770
5771
/***
5772
* DESCRIPTION:
5773
* Invalidates the listview after an item's insertion or deletion.
5774
*
5775
* PARAMETER(S):
5776
* [I] infoPtr : valid pointer to the listview structure
5777
* [I] nItem : item index
5778
* [I] dir : -1 if deleting, 1 if inserting
5779
*
5780
* RETURN:
5781
* None
5782
*/
5783
static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5784
{
5785
INT nPerCol, nItemCol, nItemRow;
5786
RECT rcScroll;
5787
POINT Origin;
5788
5789
/* if we don't refresh, what's the point of scrolling? */
5790
if (!is_redrawing(infoPtr)) return;
5791
5792
assert (abs(dir) == 1);
5793
5794
/* arrange icons if autoarrange is on */
5795
if (is_autoarrange(infoPtr))
5796
{
5797
BOOL arrange = TRUE;
5798
if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5799
if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5800
if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5801
}
5802
5803
/* scrollbars need updating */
5804
LISTVIEW_UpdateScroll(infoPtr);
5805
5806
/* figure out the item's position */
5807
if (infoPtr->uView == LV_VIEW_DETAILS)
5808
nPerCol = infoPtr->nItemCount + 1;
5809
else if (infoPtr->uView == LV_VIEW_LIST)
5810
nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5811
else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5812
return;
5813
5814
nItemCol = nItem / nPerCol;
5815
nItemRow = nItem % nPerCol;
5816
LISTVIEW_GetOrigin(infoPtr, &Origin);
5817
5818
/* move the items below up a slot */
5819
rcScroll.left = nItemCol * infoPtr->nItemWidth;
5820
rcScroll.top = nItemRow * infoPtr->nItemHeight;
5821
rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5822
rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5823
OffsetRect(&rcScroll, Origin.x, Origin.y);
5824
TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5825
if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5826
{
5827
TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5828
InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
5829
}
5830
5831
/* report has only that column, so we're done */
5832
if (infoPtr->uView == LV_VIEW_DETAILS) return;
5833
5834
/* now for LISTs, we have to deal with the columns to the right */
5835
SetRect(&rcScroll, (nItemCol + 1) * infoPtr->nItemWidth, 0,
5836
(infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth,
5837
nPerCol * infoPtr->nItemHeight);
5838
OffsetRect(&rcScroll, Origin.x, Origin.y);
5839
if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5840
InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
5841
}
5842
5843
/***
5844
* DESCRIPTION:
5845
* Removes an item from the listview control.
5846
*
5847
* PARAMETER(S):
5848
* [I] infoPtr : valid pointer to the listview structure
5849
* [I] nItem : item index
5850
*
5851
* RETURN:
5852
* SUCCESS : TRUE
5853
* FAILURE : FALSE
5854
*/
5855
static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5856
{
5857
LVITEMW item;
5858
const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5859
INT focus = infoPtr->nFocusedItem;
5860
5861
TRACE("(nItem=%d)\n", nItem);
5862
5863
if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5864
5865
/* remove selection, and focus */
5866
item.state = 0;
5867
item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5868
LISTVIEW_SetItemState(infoPtr, nItem, &item);
5869
5870
/* send LVN_DELETEITEM notification. */
5871
if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5872
5873
/* we need to do this here, because we'll be deleting stuff */
5874
if (is_icon)
5875
LISTVIEW_InvalidateItem(infoPtr, nItem);
5876
5877
if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5878
{
5879
HDPA hdpaSubItems;
5880
ITEMHDR *hdrItem;
5881
ITEM_INFO *lpItem;
5882
ITEM_ID *lpID;
5883
INT i;
5884
5885
hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5886
lpItem = DPA_GetPtr(hdpaSubItems, 0);
5887
5888
/* free id struct */
5889
i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5890
lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5891
DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5892
Free(lpID);
5893
for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5894
{
5895
hdrItem = DPA_GetPtr(hdpaSubItems, i);
5896
if (is_text(hdrItem->pszText)) Free(hdrItem->pszText);
5897
Free(hdrItem);
5898
}
5899
DPA_Destroy(hdpaSubItems);
5900
}
5901
5902
if (is_icon)
5903
{
5904
DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5905
DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5906
}
5907
5908
infoPtr->nItemCount--;
5909
LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5910
LISTVIEW_ShiftFocus(infoPtr, focus, nItem, -1);
5911
5912
/* now is the invalidation fun */
5913
if (!is_icon)
5914
LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5915
5916
NotifyWinEvent( EVENT_OBJECT_DESTROY, infoPtr->hwndSelf, OBJID_CLIENT, nItem + 1 );
5917
5918
return TRUE;
5919
}
5920
5921
5922
/***
5923
* DESCRIPTION:
5924
* Callback implementation for editlabel control
5925
*
5926
* PARAMETER(S):
5927
* [I] infoPtr : valid pointer to the listview structure
5928
* [I] storeText : store edit box text as item text
5929
* [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5930
*
5931
* RETURN:
5932
* SUCCESS : TRUE
5933
* FAILURE : FALSE
5934
*/
5935
static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5936
{
5937
HWND hwndSelf = infoPtr->hwndSelf;
5938
WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5939
NMLVDISPINFOW dispInfo;
5940
INT editedItem = infoPtr->nEditLabelItem;
5941
BOOL same;
5942
WCHAR *pszText = NULL;
5943
BOOL res;
5944
5945
if (storeText)
5946
{
5947
DWORD len = (isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit)) + 1;
5948
5949
if (!(pszText = Alloc(len * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5950
return FALSE;
5951
5952
if (isW)
5953
GetWindowTextW(infoPtr->hwndEdit, pszText, len);
5954
else
5955
GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len);
5956
}
5957
5958
TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5959
5960
ZeroMemory(&dispInfo, sizeof(dispInfo));
5961
dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5962
dispInfo.item.iItem = editedItem;
5963
dispInfo.item.iSubItem = 0;
5964
dispInfo.item.stateMask = ~0;
5965
dispInfo.item.pszText = szDispText;
5966
dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5967
if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW))
5968
{
5969
res = FALSE;
5970
goto cleanup;
5971
}
5972
5973
if (isW)
5974
same = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5975
else
5976
{
5977
LPWSTR tmp = textdupTtoW(pszText, FALSE);
5978
same = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5979
textfreeT(tmp, FALSE);
5980
}
5981
5982
/* add the text from the edit in */
5983
dispInfo.item.mask |= LVIF_TEXT;
5984
dispInfo.item.pszText = same ? NULL : pszText;
5985
dispInfo.item.cchTextMax = textlenT(dispInfo.item.pszText, isW);
5986
5987
infoPtr->notify_mask &= ~NOTIFY_MASK_END_LABEL_EDIT;
5988
5989
/* Do we need to update the Item Text */
5990
res = notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW);
5991
5992
infoPtr->notify_mask |= NOTIFY_MASK_END_LABEL_EDIT;
5993
5994
infoPtr->nEditLabelItem = -1;
5995
infoPtr->hwndEdit = 0;
5996
5997
if (!res) goto cleanup;
5998
5999
if (!IsWindow(hwndSelf))
6000
{
6001
res = FALSE;
6002
goto cleanup;
6003
}
6004
if (!pszText) return TRUE;
6005
if (same)
6006
{
6007
res = TRUE;
6008
goto cleanup;
6009
}
6010
6011
if (!(infoPtr->dwStyle & LVS_OWNERDATA))
6012
{
6013
HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
6014
ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
6015
if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
6016
{
6017
LISTVIEW_InvalidateItem(infoPtr, editedItem);
6018
res = TRUE;
6019
goto cleanup;
6020
}
6021
}
6022
6023
ZeroMemory(&dispInfo, sizeof(dispInfo));
6024
dispInfo.item.mask = LVIF_TEXT;
6025
dispInfo.item.iItem = editedItem;
6026
dispInfo.item.iSubItem = 0;
6027
dispInfo.item.pszText = pszText;
6028
dispInfo.item.cchTextMax = textlenT(pszText, isW);
6029
res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
6030
6031
cleanup:
6032
Free(pszText);
6033
6034
return res;
6035
}
6036
6037
static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
6038
{
6039
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
6040
BOOL save = TRUE;
6041
6042
TRACE("hwnd %p, uMsg %x, wParam %Ix, lParam %Ix, isW %d\n", hwnd, uMsg, wParam, lParam, isW);
6043
6044
switch (uMsg)
6045
{
6046
case WM_GETDLGCODE:
6047
return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
6048
6049
case WM_DESTROY:
6050
{
6051
WNDPROC editProc = infoPtr->EditWndProc;
6052
infoPtr->EditWndProc = 0;
6053
SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
6054
return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
6055
}
6056
6057
case WM_KEYDOWN:
6058
if (VK_ESCAPE == (INT)wParam)
6059
{
6060
save = FALSE;
6061
break;
6062
}
6063
else if (VK_RETURN == (INT)wParam)
6064
break;
6065
6066
default:
6067
return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
6068
}
6069
6070
/* kill the edit */
6071
if (infoPtr->hwndEdit)
6072
LISTVIEW_EndEditLabelT(infoPtr, save, isW);
6073
6074
SendMessageW(hwnd, WM_CLOSE, 0, 0);
6075
return 0;
6076
}
6077
6078
static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
6079
{
6080
return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
6081
}
6082
6083
static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
6084
{
6085
return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
6086
}
6087
6088
static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, BOOL isW)
6089
{
6090
static const DWORD style = WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER|WS_VISIBLE;
6091
HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
6092
HWND hedit;
6093
6094
TRACE("(%p, text=%s, isW=%d)\n", infoPtr, debugtext_t(text, isW), isW);
6095
6096
/* window will be resized and positioned after LVN_BEGINLABELEDIT */
6097
if (isW)
6098
hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
6099
else
6100
hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
6101
6102
if (!hedit) return 0;
6103
6104
infoPtr->EditWndProc = (WNDPROC)
6105
(isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
6106
SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
6107
6108
SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
6109
SendMessageW(hedit, EM_SETLIMITTEXT, DISP_TEXT_SIZE-1, 0);
6110
6111
return hedit;
6112
}
6113
6114
static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
6115
{
6116
WCHAR disptextW[DISP_TEXT_SIZE] = { 0 };
6117
HWND hwndSelf = infoPtr->hwndSelf;
6118
NMLVDISPINFOW dispInfo;
6119
HFONT hOldFont = NULL;
6120
TEXTMETRICW tm;
6121
RECT rect;
6122
SIZE sz;
6123
HDC hdc;
6124
6125
TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
6126
6127
if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
6128
6129
/* remove existing edit box */
6130
if (infoPtr->hwndEdit)
6131
{
6132
SetFocus(infoPtr->hwndSelf);
6133
infoPtr->hwndEdit = 0;
6134
}
6135
6136
if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6137
6138
infoPtr->nEditLabelItem = nItem;
6139
6140
LISTVIEW_SetSelection(infoPtr, nItem);
6141
LISTVIEW_SetItemFocus(infoPtr, nItem);
6142
LISTVIEW_InvalidateItem(infoPtr, nItem);
6143
6144
rect.left = LVIR_LABEL;
6145
if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
6146
6147
ZeroMemory(&dispInfo, sizeof(dispInfo));
6148
dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
6149
dispInfo.item.iItem = nItem;
6150
dispInfo.item.iSubItem = 0;
6151
dispInfo.item.stateMask = ~0;
6152
dispInfo.item.pszText = disptextW;
6153
dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
6154
if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
6155
6156
infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, isW);
6157
if (!infoPtr->hwndEdit) return 0;
6158
6159
if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
6160
{
6161
if (!IsWindow(hwndSelf))
6162
return 0;
6163
SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
6164
infoPtr->hwndEdit = 0;
6165
return 0;
6166
}
6167
6168
TRACE("disp text=%s\n", debugtext_t(dispInfo.item.pszText, isW));
6169
6170
/* position and display edit box */
6171
hdc = GetDC(infoPtr->hwndSelf);
6172
6173
/* select the font to get appropriate metric dimensions */
6174
if (infoPtr->hFont)
6175
hOldFont = SelectObject(hdc, infoPtr->hFont);
6176
6177
/* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */
6178
GetWindowTextW(infoPtr->hwndEdit, disptextW, DISP_TEXT_SIZE);
6179
TRACE("edit box text=%s\n", debugstr_w(disptextW));
6180
6181
/* get string length in pixels */
6182
GetTextExtentPoint32W(hdc, disptextW, lstrlenW(disptextW), &sz);
6183
6184
/* add extra spacing for the next character */
6185
GetTextMetricsW(hdc, &tm);
6186
sz.cx += tm.tmMaxCharWidth * 2;
6187
6188
if (infoPtr->hFont)
6189
SelectObject(hdc, hOldFont);
6190
6191
ReleaseDC(infoPtr->hwndSelf, hdc);
6192
6193
sz.cy = rect.bottom - rect.top + 2;
6194
rect.left -= 2;
6195
rect.top -= 1;
6196
TRACE("moving edit (%ld,%ld)-(%ld,%ld)\n", rect.left, rect.top, sz.cx, sz.cy);
6197
MoveWindow(infoPtr->hwndEdit, rect.left, rect.top, sz.cx, sz.cy, FALSE);
6198
ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
6199
SetFocus(infoPtr->hwndEdit);
6200
SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
6201
return infoPtr->hwndEdit;
6202
}
6203
6204
6205
/***
6206
* DESCRIPTION:
6207
* Ensures the specified item is visible, scrolling into view if necessary.
6208
*
6209
* PARAMETER(S):
6210
* [I] infoPtr : valid pointer to the listview structure
6211
* [I] nItem : item index
6212
* [I] bPartial : partially or entirely visible
6213
*
6214
* RETURN:
6215
* SUCCESS : TRUE
6216
* FAILURE : FALSE
6217
*/
6218
static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
6219
{
6220
INT nScrollPosHeight = 0;
6221
INT nScrollPosWidth = 0;
6222
INT nHorzAdjust = 0;
6223
INT nVertAdjust = 0;
6224
INT nHorzDiff = 0;
6225
INT nVertDiff = 0;
6226
RECT rcItem, rcTemp;
6227
6228
rcItem.left = LVIR_BOUNDS;
6229
if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
6230
6231
if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
6232
6233
if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
6234
{
6235
/* scroll left/right, but in LV_VIEW_DETAILS mode */
6236
if (infoPtr->uView == LV_VIEW_LIST)
6237
nScrollPosWidth = infoPtr->nItemWidth;
6238
else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6239
nScrollPosWidth = 1;
6240
6241
if (rcItem.left < infoPtr->rcList.left)
6242
{
6243
nHorzAdjust = -1;
6244
if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
6245
}
6246
else
6247
{
6248
nHorzAdjust = 1;
6249
if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
6250
}
6251
}
6252
6253
if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
6254
{
6255
/* scroll up/down, but not in LVS_LIST mode */
6256
if (infoPtr->uView == LV_VIEW_DETAILS)
6257
nScrollPosHeight = infoPtr->nItemHeight;
6258
else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
6259
nScrollPosHeight = 1;
6260
6261
if (rcItem.top < infoPtr->rcList.top)
6262
{
6263
nVertAdjust = -1;
6264
if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
6265
}
6266
else
6267
{
6268
nVertAdjust = 1;
6269
if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
6270
}
6271
}
6272
6273
if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
6274
6275
if (nScrollPosWidth)
6276
{
6277
INT diff = nHorzDiff / nScrollPosWidth;
6278
if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
6279
LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff);
6280
}
6281
6282
if (nScrollPosHeight)
6283
{
6284
INT diff = nVertDiff / nScrollPosHeight;
6285
if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
6286
LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff);
6287
}
6288
6289
return TRUE;
6290
}
6291
6292
/***
6293
* DESCRIPTION:
6294
* Searches for an item with specific characteristics.
6295
*
6296
* PARAMETER(S):
6297
* [I] hwnd : window handle
6298
* [I] nStart : base item index
6299
* [I] lpFindInfo : item information to look for
6300
*
6301
* RETURN:
6302
* SUCCESS : index of item
6303
* FAILURE : -1
6304
*/
6305
static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
6306
const LVFINDINFOW *lpFindInfo)
6307
{
6308
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6309
BOOL bWrap = FALSE, bNearest = FALSE;
6310
INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
6311
ULONG xdist, ydist, dist, mindist = 0x7fffffff;
6312
POINT Position, Destination;
6313
int search_len = 0;
6314
LVITEMW lvItem;
6315
6316
/* Search in virtual listviews should be done by application, not by
6317
listview control, so we just send LVN_ODFINDITEMW and return the result */
6318
if (infoPtr->dwStyle & LVS_OWNERDATA)
6319
{
6320
NMLVFINDITEMW nmlv;
6321
6322
nmlv.iStart = nStart;
6323
nmlv.lvfi = *lpFindInfo;
6324
return notify_odfinditem(infoPtr, &nmlv);
6325
}
6326
6327
if (!lpFindInfo || nItem < 0) return -1;
6328
6329
lvItem.mask = 0;
6330
if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING))
6331
{
6332
lvItem.mask |= LVIF_TEXT;
6333
lvItem.pszText = szDispText;
6334
lvItem.cchTextMax = DISP_TEXT_SIZE;
6335
}
6336
6337
if (lpFindInfo->flags & LVFI_WRAP)
6338
bWrap = TRUE;
6339
6340
if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
6341
(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
6342
{
6343
POINT Origin;
6344
RECT rcArea;
6345
6346
LISTVIEW_GetOrigin(infoPtr, &Origin);
6347
Destination.x = lpFindInfo->pt.x - Origin.x;
6348
Destination.y = lpFindInfo->pt.y - Origin.y;
6349
switch(lpFindInfo->vkDirection)
6350
{
6351
case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
6352
case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
6353
case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
6354
case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
6355
case VK_HOME: Destination.x = Destination.y = 0; break;
6356
case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
6357
case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
6358
case VK_END:
6359
LISTVIEW_GetAreaRect(infoPtr, &rcArea);
6360
Destination.x = rcArea.right;
6361
Destination.y = rcArea.bottom;
6362
break;
6363
default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
6364
}
6365
bNearest = TRUE;
6366
}
6367
else Destination.x = Destination.y = 0;
6368
6369
/* if LVFI_PARAM is specified, all other flags are ignored */
6370
if (lpFindInfo->flags & LVFI_PARAM)
6371
{
6372
lvItem.mask |= LVIF_PARAM;
6373
bNearest = FALSE;
6374
lvItem.mask &= ~LVIF_TEXT;
6375
}
6376
6377
nItem = bNearest ? -1 : nStart + 1;
6378
6379
if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING))
6380
search_len = lstrlenW(lpFindInfo->psz);
6381
6382
again:
6383
for (; nItem < nLast; nItem++)
6384
{
6385
lvItem.iItem = nItem;
6386
lvItem.iSubItem = 0;
6387
lvItem.pszText = szDispText;
6388
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6389
6390
if (lvItem.mask & LVIF_PARAM)
6391
{
6392
if (lpFindInfo->lParam == lvItem.lParam)
6393
return nItem;
6394
else
6395
continue;
6396
}
6397
6398
if (lvItem.mask & LVIF_TEXT)
6399
{
6400
if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING))
6401
{
6402
if (StrCmpNIW(lvItem.pszText, lpFindInfo->psz, search_len)) continue;
6403
}
6404
else
6405
{
6406
if (StrCmpIW(lvItem.pszText, lpFindInfo->psz)) continue;
6407
}
6408
}
6409
6410
if (!bNearest) return nItem;
6411
6412
/* This is very inefficient. To do a good job here,
6413
* we need a sorted array of (x,y) item positions */
6414
LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6415
6416
/* compute the distance^2 to the destination */
6417
xdist = Destination.x - Position.x;
6418
ydist = Destination.y - Position.y;
6419
dist = xdist * xdist + ydist * ydist;
6420
6421
/* remember the distance, and item if it's closer */
6422
if (dist < mindist)
6423
{
6424
mindist = dist;
6425
nNearestItem = nItem;
6426
}
6427
}
6428
6429
if (bWrap)
6430
{
6431
nItem = 0;
6432
nLast = min(nStart + 1, infoPtr->nItemCount);
6433
bWrap = FALSE;
6434
goto again;
6435
}
6436
6437
return nNearestItem;
6438
}
6439
6440
/***
6441
* DESCRIPTION:
6442
* Searches for an item with specific characteristics.
6443
*
6444
* PARAMETER(S):
6445
* [I] hwnd : window handle
6446
* [I] nStart : base item index
6447
* [I] lpFindInfo : item information to look for
6448
*
6449
* RETURN:
6450
* SUCCESS : index of item
6451
* FAILURE : -1
6452
*/
6453
static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
6454
const LVFINDINFOA *lpFindInfo)
6455
{
6456
LVFINDINFOW fiw;
6457
INT res;
6458
LPWSTR strW = NULL;
6459
6460
memcpy(&fiw, lpFindInfo, sizeof(fiw));
6461
if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING))
6462
fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
6463
res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
6464
textfreeT(strW, FALSE);
6465
return res;
6466
}
6467
6468
/***
6469
* DESCRIPTION:
6470
* Retrieves column attributes.
6471
*
6472
* PARAMETER(S):
6473
* [I] infoPtr : valid pointer to the listview structure
6474
* [I] nColumn : column index
6475
* [IO] lpColumn : column information
6476
* [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6477
* otherwise it is in fact a LPLVCOLUMNA
6478
*
6479
* RETURN:
6480
* SUCCESS : TRUE
6481
* FAILURE : FALSE
6482
*/
6483
static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
6484
{
6485
COLUMN_INFO *lpColumnInfo;
6486
HDITEMW hdi;
6487
6488
if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6489
lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6490
6491
/* initialize memory */
6492
ZeroMemory(&hdi, sizeof(hdi));
6493
6494
if (lpColumn->mask & LVCF_TEXT)
6495
{
6496
hdi.mask |= HDI_TEXT;
6497
hdi.pszText = lpColumn->pszText;
6498
hdi.cchTextMax = lpColumn->cchTextMax;
6499
}
6500
6501
if (lpColumn->mask & LVCF_IMAGE)
6502
hdi.mask |= HDI_IMAGE;
6503
6504
if (lpColumn->mask & LVCF_ORDER)
6505
hdi.mask |= HDI_ORDER;
6506
6507
if (lpColumn->mask & LVCF_SUBITEM)
6508
hdi.mask |= HDI_LPARAM;
6509
6510
if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
6511
6512
if (lpColumn->mask & LVCF_FMT)
6513
lpColumn->fmt = lpColumnInfo->fmt;
6514
6515
if (lpColumn->mask & LVCF_WIDTH)
6516
lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
6517
6518
if (lpColumn->mask & LVCF_IMAGE)
6519
lpColumn->iImage = hdi.iImage;
6520
6521
if (lpColumn->mask & LVCF_ORDER)
6522
lpColumn->iOrder = hdi.iOrder;
6523
6524
if (lpColumn->mask & LVCF_SUBITEM)
6525
lpColumn->iSubItem = hdi.lParam;
6526
6527
if (lpColumn->mask & LVCF_MINWIDTH)
6528
lpColumn->cxMin = lpColumnInfo->cxMin;
6529
6530
return TRUE;
6531
}
6532
6533
static inline BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6534
{
6535
if (!infoPtr->hwndHeader) return FALSE;
6536
return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
6537
}
6538
6539
/***
6540
* DESCRIPTION:
6541
* Retrieves the column width.
6542
*
6543
* PARAMETER(S):
6544
* [I] infoPtr : valid pointer to the listview structure
6545
* [I] int : column index
6546
*
6547
* RETURN:
6548
* SUCCESS : column width
6549
* FAILURE : zero
6550
*/
6551
static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
6552
{
6553
INT nColumnWidth = 0;
6554
HDITEMW hdItem;
6555
6556
TRACE("nColumn=%d\n", nColumn);
6557
6558
/* we have a 'column' in LIST and REPORT mode only */
6559
switch(infoPtr->uView)
6560
{
6561
case LV_VIEW_LIST:
6562
nColumnWidth = infoPtr->nItemWidth;
6563
break;
6564
case LV_VIEW_DETAILS:
6565
/* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6566
* There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6567
* HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6568
*/
6569
hdItem.mask = HDI_WIDTH;
6570
if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
6571
{
6572
WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
6573
return 0;
6574
}
6575
nColumnWidth = hdItem.cxy;
6576
break;
6577
}
6578
6579
TRACE("nColumnWidth=%d\n", nColumnWidth);
6580
return nColumnWidth;
6581
}
6582
6583
/***
6584
* DESCRIPTION:
6585
* In list or report display mode, retrieves the number of items that can fit
6586
* vertically in the visible area. In icon or small icon display mode,
6587
* retrieves the total number of visible items.
6588
*
6589
* PARAMETER(S):
6590
* [I] infoPtr : valid pointer to the listview structure
6591
*
6592
* RETURN:
6593
* Number of fully visible items.
6594
*/
6595
static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
6596
{
6597
switch (infoPtr->uView)
6598
{
6599
case LV_VIEW_ICON:
6600
case LV_VIEW_SMALLICON:
6601
return infoPtr->nItemCount;
6602
case LV_VIEW_DETAILS:
6603
return LISTVIEW_GetCountPerColumn(infoPtr);
6604
case LV_VIEW_LIST:
6605
return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
6606
}
6607
assert(FALSE);
6608
return 0;
6609
}
6610
6611
/***
6612
* DESCRIPTION:
6613
* Retrieves an image list handle.
6614
*
6615
* PARAMETER(S):
6616
* [I] infoPtr : valid pointer to the listview structure
6617
* [I] nImageList : image list identifier
6618
*
6619
* RETURN:
6620
* SUCCESS : image list handle
6621
* FAILURE : NULL
6622
*/
6623
static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
6624
{
6625
switch (nImageList)
6626
{
6627
case LVSIL_NORMAL: return infoPtr->himlNormal;
6628
case LVSIL_SMALL: return infoPtr->himlSmall;
6629
case LVSIL_STATE: return infoPtr->himlState;
6630
case LVSIL_GROUPHEADER:
6631
FIXME("LVSIL_GROUPHEADER not supported\n");
6632
break;
6633
default:
6634
WARN("got unknown imagelist index - %d\n", nImageList);
6635
}
6636
return NULL;
6637
}
6638
6639
/* LISTVIEW_GetISearchString */
6640
6641
/***
6642
* DESCRIPTION:
6643
* Retrieves item attributes.
6644
*
6645
* PARAMETER(S):
6646
* [I] hwnd : window handle
6647
* [IO] lpLVItem : item info
6648
* [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6649
* if FALSE, then lpLVItem is a LPLVITEMA.
6650
*
6651
* NOTE:
6652
* This is the internal 'GetItem' interface -- it tries to
6653
* be smart and avoid text copies, if possible, by modifying
6654
* lpLVItem->pszText to point to the text string. Please note
6655
* that this is not always possible (e.g. OWNERDATA), so on
6656
* entry you *must* supply valid values for pszText, and cchTextMax.
6657
* The only difference to the documented interface is that upon
6658
* return, you should use *only* the lpLVItem->pszText, rather than
6659
* the buffer pointer you provided on input. Most code already does
6660
* that, so it's not a problem.
6661
* For the two cases when the text must be copied (that is,
6662
* for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6663
*
6664
* RETURN:
6665
* SUCCESS : TRUE
6666
* FAILURE : FALSE
6667
*/
6668
static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6669
{
6670
ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
6671
BOOL is_subitem_invalid = FALSE;
6672
NMLVDISPINFOW dispInfo;
6673
ITEM_INFO *lpItem;
6674
ITEMHDR* pItemHdr;
6675
HDPA hdpaSubItems;
6676
6677
TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6678
6679
if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6680
return FALSE;
6681
6682
if (lpLVItem->mask == 0) return TRUE;
6683
TRACE("mask=%x\n", lpLVItem->mask);
6684
6685
if (lpLVItem->iSubItem && (lpLVItem->mask & LVIF_STATE))
6686
lpLVItem->state = 0;
6687
6688
/* a quick optimization if all we're asked is the focus state
6689
* these queries are worth optimising since they are common,
6690
* and can be answered in constant time, without the heavy accesses */
6691
if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
6692
!(infoPtr->uCallbackMask & LVIS_FOCUSED) )
6693
{
6694
lpLVItem->state = 0;
6695
if (infoPtr->nFocusedItem == lpLVItem->iItem && !lpLVItem->iSubItem)
6696
lpLVItem->state |= LVIS_FOCUSED;
6697
return TRUE;
6698
}
6699
6700
ZeroMemory(&dispInfo, sizeof(dispInfo));
6701
6702
/* if the app stores all the data, handle it separately */
6703
if (infoPtr->dwStyle & LVS_OWNERDATA)
6704
{
6705
if (lpLVItem->mask & LVIF_STATE) lpLVItem->state = 0;
6706
dispInfo.item.state = lpLVItem->state;
6707
6708
/* apparently, we should not callback for lParam in LVS_OWNERDATA */
6709
if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
6710
((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
6711
{
6712
UINT mask = lpLVItem->mask;
6713
6714
/* NOTE: copy only fields which we _know_ are initialized, some apps
6715
* depend on the uninitialized fields being 0 */
6716
dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
6717
dispInfo.item.iItem = lpLVItem->iItem;
6718
dispInfo.item.iSubItem = lpLVItem->iSubItem;
6719
if (lpLVItem->mask & LVIF_TEXT)
6720
{
6721
if (lpLVItem->mask & LVIF_NORECOMPUTE)
6722
/* reset mask */
6723
dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
6724
else
6725
{
6726
dispInfo.item.pszText = lpLVItem->pszText;
6727
dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6728
}
6729
}
6730
if (lpLVItem->mask & LVIF_STATE)
6731
dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
6732
/* could be zeroed on LVIF_NORECOMPUTE case */
6733
if (dispInfo.item.mask)
6734
{
6735
notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6736
dispInfo.item.stateMask = lpLVItem->stateMask;
6737
if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6738
{
6739
/* full size structure expected - _WIN32IE >= 0x560 */
6740
*lpLVItem = dispInfo.item;
6741
}
6742
else if (lpLVItem->mask & LVIF_INDENT)
6743
{
6744
/* indent member expected - _WIN32IE >= 0x300 */
6745
memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
6746
}
6747
else
6748
{
6749
/* minimal structure expected */
6750
memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
6751
}
6752
lpLVItem->mask = mask;
6753
TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
6754
}
6755
}
6756
6757
/* make sure lParam is zeroed out */
6758
if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
6759
6760
/* callback marked pointer required here */
6761
if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
6762
lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
6763
6764
/* we store only a little state, so if we're not asked, we're done */
6765
if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
6766
6767
/* if focus is handled by us, report it */
6768
if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6769
{
6770
lpLVItem->state &= ~LVIS_FOCUSED;
6771
if (infoPtr->nFocusedItem == lpLVItem->iItem)
6772
lpLVItem->state |= LVIS_FOCUSED;
6773
}
6774
6775
/* and do the same for selection, if we handle it */
6776
if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6777
{
6778
lpLVItem->state &= ~LVIS_SELECTED;
6779
if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6780
lpLVItem->state |= LVIS_SELECTED;
6781
}
6782
6783
return TRUE;
6784
}
6785
6786
/* find the item and subitem structures before we proceed */
6787
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
6788
lpItem = DPA_GetPtr(hdpaSubItems, 0);
6789
assert (lpItem);
6790
6791
if (lpLVItem->iSubItem)
6792
{
6793
SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
6794
if (lpSubItem)
6795
pItemHdr = &lpSubItem->hdr;
6796
else
6797
{
6798
pItemHdr = &callbackHdr;
6799
is_subitem_invalid = TRUE;
6800
}
6801
}
6802
else
6803
pItemHdr = &lpItem->hdr;
6804
6805
/* Do we need to query the state from the app? */
6806
if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && (!lpLVItem->iSubItem || is_subitem_invalid))
6807
{
6808
dispInfo.item.mask |= LVIF_STATE;
6809
dispInfo.item.stateMask = infoPtr->uCallbackMask;
6810
}
6811
6812
/* Do we need to enquire about the image? */
6813
if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
6814
(!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
6815
{
6816
dispInfo.item.mask |= LVIF_IMAGE;
6817
dispInfo.item.iImage = I_IMAGECALLBACK;
6818
}
6819
6820
/* Only items support indentation */
6821
if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK && !lpLVItem->iSubItem)
6822
{
6823
dispInfo.item.mask |= LVIF_INDENT;
6824
dispInfo.item.iIndent = I_INDENTCALLBACK;
6825
}
6826
6827
/* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6828
if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
6829
!is_text(pItemHdr->pszText))
6830
{
6831
dispInfo.item.mask |= LVIF_TEXT;
6832
dispInfo.item.pszText = lpLVItem->pszText;
6833
dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6834
if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
6835
*dispInfo.item.pszText = '\0';
6836
}
6837
6838
/* If we don't have all the requested info, query the application */
6839
if (dispInfo.item.mask)
6840
{
6841
dispInfo.item.iItem = lpLVItem->iItem;
6842
dispInfo.item.iSubItem = lpLVItem->iSubItem;
6843
dispInfo.item.lParam = lpItem->lParam;
6844
notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6845
TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
6846
}
6847
6848
/* we should not store values for subitems */
6849
if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
6850
6851
/* Now, handle the iImage field */
6852
if (dispInfo.item.mask & LVIF_IMAGE)
6853
{
6854
lpLVItem->iImage = dispInfo.item.iImage;
6855
if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6856
pItemHdr->iImage = dispInfo.item.iImage;
6857
}
6858
else if (lpLVItem->mask & LVIF_IMAGE)
6859
{
6860
if (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6861
lpLVItem->iImage = pItemHdr->iImage;
6862
}
6863
6864
/* The pszText field */
6865
if (dispInfo.item.mask & LVIF_TEXT)
6866
{
6867
if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6868
textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6869
6870
lpLVItem->pszText = dispInfo.item.pszText;
6871
}
6872
else if (lpLVItem->mask & LVIF_TEXT)
6873
{
6874
/* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6875
if (isW || !is_text(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
6876
else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
6877
}
6878
6879
/* Next is the lParam field */
6880
if (dispInfo.item.mask & LVIF_PARAM)
6881
{
6882
lpLVItem->lParam = dispInfo.item.lParam;
6883
if ((dispInfo.item.mask & LVIF_DI_SETITEM))
6884
lpItem->lParam = dispInfo.item.lParam;
6885
}
6886
else if (lpLVItem->mask & LVIF_PARAM)
6887
lpLVItem->lParam = lpItem->lParam;
6888
6889
/* if this is a subitem, we're done */
6890
if (lpLVItem->iSubItem) return TRUE;
6891
6892
/* ... the state field (this one is different due to uCallbackmask) */
6893
if (lpLVItem->mask & LVIF_STATE)
6894
{
6895
lpLVItem->state = lpItem->state & lpLVItem->stateMask;
6896
if (dispInfo.item.mask & LVIF_STATE)
6897
{
6898
lpLVItem->state &= ~dispInfo.item.stateMask;
6899
lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
6900
}
6901
if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6902
{
6903
lpLVItem->state &= ~LVIS_FOCUSED;
6904
if (infoPtr->nFocusedItem == lpLVItem->iItem)
6905
lpLVItem->state |= LVIS_FOCUSED;
6906
}
6907
if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6908
{
6909
lpLVItem->state &= ~LVIS_SELECTED;
6910
if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6911
lpLVItem->state |= LVIS_SELECTED;
6912
}
6913
}
6914
6915
/* and last, but not least, the indent field */
6916
if (dispInfo.item.mask & LVIF_INDENT)
6917
{
6918
lpLVItem->iIndent = dispInfo.item.iIndent;
6919
if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
6920
lpItem->iIndent = dispInfo.item.iIndent;
6921
}
6922
else if (lpLVItem->mask & LVIF_INDENT)
6923
{
6924
lpLVItem->iIndent = lpItem->iIndent;
6925
}
6926
6927
return TRUE;
6928
}
6929
6930
/***
6931
* DESCRIPTION:
6932
* Retrieves item attributes.
6933
*
6934
* PARAMETER(S):
6935
* [I] hwnd : window handle
6936
* [IO] lpLVItem : item info
6937
* [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6938
* if FALSE, then lpLVItem is a LPLVITEMA.
6939
*
6940
* NOTE:
6941
* This is the external 'GetItem' interface -- it properly copies
6942
* the text in the provided buffer.
6943
*
6944
* RETURN:
6945
* SUCCESS : TRUE
6946
* FAILURE : FALSE
6947
*/
6948
static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6949
{
6950
LPWSTR pszText;
6951
BOOL bResult;
6952
6953
if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6954
return FALSE;
6955
6956
pszText = lpLVItem->pszText;
6957
bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
6958
if (bResult && (lpLVItem->mask & LVIF_TEXT) && lpLVItem->pszText != pszText)
6959
{
6960
if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6961
textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
6962
else
6963
pszText = LPSTR_TEXTCALLBACKW;
6964
}
6965
lpLVItem->pszText = pszText;
6966
6967
return bResult;
6968
}
6969
6970
6971
/***
6972
* DESCRIPTION:
6973
* Retrieves the position (upper-left) of the listview control item.
6974
* Note that for LVS_ICON style, the upper-left is that of the icon
6975
* and not the bounding box.
6976
*
6977
* PARAMETER(S):
6978
* [I] infoPtr : valid pointer to the listview structure
6979
* [I] nItem : item index
6980
* [O] lpptPosition : coordinate information
6981
*
6982
* RETURN:
6983
* SUCCESS : TRUE
6984
* FAILURE : FALSE
6985
*/
6986
static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
6987
{
6988
POINT Origin;
6989
6990
TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6991
6992
if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6993
6994
LISTVIEW_GetOrigin(infoPtr, &Origin);
6995
LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6996
6997
if (infoPtr->uView == LV_VIEW_ICON)
6998
{
6999
lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7000
lpptPosition->y += ICON_TOP_PADDING;
7001
}
7002
lpptPosition->x += Origin.x;
7003
lpptPosition->y += Origin.y;
7004
7005
TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
7006
return TRUE;
7007
}
7008
7009
7010
/***
7011
* DESCRIPTION:
7012
* Retrieves the bounding rectangle for a listview control item.
7013
*
7014
* PARAMETER(S):
7015
* [I] infoPtr : valid pointer to the listview structure
7016
* [I] nItem : item index
7017
* [IO] lprc : bounding rectangle coordinates
7018
* lprc->left specifies the portion of the item for which the bounding
7019
* rectangle will be retrieved.
7020
*
7021
* LVIR_BOUNDS Returns the bounding rectangle of the entire item,
7022
* including the icon and label.
7023
* *
7024
* * For LVS_ICON
7025
* * Experiment shows that native control returns:
7026
* * width = min (48, length of text line)
7027
* * .left = position.x - (width - iconsize.cx)/2
7028
* * .right = .left + width
7029
* * height = #lines of text * ntmHeight + icon height + 8
7030
* * .top = position.y - 2
7031
* * .bottom = .top + height
7032
* * separation between items .y = itemSpacing.cy - height
7033
* * .x = itemSpacing.cx - width
7034
* LVIR_ICON Returns the bounding rectangle of the icon or small icon.
7035
* *
7036
* * For LVS_ICON
7037
* * Experiment shows that native control returns:
7038
* * width = iconSize.cx + 16
7039
* * .left = position.x - (width - iconsize.cx)/2
7040
* * .right = .left + width
7041
* * height = iconSize.cy + 4
7042
* * .top = position.y - 2
7043
* * .bottom = .top + height
7044
* * separation between items .y = itemSpacing.cy - height
7045
* * .x = itemSpacing.cx - width
7046
* LVIR_LABEL Returns the bounding rectangle of the item text.
7047
* *
7048
* * For LVS_ICON
7049
* * Experiment shows that native control returns:
7050
* * width = text length
7051
* * .left = position.x - width/2
7052
* * .right = .left + width
7053
* * height = ntmH * linecount + 2
7054
* * .top = position.y + iconSize.cy + 6
7055
* * .bottom = .top + height
7056
* * separation between items .y = itemSpacing.cy - height
7057
* * .x = itemSpacing.cx - width
7058
* LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
7059
* rectangles, but excludes columns in report view.
7060
*
7061
* RETURN:
7062
* SUCCESS : TRUE
7063
* FAILURE : FALSE
7064
*
7065
* NOTES
7066
* Note that the bounding rectangle of the label in the LVS_ICON view depends
7067
* upon whether the window has the focus currently and on whether the item
7068
* is the one with the focus. Ensure that the control's record of which
7069
* item has the focus agrees with the items' records.
7070
*/
7071
static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
7072
{
7073
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7074
BOOL doLabel = TRUE, oversizedBox = FALSE;
7075
POINT Position, Origin;
7076
LVITEMW lvItem;
7077
LONG mode;
7078
7079
TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
7080
7081
if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7082
7083
LISTVIEW_GetOrigin(infoPtr, &Origin);
7084
LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7085
7086
/* Be smart and try to figure out the minimum we have to do */
7087
if (lprc->left == LVIR_ICON) doLabel = FALSE;
7088
if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
7089
if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
7090
infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
7091
oversizedBox = TRUE;
7092
7093
/* get what we need from the item before hand, so we make
7094
* only one request. This can speed up things, if data
7095
* is stored on the app side */
7096
lvItem.mask = 0;
7097
if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7098
if (doLabel) lvItem.mask |= LVIF_TEXT;
7099
lvItem.iItem = nItem;
7100
lvItem.iSubItem = 0;
7101
lvItem.pszText = szDispText;
7102
lvItem.cchTextMax = DISP_TEXT_SIZE;
7103
if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
7104
/* we got the state already up, simulate it here, to avoid a reget */
7105
if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
7106
{
7107
lvItem.mask |= LVIF_STATE;
7108
lvItem.stateMask = LVIS_FOCUSED;
7109
lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
7110
}
7111
7112
if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
7113
lprc->left = LVIR_BOUNDS;
7114
7115
mode = lprc->left;
7116
switch(lprc->left)
7117
{
7118
case LVIR_ICON:
7119
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
7120
break;
7121
7122
case LVIR_LABEL:
7123
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
7124
break;
7125
7126
case LVIR_BOUNDS:
7127
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
7128
break;
7129
7130
case LVIR_SELECTBOUNDS:
7131
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
7132
break;
7133
7134
default:
7135
WARN("Unknown value: %ld\n", lprc->left);
7136
return FALSE;
7137
}
7138
7139
if (infoPtr->uView == LV_VIEW_DETAILS)
7140
{
7141
if (mode != LVIR_BOUNDS)
7142
OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left,
7143
Position.y + Origin.y);
7144
else
7145
OffsetRect(lprc, Origin.x, Position.y + Origin.y);
7146
}
7147
else
7148
OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
7149
7150
TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
7151
7152
return TRUE;
7153
}
7154
7155
/***
7156
* DESCRIPTION:
7157
* Retrieves the spacing between listview control items.
7158
*
7159
* PARAMETER(S):
7160
* [I] infoPtr : valid pointer to the listview structure
7161
* [IO] lprc : rectangle to receive the output
7162
* on input, lprc->top = nSubItem
7163
* lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
7164
*
7165
* NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
7166
* not only those of the first column.
7167
*
7168
* RETURN:
7169
* TRUE: success
7170
* FALSE: failure
7171
*/
7172
static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT item, LPRECT lprc)
7173
{
7174
RECT rect = { 0, 0, 0, 0 };
7175
POINT origin;
7176
INT y;
7177
7178
if (!lprc) return FALSE;
7179
7180
TRACE("item %d, subitem %ld, type %ld\n", item, lprc->top, lprc->left);
7181
7182
/* Subitem of '0' means item itself, and this works for all control view modes */
7183
if (lprc->top == 0)
7184
return LISTVIEW_GetItemRect(infoPtr, item, lprc);
7185
7186
if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
7187
7188
LISTVIEW_GetOrigin(infoPtr, &origin);
7189
/* this works for any item index, no matter if it exists or not */
7190
y = item * infoPtr->nItemHeight + origin.y;
7191
7192
if (infoPtr->hwndHeader && SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)&rect))
7193
{
7194
rect.top = 0;
7195
rect.bottom = infoPtr->nItemHeight;
7196
}
7197
else
7198
{
7199
/* Native implementation is broken for this case and garbage is left for left and right fields,
7200
we zero them to get predictable output */
7201
lprc->left = lprc->right = lprc->top = 0;
7202
lprc->bottom = infoPtr->nItemHeight;
7203
OffsetRect(lprc, origin.x, y);
7204
TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
7205
return TRUE;
7206
}
7207
7208
switch (lprc->left)
7209
{
7210
case LVIR_ICON:
7211
{
7212
/* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
7213
if (infoPtr->himlSmall)
7214
rect.right = rect.left + infoPtr->iconSize.cx;
7215
else
7216
rect.right = rect.left;
7217
7218
rect.bottom = rect.top + infoPtr->iconSize.cy;
7219
break;
7220
}
7221
case LVIR_LABEL:
7222
case LVIR_BOUNDS:
7223
break;
7224
7225
default:
7226
ERR("Unknown bounds %ld\n", lprc->left);
7227
return FALSE;
7228
}
7229
7230
OffsetRect(&rect, origin.x, y);
7231
*lprc = rect;
7232
TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
7233
7234
return TRUE;
7235
}
7236
7237
/***
7238
* DESCRIPTION:
7239
* Retrieves the spacing between listview control items.
7240
*
7241
* PARAMETER(S):
7242
* [I] infoPtr : valid pointer to the listview structure
7243
* [I] bSmall : flag for small or large icon
7244
*
7245
* RETURN:
7246
* Horizontal + vertical spacing
7247
*/
7248
static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
7249
{
7250
LONG lResult;
7251
7252
if (!bSmall)
7253
{
7254
lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7255
}
7256
else
7257
{
7258
if (infoPtr->uView == LV_VIEW_ICON)
7259
lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
7260
else
7261
lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
7262
}
7263
return lResult;
7264
}
7265
7266
/***
7267
* DESCRIPTION:
7268
* Retrieves the state of a listview control item.
7269
*
7270
* PARAMETER(S):
7271
* [I] infoPtr : valid pointer to the listview structure
7272
* [I] nItem : item index
7273
* [I] uMask : state mask
7274
*
7275
* RETURN:
7276
* State specified by the mask.
7277
*/
7278
static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
7279
{
7280
LVITEMW lvItem;
7281
7282
if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
7283
7284
lvItem.iItem = nItem;
7285
lvItem.iSubItem = 0;
7286
lvItem.mask = LVIF_STATE;
7287
lvItem.stateMask = uMask;
7288
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
7289
7290
return lvItem.state & uMask;
7291
}
7292
7293
/***
7294
* DESCRIPTION:
7295
* Retrieves the text of a listview control item or subitem.
7296
*
7297
* PARAMETER(S):
7298
* [I] hwnd : window handle
7299
* [I] nItem : item index
7300
* [IO] lpLVItem : item information
7301
* [I] isW : TRUE if lpLVItem is Unicode
7302
*
7303
* RETURN:
7304
* SUCCESS : string length
7305
* FAILURE : 0
7306
*/
7307
static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7308
{
7309
if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
7310
7311
lpLVItem->mask = LVIF_TEXT;
7312
lpLVItem->iItem = nItem;
7313
if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
7314
7315
return textlenT(lpLVItem->pszText, isW);
7316
}
7317
7318
/***
7319
* DESCRIPTION:
7320
* Searches for an item based on properties + relationships.
7321
*
7322
* PARAMETER(S):
7323
* [I] infoPtr : valid pointer to the listview structure
7324
* [I] nItem : item index
7325
* [I] uFlags : relationship flag
7326
*
7327
* RETURN:
7328
* SUCCESS : item index
7329
* FAILURE : -1
7330
*/
7331
static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
7332
{
7333
UINT uMask = 0;
7334
LVFINDINFOW lvFindInfo;
7335
INT nCountPerColumn;
7336
INT nCountPerRow;
7337
INT i;
7338
7339
TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
7340
if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
7341
7342
ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
7343
7344
if (uFlags & LVNI_CUT)
7345
uMask |= LVIS_CUT;
7346
7347
if (uFlags & LVNI_DROPHILITED)
7348
uMask |= LVIS_DROPHILITED;
7349
7350
if (uFlags & LVNI_FOCUSED)
7351
uMask |= LVIS_FOCUSED;
7352
7353
if (uFlags & LVNI_SELECTED)
7354
uMask |= LVIS_SELECTED;
7355
7356
/* if we're asked for the focused item, that's only one,
7357
* so it's worth optimizing */
7358
if (uFlags & LVNI_FOCUSED)
7359
{
7360
if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
7361
return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
7362
}
7363
7364
if (uFlags & LVNI_ABOVE)
7365
{
7366
if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7367
{
7368
while (nItem >= 0)
7369
{
7370
nItem--;
7371
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7372
return nItem;
7373
}
7374
}
7375
else
7376
{
7377
/* Special case for autoarrange - move 'til the top of a list */
7378
if (is_autoarrange(infoPtr))
7379
{
7380
nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7381
while (nItem - nCountPerRow >= 0)
7382
{
7383
nItem -= nCountPerRow;
7384
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7385
return nItem;
7386
}
7387
return -1;
7388
}
7389
lvFindInfo.flags = LVFI_NEARESTXY;
7390
lvFindInfo.vkDirection = VK_UP;
7391
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7392
while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7393
{
7394
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7395
return nItem;
7396
}
7397
}
7398
}
7399
else if (uFlags & LVNI_BELOW)
7400
{
7401
if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7402
{
7403
while (nItem < infoPtr->nItemCount - 1)
7404
{
7405
nItem++;
7406
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7407
return nItem;
7408
}
7409
}
7410
else
7411
{
7412
/* Special case for autoarrange - move 'til the bottom of a list */
7413
if (is_autoarrange(infoPtr))
7414
{
7415
nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7416
while (nItem + nCountPerRow < infoPtr->nItemCount )
7417
{
7418
nItem += nCountPerRow;
7419
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7420
return nItem;
7421
}
7422
return -1;
7423
}
7424
lvFindInfo.flags = LVFI_NEARESTXY;
7425
lvFindInfo.vkDirection = VK_DOWN;
7426
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7427
while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7428
{
7429
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7430
return nItem;
7431
}
7432
}
7433
}
7434
else if (uFlags & LVNI_TOLEFT)
7435
{
7436
if (infoPtr->uView == LV_VIEW_LIST)
7437
{
7438
nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7439
while (nItem - nCountPerColumn >= 0)
7440
{
7441
nItem -= nCountPerColumn;
7442
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7443
return nItem;
7444
}
7445
}
7446
else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7447
{
7448
/* Special case for autoarrange - move 'til the beginning of a row */
7449
if (is_autoarrange(infoPtr))
7450
{
7451
nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7452
while (nItem % nCountPerRow > 0)
7453
{
7454
nItem --;
7455
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7456
return nItem;
7457
}
7458
return -1;
7459
}
7460
lvFindInfo.flags = LVFI_NEARESTXY;
7461
lvFindInfo.vkDirection = VK_LEFT;
7462
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7463
while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7464
{
7465
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7466
return nItem;
7467
}
7468
}
7469
}
7470
else if (uFlags & LVNI_TORIGHT)
7471
{
7472
if (infoPtr->uView == LV_VIEW_LIST)
7473
{
7474
nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7475
while (nItem + nCountPerColumn < infoPtr->nItemCount)
7476
{
7477
nItem += nCountPerColumn;
7478
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7479
return nItem;
7480
}
7481
}
7482
else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7483
{
7484
/* Special case for autoarrange - move 'til the end of a row */
7485
if (is_autoarrange(infoPtr))
7486
{
7487
nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7488
while (nItem % nCountPerRow < nCountPerRow - 1 )
7489
{
7490
nItem ++;
7491
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7492
return nItem;
7493
}
7494
return -1;
7495
}
7496
lvFindInfo.flags = LVFI_NEARESTXY;
7497
lvFindInfo.vkDirection = VK_RIGHT;
7498
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7499
while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7500
{
7501
if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7502
return nItem;
7503
}
7504
}
7505
}
7506
else
7507
{
7508
nItem++;
7509
7510
/* search by index */
7511
for (i = nItem; i < infoPtr->nItemCount; i++)
7512
{
7513
if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
7514
return i;
7515
}
7516
}
7517
7518
return -1;
7519
}
7520
7521
static BOOL LISTVIEW_GetNextItemIndex(const LISTVIEW_INFO *infoPtr, LVITEMINDEX *index, UINT flags)
7522
{
7523
/* FIXME: specified item group is ignored */
7524
7525
if (!index)
7526
return FALSE;
7527
7528
index->iItem = LISTVIEW_GetNextItem(infoPtr, index->iItem, flags);
7529
return index->iItem != -1;
7530
}
7531
7532
/* LISTVIEW_GetNumberOfWorkAreas */
7533
7534
/***
7535
* DESCRIPTION:
7536
* Retrieves the origin coordinates when in icon or small icon display mode.
7537
*
7538
* PARAMETER(S):
7539
* [I] infoPtr : valid pointer to the listview structure
7540
* [O] lpptOrigin : coordinate information
7541
*
7542
* RETURN:
7543
* None.
7544
*/
7545
static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
7546
{
7547
INT nHorzPos = 0, nVertPos = 0;
7548
SCROLLINFO scrollInfo;
7549
7550
scrollInfo.cbSize = sizeof(SCROLLINFO);
7551
scrollInfo.fMask = SIF_POS;
7552
7553
if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
7554
nHorzPos = scrollInfo.nPos;
7555
if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7556
nVertPos = scrollInfo.nPos;
7557
7558
TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
7559
7560
lpptOrigin->x = infoPtr->rcList.left;
7561
lpptOrigin->y = infoPtr->rcList.top;
7562
if (infoPtr->uView == LV_VIEW_LIST)
7563
nHorzPos *= infoPtr->nItemWidth;
7564
else if (infoPtr->uView == LV_VIEW_DETAILS)
7565
nVertPos *= infoPtr->nItemHeight;
7566
7567
lpptOrigin->x -= nHorzPos;
7568
lpptOrigin->y -= nVertPos;
7569
7570
TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
7571
}
7572
7573
/***
7574
* DESCRIPTION:
7575
* Retrieves the width of a string.
7576
*
7577
* PARAMETER(S):
7578
* [I] hwnd : window handle
7579
* [I] lpszText : text string to process
7580
* [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7581
*
7582
* RETURN:
7583
* SUCCESS : string width (in pixels)
7584
* FAILURE : zero
7585
*/
7586
static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
7587
{
7588
SIZE stringSize;
7589
7590
stringSize.cx = 0;
7591
if (is_text(lpszText))
7592
{
7593
HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
7594
HDC hdc = GetDC(infoPtr->hwndSelf);
7595
HFONT hOldFont = SelectObject(hdc, hFont);
7596
7597
if (isW)
7598
GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
7599
else
7600
GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
7601
SelectObject(hdc, hOldFont);
7602
ReleaseDC(infoPtr->hwndSelf, hdc);
7603
}
7604
return stringSize.cx;
7605
}
7606
7607
/***
7608
* DESCRIPTION:
7609
* Determines which listview item is located at the specified position.
7610
*
7611
* PARAMETER(S):
7612
* [I] infoPtr : valid pointer to the listview structure
7613
* [IO] lpht : hit test information
7614
* [I] subitem : fill out iSubItem.
7615
* [I] select : return the index only if the hit selects the item
7616
*
7617
* NOTE:
7618
* (mm 20001022): We must not allow iSubItem to be touched, for
7619
* an app might pass only a structure with space up to iItem!
7620
* (MS Office 97 does that for instance in the file open dialog)
7621
*
7622
* RETURN:
7623
* SUCCESS : item index
7624
* FAILURE : -1
7625
*/
7626
static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
7627
{
7628
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7629
RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
7630
POINT Origin, Position, opt;
7631
BOOL is_fullrow;
7632
LVITEMW lvItem;
7633
ITERATOR i;
7634
INT iItem;
7635
7636
TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
7637
7638
lpht->flags = 0;
7639
lpht->iItem = -1;
7640
if (subitem) lpht->iSubItem = 0;
7641
7642
LISTVIEW_GetOrigin(infoPtr, &Origin);
7643
7644
/* set whole list relation flags */
7645
if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
7646
{
7647
/* LVM_SUBITEMHITTEST checks left bound of possible client area */
7648
if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
7649
lpht->flags |= LVHT_TOLEFT;
7650
7651
if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7652
opt.y = lpht->pt.y + infoPtr->rcList.top;
7653
else
7654
opt.y = lpht->pt.y;
7655
7656
if (infoPtr->rcList.bottom < opt.y)
7657
lpht->flags |= LVHT_BELOW;
7658
}
7659
else
7660
{
7661
if (infoPtr->rcList.left > lpht->pt.x)
7662
lpht->flags |= LVHT_TOLEFT;
7663
else if (infoPtr->rcList.right < lpht->pt.x)
7664
lpht->flags |= LVHT_TORIGHT;
7665
7666
if (infoPtr->rcList.top > lpht->pt.y)
7667
lpht->flags |= LVHT_ABOVE;
7668
else if (infoPtr->rcList.bottom < lpht->pt.y)
7669
lpht->flags |= LVHT_BELOW;
7670
}
7671
7672
/* even if item is invalid try to find subitem */
7673
if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
7674
{
7675
RECT *pRect;
7676
INT j;
7677
7678
opt.x = lpht->pt.x - Origin.x;
7679
7680
lpht->iSubItem = -1;
7681
for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
7682
{
7683
pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
7684
7685
if ((opt.x >= pRect->left) && (opt.x < pRect->right))
7686
{
7687
lpht->iSubItem = j;
7688
break;
7689
}
7690
}
7691
TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
7692
7693
/* if we're outside horizontal columns bounds there's nothing to test further */
7694
if (lpht->iSubItem == -1)
7695
{
7696
lpht->iItem = -1;
7697
lpht->flags = LVHT_NOWHERE;
7698
return -1;
7699
}
7700
}
7701
7702
TRACE("lpht->flags=0x%x\n", lpht->flags);
7703
if (lpht->flags) return -1;
7704
7705
lpht->flags |= LVHT_NOWHERE;
7706
7707
/* first deal with the large items */
7708
rcSearch.left = lpht->pt.x;
7709
rcSearch.top = lpht->pt.y;
7710
rcSearch.right = rcSearch.left + 1;
7711
rcSearch.bottom = rcSearch.top + 1;
7712
7713
iterator_frameditems(&i, infoPtr, &rcSearch);
7714
iterator_next(&i); /* go to first item in the sequence */
7715
iItem = i.nItem;
7716
iterator_destroy(&i);
7717
7718
TRACE("lpht->iItem=%d\n", iItem);
7719
if (iItem == -1) return -1;
7720
7721
lvItem.mask = LVIF_STATE | LVIF_TEXT;
7722
if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7723
lvItem.stateMask = LVIS_STATEIMAGEMASK;
7724
if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
7725
lvItem.iItem = iItem;
7726
lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
7727
lvItem.pszText = szDispText;
7728
lvItem.cchTextMax = DISP_TEXT_SIZE;
7729
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
7730
if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
7731
7732
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7733
LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
7734
opt.x = lpht->pt.x - Position.x - Origin.x;
7735
7736
if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7737
opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
7738
else
7739
opt.y = lpht->pt.y - Position.y - Origin.y;
7740
7741
if (infoPtr->uView == LV_VIEW_DETAILS)
7742
{
7743
rcBounds = rcBox;
7744
if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
7745
opt.x = lpht->pt.x - Origin.x;
7746
}
7747
else
7748
{
7749
UnionRect(&rcBounds, &rcIcon, &rcLabel);
7750
UnionRect(&rcBounds, &rcBounds, &rcState);
7751
}
7752
TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
7753
if (!PtInRect(&rcBounds, opt)) return -1;
7754
7755
/* That's a special case - row rectangle is used as item rectangle and
7756
returned flags contain all item parts. */
7757
is_fullrow = (infoPtr->uView == LV_VIEW_DETAILS) && ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || (infoPtr->dwStyle & LVS_OWNERDRAWFIXED));
7758
7759
if (PtInRect(&rcIcon, opt))
7760
lpht->flags |= LVHT_ONITEMICON;
7761
else if (PtInRect(&rcLabel, opt))
7762
lpht->flags |= LVHT_ONITEMLABEL;
7763
else if (infoPtr->himlState && PtInRect(&rcState, opt))
7764
lpht->flags |= LVHT_ONITEMSTATEICON;
7765
if (is_fullrow && !(lpht->flags & LVHT_ONITEM))
7766
{
7767
lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
7768
}
7769
if (lpht->flags & LVHT_ONITEM)
7770
lpht->flags &= ~LVHT_NOWHERE;
7771
TRACE("lpht->flags=0x%x\n", lpht->flags);
7772
7773
if (select && !is_fullrow)
7774
{
7775
if (infoPtr->uView == LV_VIEW_DETAILS)
7776
{
7777
/* get main item bounds */
7778
lvItem.iSubItem = 0;
7779
LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7780
UnionRect(&rcBounds, &rcIcon, &rcLabel);
7781
UnionRect(&rcBounds, &rcBounds, &rcState);
7782
}
7783
if (!PtInRect(&rcBounds, opt)) iItem = -1;
7784
}
7785
return lpht->iItem = iItem;
7786
}
7787
7788
/***
7789
* DESCRIPTION:
7790
* Inserts a new item in the listview control.
7791
*
7792
* PARAMETER(S):
7793
* [I] infoPtr : valid pointer to the listview structure
7794
* [I] lpLVItem : item information
7795
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7796
*
7797
* RETURN:
7798
* SUCCESS : new item index
7799
* FAILURE : -1
7800
*/
7801
static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
7802
{
7803
INT nItem;
7804
HDPA hdpaSubItems;
7805
NMLISTVIEW nmlv;
7806
ITEM_INFO *lpItem;
7807
ITEM_ID *lpID;
7808
BOOL is_sorted, has_changed;
7809
LVITEMW item;
7810
HWND hwndSelf = infoPtr->hwndSelf;
7811
7812
TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
7813
7814
if (infoPtr->dwStyle & LVS_OWNERDATA)
7815
{
7816
NotifyWinEvent( EVENT_OBJECT_CREATE, hwndSelf, OBJID_CLIENT, infoPtr->nItemCount + 1 );
7817
return infoPtr->nItemCount++;
7818
}
7819
7820
/* make sure it's an item, and not a subitem; cannot insert a subitem */
7821
if (!lpLVItem || lpLVItem->iSubItem) return -1;
7822
7823
if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7824
7825
if (!(lpItem = Alloc(sizeof(*lpItem)))) return -1;
7826
7827
/* insert item in listview control data structure */
7828
if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7829
if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7830
7831
/* link with id struct */
7832
if (!(lpID = Alloc(sizeof(*lpID)))) goto fail;
7833
lpItem->id = lpID;
7834
lpID->item = hdpaSubItems;
7835
lpID->id = get_next_itemid(infoPtr);
7836
if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7837
7838
is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7839
!(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7840
7841
if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7842
7843
/* calculate new item index */
7844
if (is_sorted)
7845
{
7846
HDPA hItem;
7847
ITEM_INFO *item_s;
7848
INT i = 0, cmpv;
7849
WCHAR *textW;
7850
7851
textW = textdupTtoW(lpLVItem->pszText, isW);
7852
7853
while (i < infoPtr->nItemCount)
7854
{
7855
hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
7856
item_s = DPA_GetPtr(hItem, 0);
7857
7858
cmpv = textcmpWT(item_s->hdr.pszText, textW, TRUE);
7859
if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7860
7861
if (cmpv >= 0) break;
7862
i++;
7863
}
7864
7865
textfreeT(textW, isW);
7866
7867
nItem = i;
7868
}
7869
else
7870
nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7871
7872
TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7873
nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7874
if (nItem == -1) goto fail;
7875
infoPtr->nItemCount++;
7876
7877
/* shift indices first so they don't get tangled */
7878
LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7879
7880
/* set the item attributes */
7881
if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7882
{
7883
/* full size structure expected - _WIN32IE >= 0x560 */
7884
item = *lpLVItem;
7885
}
7886
else if (lpLVItem->mask & LVIF_INDENT)
7887
{
7888
/* indent member expected - _WIN32IE >= 0x300 */
7889
memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7890
}
7891
else
7892
{
7893
/* minimal structure expected */
7894
memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7895
}
7896
item.iItem = nItem;
7897
if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7898
{
7899
if (item.mask & LVIF_STATE)
7900
{
7901
item.stateMask |= LVIS_STATEIMAGEMASK;
7902
item.state &= ~LVIS_STATEIMAGEMASK;
7903
item.state |= INDEXTOSTATEIMAGEMASK(1);
7904
}
7905
else
7906
{
7907
item.mask |= LVIF_STATE;
7908
item.stateMask = LVIS_STATEIMAGEMASK;
7909
item.state = INDEXTOSTATEIMAGEMASK(1);
7910
}
7911
}
7912
7913
if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7914
7915
/* make room for the position, if we are in the right mode */
7916
if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7917
{
7918
if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7919
goto undo;
7920
if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7921
{
7922
DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7923
goto undo;
7924
}
7925
}
7926
7927
/* send LVN_INSERTITEM notification */
7928
memset(&nmlv, 0, sizeof(NMLISTVIEW));
7929
nmlv.iItem = nItem;
7930
nmlv.lParam = lpItem->lParam;
7931
notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7932
if (!IsWindow(hwndSelf))
7933
return -1;
7934
7935
/* align items (set position of each item) */
7936
if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7937
{
7938
POINT pt;
7939
7940
if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7941
LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7942
else
7943
LISTVIEW_NextIconPosTop(infoPtr, &pt);
7944
7945
LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7946
}
7947
7948
/* now is the invalidation fun */
7949
LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7950
7951
NotifyWinEvent( EVENT_OBJECT_CREATE, hwndSelf, OBJID_CLIENT, nItem + 1 );
7952
7953
return nItem;
7954
7955
undo:
7956
LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7957
LISTVIEW_ShiftFocus(infoPtr, infoPtr->nFocusedItem, nItem, -1);
7958
DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7959
infoPtr->nItemCount--;
7960
fail:
7961
DPA_DeletePtr(hdpaSubItems, 0);
7962
DPA_Destroy (hdpaSubItems);
7963
Free (lpItem);
7964
return -1;
7965
}
7966
7967
/***
7968
* DESCRIPTION:
7969
* Checks item visibility.
7970
*
7971
* PARAMETER(S):
7972
* [I] infoPtr : valid pointer to the listview structure
7973
* [I] nFirst : item index to check for
7974
*
7975
* RETURN:
7976
* Item visible : TRUE
7977
* Item invisible or failure : FALSE
7978
*/
7979
static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7980
{
7981
POINT Origin, Position;
7982
RECT rcItem;
7983
HDC hdc;
7984
BOOL ret;
7985
7986
TRACE("nItem=%d\n", nItem);
7987
7988
if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7989
7990
LISTVIEW_GetOrigin(infoPtr, &Origin);
7991
LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7992
rcItem.left = Position.x + Origin.x;
7993
rcItem.top = Position.y + Origin.y;
7994
rcItem.right = rcItem.left + infoPtr->nItemWidth;
7995
rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7996
7997
hdc = GetDC(infoPtr->hwndSelf);
7998
if (!hdc) return FALSE;
7999
ret = RectVisible(hdc, &rcItem);
8000
ReleaseDC(infoPtr->hwndSelf, hdc);
8001
8002
return ret;
8003
}
8004
8005
/***
8006
* DESCRIPTION:
8007
* Redraws a range of items.
8008
*
8009
* PARAMETER(S):
8010
* [I] infoPtr : valid pointer to the listview structure
8011
* [I] nFirst : first item
8012
* [I] nLast : last item
8013
*
8014
* RETURN:
8015
* SUCCESS : TRUE
8016
* FAILURE : FALSE
8017
*/
8018
static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
8019
{
8020
INT i;
8021
8022
for (i = max(nFirst, 0); i <= min(nLast, infoPtr->nItemCount - 1); i++)
8023
LISTVIEW_InvalidateItem(infoPtr, i);
8024
8025
return TRUE;
8026
}
8027
8028
/***
8029
* DESCRIPTION:
8030
* Scroll the content of a listview.
8031
*
8032
* PARAMETER(S):
8033
* [I] infoPtr : valid pointer to the listview structure
8034
* [I] dx : horizontal scroll amount in pixels
8035
* [I] dy : vertical scroll amount in pixels
8036
*
8037
* RETURN:
8038
* SUCCESS : TRUE
8039
* FAILURE : FALSE
8040
*
8041
* COMMENTS:
8042
* If the control is in report view (LV_VIEW_DETAILS) the control can
8043
* be scrolled only in line increments. "dy" will be rounded to the
8044
* nearest number of pixels that are a whole line. Ex: if line height
8045
* is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
8046
* is passed, then the scroll will be 0. (per MSDN 7/2002)
8047
*/
8048
static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8049
{
8050
switch(infoPtr->uView) {
8051
case LV_VIEW_DETAILS:
8052
dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
8053
dy /= infoPtr->nItemHeight;
8054
break;
8055
case LV_VIEW_LIST:
8056
if (dy != 0) return FALSE;
8057
break;
8058
default: /* icon */
8059
break;
8060
}
8061
8062
if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx);
8063
if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy);
8064
8065
return TRUE;
8066
}
8067
8068
/***
8069
* DESCRIPTION:
8070
* Sets the background color.
8071
*
8072
* PARAMETER(S):
8073
* [I] infoPtr : valid pointer to the listview structure
8074
* [I] color : background color
8075
*
8076
* RETURN:
8077
* SUCCESS : TRUE
8078
* FAILURE : FALSE
8079
*/
8080
static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF color)
8081
{
8082
TRACE("color %lx\n", color);
8083
8084
if(infoPtr->clrBk != color) {
8085
if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8086
infoPtr->clrBk = color;
8087
if (color == CLR_NONE)
8088
infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
8089
else
8090
{
8091
infoPtr->hBkBrush = CreateSolidBrush(color);
8092
infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND;
8093
}
8094
}
8095
8096
return TRUE;
8097
}
8098
8099
static BOOL LISTVIEW_SetBkImage(LISTVIEW_INFO *infoPtr, const LVBKIMAGEW *image, BOOL isW)
8100
{
8101
TRACE("%08lx, %p, %p, %u, %d, %d\n", image->ulFlags, image->hbm, image->pszImage,
8102
image->cchImageMax, image->xOffsetPercent, image->yOffsetPercent);
8103
8104
if (image->ulFlags & ~LVBKIF_SOURCE_MASK)
8105
FIXME("unsupported flags %08lx\n", image->ulFlags & ~LVBKIF_SOURCE_MASK);
8106
8107
if (image->xOffsetPercent || image->yOffsetPercent)
8108
FIXME("unsupported offset %d,%d\n", image->xOffsetPercent, image->yOffsetPercent);
8109
8110
switch (image->ulFlags & LVBKIF_SOURCE_MASK)
8111
{
8112
case LVBKIF_SOURCE_NONE:
8113
if (infoPtr->hBkBitmap)
8114
{
8115
DeleteObject(infoPtr->hBkBitmap);
8116
infoPtr->hBkBitmap = NULL;
8117
}
8118
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8119
break;
8120
8121
case LVBKIF_SOURCE_HBITMAP:
8122
{
8123
BITMAP bm;
8124
8125
if (infoPtr->hBkBitmap)
8126
{
8127
DeleteObject(infoPtr->hBkBitmap);
8128
infoPtr->hBkBitmap = NULL;
8129
}
8130
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8131
if (GetObjectW(image->hbm, sizeof(bm), &bm) == sizeof(bm))
8132
{
8133
infoPtr->hBkBitmap = image->hbm;
8134
return TRUE;
8135
}
8136
break;
8137
}
8138
8139
case LVBKIF_SOURCE_URL:
8140
FIXME("LVBKIF_SOURCE_URL: %s\n", isW ? debugstr_w(image->pszImage) : debugstr_a((LPCSTR)image->pszImage));
8141
break;
8142
}
8143
8144
return FALSE;
8145
}
8146
8147
/*** Helper for {Insert,Set}ColumnT *only* */
8148
static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
8149
const LVCOLUMNW *lpColumn, BOOL isW)
8150
{
8151
if (lpColumn->mask & LVCF_FMT)
8152
{
8153
/* format member is valid */
8154
lphdi->mask |= HDI_FORMAT;
8155
8156
/* set text alignment (leftmost column must be left-aligned) */
8157
if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8158
lphdi->fmt |= HDF_LEFT;
8159
else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
8160
lphdi->fmt |= HDF_RIGHT;
8161
else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
8162
lphdi->fmt |= HDF_CENTER;
8163
8164
if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
8165
lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
8166
8167
if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
8168
{
8169
lphdi->fmt |= HDF_IMAGE;
8170
lphdi->iImage = I_IMAGECALLBACK;
8171
}
8172
8173
if (lpColumn->fmt & LVCFMT_FIXED_WIDTH)
8174
lphdi->fmt |= HDF_FIXEDWIDTH;
8175
}
8176
8177
if (lpColumn->mask & LVCF_WIDTH)
8178
{
8179
lphdi->mask |= HDI_WIDTH;
8180
if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
8181
{
8182
/* make it fill the remainder of the controls width */
8183
RECT rcHeader;
8184
INT item_index;
8185
8186
for(item_index = 0; item_index < (nColumn - 1); item_index++)
8187
{
8188
LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
8189
lphdi->cxy += rcHeader.right - rcHeader.left;
8190
}
8191
8192
/* retrieve the layout of the header */
8193
GetClientRect(infoPtr->hwndSelf, &rcHeader);
8194
TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
8195
8196
lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
8197
}
8198
else
8199
lphdi->cxy = lpColumn->cx;
8200
}
8201
8202
if (lpColumn->mask & LVCF_TEXT)
8203
{
8204
lphdi->mask |= HDI_TEXT | HDI_FORMAT;
8205
lphdi->fmt |= HDF_STRING;
8206
lphdi->pszText = lpColumn->pszText;
8207
lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
8208
}
8209
8210
if (lpColumn->mask & LVCF_IMAGE)
8211
{
8212
lphdi->mask |= HDI_IMAGE;
8213
lphdi->iImage = lpColumn->iImage;
8214
}
8215
8216
if (lpColumn->mask & LVCF_ORDER)
8217
{
8218
lphdi->mask |= HDI_ORDER;
8219
lphdi->iOrder = lpColumn->iOrder;
8220
}
8221
}
8222
8223
8224
/***
8225
* DESCRIPTION:
8226
* Inserts a new column.
8227
*
8228
* PARAMETER(S):
8229
* [I] infoPtr : valid pointer to the listview structure
8230
* [I] nColumn : column index
8231
* [I] lpColumn : column information
8232
* [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
8233
*
8234
* RETURN:
8235
* SUCCESS : new column index
8236
* FAILURE : -1
8237
*/
8238
static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
8239
const LVCOLUMNW *lpColumn, BOOL isW)
8240
{
8241
COLUMN_INFO *lpColumnInfo;
8242
INT nNewColumn;
8243
HDITEMW hdi;
8244
8245
TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
8246
8247
if (!lpColumn || nColumn < 0) return -1;
8248
nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
8249
8250
ZeroMemory(&hdi, sizeof(HDITEMW));
8251
column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
8252
8253
/*
8254
* A mask not including LVCF_WIDTH turns into a mask of width, width 10
8255
* (can be seen in SPY) otherwise column never gets added.
8256
*/
8257
if (!(lpColumn->mask & LVCF_WIDTH)) {
8258
hdi.mask |= HDI_WIDTH;
8259
hdi.cxy = 10;
8260
}
8261
8262
/*
8263
* when the iSubItem is available Windows copies it to the header lParam. It seems
8264
* to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
8265
*/
8266
if (lpColumn->mask & LVCF_SUBITEM)
8267
{
8268
hdi.mask |= HDI_LPARAM;
8269
hdi.lParam = lpColumn->iSubItem;
8270
}
8271
8272
/* create header if not present */
8273
LISTVIEW_CreateHeader(infoPtr);
8274
if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
8275
(infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
8276
{
8277
ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8278
}
8279
8280
/* insert item in header control */
8281
nNewColumn = SendMessageW(infoPtr->hwndHeader,
8282
isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
8283
nColumn, (LPARAM)&hdi);
8284
if (nNewColumn == -1) return -1;
8285
if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
8286
8287
/* create our own column info */
8288
if (!(lpColumnInfo = Alloc(sizeof(*lpColumnInfo)))) goto fail;
8289
if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
8290
8291
if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
8292
if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin;
8293
if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
8294
goto fail;
8295
8296
/* now we have to actually adjust the data */
8297
if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
8298
{
8299
SUBITEM_INFO *lpSubItem;
8300
HDPA hdpaSubItems;
8301
INT nItem, i;
8302
LVITEMW item;
8303
BOOL changed;
8304
8305
item.iSubItem = nNewColumn;
8306
item.mask = LVIF_TEXT | LVIF_IMAGE;
8307
item.iImage = I_IMAGECALLBACK;
8308
item.pszText = LPSTR_TEXTCALLBACKW;
8309
8310
for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
8311
{
8312
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
8313
for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
8314
{
8315
lpSubItem = DPA_GetPtr(hdpaSubItems, i);
8316
if (lpSubItem->iSubItem >= nNewColumn)
8317
lpSubItem->iSubItem++;
8318
}
8319
8320
/* add new subitem for each item */
8321
item.iItem = nItem;
8322
set_sub_item(infoPtr, &item, isW, &changed);
8323
}
8324
}
8325
8326
/* make space for the new column */
8327
LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8328
LISTVIEW_UpdateItemSize(infoPtr);
8329
8330
return nNewColumn;
8331
8332
fail:
8333
if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
8334
if (lpColumnInfo)
8335
{
8336
DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
8337
Free(lpColumnInfo);
8338
}
8339
return -1;
8340
}
8341
8342
/***
8343
* DESCRIPTION:
8344
* Sets the attributes of a header item.
8345
*
8346
* PARAMETER(S):
8347
* [I] infoPtr : valid pointer to the listview structure
8348
* [I] nColumn : column index
8349
* [I] lpColumn : column attributes
8350
* [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
8351
*
8352
* RETURN:
8353
* SUCCESS : TRUE
8354
* FAILURE : FALSE
8355
*/
8356
static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
8357
const LVCOLUMNW *lpColumn, BOOL isW)
8358
{
8359
HDITEMW hdi, hdiget;
8360
BOOL bResult;
8361
8362
TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
8363
8364
if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8365
8366
ZeroMemory(&hdi, sizeof(HDITEMW));
8367
if (lpColumn->mask & LVCF_FMT)
8368
{
8369
hdi.mask |= HDI_FORMAT;
8370
hdiget.mask = HDI_FORMAT;
8371
if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
8372
hdi.fmt = hdiget.fmt & HDF_STRING;
8373
}
8374
column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
8375
8376
/* set header item attributes */
8377
bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, nColumn, (LPARAM)&hdi);
8378
if (!bResult) return FALSE;
8379
8380
if (lpColumn->mask & LVCF_FMT)
8381
{
8382
COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
8383
INT oldFmt = lpColumnInfo->fmt;
8384
8385
lpColumnInfo->fmt = lpColumn->fmt;
8386
if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
8387
{
8388
if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
8389
}
8390
}
8391
8392
if (lpColumn->mask & LVCF_MINWIDTH)
8393
LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin;
8394
8395
return TRUE;
8396
}
8397
8398
/***
8399
* DESCRIPTION:
8400
* Sets the column order array
8401
*
8402
* PARAMETERS:
8403
* [I] infoPtr : valid pointer to the listview structure
8404
* [I] iCount : number of elements in column order array
8405
* [I] lpiArray : pointer to column order array
8406
*
8407
* RETURN:
8408
* SUCCESS : TRUE
8409
* FAILURE : FALSE
8410
*/
8411
static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
8412
{
8413
if (!infoPtr->hwndHeader) return FALSE;
8414
infoPtr->colRectsDirty = TRUE;
8415
return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray);
8416
}
8417
8418
/***
8419
* DESCRIPTION:
8420
* Sets the width of a column
8421
*
8422
* PARAMETERS:
8423
* [I] infoPtr : valid pointer to the listview structure
8424
* [I] nColumn : column index
8425
* [I] cx : column width
8426
*
8427
* RETURN:
8428
* SUCCESS : TRUE
8429
* FAILURE : FALSE
8430
*/
8431
static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
8432
{
8433
WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
8434
INT max_cx = 0;
8435
HDITEMW hdi;
8436
8437
TRACE("(nColumn=%d, cx=%d)\n", nColumn, cx);
8438
8439
/* set column width only if in report or list mode */
8440
if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
8441
8442
/* take care of invalid cx values - LVSCW_AUTOSIZE_* values are negative,
8443
with _USEHEADER being the lowest */
8444
if (infoPtr->uView == LV_VIEW_DETAILS && cx < LVSCW_AUTOSIZE_USEHEADER) cx = LVSCW_AUTOSIZE;
8445
else if (infoPtr->uView == LV_VIEW_LIST && cx <= 0) return FALSE;
8446
8447
/* resize all columns if in LV_VIEW_LIST mode */
8448
if(infoPtr->uView == LV_VIEW_LIST)
8449
{
8450
infoPtr->nItemWidth = cx;
8451
LISTVIEW_InvalidateList(infoPtr);
8452
return TRUE;
8453
}
8454
8455
if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8456
8457
if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
8458
{
8459
INT nLabelWidth;
8460
LVITEMW lvItem;
8461
8462
lvItem.mask = LVIF_TEXT;
8463
lvItem.iItem = 0;
8464
lvItem.iSubItem = nColumn;
8465
lvItem.cchTextMax = DISP_TEXT_SIZE;
8466
for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8467
{
8468
lvItem.pszText = szDispText;
8469
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
8470
nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
8471
if (max_cx < nLabelWidth) max_cx = nLabelWidth;
8472
}
8473
if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
8474
max_cx += infoPtr->iconSize.cx;
8475
max_cx += TRAILING_LABEL_PADDING;
8476
if (nColumn == 0 && infoPtr->himlState)
8477
max_cx += infoPtr->iconStateSize.cx;
8478
}
8479
8480
/* autosize based on listview items width */
8481
if(cx == LVSCW_AUTOSIZE)
8482
cx = max_cx;
8483
else if(cx == LVSCW_AUTOSIZE_USEHEADER)
8484
{
8485
/* if iCol is the last column make it fill the remainder of the controls width */
8486
if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
8487
{
8488
RECT rcHeader;
8489
POINT Origin;
8490
8491
LISTVIEW_GetOrigin(infoPtr, &Origin);
8492
LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
8493
8494
cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
8495
}
8496
else
8497
{
8498
/* Despite what the MS docs say, if this is not the last
8499
column, then MS resizes the column to the width of the
8500
largest text string in the column, including headers
8501
and items. This is different from LVSCW_AUTOSIZE in that
8502
LVSCW_AUTOSIZE ignores the header string length. */
8503
cx = 0;
8504
8505
/* retrieve header text */
8506
hdi.mask = HDI_TEXT|HDI_FORMAT|HDI_IMAGE|HDI_BITMAP;
8507
hdi.cchTextMax = DISP_TEXT_SIZE;
8508
hdi.pszText = szDispText;
8509
if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
8510
{
8511
HDC hdc = GetDC(infoPtr->hwndSelf);
8512
HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
8513
HIMAGELIST himl = (HIMAGELIST)SendMessageW(infoPtr->hwndHeader, HDM_GETIMAGELIST, 0, 0);
8514
INT bitmap_margin = 0;
8515
SIZE size;
8516
8517
if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
8518
cx = size.cx + TRAILING_HEADER_PADDING;
8519
8520
if (hdi.fmt & (HDF_IMAGE|HDF_BITMAP))
8521
bitmap_margin = SendMessageW(infoPtr->hwndHeader, HDM_GETBITMAPMARGIN, 0, 0);
8522
8523
if ((hdi.fmt & HDF_IMAGE) && himl)
8524
{
8525
INT icon_cx, icon_cy;
8526
8527
if (!ImageList_GetIconSize(himl, &icon_cx, &icon_cy))
8528
cx += icon_cx + 2*bitmap_margin;
8529
}
8530
else if (hdi.fmt & HDF_BITMAP)
8531
{
8532
BITMAP bmp;
8533
8534
GetObjectW(hdi.hbm, sizeof(BITMAP), &bmp);
8535
cx += bmp.bmWidth + 2*bitmap_margin;
8536
}
8537
8538
SelectObject(hdc, old_font);
8539
ReleaseDC(infoPtr->hwndSelf, hdc);
8540
}
8541
cx = max (cx, max_cx);
8542
}
8543
}
8544
8545
if (cx < 0) return FALSE;
8546
8547
/* call header to update the column change */
8548
hdi.mask = HDI_WIDTH;
8549
hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin);
8550
TRACE("hdi.cxy=%d\n", hdi.cxy);
8551
return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
8552
}
8553
8554
/***
8555
* Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8556
*
8557
*/
8558
#if __WINE_COMCTL32_VERSION == 6
8559
static HIMAGELIST LISTVIEW_CreateThemedCheckBoxImageList(const LISTVIEW_INFO *info)
8560
{
8561
HBITMAP bitmap, old_bitmap;
8562
HIMAGELIST image_list;
8563
HDC hdc, mem_hdc;
8564
HTHEME theme;
8565
RECT rect;
8566
SIZE size;
8567
8568
if (!GetWindowTheme(info->hwndSelf))
8569
return NULL;
8570
8571
theme = OpenThemeDataForDpi(NULL, L"Button", GetDpiForWindow(info->hwndSelf));
8572
if (!theme)
8573
return NULL;
8574
8575
hdc = GetDC(info->hwndSelf);
8576
GetThemePartSize(theme, hdc, BP_CHECKBOX, 0, NULL, TS_DRAW, &size);
8577
SetRect(&rect, 0, 0, size.cx, size.cy);
8578
image_list = ImageList_Create(size.cx, size.cy, ILC_COLOR32, 2, 2);
8579
mem_hdc = CreateCompatibleDC(hdc);
8580
bitmap = CreateCompatibleBitmap(hdc, size.cx, size.cy);
8581
old_bitmap = SelectObject(mem_hdc, bitmap);
8582
ReleaseDC(info->hwndSelf, hdc);
8583
8584
if (IsThemeBackgroundPartiallyTransparent(theme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL))
8585
FillRect(mem_hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));
8586
DrawThemeBackground(theme, mem_hdc, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, &rect, NULL);
8587
ImageList_Add(image_list, bitmap, NULL);
8588
8589
if (IsThemeBackgroundPartiallyTransparent(theme, BP_CHECKBOX, CBS_CHECKEDNORMAL))
8590
FillRect(mem_hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));
8591
DrawThemeBackground(theme, mem_hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, &rect, NULL);
8592
ImageList_Add(image_list, bitmap, NULL);
8593
8594
SelectObject(mem_hdc, old_bitmap);
8595
DeleteObject(bitmap);
8596
DeleteDC(mem_hdc);
8597
CloseThemeData(theme);
8598
return image_list;
8599
}
8600
#endif /* __WINE_COMCTL32_VERSION == 6 */
8601
8602
static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
8603
{
8604
HDC hdc_wnd, hdc;
8605
HBITMAP hbm_im, hbm_mask, hbm_orig;
8606
RECT rc;
8607
HBRUSH hbr_white, hbr_black;
8608
HIMAGELIST himl;
8609
8610
#if __WINE_COMCTL32_VERSION == 6
8611
himl = LISTVIEW_CreateThemedCheckBoxImageList(infoPtr);
8612
if (himl)
8613
return himl;
8614
#endif
8615
8616
hbr_white = GetStockObject(WHITE_BRUSH);
8617
hbr_black = GetStockObject(BLACK_BRUSH);
8618
himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
8619
ILC_COLOR | ILC_MASK, 2, 2);
8620
hdc_wnd = GetDC(infoPtr->hwndSelf);
8621
hdc = CreateCompatibleDC(hdc_wnd);
8622
hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8623
hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
8624
ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
8625
8626
SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8627
hbm_orig = SelectObject(hdc, hbm_mask);
8628
FillRect(hdc, &rc, hbr_white);
8629
InflateRect(&rc, -2, -2);
8630
FillRect(hdc, &rc, hbr_black);
8631
8632
SelectObject(hdc, hbm_im);
8633
DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
8634
SelectObject(hdc, hbm_orig);
8635
ImageList_Add(himl, hbm_im, hbm_mask);
8636
8637
SelectObject(hdc, hbm_im);
8638
DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
8639
SelectObject(hdc, hbm_orig);
8640
ImageList_Add(himl, hbm_im, hbm_mask);
8641
8642
DeleteObject(hbm_mask);
8643
DeleteObject(hbm_im);
8644
DeleteDC(hdc);
8645
8646
return himl;
8647
}
8648
8649
/***
8650
* DESCRIPTION:
8651
* Sets the extended listview style.
8652
*
8653
* PARAMETERS:
8654
* [I] infoPtr : valid pointer to the listview structure
8655
* [I] dwMask : mask
8656
* [I] dwStyle : style
8657
*
8658
* RETURN:
8659
* SUCCESS : previous style
8660
* FAILURE : 0
8661
*/
8662
static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD mask, DWORD ex_style)
8663
{
8664
DWORD old_ex_style = infoPtr->dwLvExStyle;
8665
8666
TRACE("mask %#lx, ex_style %#lx\n", mask, ex_style);
8667
8668
/* set new style */
8669
if (mask)
8670
infoPtr->dwLvExStyle = (old_ex_style & ~mask) | (ex_style & mask);
8671
else
8672
infoPtr->dwLvExStyle = ex_style;
8673
8674
if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_CHECKBOXES)
8675
{
8676
HIMAGELIST himl = 0;
8677
if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8678
{
8679
LVITEMW item;
8680
item.mask = LVIF_STATE;
8681
item.stateMask = LVIS_STATEIMAGEMASK;
8682
item.state = INDEXTOSTATEIMAGEMASK(1);
8683
LISTVIEW_SetItemState(infoPtr, -1, &item);
8684
8685
himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
8686
if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8687
ImageList_Destroy(infoPtr->himlState);
8688
}
8689
himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
8690
/* checkbox list replaces previous custom list or... */
8691
if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) &&
8692
!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) ||
8693
/* ...previous was checkbox list */
8694
(old_ex_style & LVS_EX_CHECKBOXES))
8695
ImageList_Destroy(himl);
8696
}
8697
8698
if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERDRAGDROP)
8699
{
8700
DWORD style;
8701
8702
/* if not already created */
8703
LISTVIEW_CreateHeader(infoPtr);
8704
8705
style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
8706
if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
8707
style |= HDS_DRAGDROP;
8708
else
8709
style &= ~HDS_DRAGDROP;
8710
SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style);
8711
}
8712
8713
/* GRIDLINES adds decoration at top so changes sizes */
8714
if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_GRIDLINES)
8715
{
8716
LISTVIEW_CreateHeader(infoPtr);
8717
LISTVIEW_UpdateSize(infoPtr);
8718
}
8719
8720
if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_FULLROWSELECT)
8721
{
8722
LISTVIEW_CreateHeader(infoPtr);
8723
}
8724
8725
if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_TRANSPARENTBKGND)
8726
{
8727
if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
8728
LISTVIEW_SetBkColor(infoPtr, CLR_NONE);
8729
}
8730
8731
if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERINALLVIEWS)
8732
{
8733
if (infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
8734
LISTVIEW_CreateHeader(infoPtr);
8735
else
8736
ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8737
LISTVIEW_UpdateSize(infoPtr);
8738
LISTVIEW_UpdateScroll(infoPtr);
8739
}
8740
8741
LISTVIEW_InvalidateList(infoPtr);
8742
return old_ex_style;
8743
}
8744
8745
/***
8746
* DESCRIPTION:
8747
* Sets the new hot cursor used during hot tracking and hover selection.
8748
*
8749
* PARAMETER(S):
8750
* [I] infoPtr : valid pointer to the listview structure
8751
* [I] hCursor : the new hot cursor handle
8752
*
8753
* RETURN:
8754
* Returns the previous hot cursor
8755
*/
8756
static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
8757
{
8758
HCURSOR oldCursor = infoPtr->hHotCursor;
8759
8760
infoPtr->hHotCursor = hCursor;
8761
8762
return oldCursor;
8763
}
8764
8765
8766
/***
8767
* DESCRIPTION:
8768
* Sets the hot item index.
8769
*
8770
* PARAMETERS:
8771
* [I] infoPtr : valid pointer to the listview structure
8772
* [I] iIndex : index
8773
*
8774
* RETURN:
8775
* SUCCESS : previous hot item index
8776
* FAILURE : -1 (no hot item)
8777
*/
8778
static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
8779
{
8780
INT iOldIndex = infoPtr->nHotItem;
8781
8782
infoPtr->nHotItem = iIndex;
8783
8784
return iOldIndex;
8785
}
8786
8787
8788
/***
8789
* DESCRIPTION:
8790
* Sets the amount of time the cursor must hover over an item before it is selected.
8791
*
8792
* PARAMETER(S):
8793
* [I] infoPtr : valid pointer to the listview structure
8794
* [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8795
*
8796
* RETURN:
8797
* Returns the previous hover time
8798
*/
8799
static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
8800
{
8801
DWORD oldHoverTime = infoPtr->dwHoverTime;
8802
8803
infoPtr->dwHoverTime = dwHoverTime;
8804
8805
return oldHoverTime;
8806
}
8807
8808
/***
8809
* DESCRIPTION:
8810
* Sets spacing for icons of LVS_ICON style.
8811
*
8812
* PARAMETER(S):
8813
* [I] infoPtr : valid pointer to the listview structure
8814
* [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8815
* [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8816
*
8817
* RETURN:
8818
* MAKELONG(oldcx, oldcy)
8819
*/
8820
static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
8821
{
8822
INT iconWidth = 0, iconHeight = 0;
8823
DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
8824
8825
TRACE("requested=(%d,%d)\n", cx, cy);
8826
8827
/* set to defaults, if instructed to */
8828
if (cx == -1 && cy == -1)
8829
{
8830
infoPtr->autoSpacing = TRUE;
8831
if (infoPtr->himlNormal)
8832
ImageList_GetIconSize(infoPtr->himlNormal, &iconWidth, &iconHeight);
8833
cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON) + iconWidth;
8834
cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON) + iconHeight;
8835
}
8836
else
8837
infoPtr->autoSpacing = FALSE;
8838
8839
/* if 0 then keep width */
8840
if (cx != 0)
8841
infoPtr->iconSpacing.cx = cx;
8842
8843
/* if 0 then keep height */
8844
if (cy != 0)
8845
infoPtr->iconSpacing.cy = cy;
8846
8847
TRACE("old=(%d,%d), new=(%ld,%ld), iconSize=(%ld,%ld), ntmH=%d\n",
8848
LOWORD(oldspacing), HIWORD(oldspacing), infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
8849
infoPtr->iconSize.cx, infoPtr->iconSize.cy,
8850
infoPtr->ntmHeight);
8851
8852
/* these depend on the iconSpacing */
8853
LISTVIEW_UpdateItemSize(infoPtr);
8854
8855
return oldspacing;
8856
}
8857
8858
static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL is_small)
8859
{
8860
INT cx, cy;
8861
8862
if (himl && ImageList_GetIconSize(himl, &cx, &cy))
8863
{
8864
size->cx = cx;
8865
size->cy = cy;
8866
}
8867
else
8868
{
8869
size->cx = GetSystemMetrics(is_small ? SM_CXSMICON : SM_CXICON);
8870
size->cy = GetSystemMetrics(is_small ? SM_CYSMICON : SM_CYICON);
8871
}
8872
}
8873
8874
/***
8875
* DESCRIPTION:
8876
* Sets image lists.
8877
*
8878
* PARAMETER(S):
8879
* [I] infoPtr : valid pointer to the listview structure
8880
* [I] nType : image list type
8881
* [I] himl : image list handle
8882
*
8883
* RETURN:
8884
* SUCCESS : old image list
8885
* FAILURE : NULL
8886
*/
8887
static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
8888
{
8889
INT oldHeight = infoPtr->nItemHeight;
8890
HIMAGELIST himlOld = 0;
8891
8892
TRACE("(nType=%d, himl=%p)\n", nType, himl);
8893
8894
switch (nType)
8895
{
8896
case LVSIL_NORMAL:
8897
himlOld = infoPtr->himlNormal;
8898
infoPtr->himlNormal = himl;
8899
if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
8900
if (infoPtr->autoSpacing)
8901
LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
8902
break;
8903
8904
case LVSIL_SMALL:
8905
himlOld = infoPtr->himlSmall;
8906
infoPtr->himlSmall = himl;
8907
if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
8908
if (infoPtr->hwndHeader)
8909
SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)himl);
8910
break;
8911
8912
case LVSIL_STATE:
8913
himlOld = infoPtr->himlState;
8914
infoPtr->himlState = himl;
8915
set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
8916
ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
8917
break;
8918
8919
default:
8920
ERR("Unknown icon type=%d\n", nType);
8921
return NULL;
8922
}
8923
8924
infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
8925
if (infoPtr->nItemHeight != oldHeight)
8926
LISTVIEW_UpdateScroll(infoPtr);
8927
8928
return himlOld;
8929
}
8930
8931
/***
8932
* DESCRIPTION:
8933
* Preallocates memory (does *not* set the actual count of items !)
8934
*
8935
* PARAMETER(S):
8936
* [I] infoPtr : valid pointer to the listview structure
8937
* [I] nItems : item count (projected number of items to allocate)
8938
* [I] dwFlags : update flags
8939
*
8940
* RETURN:
8941
* SUCCESS : TRUE
8942
* FAILURE : FALSE
8943
*/
8944
static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
8945
{
8946
TRACE("nItems %d, flags %#lx\n", nItems, dwFlags);
8947
8948
if (infoPtr->dwStyle & LVS_OWNERDATA)
8949
{
8950
INT nOldCount = infoPtr->nItemCount;
8951
infoPtr->nItemCount = nItems;
8952
8953
if (nItems < nOldCount)
8954
{
8955
RANGE range = { nItems, nOldCount };
8956
ranges_del(infoPtr->selectionRanges, range);
8957
if (infoPtr->nFocusedItem >= nItems)
8958
{
8959
LISTVIEW_SetItemFocus(infoPtr, -1);
8960
infoPtr->nFocusedItem = -1;
8961
SetRectEmpty(&infoPtr->rcFocus);
8962
}
8963
}
8964
8965
LISTVIEW_UpdateScroll(infoPtr);
8966
8967
/* the flags are valid only in ownerdata report and list modes */
8968
if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
8969
8970
if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
8971
LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
8972
8973
if (!(dwFlags & LVSICF_NOINVALIDATEALL))
8974
LISTVIEW_InvalidateList(infoPtr);
8975
else
8976
{
8977
INT nFrom, nTo;
8978
POINT Origin;
8979
RECT rcErase;
8980
8981
LISTVIEW_GetOrigin(infoPtr, &Origin);
8982
nFrom = min(nOldCount, nItems);
8983
nTo = max(nOldCount, nItems);
8984
8985
if (infoPtr->uView == LV_VIEW_DETAILS)
8986
{
8987
SetRect(&rcErase, 0, nFrom * infoPtr->nItemHeight, infoPtr->nItemWidth,
8988
nTo * infoPtr->nItemHeight);
8989
OffsetRect(&rcErase, Origin.x, Origin.y);
8990
if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8991
LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8992
}
8993
else /* LV_VIEW_LIST */
8994
{
8995
INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
8996
8997
rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
8998
rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
8999
rcErase.right = rcErase.left + infoPtr->nItemWidth;
9000
rcErase.bottom = nPerCol * infoPtr->nItemHeight;
9001
OffsetRect(&rcErase, Origin.x, Origin.y);
9002
if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
9003
LISTVIEW_InvalidateRect(infoPtr, &rcErase);
9004
9005
rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
9006
rcErase.top = 0;
9007
rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
9008
rcErase.bottom = nPerCol * infoPtr->nItemHeight;
9009
OffsetRect(&rcErase, Origin.x, Origin.y);
9010
if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
9011
LISTVIEW_InvalidateRect(infoPtr, &rcErase);
9012
}
9013
}
9014
}
9015
else
9016
{
9017
/* According to MSDN for non-LVS_OWNERDATA this is just
9018
* a performance issue. The control allocates its internal
9019
* data structures for the number of items specified. It
9020
* cuts down on the number of memory allocations. Therefore
9021
* we will just issue a WARN here
9022
*/
9023
WARN("for non-ownerdata performance option not implemented.\n");
9024
}
9025
9026
return TRUE;
9027
}
9028
9029
/***
9030
* DESCRIPTION:
9031
* Sets the position of an item.
9032
*
9033
* PARAMETER(S):
9034
* [I] infoPtr : valid pointer to the listview structure
9035
* [I] nItem : item index
9036
* [I] pt : coordinate
9037
*
9038
* RETURN:
9039
* SUCCESS : TRUE
9040
* FAILURE : FALSE
9041
*/
9042
static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *pt)
9043
{
9044
POINT Origin, Pt;
9045
9046
TRACE("(nItem=%d, pt=%s)\n", nItem, wine_dbgstr_point(pt));
9047
9048
if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
9049
!(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
9050
9051
Pt = *pt;
9052
LISTVIEW_GetOrigin(infoPtr, &Origin);
9053
9054
/* This point value seems to be an undocumented feature.
9055
* The best guess is that it means either at the origin,
9056
* or at true beginning of the list. I will assume the origin. */
9057
if ((Pt.x == -1) && (Pt.y == -1))
9058
Pt = Origin;
9059
9060
if (infoPtr->uView == LV_VIEW_ICON)
9061
{
9062
Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
9063
Pt.y -= ICON_TOP_PADDING;
9064
}
9065
Pt.x -= Origin.x;
9066
Pt.y -= Origin.y;
9067
9068
return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE);
9069
}
9070
9071
/* Make sure to also disable per item notifications via the notification mask. */
9072
static VOID LISTVIEW_SetOwnerDataState(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast, const LVITEMW *item)
9073
{
9074
NMLVODSTATECHANGE nmlv;
9075
9076
if (nFirst == nLast) return;
9077
if (!item) return;
9078
9079
ZeroMemory(&nmlv, sizeof(nmlv));
9080
nmlv.iFrom = nFirst;
9081
nmlv.iTo = nLast;
9082
nmlv.uOldState = 0;
9083
nmlv.uNewState = item->state;
9084
9085
notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
9086
}
9087
9088
/***
9089
* DESCRIPTION:
9090
* Sets the state of one or many items.
9091
*
9092
* PARAMETER(S):
9093
* [I] infoPtr : valid pointer to the listview structure
9094
* [I] nItem : item index
9095
* [I] item : item or subitem info
9096
*
9097
* RETURN:
9098
* SUCCESS : TRUE
9099
* FAILURE : FALSE
9100
*/
9101
static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *item)
9102
{
9103
BOOL ret = TRUE;
9104
LVITEMW lvItem;
9105
9106
if (!item) return FALSE;
9107
9108
lvItem.iItem = nItem;
9109
lvItem.iSubItem = 0;
9110
lvItem.mask = LVIF_STATE;
9111
lvItem.state = item->state;
9112
lvItem.stateMask = item->stateMask;
9113
TRACE("item=%s\n", debuglvitem_t(&lvItem, TRUE));
9114
9115
if (nItem == -1)
9116
{
9117
UINT oldstate = 0;
9118
DWORD old_mask;
9119
9120
/* special case optimization for recurring attempt to deselect all */
9121
if (lvItem.state == 0 && lvItem.stateMask == LVIS_SELECTED && !LISTVIEW_GetSelectedCount(infoPtr))
9122
return TRUE;
9123
9124
/* select all isn't allowed in LVS_SINGLESEL */
9125
if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
9126
return FALSE;
9127
9128
/* focus all isn't allowed */
9129
if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
9130
9131
old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
9132
if (infoPtr->dwStyle & LVS_OWNERDATA)
9133
{
9134
infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
9135
if (!(lvItem.state & LVIS_SELECTED) && LISTVIEW_GetSelectedCount(infoPtr))
9136
oldstate |= LVIS_SELECTED;
9137
if (infoPtr->nFocusedItem != -1) oldstate |= LVIS_FOCUSED;
9138
}
9139
9140
/* apply to all items */
9141
for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
9142
if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) ret = FALSE;
9143
9144
if (infoPtr->dwStyle & LVS_OWNERDATA)
9145
{
9146
NMLISTVIEW nmlv;
9147
9148
infoPtr->notify_mask |= old_mask;
9149
9150
nmlv.iItem = -1;
9151
nmlv.iSubItem = 0;
9152
nmlv.uNewState = lvItem.state & lvItem.stateMask;
9153
nmlv.uOldState = oldstate & lvItem.stateMask;
9154
nmlv.uChanged = LVIF_STATE;
9155
nmlv.ptAction.x = nmlv.ptAction.y = 0;
9156
nmlv.lParam = 0;
9157
9158
notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
9159
}
9160
}
9161
else
9162
ret = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
9163
9164
return ret;
9165
}
9166
9167
/***
9168
* DESCRIPTION:
9169
* Sets the text of an item or subitem.
9170
*
9171
* PARAMETER(S):
9172
* [I] hwnd : window handle
9173
* [I] nItem : item index
9174
* [I] lpLVItem : item or subitem info
9175
* [I] isW : TRUE if input is Unicode
9176
*
9177
* RETURN:
9178
* SUCCESS : TRUE
9179
* FAILURE : FALSE
9180
*/
9181
static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
9182
{
9183
LVITEMW lvItem;
9184
9185
if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
9186
if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
9187
9188
lvItem.iItem = nItem;
9189
lvItem.iSubItem = lpLVItem->iSubItem;
9190
lvItem.mask = LVIF_TEXT;
9191
lvItem.pszText = lpLVItem->pszText;
9192
lvItem.cchTextMax = lpLVItem->cchTextMax;
9193
9194
TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
9195
9196
return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
9197
}
9198
9199
/***
9200
* DESCRIPTION:
9201
* Set item index that marks the start of a multiple selection.
9202
*
9203
* PARAMETER(S):
9204
* [I] infoPtr : valid pointer to the listview structure
9205
* [I] nIndex : index
9206
*
9207
* RETURN:
9208
* Index number or -1 if there is no selection mark.
9209
*/
9210
static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
9211
{
9212
INT nOldIndex = infoPtr->nSelectionMark;
9213
9214
TRACE("(nIndex=%d)\n", nIndex);
9215
9216
if (nIndex >= -1 && nIndex < infoPtr->nItemCount)
9217
infoPtr->nSelectionMark = nIndex;
9218
9219
return nOldIndex;
9220
}
9221
9222
/***
9223
* DESCRIPTION:
9224
* Sets the text background color.
9225
*
9226
* PARAMETER(S):
9227
* [I] infoPtr : valid pointer to the listview structure
9228
* [I] color : text background color
9229
*
9230
* RETURN:
9231
* SUCCESS : TRUE
9232
* FAILURE : FALSE
9233
*/
9234
static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF color)
9235
{
9236
TRACE("color %#lx\n", color);
9237
9238
infoPtr->clrTextBk = color;
9239
return TRUE;
9240
}
9241
9242
/***
9243
* DESCRIPTION:
9244
* Sets the text foreground color.
9245
*
9246
* PARAMETER(S):
9247
* [I] infoPtr : valid pointer to the listview structure
9248
* [I] color : text color
9249
*
9250
* RETURN:
9251
* SUCCESS : TRUE
9252
* FAILURE : FALSE
9253
*/
9254
static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF color)
9255
{
9256
TRACE("color %#lx\n", color);
9257
9258
infoPtr->clrText = color;
9259
return TRUE;
9260
}
9261
9262
/***
9263
* DESCRIPTION:
9264
* Sets new ToolTip window to ListView control.
9265
*
9266
* PARAMETER(S):
9267
* [I] infoPtr : valid pointer to the listview structure
9268
* [I] hwndNewToolTip : handle to new ToolTip
9269
*
9270
* RETURN:
9271
* old tool tip
9272
*/
9273
static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
9274
{
9275
HWND hwndOldToolTip = infoPtr->hwndToolTip;
9276
infoPtr->hwndToolTip = hwndNewToolTip;
9277
return hwndOldToolTip;
9278
}
9279
9280
/*
9281
* DESCRIPTION:
9282
* sets the Unicode character format flag for the control
9283
* PARAMETER(S):
9284
* [I] infoPtr :valid pointer to the listview structure
9285
* [I] fUnicode :true to switch to UNICODE false to switch to ANSI
9286
*
9287
* RETURN:
9288
* Old Unicode Format
9289
*/
9290
static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL unicode)
9291
{
9292
SHORT rc = infoPtr->notifyFormat;
9293
infoPtr->notifyFormat = (unicode) ? NFR_UNICODE : NFR_ANSI;
9294
return rc == NFR_UNICODE;
9295
}
9296
9297
/*
9298
* DESCRIPTION:
9299
* sets the control view mode
9300
* PARAMETER(S):
9301
* [I] infoPtr :valid pointer to the listview structure
9302
* [I] nView :new view mode value
9303
*
9304
* RETURN:
9305
* SUCCESS: 1
9306
* FAILURE: -1
9307
*/
9308
static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
9309
{
9310
HIMAGELIST himl;
9311
9312
if (infoPtr->uView == nView) return 1;
9313
9314
if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
9315
if (nView == LV_VIEW_TILE)
9316
{
9317
FIXME("View LV_VIEW_TILE unimplemented\n");
9318
return -1;
9319
}
9320
9321
infoPtr->uView = nView;
9322
9323
SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9324
ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9325
9326
ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9327
SetRectEmpty(&infoPtr->rcFocus);
9328
9329
himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9330
set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
9331
9332
switch (nView)
9333
{
9334
case LV_VIEW_ICON:
9335
case LV_VIEW_SMALLICON:
9336
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9337
break;
9338
case LV_VIEW_DETAILS:
9339
{
9340
HDLAYOUT hl;
9341
WINDOWPOS wp;
9342
9343
LISTVIEW_CreateHeader( infoPtr );
9344
9345
hl.prc = &infoPtr->rcList;
9346
hl.pwpos = &wp;
9347
SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
9348
SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9349
wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9350
break;
9351
}
9352
case LV_VIEW_LIST:
9353
break;
9354
}
9355
9356
LISTVIEW_UpdateItemSize(infoPtr);
9357
LISTVIEW_UpdateSize(infoPtr);
9358
LISTVIEW_UpdateScroll(infoPtr);
9359
LISTVIEW_InvalidateList(infoPtr);
9360
9361
NotifyWinEvent( EVENT_OBJECT_REORDER, infoPtr->hwndSelf, OBJID_CLIENT, 0 );
9362
9363
TRACE("nView %ld\n", nView);
9364
9365
return 1;
9366
}
9367
9368
/* LISTVIEW_SetWorkAreas */
9369
9370
struct sorting_context
9371
{
9372
HDPA items;
9373
PFNLVCOMPARE compare_func;
9374
LPARAM lParam;
9375
};
9376
9377
/* DPA_Sort() callback used for LVM_SORTITEMS */
9378
static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
9379
{
9380
struct sorting_context *context = (struct sorting_context *)lParam;
9381
ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
9382
ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
9383
9384
return context->compare_func(lv_first->lParam, lv_second->lParam, context->lParam);
9385
}
9386
9387
/* DPA_Sort() callback used for LVM_SORTITEMSEX */
9388
static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
9389
{
9390
struct sorting_context *context = (struct sorting_context *)lParam;
9391
INT first_idx = DPA_GetPtrIndex( context->items, first );
9392
INT second_idx = DPA_GetPtrIndex( context->items, second );
9393
9394
return context->compare_func(first_idx, second_idx, context->lParam);
9395
}
9396
9397
/***
9398
* DESCRIPTION:
9399
* Sorts the listview items.
9400
*
9401
* PARAMETER(S):
9402
* [I] infoPtr : valid pointer to the listview structure
9403
* [I] pfnCompare : application-defined value
9404
* [I] lParamSort : pointer to comparison callback
9405
* [I] IsEx : TRUE when LVM_SORTITEMSEX used
9406
*
9407
* RETURN:
9408
* SUCCESS : TRUE
9409
* FAILURE : FALSE
9410
*/
9411
static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
9412
LPARAM lParamSort, BOOL IsEx)
9413
{
9414
HDPA hdpaSubItems, hdpaItems;
9415
ITEM_INFO *lpItem;
9416
LPVOID selectionMarkItem = NULL;
9417
LPVOID focusedItem = NULL;
9418
struct sorting_context context;
9419
int i;
9420
9421
TRACE("pfnCompare %p, lParamSort %Ix\n", pfnCompare, lParamSort);
9422
9423
if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
9424
9425
if (!pfnCompare) return FALSE;
9426
if (!infoPtr->hdpaItems) return FALSE;
9427
9428
/* if there are 0 or 1 items, there is no need to sort */
9429
if (infoPtr->nItemCount < 2) return TRUE;
9430
if (!(hdpaItems = DPA_Clone(infoPtr->hdpaItems, NULL))) return FALSE;
9431
9432
/* clear selection */
9433
ranges_clear(infoPtr->selectionRanges);
9434
9435
/* save selection mark and focused item */
9436
if (infoPtr->nSelectionMark >= 0)
9437
selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
9438
if (infoPtr->nFocusedItem >= 0)
9439
focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
9440
9441
context.items = infoPtr->hdpaItems;
9442
context.compare_func = pfnCompare;
9443
context.lParam = lParamSort;
9444
if (IsEx)
9445
DPA_Sort(hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)&context);
9446
else
9447
DPA_Sort(hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)&context);
9448
DPA_Destroy(infoPtr->hdpaItems);
9449
infoPtr->hdpaItems = hdpaItems;
9450
9451
/* restore selection ranges */
9452
for (i=0; i < infoPtr->nItemCount; i++)
9453
{
9454
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
9455
lpItem = DPA_GetPtr(hdpaSubItems, 0);
9456
9457
if (lpItem->state & LVIS_SELECTED)
9458
ranges_additem(infoPtr->selectionRanges, i);
9459
}
9460
/* restore selection mark and focused item */
9461
infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
9462
infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
9463
9464
/* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
9465
9466
/* refresh the display */
9467
LISTVIEW_InvalidateList(infoPtr);
9468
return TRUE;
9469
}
9470
9471
/***
9472
* DESCRIPTION:
9473
* Updates an items or rearranges the listview control.
9474
*
9475
* PARAMETER(S):
9476
* [I] infoPtr : valid pointer to the listview structure
9477
* [I] nItem : item index
9478
*
9479
* RETURN:
9480
* SUCCESS : TRUE
9481
* FAILURE : FALSE
9482
*/
9483
static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
9484
{
9485
TRACE("(nItem=%d)\n", nItem);
9486
9487
if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
9488
9489
/* rearrange with default alignment style */
9490
if (is_autoarrange(infoPtr))
9491
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9492
else
9493
LISTVIEW_InvalidateItem(infoPtr, nItem);
9494
9495
return TRUE;
9496
}
9497
9498
/***
9499
* DESCRIPTION:
9500
* Draw the track line at the place defined in the infoPtr structure.
9501
* The line is drawn with a XOR pen so drawing the line for the second time
9502
* in the same place erases the line.
9503
*
9504
* PARAMETER(S):
9505
* [I] infoPtr : valid pointer to the listview structure
9506
*
9507
* RETURN:
9508
* SUCCESS : TRUE
9509
* FAILURE : FALSE
9510
*/
9511
static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
9512
{
9513
HDC hdc;
9514
9515
if (infoPtr->xTrackLine == -1)
9516
return FALSE;
9517
9518
if (!(hdc = GetDC(infoPtr->hwndSelf)))
9519
return FALSE;
9520
PatBlt( hdc, infoPtr->xTrackLine, infoPtr->rcList.top,
9521
1, infoPtr->rcList.bottom - infoPtr->rcList.top, DSTINVERT );
9522
ReleaseDC(infoPtr->hwndSelf, hdc);
9523
return TRUE;
9524
}
9525
9526
/***
9527
* DESCRIPTION:
9528
* Called when an edit control should be displayed. This function is called after
9529
* we are sure that there was a single click - not a double click (this is a TIMERPROC).
9530
*
9531
* PARAMETER(S):
9532
* [I] hwnd : Handle to the listview
9533
* [I] uMsg : WM_TIMER (ignored)
9534
* [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9535
* [I] dwTimer : The elapsed time (ignored)
9536
*
9537
* RETURN:
9538
* None.
9539
*/
9540
static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
9541
{
9542
DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
9543
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9544
9545
KillTimer(hwnd, idEvent);
9546
editItem->fEnabled = FALSE;
9547
/* check if the item is still selected */
9548
if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
9549
LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
9550
}
9551
9552
/***
9553
* DESCRIPTION:
9554
* Creates the listview control - the WM_NCCREATE phase.
9555
*
9556
* PARAMETER(S):
9557
* [I] hwnd : window handle
9558
* [I] lpcs : the create parameters
9559
*
9560
* RETURN:
9561
* Success: TRUE
9562
* Failure: FALSE
9563
*/
9564
static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, const CREATESTRUCTW *lpcs)
9565
{
9566
LISTVIEW_INFO *infoPtr;
9567
LOGFONTW logFont;
9568
9569
TRACE("(lpcs=%p)\n", lpcs);
9570
9571
/* initialize info pointer */
9572
infoPtr = Alloc(sizeof(*infoPtr));
9573
if (!infoPtr) return FALSE;
9574
9575
SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
9576
9577
infoPtr->hwndSelf = hwnd;
9578
infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
9579
map_style_view(infoPtr);
9580
/* determine the type of structures to use */
9581
infoPtr->hwndNotify = lpcs->hwndParent;
9582
/* infoPtr->notifyFormat will be filled in WM_CREATE */
9583
9584
/* initialize color information */
9585
infoPtr->clrBk = CLR_NONE;
9586
infoPtr->clrText = CLR_DEFAULT;
9587
infoPtr->clrTextBk = CLR_DEFAULT;
9588
LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
9589
9590
/* set default values */
9591
infoPtr->nFocusedItem = -1;
9592
infoPtr->nSelectionMark = -1;
9593
infoPtr->nHotItem = -1;
9594
infoPtr->redraw = TRUE;
9595
infoPtr->bNoItemMetrics = TRUE;
9596
infoPtr->notify_mask = NOTIFY_MASK_UNMASK_ALL;
9597
infoPtr->autoSpacing = TRUE;
9598
infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON);
9599
infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
9600
infoPtr->nEditLabelItem = -1;
9601
infoPtr->nLButtonDownItem = -1;
9602
infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */
9603
infoPtr->cWheelRemainder = 0;
9604
infoPtr->nMeasureItemHeight = 0;
9605
infoPtr->xTrackLine = -1; /* no track line */
9606
infoPtr->itemEdit.fEnabled = FALSE;
9607
#if __WINE_COMCTL32_VERSION == 6
9608
infoPtr->iVersion = 6;
9609
#else
9610
infoPtr->iVersion = 0;
9611
#endif
9612
infoPtr->colRectsDirty = FALSE;
9613
infoPtr->selected_column = -1;
9614
infoPtr->hHotCursor = LoadCursorW(NULL, (LPWSTR)IDC_HAND);
9615
9616
/* get default font (icon title) */
9617
SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
9618
infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
9619
infoPtr->hFont = infoPtr->hDefaultFont;
9620
LISTVIEW_SaveTextMetrics(infoPtr);
9621
9622
/* allocate memory for the data structure */
9623
if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
9624
if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
9625
if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
9626
if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
9627
if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
9628
if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
9629
9630
return DefWindowProcW(hwnd, WM_NCCREATE, wParam, (LPARAM)lpcs);
9631
9632
fail:
9633
DestroyWindow(infoPtr->hwndHeader);
9634
ranges_destroy(infoPtr->selectionRanges);
9635
DPA_Destroy(infoPtr->hdpaItems);
9636
DPA_Destroy(infoPtr->hdpaItemIds);
9637
DPA_Destroy(infoPtr->hdpaPosX);
9638
DPA_Destroy(infoPtr->hdpaPosY);
9639
DPA_Destroy(infoPtr->hdpaColumns);
9640
Free(infoPtr);
9641
return FALSE;
9642
}
9643
9644
/***
9645
* DESCRIPTION:
9646
* Creates the listview control - the WM_CREATE phase. Most of the data is
9647
* already set up in LISTVIEW_NCCreate
9648
*
9649
* PARAMETER(S):
9650
* [I] hwnd : window handle
9651
* [I] lpcs : the create parameters
9652
*
9653
* RETURN:
9654
* Success: 0
9655
* Failure: -1
9656
*/
9657
static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
9658
{
9659
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9660
9661
TRACE("lpcs %p, style %#lx\n", lpcs, lpcs->style);
9662
9663
infoPtr->dwStyle = lpcs->style;
9664
map_style_view(infoPtr);
9665
9666
infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
9667
(WPARAM)infoPtr->hwndSelf, NF_QUERY);
9668
/* on error defaulting to ANSI notifications */
9669
if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
9670
TRACE("notify format=%d\n", infoPtr->notifyFormat);
9671
9672
if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
9673
{
9674
if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
9675
}
9676
else
9677
infoPtr->hwndHeader = 0;
9678
9679
/* init item size to avoid division by 0 */
9680
LISTVIEW_UpdateItemSize (infoPtr);
9681
LISTVIEW_UpdateSize (infoPtr);
9682
9683
if (infoPtr->uView == LV_VIEW_DETAILS)
9684
{
9685
if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
9686
{
9687
ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9688
}
9689
LISTVIEW_UpdateScroll(infoPtr);
9690
/* send WM_MEASUREITEM notification */
9691
if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
9692
}
9693
9694
COMCTL32_OpenThemeForWindow(hwnd, L"ListView");
9695
9696
/* initialize the icon sizes */
9697
set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
9698
set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
9699
return 0;
9700
}
9701
9702
/***
9703
* DESCRIPTION:
9704
* Destroys the listview control.
9705
*
9706
* PARAMETER(S):
9707
* [I] infoPtr : valid pointer to the listview structure
9708
*
9709
* RETURN:
9710
* Success: 0
9711
* Failure: -1
9712
*/
9713
static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
9714
{
9715
COMCTL32_CloseThemeForWindow(infoPtr->hwndSelf);
9716
9717
/* delete all items */
9718
LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9719
9720
return 0;
9721
}
9722
9723
/***
9724
* DESCRIPTION:
9725
* Enables the listview control.
9726
*
9727
* PARAMETER(S):
9728
* [I] infoPtr : valid pointer to the listview structure
9729
* [I] bEnable : specifies whether to enable or disable the window
9730
*
9731
* RETURN:
9732
* SUCCESS : TRUE
9733
* FAILURE : FALSE
9734
*/
9735
static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr)
9736
{
9737
if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
9738
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9739
return TRUE;
9740
}
9741
9742
/***
9743
* DESCRIPTION:
9744
* Erases the background of the listview control.
9745
*
9746
* PARAMETER(S):
9747
* [I] infoPtr : valid pointer to the listview structure
9748
* [I] hdc : device context handle
9749
*
9750
* RETURN:
9751
* SUCCESS : TRUE
9752
* FAILURE : FALSE
9753
*/
9754
static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
9755
{
9756
RECT rc;
9757
9758
TRACE("(hdc=%p)\n", hdc);
9759
9760
if (!GetClipBox(hdc, &rc)) return FALSE;
9761
9762
if (infoPtr->clrBk == CLR_NONE)
9763
{
9764
if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
9765
SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT, (WPARAM)hdc, PRF_ERASEBKGND);
9766
else
9767
SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
9768
LISTVIEW_DrawBackgroundBitmap(infoPtr, hdc, &rc);
9769
return TRUE;
9770
}
9771
9772
/* for double buffered controls we need to do this during refresh */
9773
if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
9774
9775
return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
9776
}
9777
9778
9779
/***
9780
* DESCRIPTION:
9781
* Helper function for LISTVIEW_[HV]Scroll *only*.
9782
* Performs vertical/horizontal scrolling by a give amount.
9783
*
9784
* PARAMETER(S):
9785
* [I] infoPtr : valid pointer to the listview structure
9786
* [I] dx : amount of horizontal scroll
9787
* [I] dy : amount of vertical scroll
9788
*/
9789
static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
9790
{
9791
/* now we can scroll the list */
9792
ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
9793
&infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
9794
/* if we have focus, adjust rect */
9795
OffsetRect(&infoPtr->rcFocus, dx, dy);
9796
UpdateWindow(infoPtr->hwndSelf);
9797
}
9798
9799
/***
9800
* DESCRIPTION:
9801
* Performs vertical scrolling.
9802
*
9803
* PARAMETER(S):
9804
* [I] infoPtr : valid pointer to the listview structure
9805
* [I] nScrollCode : scroll code
9806
* [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9807
* [I] hScrollWnd : scrollbar control window handle
9808
*
9809
* RETURN:
9810
* Zero
9811
*
9812
* NOTES:
9813
* SB_LINEUP/SB_LINEDOWN:
9814
* for LVS_ICON, LVS_SMALLICON is 37 by experiment
9815
* for LVS_REPORT is 1 line
9816
* for LVS_LIST cannot occur
9817
*
9818
*/
9819
static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9820
INT nScrollDiff)
9821
{
9822
INT nOldScrollPos, nNewScrollPos;
9823
SCROLLINFO scrollInfo;
9824
BOOL is_an_icon;
9825
9826
TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9827
debugscrollcode(nScrollCode), nScrollDiff);
9828
9829
if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9830
9831
scrollInfo.cbSize = sizeof(SCROLLINFO);
9832
scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9833
9834
is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9835
9836
if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
9837
9838
nOldScrollPos = scrollInfo.nPos;
9839
switch (nScrollCode)
9840
{
9841
case SB_INTERNAL:
9842
break;
9843
9844
case SB_LINEUP:
9845
nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9846
break;
9847
9848
case SB_LINEDOWN:
9849
nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9850
break;
9851
9852
case SB_PAGEUP:
9853
nScrollDiff = -scrollInfo.nPage;
9854
break;
9855
9856
case SB_PAGEDOWN:
9857
nScrollDiff = scrollInfo.nPage;
9858
break;
9859
9860
case SB_TOP:
9861
nScrollDiff = -nOldScrollPos;
9862
break;
9863
9864
case SB_BOTTOM:
9865
nScrollDiff = scrollInfo.nMax - nOldScrollPos;
9866
break;
9867
9868
case SB_THUMBPOSITION:
9869
case SB_THUMBTRACK:
9870
nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9871
break;
9872
9873
default:
9874
nScrollDiff = 0;
9875
}
9876
9877
/* quit right away if pos isn't changing */
9878
if (nScrollDiff == 0) return 0;
9879
9880
/* calculate new position, and handle overflows */
9881
nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9882
if (nScrollDiff > 0) {
9883
if (nNewScrollPos < nOldScrollPos ||
9884
nNewScrollPos > scrollInfo.nMax)
9885
nNewScrollPos = scrollInfo.nMax;
9886
} else {
9887
if (nNewScrollPos > nOldScrollPos ||
9888
nNewScrollPos < scrollInfo.nMin)
9889
nNewScrollPos = scrollInfo.nMin;
9890
}
9891
9892
/* set the new position, and reread in case it changed */
9893
scrollInfo.fMask = SIF_POS;
9894
scrollInfo.nPos = nNewScrollPos;
9895
nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
9896
9897
/* carry on only if it really changed */
9898
if (nNewScrollPos == nOldScrollPos) return 0;
9899
9900
/* now adjust to client coordinates */
9901
nScrollDiff = nOldScrollPos - nNewScrollPos;
9902
if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
9903
9904
/* and scroll the window */
9905
scroll_list(infoPtr, 0, nScrollDiff);
9906
9907
return 0;
9908
}
9909
9910
/***
9911
* DESCRIPTION:
9912
* Performs horizontal scrolling.
9913
*
9914
* PARAMETER(S):
9915
* [I] infoPtr : valid pointer to the listview structure
9916
* [I] nScrollCode : scroll code
9917
* [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9918
* [I] hScrollWnd : scrollbar control window handle
9919
*
9920
* RETURN:
9921
* Zero
9922
*
9923
* NOTES:
9924
* SB_LINELEFT/SB_LINERIGHT:
9925
* for LVS_ICON, LVS_SMALLICON 1 pixel
9926
* for LVS_REPORT is 1 pixel
9927
* for LVS_LIST is 1 column --> which is a 1 because the
9928
* scroll is based on columns not pixels
9929
*
9930
*/
9931
static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9932
INT nScrollDiff)
9933
{
9934
INT nOldScrollPos, nNewScrollPos;
9935
SCROLLINFO scrollInfo;
9936
BOOL is_an_icon;
9937
9938
TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9939
debugscrollcode(nScrollCode), nScrollDiff);
9940
9941
if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9942
9943
scrollInfo.cbSize = sizeof(SCROLLINFO);
9944
scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9945
9946
is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9947
9948
if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
9949
9950
nOldScrollPos = scrollInfo.nPos;
9951
9952
switch (nScrollCode)
9953
{
9954
case SB_INTERNAL:
9955
break;
9956
9957
case SB_LINELEFT:
9958
nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9959
break;
9960
9961
case SB_LINERIGHT:
9962
nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9963
break;
9964
9965
case SB_PAGELEFT:
9966
nScrollDiff = -scrollInfo.nPage;
9967
break;
9968
9969
case SB_PAGERIGHT:
9970
nScrollDiff = scrollInfo.nPage;
9971
break;
9972
9973
case SB_THUMBPOSITION:
9974
case SB_THUMBTRACK:
9975
nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9976
break;
9977
9978
default:
9979
nScrollDiff = 0;
9980
}
9981
9982
/* quit right away if pos isn't changing */
9983
if (nScrollDiff == 0) return 0;
9984
9985
/* calculate new position, and handle overflows */
9986
nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9987
if (nScrollDiff > 0) {
9988
if (nNewScrollPos < nOldScrollPos ||
9989
nNewScrollPos > scrollInfo.nMax)
9990
nNewScrollPos = scrollInfo.nMax;
9991
} else {
9992
if (nNewScrollPos > nOldScrollPos ||
9993
nNewScrollPos < scrollInfo.nMin)
9994
nNewScrollPos = scrollInfo.nMin;
9995
}
9996
9997
/* set the new position, and reread in case it changed */
9998
scrollInfo.fMask = SIF_POS;
9999
scrollInfo.nPos = nNewScrollPos;
10000
nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
10001
10002
/* carry on only if it really changed */
10003
if (nNewScrollPos == nOldScrollPos) return 0;
10004
10005
LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
10006
10007
/* now adjust to client coordinates */
10008
nScrollDiff = nOldScrollPos - nNewScrollPos;
10009
if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
10010
10011
/* and scroll the window */
10012
scroll_list(infoPtr, nScrollDiff, 0);
10013
10014
return 0;
10015
}
10016
10017
static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
10018
{
10019
INT pulScrollLines = 3;
10020
10021
TRACE("(wheelDelta=%d)\n", wheelDelta);
10022
10023
switch(infoPtr->uView)
10024
{
10025
case LV_VIEW_ICON:
10026
case LV_VIEW_SMALLICON:
10027
/*
10028
* listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
10029
* should be fixed in the future.
10030
*/
10031
LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (wheelDelta > 0) ?
10032
-LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE);
10033
break;
10034
10035
case LV_VIEW_DETAILS:
10036
SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
10037
10038
/* if scrolling changes direction, ignore left overs */
10039
if ((wheelDelta < 0 && infoPtr->cWheelRemainder < 0) ||
10040
(wheelDelta > 0 && infoPtr->cWheelRemainder > 0))
10041
infoPtr->cWheelRemainder += wheelDelta;
10042
else
10043
infoPtr->cWheelRemainder = wheelDelta;
10044
if (infoPtr->cWheelRemainder && pulScrollLines)
10045
{
10046
int cLineScroll;
10047
pulScrollLines = min((UINT)LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
10048
cLineScroll = pulScrollLines * infoPtr->cWheelRemainder / WHEEL_DELTA;
10049
infoPtr->cWheelRemainder -= WHEEL_DELTA * cLineScroll / pulScrollLines;
10050
LISTVIEW_VScroll(infoPtr, SB_INTERNAL, -cLineScroll);
10051
}
10052
break;
10053
10054
case LV_VIEW_LIST:
10055
LISTVIEW_HScroll(infoPtr, (wheelDelta > 0) ? SB_LINELEFT : SB_LINERIGHT, 0);
10056
break;
10057
}
10058
return 0;
10059
}
10060
10061
/***
10062
* DESCRIPTION:
10063
* ???
10064
*
10065
* PARAMETER(S):
10066
* [I] infoPtr : valid pointer to the listview structure
10067
* [I] nVirtualKey : virtual key
10068
* [I] lKeyData : key data
10069
*
10070
* RETURN:
10071
* Zero
10072
*/
10073
static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
10074
{
10075
HWND hwndSelf = infoPtr->hwndSelf;
10076
INT nItem = -1;
10077
NMLVKEYDOWN nmKeyDown;
10078
10079
TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
10080
10081
/* send LVN_KEYDOWN notification */
10082
nmKeyDown.wVKey = nVirtualKey;
10083
nmKeyDown.flags = 0;
10084
notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
10085
if (!IsWindow(hwndSelf))
10086
return 0;
10087
10088
switch (nVirtualKey)
10089
{
10090
case VK_SPACE:
10091
nItem = infoPtr->nFocusedItem;
10092
if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
10093
toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
10094
break;
10095
10096
case VK_RETURN:
10097
if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
10098
{
10099
if (!notify(infoPtr, NM_RETURN)) return 0;
10100
if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
10101
}
10102
break;
10103
10104
case VK_HOME:
10105
if (infoPtr->nItemCount > 0)
10106
nItem = 0;
10107
break;
10108
10109
case VK_END:
10110
if (infoPtr->nItemCount > 0)
10111
nItem = infoPtr->nItemCount - 1;
10112
break;
10113
10114
case VK_LEFT:
10115
nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
10116
break;
10117
10118
case VK_UP:
10119
nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
10120
break;
10121
10122
case VK_RIGHT:
10123
nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
10124
break;
10125
10126
case VK_DOWN:
10127
nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
10128
break;
10129
10130
case VK_PRIOR:
10131
if (infoPtr->uView == LV_VIEW_DETAILS)
10132
{
10133
INT topidx = LISTVIEW_GetTopIndex(infoPtr);
10134
if (infoPtr->nFocusedItem == topidx)
10135
nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
10136
else
10137
nItem = topidx;
10138
}
10139
else
10140
nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
10141
* LISTVIEW_GetCountPerRow(infoPtr);
10142
if(nItem < 0) nItem = 0;
10143
break;
10144
10145
case VK_NEXT:
10146
if (infoPtr->uView == LV_VIEW_DETAILS)
10147
{
10148
INT topidx = LISTVIEW_GetTopIndex(infoPtr);
10149
INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
10150
if (infoPtr->nFocusedItem == topidx + cnt - 1)
10151
nItem = infoPtr->nFocusedItem + cnt - 1;
10152
else
10153
nItem = topidx + cnt - 1;
10154
}
10155
else
10156
nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
10157
* LISTVIEW_GetCountPerRow(infoPtr);
10158
if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
10159
break;
10160
}
10161
10162
if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
10163
{
10164
DWORD keys = 0;
10165
10166
if (GetKeyState(VK_SHIFT) & 0x8000)
10167
keys |= SHIFT_KEY;
10168
if (GetKeyState(VK_CONTROL) & 0x8000)
10169
keys |= CTRL_KEY;
10170
if (nVirtualKey == VK_SPACE)
10171
keys |= SPACE_KEY;
10172
10173
LISTVIEW_KeySelection(infoPtr, nItem, keys);
10174
}
10175
10176
return 0;
10177
}
10178
10179
/***
10180
* DESCRIPTION:
10181
* Kills the focus.
10182
*
10183
* PARAMETER(S):
10184
* [I] infoPtr : valid pointer to the listview structure
10185
*
10186
* RETURN:
10187
* Zero
10188
*/
10189
static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
10190
{
10191
TRACE("()\n");
10192
10193
/* drop any left over scroll amount */
10194
infoPtr->cWheelRemainder = 0;
10195
10196
/* if we did not have the focus, there's nothing more to do */
10197
if (!infoPtr->bFocus) return 0;
10198
10199
/* send NM_KILLFOCUS notification */
10200
if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
10201
10202
/* if we have a focus rectangle, get rid of it */
10203
LISTVIEW_ShowFocusRect(infoPtr, FALSE);
10204
10205
/* if have a marquee selection, stop it */
10206
if (infoPtr->bMarqueeSelect)
10207
{
10208
/* Remove the marquee rectangle and release our mouse capture */
10209
LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
10210
ReleaseCapture();
10211
10212
SetRectEmpty(&infoPtr->marqueeRect);
10213
10214
infoPtr->bMarqueeSelect = FALSE;
10215
infoPtr->bScrolling = FALSE;
10216
KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
10217
}
10218
10219
/* set window focus flag */
10220
infoPtr->bFocus = FALSE;
10221
10222
/* invalidate the selected items before resetting focus flag */
10223
LISTVIEW_InvalidateSelectedItems(infoPtr);
10224
10225
return 0;
10226
}
10227
10228
/***
10229
* DESCRIPTION:
10230
* Processes double click messages (left mouse button).
10231
*
10232
* PARAMETER(S):
10233
* [I] infoPtr : valid pointer to the listview structure
10234
* [I] wKey : key flag
10235
* [I] x,y : mouse coordinate
10236
*
10237
* RETURN:
10238
* Zero
10239
*/
10240
static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10241
{
10242
LVHITTESTINFO htInfo;
10243
10244
TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10245
10246
/* Cancel the item edition if any */
10247
if (infoPtr->itemEdit.fEnabled)
10248
{
10249
KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
10250
infoPtr->itemEdit.fEnabled = FALSE;
10251
}
10252
10253
/* send NM_RELEASEDCAPTURE notification */
10254
if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10255
10256
htInfo.pt.x = x;
10257
htInfo.pt.y = y;
10258
10259
/* send NM_DBLCLK notification */
10260
LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
10261
if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
10262
10263
/* To send the LVN_ITEMACTIVATE, it must be on an Item */
10264
if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
10265
10266
return 0;
10267
}
10268
10269
static LRESULT LISTVIEW_TrackMouse(const LISTVIEW_INFO *infoPtr, POINT pt)
10270
{
10271
MSG msg;
10272
RECT r;
10273
10274
r.top = r.bottom = pt.y;
10275
r.left = r.right = pt.x;
10276
10277
InflateRect(&r, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
10278
10279
SetCapture(infoPtr->hwndSelf);
10280
10281
while (1)
10282
{
10283
if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
10284
{
10285
if (msg.message == WM_MOUSEMOVE)
10286
{
10287
pt.x = (short)LOWORD(msg.lParam);
10288
pt.y = (short)HIWORD(msg.lParam);
10289
if (PtInRect(&r, pt))
10290
continue;
10291
else
10292
{
10293
ReleaseCapture();
10294
return 1;
10295
}
10296
}
10297
else if (msg.message >= WM_LBUTTONDOWN &&
10298
msg.message <= WM_RBUTTONDBLCLK)
10299
{
10300
break;
10301
}
10302
10303
DispatchMessageW(&msg);
10304
}
10305
10306
if (GetCapture() != infoPtr->hwndSelf)
10307
return 0;
10308
}
10309
10310
ReleaseCapture();
10311
return 0;
10312
}
10313
10314
10315
/***
10316
* DESCRIPTION:
10317
* Processes mouse down messages (left mouse button).
10318
*
10319
* PARAMETERS:
10320
* infoPtr [I ] valid pointer to the listview structure
10321
* wKey [I ] key flag
10322
* x,y [I ] mouse coordinate
10323
*
10324
* RETURN:
10325
* Zero
10326
*/
10327
static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10328
{
10329
LVHITTESTINFO lvHitTestInfo;
10330
POINT pt = { x, y };
10331
INT nItem;
10332
10333
TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10334
10335
/* send NM_RELEASEDCAPTURE notification */
10336
if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10337
10338
/* set left button down flag and record the click position */
10339
infoPtr->bLButtonDown = TRUE;
10340
infoPtr->ptClickPos = pt;
10341
infoPtr->bDragging = FALSE;
10342
infoPtr->bMarqueeSelect = FALSE;
10343
infoPtr->bScrolling = FALSE;
10344
10345
lvHitTestInfo.pt.x = x;
10346
lvHitTestInfo.pt.y = y;
10347
10348
nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
10349
TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
10350
if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
10351
{
10352
if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
10353
{
10354
notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
10355
toggle_checkbox_state(infoPtr, nItem);
10356
infoPtr->bLButtonDown = FALSE;
10357
return 0;
10358
}
10359
10360
if (infoPtr->dwStyle & LVS_SINGLESEL)
10361
{
10362
if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10363
infoPtr->nEditLabelItem = nItem;
10364
else
10365
LISTVIEW_SetSelection(infoPtr, nItem);
10366
}
10367
else
10368
{
10369
if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
10370
{
10371
if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
10372
LISTVIEW_SetItemFocus(infoPtr, nItem);
10373
}
10374
else if (wKey & MK_CONTROL)
10375
{
10376
LVITEMW item;
10377
BOOL select;
10378
10379
select = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
10380
10381
item.state = (select ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
10382
item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
10383
LISTVIEW_SetItemState(infoPtr, nItem, &item);
10384
infoPtr->nSelectionMark = nItem;
10385
}
10386
else if (wKey & MK_SHIFT)
10387
{
10388
LISTVIEW_SetGroupSelection(infoPtr, nItem);
10389
}
10390
else
10391
{
10392
if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10393
{
10394
infoPtr->nEditLabelItem = nItem;
10395
infoPtr->nLButtonDownItem = nItem;
10396
10397
LISTVIEW_SetItemFocus(infoPtr, nItem);
10398
}
10399
else
10400
/* set selection (clears other pre-existing selections) */
10401
LISTVIEW_SetSelection(infoPtr, nItem);
10402
}
10403
}
10404
10405
if (!infoPtr->bFocus)
10406
SetFocus(infoPtr->hwndSelf);
10407
10408
if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
10409
if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
10410
}
10411
else
10412
{
10413
if (!infoPtr->bFocus)
10414
SetFocus(infoPtr->hwndSelf);
10415
10416
/* remove all selections */
10417
if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
10418
LISTVIEW_DeselectAll(infoPtr);
10419
ReleaseCapture();
10420
}
10421
10422
return 0;
10423
}
10424
10425
/***
10426
* DESCRIPTION:
10427
* Processes mouse up messages (left mouse button).
10428
*
10429
* PARAMETERS:
10430
* infoPtr [I ] valid pointer to the listview structure
10431
* wKey [I ] key flag
10432
* x,y [I ] mouse coordinate
10433
*
10434
* RETURN:
10435
* Zero
10436
*/
10437
static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10438
{
10439
LVHITTESTINFO lvHitTestInfo;
10440
10441
TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10442
10443
if (!infoPtr->bLButtonDown) return 0;
10444
10445
lvHitTestInfo.pt.x = x;
10446
lvHitTestInfo.pt.y = y;
10447
10448
/* send NM_CLICK notification */
10449
LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10450
if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
10451
10452
/* set left button flag */
10453
infoPtr->bLButtonDown = FALSE;
10454
10455
/* set a single selection, reset others */
10456
if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
10457
LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
10458
infoPtr->nLButtonDownItem = -1;
10459
10460
if (infoPtr->bDragging || infoPtr->bMarqueeSelect)
10461
{
10462
/* Remove the marquee rectangle and release our mouse capture */
10463
if (infoPtr->bMarqueeSelect)
10464
{
10465
LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
10466
ReleaseCapture();
10467
}
10468
10469
SetRectEmpty(&infoPtr->marqueeRect);
10470
SetRectEmpty(&infoPtr->marqueeDrawRect);
10471
10472
infoPtr->bDragging = FALSE;
10473
infoPtr->bMarqueeSelect = FALSE;
10474
infoPtr->bScrolling = FALSE;
10475
10476
KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
10477
return 0;
10478
}
10479
10480
/* if we clicked on a selected item, edit the label */
10481
if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
10482
{
10483
/* we want to make sure the user doesn't want to do a double click. So we will
10484
* delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
10485
*/
10486
infoPtr->itemEdit.fEnabled = TRUE;
10487
infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
10488
SetTimer(infoPtr->hwndSelf,
10489
(UINT_PTR)&infoPtr->itemEdit,
10490
GetDoubleClickTime(),
10491
LISTVIEW_DelayedEditItem);
10492
}
10493
10494
return 0;
10495
}
10496
10497
/***
10498
* DESCRIPTION:
10499
* Destroys the listview control (called after WM_DESTROY).
10500
*
10501
* PARAMETER(S):
10502
* [I] infoPtr : valid pointer to the listview structure
10503
*
10504
* RETURN:
10505
* Zero
10506
*/
10507
static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
10508
{
10509
INT i;
10510
10511
TRACE("()\n");
10512
10513
/* destroy data structure */
10514
DPA_Destroy(infoPtr->hdpaItems);
10515
DPA_Destroy(infoPtr->hdpaItemIds);
10516
DPA_Destroy(infoPtr->hdpaPosX);
10517
DPA_Destroy(infoPtr->hdpaPosY);
10518
/* columns */
10519
for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++)
10520
Free(DPA_GetPtr(infoPtr->hdpaColumns, i));
10521
DPA_Destroy(infoPtr->hdpaColumns);
10522
ranges_destroy(infoPtr->selectionRanges);
10523
10524
/* destroy image lists */
10525
if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
10526
{
10527
ImageList_Destroy(infoPtr->himlNormal);
10528
ImageList_Destroy(infoPtr->himlSmall);
10529
ImageList_Destroy(infoPtr->himlState);
10530
}
10531
10532
/* destroy font, bkgnd brush */
10533
infoPtr->hFont = 0;
10534
if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
10535
if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
10536
if (infoPtr->hBkBitmap) DeleteObject(infoPtr->hBkBitmap);
10537
10538
SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
10539
10540
Free(infoPtr);
10541
10542
return 0;
10543
}
10544
10545
/***
10546
* DESCRIPTION:
10547
* Handles notifications.
10548
*
10549
* PARAMETER(S):
10550
* [I] infoPtr : valid pointer to the listview structure
10551
* [I] lpnmhdr : notification information
10552
*
10553
* RETURN:
10554
* Zero
10555
*/
10556
static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, NMHDR *lpnmhdr)
10557
{
10558
NMHEADERW *lpnmh;
10559
10560
TRACE("(lpnmhdr=%p)\n", lpnmhdr);
10561
10562
if (!lpnmhdr || lpnmhdr->hwndFrom != infoPtr->hwndHeader) return 0;
10563
10564
/* remember: HDN_LAST < HDN_FIRST */
10565
if (lpnmhdr->code > HDN_FIRST || lpnmhdr->code < HDN_LAST) return 0;
10566
lpnmh = (NMHEADERW *)lpnmhdr;
10567
10568
if (lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
10569
10570
switch (lpnmhdr->code)
10571
{
10572
case HDN_TRACKW:
10573
case HDN_TRACKA:
10574
{
10575
COLUMN_INFO *lpColumnInfo;
10576
POINT ptOrigin;
10577
INT x;
10578
10579
if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10580
break;
10581
10582
/* remove the old line (if any) */
10583
LISTVIEW_DrawTrackLine(infoPtr);
10584
10585
/* compute & draw the new line */
10586
lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10587
x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
10588
LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10589
infoPtr->xTrackLine = x + ptOrigin.x;
10590
LISTVIEW_DrawTrackLine(infoPtr);
10591
return notify_forward_header(infoPtr, lpnmh);
10592
}
10593
10594
case HDN_ENDTRACKA:
10595
case HDN_ENDTRACKW:
10596
/* remove the track line (if any) */
10597
LISTVIEW_DrawTrackLine(infoPtr);
10598
infoPtr->xTrackLine = -1;
10599
return notify_forward_header(infoPtr, lpnmh);
10600
10601
case HDN_BEGINDRAG:
10602
if ((infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0) return 1;
10603
return notify_forward_header(infoPtr, lpnmh);
10604
10605
case HDN_ENDDRAG:
10606
infoPtr->colRectsDirty = TRUE;
10607
LISTVIEW_InvalidateList(infoPtr);
10608
return notify_forward_header(infoPtr, lpnmh);
10609
10610
case HDN_ITEMCHANGEDW:
10611
case HDN_ITEMCHANGEDA:
10612
{
10613
COLUMN_INFO *lpColumnInfo;
10614
HDITEMW hdi;
10615
INT dx, cxy;
10616
10617
if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10618
{
10619
hdi.mask = HDI_WIDTH;
10620
if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
10621
cxy = hdi.cxy;
10622
}
10623
else
10624
cxy = lpnmh->pitem->cxy;
10625
10626
/* determine how much we change since the last know position */
10627
lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10628
dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
10629
if (dx != 0)
10630
{
10631
lpColumnInfo->rcHeader.right += dx;
10632
10633
hdi.mask = HDI_ORDER;
10634
SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi);
10635
10636
/* not the rightmost one */
10637
if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
10638
{
10639
INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
10640
hdi.iOrder + 1, 0);
10641
LISTVIEW_ScrollColumns(infoPtr, nIndex, dx);
10642
}
10643
else
10644
{
10645
/* only needs to update the scrolls */
10646
infoPtr->nItemWidth += dx;
10647
LISTVIEW_UpdateScroll(infoPtr);
10648
}
10649
LISTVIEW_UpdateItemSize(infoPtr);
10650
if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
10651
{
10652
POINT ptOrigin;
10653
RECT rcCol = lpColumnInfo->rcHeader;
10654
10655
LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10656
OffsetRect(&rcCol, ptOrigin.x, 0);
10657
10658
rcCol.top = infoPtr->rcList.top;
10659
rcCol.bottom = infoPtr->rcList.bottom;
10660
10661
/* resizing left-aligned columns leaves most of the left side untouched */
10662
if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
10663
{
10664
INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
10665
if (dx > 0)
10666
nMaxDirty += dx;
10667
rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
10668
}
10669
10670
/* when shrinking the last column clear the now unused field */
10671
if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
10672
{
10673
RECT right;
10674
10675
rcCol.right -= dx;
10676
10677
/* deal with right from rightmost column area */
10678
right.left = rcCol.right;
10679
right.top = rcCol.top;
10680
right.bottom = rcCol.bottom;
10681
right.right = infoPtr->rcList.right;
10682
10683
LISTVIEW_InvalidateRect(infoPtr, &right);
10684
}
10685
10686
LISTVIEW_InvalidateRect(infoPtr, &rcCol);
10687
}
10688
}
10689
break;
10690
}
10691
10692
case HDN_ITEMCLICKW:
10693
case HDN_ITEMCLICKA:
10694
{
10695
/* Handle sorting by Header Column */
10696
NMLISTVIEW nmlv;
10697
10698
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
10699
nmlv.iItem = -1;
10700
nmlv.iSubItem = lpnmh->iItem;
10701
notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
10702
return notify_forward_header(infoPtr, lpnmh);
10703
}
10704
10705
case HDN_DIVIDERDBLCLICKW:
10706
case HDN_DIVIDERDBLCLICKA:
10707
/* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10708
we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10709
split needed for that */
10710
LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
10711
return notify_forward_header(infoPtr, lpnmh);
10712
}
10713
return 0;
10714
}
10715
10716
/***
10717
* DESCRIPTION:
10718
* Paint non-client area of control.
10719
*
10720
* PARAMETER(S):
10721
* [I] infoPtr : valid pointer to the listview structureof the sender
10722
* [I] region : update region
10723
*
10724
* RETURN:
10725
* 0 - frame was painted
10726
*/
10727
static LRESULT LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
10728
{
10729
#if __WINE_COMCTL32_VERSION == 6
10730
LONG exstyle = GetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE);
10731
HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
10732
RECT r, window_rect;
10733
HDC dc;
10734
HRGN cliprgn;
10735
int cxEdge = GetSystemMetrics (SM_CXEDGE),
10736
cyEdge = GetSystemMetrics (SM_CYEDGE);
10737
10738
if (!theme || !(exstyle & WS_EX_CLIENTEDGE))
10739
return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0);
10740
10741
GetWindowRect(infoPtr->hwndSelf, &r);
10742
10743
cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
10744
r.right - cxEdge, r.bottom - cyEdge);
10745
if (region != (HRGN)1)
10746
CombineRgn (cliprgn, cliprgn, region, RGN_AND);
10747
10748
dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW | DCX_INTERSECTRGN);
10749
if (infoPtr->hwndHeader && LISTVIEW_IsHeaderEnabled(infoPtr))
10750
{
10751
GetWindowRect(infoPtr->hwndHeader, &window_rect);
10752
OffsetRect(&window_rect, -r.left, -r.top);
10753
ExcludeClipRect(dc, window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
10754
}
10755
10756
OffsetRect(&r, -r.left, -r.top);
10757
if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
10758
DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
10759
DrawThemeBackground (theme, dc, 0, 0, &r, 0);
10760
ReleaseDC(infoPtr->hwndSelf, dc);
10761
10762
/* Call default proc to get the scrollbars etc. painted */
10763
DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
10764
DeleteObject(cliprgn);
10765
10766
return 0;
10767
#else /* __WINE_COMCTL32_VERSION == 6 */
10768
return DefWindowProcW(infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0);
10769
#endif
10770
}
10771
10772
/***
10773
* DESCRIPTION:
10774
* Determines the type of structure to use.
10775
*
10776
* PARAMETER(S):
10777
* [I] infoPtr : valid pointer to the listview structureof the sender
10778
* [I] hwndFrom : listview window handle
10779
* [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10780
*
10781
* RETURN:
10782
* Zero
10783
*/
10784
static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
10785
{
10786
TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
10787
10788
if (nCommand == NF_REQUERY)
10789
infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
10790
10791
return infoPtr->notifyFormat;
10792
}
10793
10794
/***
10795
* DESCRIPTION:
10796
* Paints/Repaints the listview control. Internal use.
10797
*
10798
* PARAMETER(S):
10799
* [I] infoPtr : valid pointer to the listview structure
10800
* [I] hdc : device context handle
10801
*
10802
* RETURN:
10803
* Zero
10804
*/
10805
static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
10806
{
10807
TRACE("(hdc=%p)\n", hdc);
10808
10809
if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
10810
{
10811
infoPtr->bNoItemMetrics = FALSE;
10812
LISTVIEW_UpdateItemSize(infoPtr);
10813
if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
10814
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10815
LISTVIEW_UpdateScroll(infoPtr);
10816
}
10817
10818
if (infoPtr->hwndHeader)
10819
{
10820
RECT rect;
10821
10822
UpdateWindow(infoPtr->hwndHeader);
10823
GetClientRect(infoPtr->hwndHeader, &rect);
10824
MapWindowPoints(infoPtr->hwndHeader, infoPtr->hwndSelf, (POINT *)&rect, 2);
10825
ValidateRect(infoPtr->hwndSelf, &rect);
10826
}
10827
10828
if (hdc)
10829
LISTVIEW_Refresh(infoPtr, hdc, NULL);
10830
else
10831
{
10832
PAINTSTRUCT ps;
10833
10834
hdc = BeginPaint(infoPtr->hwndSelf, &ps);
10835
if (!hdc) return 1;
10836
LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
10837
EndPaint(infoPtr->hwndSelf, &ps);
10838
}
10839
10840
return 0;
10841
}
10842
10843
/***
10844
* DESCRIPTION:
10845
* Paints/Repaints the listview control, WM_PAINT handler.
10846
*
10847
* PARAMETER(S):
10848
* [I] infoPtr : valid pointer to the listview structure
10849
* [I] hdc : device context handle
10850
*
10851
* RETURN:
10852
* Zero
10853
*/
10854
static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc)
10855
{
10856
TRACE("(hdc=%p)\n", hdc);
10857
10858
if (!is_redrawing(infoPtr))
10859
return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0);
10860
10861
return LISTVIEW_Paint(infoPtr, hdc);
10862
}
10863
10864
/***
10865
* DESCRIPTION:
10866
* Paints/Repaints the listview control.
10867
*
10868
* PARAMETER(S):
10869
* [I] infoPtr : valid pointer to the listview structure
10870
* [I] hdc : device context handle
10871
* [I] options : drawing options
10872
*
10873
* RETURN:
10874
* Zero
10875
*/
10876
static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
10877
{
10878
FIXME("(hdc=%p options=%#lx) partial stub\n", hdc, options);
10879
10880
LISTVIEW_Paint(infoPtr, hdc);
10881
10882
return 0;
10883
}
10884
10885
10886
/***
10887
* DESCRIPTION:
10888
* Processes double click messages (right mouse button).
10889
*
10890
* PARAMETER(S):
10891
* [I] infoPtr : valid pointer to the listview structure
10892
* [I] wKey : key flag
10893
* [I] x,y : mouse coordinate
10894
*
10895
* RETURN:
10896
* Zero
10897
*/
10898
static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10899
{
10900
LVHITTESTINFO lvHitTestInfo;
10901
10902
TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y);
10903
10904
/* send NM_RELEASEDCAPTURE notification */
10905
if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10906
10907
/* send NM_RDBLCLK notification */
10908
lvHitTestInfo.pt.x = x;
10909
lvHitTestInfo.pt.y = y;
10910
LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10911
notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
10912
10913
return 0;
10914
}
10915
10916
/***
10917
* DESCRIPTION:
10918
* Processes WM_RBUTTONDOWN message and corresponding drag operation.
10919
*
10920
* PARAMETER(S):
10921
* [I] infoPtr : valid pointer to the listview structure
10922
* [I] wKey : key flag
10923
* [I] x, y : mouse coordinate
10924
*
10925
* RETURN:
10926
* Zero
10927
*/
10928
static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10929
{
10930
LVHITTESTINFO ht;
10931
INT item;
10932
10933
TRACE("(key=%hu, x=%d, y=%d)\n", wKey, x, y);
10934
10935
/* send NM_RELEASEDCAPTURE notification */
10936
if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10937
10938
/* determine the index of the selected item */
10939
ht.pt.x = x;
10940
ht.pt.y = y;
10941
item = LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
10942
10943
/* make sure the listview control window has the focus */
10944
if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
10945
10946
if ((item >= 0) && (item < infoPtr->nItemCount))
10947
{
10948
LISTVIEW_SetItemFocus(infoPtr, item);
10949
if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
10950
!LISTVIEW_GetItemState(infoPtr, item, LVIS_SELECTED))
10951
LISTVIEW_SetSelection(infoPtr, item);
10952
}
10953
else
10954
LISTVIEW_DeselectAll(infoPtr);
10955
10956
if (LISTVIEW_TrackMouse(infoPtr, ht.pt))
10957
{
10958
if (ht.iItem != -1)
10959
{
10960
NMLISTVIEW nmlv;
10961
10962
memset(&nmlv, 0, sizeof(nmlv));
10963
nmlv.iItem = ht.iItem;
10964
nmlv.ptAction = ht.pt;
10965
10966
notify_listview(infoPtr, LVN_BEGINRDRAG, &nmlv);
10967
}
10968
}
10969
else
10970
{
10971
SetFocus(infoPtr->hwndSelf);
10972
10973
ht.pt.x = x;
10974
ht.pt.y = y;
10975
LISTVIEW_HitTest(infoPtr, &ht, TRUE, FALSE);
10976
10977
if (notify_click(infoPtr, NM_RCLICK, &ht))
10978
{
10979
/* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
10980
SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
10981
(WPARAM)infoPtr->hwndSelf, (LPARAM)GetMessagePos());
10982
}
10983
}
10984
10985
return 0;
10986
}
10987
10988
/***
10989
* DESCRIPTION:
10990
* Sets the cursor.
10991
*
10992
* PARAMETER(S):
10993
* [I] infoPtr : valid pointer to the listview structure
10994
* [I] hwnd : window handle of window containing the cursor
10995
* [I] nHittest : hit-test code
10996
* [I] wMouseMsg : ideintifier of the mouse message
10997
*
10998
* RETURN:
10999
* TRUE if cursor is set
11000
* FALSE otherwise
11001
*/
11002
static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
11003
{
11004
LVHITTESTINFO lvHitTestInfo;
11005
11006
if (!LISTVIEW_IsHotTracking(infoPtr)) goto forward;
11007
11008
if (!infoPtr->hHotCursor) goto forward;
11009
11010
GetCursorPos(&lvHitTestInfo.pt);
11011
if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward;
11012
11013
SetCursor(infoPtr->hHotCursor);
11014
11015
return TRUE;
11016
11017
forward:
11018
11019
return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam);
11020
}
11021
11022
/***
11023
* DESCRIPTION:
11024
* Sets the focus.
11025
*
11026
* PARAMETER(S):
11027
* [I] infoPtr : valid pointer to the listview structure
11028
* [I] hwndLoseFocus : handle of previously focused window
11029
*
11030
* RETURN:
11031
* Zero
11032
*/
11033
static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
11034
{
11035
TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
11036
11037
/* if we have the focus already, there's nothing to do */
11038
if (infoPtr->bFocus) return 0;
11039
11040
/* send NM_SETFOCUS notification */
11041
if (!notify(infoPtr, NM_SETFOCUS)) return 0;
11042
11043
/* set window focus flag */
11044
infoPtr->bFocus = TRUE;
11045
11046
/* put the focus rect back on */
11047
LISTVIEW_ShowFocusRect(infoPtr, TRUE);
11048
11049
/* redraw all visible selected items */
11050
LISTVIEW_InvalidateSelectedItems(infoPtr);
11051
11052
return 0;
11053
}
11054
11055
/***
11056
* DESCRIPTION:
11057
* Sets the font.
11058
*
11059
* PARAMETER(S):
11060
* [I] infoPtr : valid pointer to the listview structure
11061
* [I] fRedraw : font handle
11062
* [I] fRedraw : redraw flag
11063
*
11064
* RETURN:
11065
* Zero
11066
*/
11067
static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
11068
{
11069
HFONT oldFont = infoPtr->hFont;
11070
INT oldHeight = infoPtr->nItemHeight;
11071
11072
TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
11073
11074
infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
11075
if (infoPtr->hFont == oldFont) return 0;
11076
11077
LISTVIEW_SaveTextMetrics(infoPtr);
11078
11079
infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
11080
11081
if (infoPtr->uView == LV_VIEW_DETAILS)
11082
{
11083
SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
11084
LISTVIEW_UpdateSize(infoPtr);
11085
LISTVIEW_UpdateScroll(infoPtr);
11086
}
11087
else if (infoPtr->nItemHeight != oldHeight)
11088
LISTVIEW_UpdateScroll(infoPtr);
11089
11090
if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
11091
11092
return 0;
11093
}
11094
11095
/***
11096
* DESCRIPTION:
11097
* Message handling for WM_SETREDRAW.
11098
* For the Listview, it invalidates the entire window (the doc specifies otherwise)
11099
*
11100
* PARAMETER(S):
11101
* [I] infoPtr : valid pointer to the listview structure
11102
* [I] redraw: state of redraw flag
11103
*
11104
* RETURN:
11105
* Zero.
11106
*/
11107
static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL redraw)
11108
{
11109
TRACE("old=%d, new=%d\n", infoPtr->redraw, redraw);
11110
11111
if (infoPtr->redraw == !!redraw)
11112
return 0;
11113
11114
if (!(infoPtr->redraw = !!redraw))
11115
return 0;
11116
11117
if (is_autoarrange(infoPtr))
11118
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
11119
LISTVIEW_UpdateScroll(infoPtr);
11120
11121
/* despite what the WM_SETREDRAW docs says, apps expect us
11122
* to invalidate the listview here... stupid! */
11123
LISTVIEW_InvalidateList(infoPtr);
11124
11125
return 0;
11126
}
11127
11128
/***
11129
* DESCRIPTION:
11130
* Resizes the listview control. This function processes WM_SIZE
11131
* messages. At this time, the width and height are not used.
11132
*
11133
* PARAMETER(S):
11134
* [I] infoPtr : valid pointer to the listview structure
11135
* [I] Width : new width
11136
* [I] Height : new height
11137
*
11138
* RETURN:
11139
* Zero
11140
*/
11141
static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
11142
{
11143
RECT rcOld = infoPtr->rcList;
11144
11145
TRACE("(width=%d, height=%d)\n", Width, Height);
11146
11147
LISTVIEW_UpdateSize(infoPtr);
11148
if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
11149
11150
/* do not bother with display related stuff if we're not redrawing */
11151
if (!is_redrawing(infoPtr)) return 0;
11152
11153
if (is_autoarrange(infoPtr))
11154
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
11155
11156
LISTVIEW_UpdateScroll(infoPtr);
11157
11158
/* refresh all only for lists whose height changed significantly */
11159
if ((infoPtr->uView == LV_VIEW_LIST) &&
11160
(rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
11161
(infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
11162
LISTVIEW_InvalidateList(infoPtr);
11163
11164
return 0;
11165
}
11166
11167
/***
11168
* DESCRIPTION:
11169
* Sets the size information.
11170
*
11171
* PARAMETER(S):
11172
* [I] infoPtr : valid pointer to the listview structure
11173
*
11174
* RETURN:
11175
* None
11176
*/
11177
static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
11178
{
11179
TRACE("uView %ld, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
11180
11181
GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
11182
11183
if (infoPtr->uView == LV_VIEW_LIST)
11184
{
11185
/* Apparently the "LIST" style is supposed to have the same
11186
* number of items in a column even if there is no scroll bar.
11187
* Since if a scroll bar already exists then the bottom is already
11188
* reduced, only reduce if the scroll bar does not currently exist.
11189
* The "2" is there to mimic the native control. I think it may be
11190
* related to either padding or edges. (GLA 7/2002)
11191
*/
11192
if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
11193
infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
11194
infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
11195
}
11196
11197
/* When ListView control is created invisible, header isn't created right away. */
11198
if (infoPtr->hwndHeader)
11199
{
11200
POINT origin;
11201
WINDOWPOS wp;
11202
HDLAYOUT hl;
11203
RECT rect;
11204
11205
LISTVIEW_GetOrigin(infoPtr, &origin);
11206
11207
rect = infoPtr->rcList;
11208
rect.left += origin.x;
11209
11210
hl.prc = &rect;
11211
hl.pwpos = &wp;
11212
SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
11213
TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
11214
11215
if (LISTVIEW_IsHeaderEnabled(infoPtr))
11216
wp.flags |= SWP_SHOWWINDOW;
11217
else
11218
{
11219
wp.flags |= SWP_HIDEWINDOW;
11220
wp.cy = 0;
11221
}
11222
11223
SetWindowPos(infoPtr->hwndHeader, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
11224
TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
11225
11226
infoPtr->rcList.top = max(wp.cy, 0);
11227
}
11228
/* extra padding for grid */
11229
if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
11230
infoPtr->rcList.top += 2;
11231
11232
TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
11233
}
11234
11235
/***
11236
* DESCRIPTION:
11237
* Processes WM_STYLECHANGED messages.
11238
*
11239
* PARAMETER(S):
11240
* [I] infoPtr : valid pointer to the listview structure
11241
* [I] wStyleType : window style type (normal or extended)
11242
* [I] lpss : window style information
11243
*
11244
* RETURN:
11245
* Zero
11246
*/
11247
static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
11248
const STYLESTRUCT *lpss)
11249
{
11250
UINT uNewView, uOldView;
11251
BOOL repaint = FALSE;
11252
UINT style;
11253
11254
TRACE("styletype %Ix, styleOld %#lx, styleNew %#lx\n",
11255
wStyleType, lpss->styleOld, lpss->styleNew);
11256
11257
if (wStyleType != GWL_STYLE || lpss->styleNew == infoPtr->dwStyle) return 0;
11258
11259
infoPtr->dwStyle = lpss->styleNew;
11260
11261
if (((lpss->styleOld & WS_HSCROLL) != 0)&&
11262
((lpss->styleNew & WS_HSCROLL) == 0))
11263
ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
11264
11265
if (((lpss->styleOld & WS_VSCROLL) != 0)&&
11266
((lpss->styleNew & WS_VSCROLL) == 0))
11267
ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
11268
11269
uNewView = lpss->styleNew & LVS_TYPEMASK;
11270
uOldView = lpss->styleOld & LVS_TYPEMASK;
11271
11272
if (uNewView != uOldView)
11273
{
11274
HIMAGELIST himl;
11275
11276
repaint = TRUE;
11277
11278
/* LVM_SETVIEW doesn't change window style bits within LVS_TYPEMASK,
11279
changing style updates current view only when view bits change. */
11280
map_style_view(infoPtr);
11281
SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
11282
ShowWindow(infoPtr->hwndHeader, SW_HIDE);
11283
11284
ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
11285
SetRectEmpty(&infoPtr->rcFocus);
11286
11287
himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
11288
set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
11289
11290
if (uNewView == LVS_REPORT)
11291
{
11292
HDLAYOUT hl;
11293
WINDOWPOS wp;
11294
11295
LISTVIEW_CreateHeader( infoPtr );
11296
11297
hl.prc = &infoPtr->rcList;
11298
hl.pwpos = &wp;
11299
SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
11300
SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
11301
wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
11302
? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
11303
}
11304
11305
LISTVIEW_UpdateItemSize(infoPtr);
11306
}
11307
11308
if (uNewView == LVS_REPORT || infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
11309
{
11310
if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
11311
{
11312
if (lpss->styleNew & LVS_NOCOLUMNHEADER)
11313
{
11314
/* Turn off the header control */
11315
style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
11316
TRACE("Hide header control, was 0x%08x\n", style);
11317
SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
11318
} else {
11319
/* Turn on the header control */
11320
if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
11321
{
11322
TRACE("Show header control, was 0x%08x\n", style);
11323
SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
11324
}
11325
}
11326
}
11327
}
11328
11329
if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
11330
(uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
11331
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
11332
11333
/* update the size of the client area */
11334
LISTVIEW_UpdateSize(infoPtr);
11335
11336
/* add scrollbars if needed */
11337
LISTVIEW_UpdateScroll(infoPtr);
11338
11339
if (repaint)
11340
LISTVIEW_InvalidateList(infoPtr);
11341
11342
return 0;
11343
}
11344
11345
/***
11346
* DESCRIPTION:
11347
* Processes WM_STYLECHANGING messages.
11348
*
11349
* PARAMETER(S):
11350
* [I] wStyleType : window style type (normal or extended)
11351
* [I0] lpss : window style information
11352
*
11353
* RETURN:
11354
* Zero
11355
*/
11356
static INT LISTVIEW_StyleChanging(WPARAM wStyleType,
11357
STYLESTRUCT *lpss)
11358
{
11359
TRACE("styletype %Ix, styleOld %#lx, styleNew %#lx\n",
11360
wStyleType, lpss->styleOld, lpss->styleNew);
11361
11362
/* don't forward LVS_OWNERDATA only if not already set to */
11363
if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
11364
{
11365
if (lpss->styleOld & LVS_OWNERDATA)
11366
lpss->styleNew |= LVS_OWNERDATA;
11367
else
11368
lpss->styleNew &= ~LVS_OWNERDATA;
11369
}
11370
11371
return 0;
11372
}
11373
11374
/***
11375
* DESCRIPTION:
11376
* Processes WM_SHOWWINDOW messages.
11377
*
11378
* PARAMETER(S):
11379
* [I] infoPtr : valid pointer to the listview structure
11380
* [I] bShown : window is being shown (FALSE when hidden)
11381
* [I] iStatus : window show status
11382
*
11383
* RETURN:
11384
* Zero
11385
*/
11386
static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus)
11387
{
11388
/* header delayed creation */
11389
if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
11390
{
11391
LISTVIEW_CreateHeader(infoPtr);
11392
11393
if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
11394
ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
11395
}
11396
11397
return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus);
11398
}
11399
11400
/***
11401
* DESCRIPTION:
11402
* Processes CCM_GETVERSION messages.
11403
*
11404
* PARAMETER(S):
11405
* [I] infoPtr : valid pointer to the listview structure
11406
*
11407
* RETURN:
11408
* Current version
11409
*/
11410
static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
11411
{
11412
return infoPtr->iVersion;
11413
}
11414
11415
/***
11416
* DESCRIPTION:
11417
* Processes CCM_SETVERSION messages.
11418
*
11419
* PARAMETER(S):
11420
* [I] infoPtr : valid pointer to the listview structure
11421
* [I] iVersion : version to be set
11422
*
11423
* RETURN:
11424
* -1 when requested version is greater than DLL version;
11425
* previous version otherwise
11426
*/
11427
static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, INT iVersion)
11428
{
11429
return COMCTL32_SetVersion(&infoPtr->iVersion, iVersion);
11430
}
11431
11432
/***
11433
* DESCRIPTION:
11434
* Window procedure of the listview control.
11435
*
11436
*/
11437
static LRESULT WINAPI
11438
LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11439
{
11440
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
11441
11442
TRACE("hwnd %p, uMsg %x, wParam %Ix, lParam %Ix\n", hwnd, uMsg, wParam, lParam);
11443
11444
if (!infoPtr && (uMsg != WM_NCCREATE))
11445
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11446
11447
switch (uMsg)
11448
{
11449
case LVM_APPROXIMATEVIEWRECT:
11450
return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
11451
LOWORD(lParam), HIWORD(lParam));
11452
case LVM_ARRANGE:
11453
return LISTVIEW_Arrange(infoPtr, (INT)wParam);
11454
11455
case LVM_CANCELEDITLABEL:
11456
return LISTVIEW_CancelEditLabel(infoPtr);
11457
11458
case LVM_CREATEDRAGIMAGE:
11459
return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
11460
11461
case LVM_DELETEALLITEMS:
11462
return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
11463
11464
case LVM_DELETECOLUMN:
11465
return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
11466
11467
case LVM_DELETEITEM:
11468
return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
11469
11470
case LVM_EDITLABELA:
11471
case LVM_EDITLABELW:
11472
return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam,
11473
uMsg == LVM_EDITLABELW);
11474
/* case LVM_ENABLEGROUPVIEW: */
11475
11476
case LVM_ENSUREVISIBLE:
11477
return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
11478
11479
case LVM_FINDITEMW:
11480
return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
11481
11482
case LVM_FINDITEMA:
11483
return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
11484
11485
case LVM_GETBKCOLOR:
11486
return infoPtr->clrBk;
11487
11488
/* case LVM_GETBKIMAGE: */
11489
11490
case LVM_GETCALLBACKMASK:
11491
return infoPtr->uCallbackMask;
11492
11493
case LVM_GETCOLUMNA:
11494
case LVM_GETCOLUMNW:
11495
return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11496
uMsg == LVM_GETCOLUMNW);
11497
11498
case LVM_GETCOLUMNORDERARRAY:
11499
return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11500
11501
case LVM_GETCOLUMNWIDTH:
11502
return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
11503
11504
case LVM_GETCOUNTPERPAGE:
11505
return LISTVIEW_GetCountPerPage(infoPtr);
11506
11507
case LVM_GETEDITCONTROL:
11508
return (LRESULT)infoPtr->hwndEdit;
11509
11510
case LVM_GETEXTENDEDLISTVIEWSTYLE:
11511
return infoPtr->dwLvExStyle;
11512
11513
/* case LVM_GETGROUPINFO: */
11514
11515
/* case LVM_GETGROUPMETRICS: */
11516
11517
case LVM_GETHEADER:
11518
return (LRESULT)infoPtr->hwndHeader;
11519
11520
case LVM_GETHOTCURSOR:
11521
return (LRESULT)infoPtr->hHotCursor;
11522
11523
case LVM_GETHOTITEM:
11524
return infoPtr->nHotItem;
11525
11526
case LVM_GETHOVERTIME:
11527
return infoPtr->dwHoverTime;
11528
11529
case LVM_GETIMAGELIST:
11530
return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
11531
11532
/* case LVM_GETINSERTMARK: */
11533
11534
/* case LVM_GETINSERTMARKCOLOR: */
11535
11536
/* case LVM_GETINSERTMARKRECT: */
11537
11538
case LVM_GETISEARCHSTRINGA:
11539
case LVM_GETISEARCHSTRINGW:
11540
FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
11541
return FALSE;
11542
11543
case LVM_GETITEMA:
11544
case LVM_GETITEMW:
11545
return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW);
11546
11547
case LVM_GETITEMCOUNT:
11548
return infoPtr->nItemCount;
11549
11550
case LVM_GETITEMPOSITION:
11551
return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
11552
11553
case LVM_GETITEMRECT:
11554
return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
11555
11556
case LVM_GETITEMSPACING:
11557
return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
11558
11559
case LVM_GETITEMSTATE:
11560
return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
11561
11562
case LVM_GETITEMTEXTA:
11563
case LVM_GETITEMTEXTW:
11564
return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11565
uMsg == LVM_GETITEMTEXTW);
11566
11567
case LVM_GETNEXTITEM:
11568
return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
11569
11570
case LVM_GETNEXTITEMINDEX:
11571
return LISTVIEW_GetNextItemIndex(infoPtr, (LVITEMINDEX *)wParam, lParam);
11572
11573
case LVM_GETNUMBEROFWORKAREAS:
11574
FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11575
return 1;
11576
11577
case LVM_GETORIGIN:
11578
{
11579
POINT *point = (POINT *)lParam;
11580
11581
if (!point) return FALSE;
11582
if (infoPtr->uView == LV_VIEW_DETAILS ||
11583
infoPtr->uView == LV_VIEW_LIST) return FALSE;
11584
LISTVIEW_GetOrigin(infoPtr, point);
11585
point->x = -point->x;
11586
point->y = -point->y;
11587
return TRUE;
11588
}
11589
11590
/* case LVM_GETOUTLINECOLOR: */
11591
11592
case LVM_GETSELECTEDCOLUMN:
11593
return infoPtr->selected_column;
11594
11595
case LVM_GETSELECTEDCOUNT:
11596
return LISTVIEW_GetSelectedCount(infoPtr);
11597
11598
case LVM_GETSELECTIONMARK:
11599
return infoPtr->nSelectionMark;
11600
11601
case LVM_GETSTRINGWIDTHA:
11602
case LVM_GETSTRINGWIDTHW:
11603
return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam,
11604
uMsg == LVM_GETSTRINGWIDTHW);
11605
11606
case LVM_GETSUBITEMRECT:
11607
return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
11608
11609
case LVM_GETTEXTBKCOLOR:
11610
return infoPtr->clrTextBk;
11611
11612
case LVM_GETTEXTCOLOR:
11613
return infoPtr->clrText;
11614
11615
/* case LVM_GETTILEINFO: */
11616
11617
/* case LVM_GETTILEVIEWINFO: */
11618
11619
case LVM_GETTOOLTIPS:
11620
if( !infoPtr->hwndToolTip )
11621
infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
11622
return (LRESULT)infoPtr->hwndToolTip;
11623
11624
case LVM_GETTOPINDEX:
11625
return LISTVIEW_GetTopIndex(infoPtr);
11626
11627
case LVM_GETUNICODEFORMAT:
11628
return (infoPtr->notifyFormat == NFR_UNICODE);
11629
11630
case LVM_GETVIEW:
11631
return infoPtr->uView;
11632
11633
case LVM_GETVIEWRECT:
11634
return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
11635
11636
case LVM_GETWORKAREAS:
11637
FIXME("LVM_GETWORKAREAS: unimplemented\n");
11638
return FALSE;
11639
11640
/* case LVM_HASGROUP: */
11641
11642
case LVM_HITTEST:
11643
return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
11644
11645
case LVM_INSERTCOLUMNA:
11646
case LVM_INSERTCOLUMNW:
11647
return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11648
uMsg == LVM_INSERTCOLUMNW);
11649
11650
/* case LVM_INSERTGROUP: */
11651
11652
/* case LVM_INSERTGROUPSORTED: */
11653
11654
case LVM_INSERTITEMA:
11655
case LVM_INSERTITEMW:
11656
return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW);
11657
11658
/* case LVM_INSERTMARKHITTEST: */
11659
11660
/* case LVM_ISGROUPVIEWENABLED: */
11661
11662
case LVM_ISITEMVISIBLE:
11663
return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
11664
11665
case LVM_MAPIDTOINDEX:
11666
return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
11667
11668
case LVM_MAPINDEXTOID:
11669
return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
11670
11671
/* case LVM_MOVEGROUP: */
11672
11673
/* case LVM_MOVEITEMTOGROUP: */
11674
11675
case LVM_REDRAWITEMS:
11676
return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
11677
11678
/* case LVM_REMOVEALLGROUPS: */
11679
11680
/* case LVM_REMOVEGROUP: */
11681
11682
case LVM_SCROLL:
11683
return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
11684
11685
case LVM_SETBKCOLOR:
11686
return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
11687
11688
case LVM_SETBKIMAGEA:
11689
case LVM_SETBKIMAGEW:
11690
return LISTVIEW_SetBkImage(infoPtr, (LVBKIMAGEW *)lParam, uMsg == LVM_SETBKIMAGEW);
11691
11692
case LVM_SETCALLBACKMASK:
11693
infoPtr->uCallbackMask = (UINT)wParam;
11694
return TRUE;
11695
11696
case LVM_SETCOLUMNA:
11697
case LVM_SETCOLUMNW:
11698
return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11699
uMsg == LVM_SETCOLUMNW);
11700
11701
case LVM_SETCOLUMNORDERARRAY:
11702
return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11703
11704
case LVM_SETCOLUMNWIDTH:
11705
return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
11706
11707
case LVM_SETEXTENDEDLISTVIEWSTYLE:
11708
return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
11709
11710
/* case LVM_SETGROUPINFO: */
11711
11712
/* case LVM_SETGROUPMETRICS: */
11713
11714
case LVM_SETHOTCURSOR:
11715
return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
11716
11717
case LVM_SETHOTITEM:
11718
return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
11719
11720
case LVM_SETHOVERTIME:
11721
return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam);
11722
11723
case LVM_SETICONSPACING:
11724
if(lParam == -1)
11725
return LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
11726
return LISTVIEW_SetIconSpacing(infoPtr, LOWORD(lParam), HIWORD(lParam));
11727
11728
case LVM_SETIMAGELIST:
11729
return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
11730
11731
/* case LVM_SETINFOTIP: */
11732
11733
/* case LVM_SETINSERTMARK: */
11734
11735
/* case LVM_SETINSERTMARKCOLOR: */
11736
11737
case LVM_SETITEMA:
11738
case LVM_SETITEMW:
11739
{
11740
if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
11741
return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
11742
}
11743
11744
case LVM_SETITEMCOUNT:
11745
return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
11746
11747
case LVM_SETITEMPOSITION:
11748
{
11749
POINT pt;
11750
pt.x = (short)LOWORD(lParam);
11751
pt.y = (short)HIWORD(lParam);
11752
return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt);
11753
}
11754
11755
case LVM_SETITEMPOSITION32:
11756
return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
11757
11758
case LVM_SETITEMSTATE:
11759
return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
11760
11761
case LVM_SETITEMTEXTA:
11762
case LVM_SETITEMTEXTW:
11763
return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11764
uMsg == LVM_SETITEMTEXTW);
11765
11766
/* case LVM_SETOUTLINECOLOR: */
11767
11768
case LVM_SETSELECTEDCOLUMN:
11769
infoPtr->selected_column = (INT)wParam;
11770
return TRUE;
11771
11772
case LVM_SETSELECTIONMARK:
11773
return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
11774
11775
case LVM_SETTEXTBKCOLOR:
11776
return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
11777
11778
case LVM_SETTEXTCOLOR:
11779
return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
11780
11781
/* case LVM_SETTILEINFO: */
11782
11783
/* case LVM_SETTILEVIEWINFO: */
11784
11785
/* case LVM_SETTILEWIDTH: */
11786
11787
case LVM_SETTOOLTIPS:
11788
return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
11789
11790
case LVM_SETUNICODEFORMAT:
11791
return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
11792
11793
case LVM_SETVIEW:
11794
return LISTVIEW_SetView(infoPtr, wParam);
11795
11796
/* case LVM_SETWORKAREAS: */
11797
11798
/* case LVM_SORTGROUPS: */
11799
11800
case LVM_SORTITEMS:
11801
case LVM_SORTITEMSEX:
11802
return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, wParam,
11803
uMsg == LVM_SORTITEMSEX);
11804
case LVM_SUBITEMHITTEST:
11805
return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
11806
11807
case LVM_UPDATE:
11808
return LISTVIEW_Update(infoPtr, (INT)wParam);
11809
11810
case CCM_GETVERSION:
11811
return LISTVIEW_GetVersion(infoPtr);
11812
11813
case CCM_SETVERSION:
11814
return LISTVIEW_SetVersion(infoPtr, wParam);
11815
11816
case WM_CHAR:
11817
return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
11818
11819
case WM_COMMAND:
11820
return LISTVIEW_Command(infoPtr, wParam, lParam);
11821
11822
case WM_NCCREATE:
11823
return LISTVIEW_NCCreate(hwnd, wParam, (LPCREATESTRUCTW)lParam);
11824
11825
case WM_CREATE:
11826
return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
11827
11828
case WM_DESTROY:
11829
return LISTVIEW_Destroy(infoPtr);
11830
11831
case WM_ENABLE:
11832
return LISTVIEW_Enable(infoPtr);
11833
11834
case WM_ERASEBKGND:
11835
return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
11836
11837
case WM_GETDLGCODE:
11838
return DLGC_WANTCHARS | DLGC_WANTARROWS;
11839
11840
case WM_GETFONT:
11841
return (LRESULT)infoPtr->hFont;
11842
11843
case WM_GETOBJECT:
11844
if ((LONG)lParam == OBJID_QUERYCLASSNAMEIDX)
11845
return 0x10013;
11846
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11847
11848
case WM_HSCROLL:
11849
return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0);
11850
11851
case WM_KEYDOWN:
11852
return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
11853
11854
case WM_KILLFOCUS:
11855
return LISTVIEW_KillFocus(infoPtr);
11856
11857
case WM_LBUTTONDBLCLK:
11858
return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11859
11860
case WM_LBUTTONDOWN:
11861
return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11862
11863
case WM_LBUTTONUP:
11864
return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11865
11866
case WM_MOUSEMOVE:
11867
return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11868
11869
case WM_MOUSEHOVER:
11870
return LISTVIEW_MouseHover(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11871
11872
case WM_NCDESTROY:
11873
return LISTVIEW_NCDestroy(infoPtr);
11874
11875
case WM_NCPAINT:
11876
return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam);
11877
11878
case WM_NOTIFY:
11879
return LISTVIEW_Notify(infoPtr, (LPNMHDR)lParam);
11880
11881
case WM_NOTIFYFORMAT:
11882
return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
11883
11884
case WM_PRINTCLIENT:
11885
return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
11886
11887
case WM_PAINT:
11888
return LISTVIEW_WMPaint(infoPtr, (HDC)wParam);
11889
11890
case WM_RBUTTONDBLCLK:
11891
return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11892
11893
case WM_RBUTTONDOWN:
11894
return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11895
11896
case WM_SETCURSOR:
11897
return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
11898
11899
case WM_SETFOCUS:
11900
return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
11901
11902
case WM_SETFONT:
11903
return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
11904
11905
case WM_SETREDRAW:
11906
return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
11907
11908
case WM_SHOWWINDOW:
11909
return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
11910
11911
case WM_STYLECHANGED:
11912
return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11913
11914
case WM_STYLECHANGING:
11915
return LISTVIEW_StyleChanging(wParam, (LPSTYLESTRUCT)lParam);
11916
11917
case WM_SYSCOLORCHANGE:
11918
COMCTL32_RefreshSysColors();
11919
return 0;
11920
11921
/* case WM_TIMER: */
11922
case WM_THEMECHANGED:
11923
return COMCTL32_ThemeChanged(infoPtr->hwndSelf, L"ListView", TRUE, TRUE);
11924
11925
case WM_VSCROLL:
11926
return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0);
11927
11928
case WM_MOUSEWHEEL:
11929
if (wParam & (MK_SHIFT | MK_CONTROL))
11930
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11931
return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
11932
11933
case WM_WINDOWPOSCHANGED:
11934
if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
11935
{
11936
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
11937
SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
11938
11939
if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
11940
{
11941
if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
11942
}
11943
LISTVIEW_Size(infoPtr, ((WINDOWPOS *)lParam)->cx, ((WINDOWPOS *)lParam)->cy);
11944
}
11945
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11946
11947
/* case WM_WININICHANGE: */
11948
11949
default:
11950
if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
11951
ERR("unknown msg %04x, wp %Ix, lp %Ix\n", uMsg, wParam, lParam);
11952
11953
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11954
}
11955
11956
}
11957
11958
/***
11959
* DESCRIPTION:
11960
* Registers the window class.
11961
*
11962
* PARAMETER(S):
11963
* None
11964
*
11965
* RETURN:
11966
* None
11967
*/
11968
void LISTVIEW_Register(void)
11969
{
11970
WNDCLASSW wndClass;
11971
11972
ZeroMemory(&wndClass, sizeof(WNDCLASSW));
11973
wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
11974
wndClass.lpfnWndProc = LISTVIEW_WindowProc;
11975
wndClass.cbClsExtra = 0;
11976
wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
11977
wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
11978
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
11979
wndClass.lpszClassName = WC_LISTVIEWW;
11980
RegisterClassW(&wndClass);
11981
}
11982
11983
/***
11984
* DESCRIPTION:
11985
* Unregisters the window class.
11986
*
11987
* PARAMETER(S):
11988
* None
11989
*
11990
* RETURN:
11991
* None
11992
*/
11993
void LISTVIEW_Unregister(void)
11994
{
11995
UnregisterClassW(WC_LISTVIEWW, NULL);
11996
}
11997
11998
/***
11999
* DESCRIPTION:
12000
* Handle any WM_COMMAND messages
12001
*
12002
* PARAMETER(S):
12003
* [I] infoPtr : valid pointer to the listview structure
12004
* [I] wParam : the first message parameter
12005
* [I] lParam : the second message parameter
12006
*
12007
* RETURN:
12008
* Zero.
12009
*/
12010
static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
12011
{
12012
12013
TRACE("%p, %x, %x, %Ix\n", infoPtr, HIWORD(wParam), LOWORD(wParam), lParam);
12014
12015
if (!infoPtr->hwndEdit) return 0;
12016
12017
switch (HIWORD(wParam))
12018
{
12019
case EN_UPDATE:
12020
{
12021
/*
12022
* Adjust the edit window size
12023
*/
12024
WCHAR buffer[1024];
12025
HDC hdc = GetDC(infoPtr->hwndEdit);
12026
HFONT hFont, hOldFont = 0;
12027
RECT rect;
12028
SIZE sz;
12029
12030
if (!infoPtr->hwndEdit || !hdc) return 0;
12031
GetWindowTextW(infoPtr->hwndEdit, buffer, ARRAY_SIZE(buffer));
12032
GetWindowRect(infoPtr->hwndEdit, &rect);
12033
12034
/* Select font to get the right dimension of the string */
12035
hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
12036
if (hFont)
12037
{
12038
hOldFont = SelectObject(hdc, hFont);
12039
}
12040
12041
if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
12042
{
12043
TEXTMETRICW textMetric;
12044
12045
/* Add Extra spacing for the next character */
12046
GetTextMetricsW(hdc, &textMetric);
12047
sz.cx += (textMetric.tmMaxCharWidth * 2);
12048
12049
SetWindowPos(infoPtr->hwndEdit, NULL, 0, 0, sz.cx,
12050
rect.bottom - rect.top, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOZORDER);
12051
}
12052
if (hFont)
12053
SelectObject(hdc, hOldFont);
12054
12055
ReleaseDC(infoPtr->hwndEdit, hdc);
12056
12057
break;
12058
}
12059
case EN_KILLFOCUS:
12060
{
12061
if (infoPtr->notify_mask & NOTIFY_MASK_END_LABEL_EDIT)
12062
LISTVIEW_CancelEditLabel(infoPtr);
12063
break;
12064
}
12065
12066
default:
12067
return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
12068
}
12069
12070
return 0;
12071
}
12072
12073