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