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