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