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