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