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