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