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