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