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