user32/tests: Fix a couple of dimension checks on W2k3 and Vista.
[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     TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1381
1382     /* setup creation flags */
1383     dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1384     dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1385
1386     hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1387
1388     /* create header */
1389     infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1390       0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1391     if (!infoPtr->hwndHeader) return -1;
1392
1393     /* set header unicode format */
1394     SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1395
1396     /* set header font */
1397     SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1398
1399     LISTVIEW_UpdateSize(infoPtr);
1400
1401     return 0;
1402 }
1403
1404 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1405 {
1406     *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1407 }
1408         
1409 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1410 {
1411     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1412 }
1413
1414 /* Listview invalidation functions: use _only_ these functions to invalidate */
1415
1416 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1417 {
1418     return infoPtr->bRedraw;
1419 }
1420
1421 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1422 {
1423     if(!is_redrawing(infoPtr)) return; 
1424     TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1425     InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1426 }
1427
1428 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1429 {
1430     RECT rcBox;
1431
1432     if(!is_redrawing(infoPtr)) return; 
1433     LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1434     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1435 }
1436
1437 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1438 {
1439     POINT Origin, Position;
1440     RECT rcBox;
1441     
1442     if(!is_redrawing(infoPtr)) return; 
1443     assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1444     LISTVIEW_GetOrigin(infoPtr, &Origin);
1445     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1446     LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1447     rcBox.top = 0;
1448     rcBox.bottom = infoPtr->nItemHeight;
1449     OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1450     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1451 }
1452
1453 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1454 {
1455     LISTVIEW_InvalidateRect(infoPtr, NULL);
1456 }
1457
1458 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1459 {
1460     RECT rcCol;
1461     
1462     if(!is_redrawing(infoPtr)) return; 
1463     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1464     rcCol.top = infoPtr->rcList.top;
1465     rcCol.bottom = infoPtr->rcList.bottom;
1466     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1467 }
1468
1469 /***
1470  * DESCRIPTION:
1471  * Retrieves the number of items that can fit vertically in the client area.
1472  *
1473  * PARAMETER(S):
1474  * [I] infoPtr : valid pointer to the listview structure
1475  *
1476  * RETURN:
1477  * Number of items per row.
1478  */
1479 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1480 {
1481     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1482
1483     return max(nListWidth/infoPtr->nItemWidth, 1);
1484 }
1485
1486 /***
1487  * DESCRIPTION:
1488  * Retrieves the number of items that can fit horizontally in the client
1489  * area.
1490  *
1491  * PARAMETER(S):
1492  * [I] infoPtr : valid pointer to the listview structure
1493  *
1494  * RETURN:
1495  * Number of items per column.
1496  */
1497 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1498 {
1499     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1500
1501     return max(nListHeight / infoPtr->nItemHeight, 1);
1502 }
1503
1504
1505 /*************************************************************************
1506  *              LISTVIEW_ProcessLetterKeys
1507  *
1508  *  Processes keyboard messages generated by pressing the letter keys
1509  *  on the keyboard.
1510  *  What this does is perform a case insensitive search from the
1511  *  current position with the following quirks:
1512  *  - If two chars or more are pressed in quick succession we search
1513  *    for the corresponding string (e.g. 'abc').
1514  *  - If there is a delay we wipe away the current search string and
1515  *    restart with just that char.
1516  *  - If the user keeps pressing the same character, whether slowly or
1517  *    fast, so that the search string is entirely composed of this
1518  *    character ('aaaaa' for instance), then we search for first item
1519  *    that starting with that character.
1520  *  - If the user types the above character in quick succession, then
1521  *    we must also search for the corresponding string ('aaaaa'), and
1522  *    go to that string if there is a match.
1523  *
1524  * PARAMETERS
1525  *   [I] hwnd : handle to the window
1526  *   [I] charCode : the character code, the actual character
1527  *   [I] keyData : key data
1528  *
1529  * RETURNS
1530  *
1531  *  Zero.
1532  *
1533  * BUGS
1534  *
1535  *  - The current implementation has a list of characters it will
1536  *    accept and it ignores everything else. In particular it will
1537  *    ignore accentuated characters which seems to match what
1538  *    Windows does. But I'm not sure it makes sense to follow
1539  *    Windows there.
1540  *  - We don't sound a beep when the search fails.
1541  *
1542  * SEE ALSO
1543  *
1544  *  TREEVIEW_ProcessLetterKeys
1545  */
1546 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1547 {
1548     INT nItem;
1549     INT endidx,idx;
1550     LVITEMW item;
1551     WCHAR buffer[MAX_PATH];
1552     DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1553
1554     /* simple parameter checking */
1555     if (!charCode || !keyData) return 0;
1556
1557     /* only allow the valid WM_CHARs through */
1558     if (!isalnumW(charCode) &&
1559         charCode != '.' && charCode != '`' && charCode != '!' &&
1560         charCode != '@' && charCode != '#' && charCode != '$' &&
1561         charCode != '%' && charCode != '^' && charCode != '&' &&
1562         charCode != '*' && charCode != '(' && charCode != ')' &&
1563         charCode != '-' && charCode != '_' && charCode != '+' &&
1564         charCode != '=' && charCode != '\\'&& charCode != ']' &&
1565         charCode != '}' && charCode != '[' && charCode != '{' &&
1566         charCode != '/' && charCode != '?' && charCode != '>' &&
1567         charCode != '<' && charCode != ',' && charCode != '~')
1568         return 0;
1569
1570     /* if there's one item or less, there is no where to go */
1571     if (infoPtr->nItemCount <= 1) return 0;
1572
1573     /* update the search parameters */
1574     infoPtr->lastKeyPressTimestamp = GetTickCount();
1575     if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1576         if (infoPtr->nSearchParamLength < MAX_PATH-1)
1577             infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1578         if (infoPtr->charCode != charCode)
1579             infoPtr->charCode = charCode = 0;
1580     } else {
1581         infoPtr->charCode=charCode;
1582         infoPtr->szSearchParam[0]=charCode;
1583         infoPtr->nSearchParamLength=1;
1584         /* Redundant with the 1 char string */
1585         charCode=0;
1586     }
1587
1588     /* and search from the current position */
1589     nItem=-1;
1590     if (infoPtr->nFocusedItem >= 0) {
1591         endidx=infoPtr->nFocusedItem;
1592         idx=endidx;
1593         /* if looking for single character match,
1594          * then we must always move forward
1595          */
1596         if (infoPtr->nSearchParamLength == 1)
1597             idx++;
1598     } else {
1599         endidx=infoPtr->nItemCount;
1600         idx=0;
1601     }
1602
1603     /* Let application handle this for virtual listview */
1604     if (infoPtr->dwStyle & LVS_OWNERDATA)
1605     {
1606         NMLVFINDITEMW nmlv;
1607         LVFINDINFOW lvfi;
1608
1609         ZeroMemory(&lvfi, sizeof(lvfi));
1610         lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1611         infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1612         lvfi.psz = infoPtr->szSearchParam;
1613         nmlv.iStart = idx;
1614         nmlv.lvfi = lvfi;
1615
1616         nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1617
1618         if (nItem != -1)
1619             LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1620
1621         return 0;
1622     }
1623
1624     do {
1625         if (idx == infoPtr->nItemCount) {
1626             if (endidx == infoPtr->nItemCount || endidx == 0)
1627                 break;
1628             idx=0;
1629         }
1630
1631         /* get item */
1632         item.mask = LVIF_TEXT;
1633         item.iItem = idx;
1634         item.iSubItem = 0;
1635         item.pszText = buffer;
1636         item.cchTextMax = MAX_PATH;
1637         if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1638
1639         /* check for a match */
1640         if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1641             nItem=idx;
1642             break;
1643         } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1644                     (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1645             /* This would work but we must keep looking for a longer match */
1646             nItem=idx;
1647         }
1648         idx++;
1649     } while (idx != endidx);
1650
1651     if (nItem != -1)
1652         LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1653
1654     return 0;
1655 }
1656
1657 /*************************************************************************
1658  * LISTVIEW_UpdateHeaderSize [Internal]
1659  *
1660  * Function to resize the header control
1661  *
1662  * PARAMS
1663  * [I]  hwnd : handle to a window
1664  * [I]  nNewScrollPos : scroll pos to set
1665  *
1666  * RETURNS
1667  * None.
1668  */
1669 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1670 {
1671     RECT winRect;
1672     POINT point[2];
1673
1674     TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1675
1676     if (!infoPtr->hwndHeader)  return;
1677
1678     GetWindowRect(infoPtr->hwndHeader, &winRect);
1679     point[0].x = winRect.left;
1680     point[0].y = winRect.top;
1681     point[1].x = winRect.right;
1682     point[1].y = winRect.bottom;
1683
1684     MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1685     point[0].x = -nNewScrollPos;
1686     point[1].x += nNewScrollPos;
1687
1688     SetWindowPos(infoPtr->hwndHeader,0,
1689         point[0].x,point[0].y,point[1].x,point[1].y,
1690         (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1691         SWP_NOZORDER | SWP_NOACTIVATE);
1692 }
1693
1694 /***
1695  * DESCRIPTION:
1696  * Update the scrollbars. This functions should be called whenever
1697  * the content, size or view changes.
1698  *
1699  * PARAMETER(S):
1700  * [I] infoPtr : valid pointer to the listview structure
1701  *
1702  * RETURN:
1703  * None
1704  */
1705 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1706 {
1707     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1708     SCROLLINFO horzInfo, vertInfo;
1709     INT dx, dy;
1710
1711     if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1712
1713     ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1714     horzInfo.cbSize = sizeof(SCROLLINFO);
1715     horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1716
1717     /* for now, we'll set info.nMax to the _count_, and adjust it later */
1718     if (uView == LVS_LIST)
1719     {
1720         INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1721         horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1722
1723         /* scroll by at least one column per page */
1724         if(horzInfo.nPage < infoPtr->nItemWidth)
1725                 horzInfo.nPage = infoPtr->nItemWidth;
1726
1727         horzInfo.nPage /= infoPtr->nItemWidth;
1728     }
1729     else if (uView == LVS_REPORT)
1730     {
1731         horzInfo.nMax = infoPtr->nItemWidth;
1732     }
1733     else /* LVS_ICON, or LVS_SMALLICON */
1734     {
1735         RECT rcView;
1736
1737         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1738     }
1739   
1740     horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1741     horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1742     dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1743     dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1744     TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1745
1746     /* Setting the horizontal scroll can change the listview size
1747      * (and potentially everything else) so we need to recompute
1748      * everything again for the vertical scroll
1749      */
1750
1751     ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1752     vertInfo.cbSize = sizeof(SCROLLINFO);
1753     vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1754
1755     if (uView == LVS_REPORT)
1756     {
1757         vertInfo.nMax = infoPtr->nItemCount;
1758         
1759         /* scroll by at least one page */
1760         if(vertInfo.nPage < infoPtr->nItemHeight)
1761           vertInfo.nPage = infoPtr->nItemHeight;
1762
1763         if (infoPtr->nItemHeight > 0)
1764             vertInfo.nPage /= infoPtr->nItemHeight;
1765     }
1766     else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1767     {
1768         RECT rcView;
1769
1770         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1771     }
1772
1773     vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1774     vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1775     dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1776     dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1777     TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1778
1779     /* Change of the range may have changed the scroll pos. If so move the content */
1780     if (dx != 0 || dy != 0)
1781     {
1782         RECT listRect;
1783         listRect = infoPtr->rcList;
1784         ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1785             SW_ERASE | SW_INVALIDATE);
1786     }
1787
1788     /* Update the Header Control */
1789     if (uView == LVS_REPORT)
1790     {
1791         horzInfo.fMask = SIF_POS;
1792         GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1793         LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1794     }
1795 }
1796
1797
1798 /***
1799  * DESCRIPTION:
1800  * Shows/hides the focus rectangle. 
1801  *
1802  * PARAMETER(S):
1803  * [I] infoPtr : valid pointer to the listview structure
1804  * [I] fShow : TRUE to show the focus, FALSE to hide it.
1805  *
1806  * RETURN:
1807  * None
1808  */
1809 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1810 {
1811     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1812     HDC hdc;
1813
1814     TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1815
1816     if (infoPtr->nFocusedItem < 0) return;
1817
1818     /* we need some gymnastics in ICON mode to handle large items */
1819     if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1820     {
1821         RECT rcBox;
1822
1823         LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox); 
1824         if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1825         {
1826             LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1827             return;
1828         }
1829     }
1830
1831     if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1832
1833     /* for some reason, owner draw should work only in report mode */
1834     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1835     {
1836         DRAWITEMSTRUCT dis;
1837         LVITEMW item;
1838
1839         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1840         HFONT hOldFont = SelectObject(hdc, hFont);
1841
1842         item.iItem = infoPtr->nFocusedItem;
1843         item.iSubItem = 0;
1844         item.mask = LVIF_PARAM;
1845         if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1846            
1847         ZeroMemory(&dis, sizeof(dis)); 
1848         dis.CtlType = ODT_LISTVIEW;
1849         dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1850         dis.itemID = item.iItem;
1851         dis.itemAction = ODA_FOCUS;
1852         if (fShow) dis.itemState |= ODS_FOCUS;
1853         dis.hwndItem = infoPtr->hwndSelf;
1854         dis.hDC = hdc;
1855         LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1856         dis.itemData = item.lParam;
1857
1858         SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1859
1860         SelectObject(hdc, hOldFont);
1861     }
1862     else
1863     {
1864         DrawFocusRect(hdc, &infoPtr->rcFocus);
1865     }
1866 done:
1867     ReleaseDC(infoPtr->hwndSelf, hdc);
1868 }
1869
1870 /***
1871  * Invalidates all visible selected items.
1872  */
1873 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1874 {
1875     ITERATOR i; 
1876    
1877     iterator_frameditems(&i, infoPtr, &infoPtr->rcList); 
1878     while(iterator_next(&i))
1879     {
1880         if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1881             LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1882     }
1883     iterator_destroy(&i);
1884 }
1885
1886             
1887 /***
1888  * DESCRIPTION:            [INTERNAL]
1889  * Computes an item's (left,top) corner, relative to rcView.
1890  * That is, the position has NOT been made relative to the Origin.
1891  * This is deliberate, to avoid computing the Origin over, and
1892  * over again, when this function is called in a loop. Instead,
1893  * one can factor the computation of the Origin before the loop,
1894  * and offset the value returned by this function, on every iteration.
1895  * 
1896  * PARAMETER(S):
1897  * [I] infoPtr : valid pointer to the listview structure
1898  * [I] nItem  : item number
1899  * [O] lpptOrig : item top, left corner
1900  *
1901  * RETURN:
1902  *   None.
1903  */
1904 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1905 {
1906     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1907
1908     assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1909
1910     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1911     {
1912         lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1913         lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1914     }
1915     else if (uView == LVS_LIST)
1916     {
1917         INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1918         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1919         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1920     }
1921     else /* LVS_REPORT */
1922     {
1923         lpptPosition->x = 0;
1924         lpptPosition->y = nItem * infoPtr->nItemHeight;
1925     }
1926 }
1927     
1928 /***
1929  * DESCRIPTION:            [INTERNAL]
1930  * Compute the rectangles of an item.  This is to localize all
1931  * the computations in one place. If you are not interested in some
1932  * of these values, simply pass in a NULL -- the function is smart
1933  * enough to compute only what's necessary. The function computes
1934  * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1935  * one, the BOX rectangle. This rectangle is very cheap to compute,
1936  * and is guaranteed to contain all the other rectangles. Computing
1937  * the ICON rect is also cheap, but all the others are potentially
1938  * expensive. This gives an easy and effective optimization when
1939  * searching (like point inclusion, or rectangle intersection):
1940  * first test against the BOX, and if TRUE, test against the desired
1941  * rectangle.
1942  * If the function does not have all the necessary information
1943  * to computed the requested rectangles, will crash with a
1944  * failed assertion. This is done so we catch all programming
1945  * errors, given that the function is called only from our code.
1946  *
1947  * We have the following 'special' meanings for a few fields:
1948  *   * If LVIS_FOCUSED is set, we assume the item has the focus
1949  *     This is important in ICON mode, where it might get a larger
1950  *     then usual rectangle
1951  *
1952  * Please note that subitem support works only in REPORT mode.
1953  *
1954  * PARAMETER(S):
1955  * [I] infoPtr : valid pointer to the listview structure
1956  * [I] lpLVItem : item to compute the measures for
1957  * [O] lprcBox : ptr to Box rectangle
1958  *                Same as LVM_GETITEMRECT with LVIR_BOUNDS
1959  * [0] lprcSelectBox : ptr to select box rectangle
1960  *                Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1961  * [O] lprcIcon : ptr to Icon rectangle
1962  *                Same as LVM_GETITEMRECT with LVIR_ICON
1963  * [O] lprcStateIcon: ptr to State Icon rectangle
1964  * [O] lprcLabel : ptr to Label rectangle
1965  *                Same as LVM_GETITEMRECT with LVIR_LABEL
1966  *
1967  * RETURN:
1968  *   None.
1969  */
1970 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1971                                     LPRECT lprcBox, LPRECT lprcSelectBox,
1972                                     LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1973 {
1974     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1975     BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1976     RECT Box, SelectBox, Icon, Label;
1977     COLUMN_INFO *lpColumnInfo = NULL;
1978     SIZE labelSize = { 0, 0 };
1979
1980     TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1981
1982     /* Be smart and try to figure out the minimum we have to do */
1983     if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1984     if (uView == LVS_ICON && (lprcBox || lprcLabel))
1985     {
1986         assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1987         if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1988     }
1989     if (lprcSelectBox) doSelectBox = TRUE;
1990     if (lprcLabel) doLabel = TRUE;
1991     if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1992     if (doSelectBox)
1993     {
1994         doIcon = TRUE;
1995         doLabel = TRUE;
1996     }
1997
1998     /************************************************************/
1999     /* compute the box rectangle (it should be cheap to do)     */
2000     /************************************************************/
2001     if (lpLVItem->iSubItem || uView == LVS_REPORT)
2002         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2003
2004     if (lpLVItem->iSubItem)    
2005     {
2006         Box = lpColumnInfo->rcHeader;
2007     }
2008     else
2009     {
2010         Box.left = 0;
2011         Box.right = infoPtr->nItemWidth;
2012     }
2013     Box.top = 0;
2014     Box.bottom = infoPtr->nItemHeight;
2015
2016     /******************************************************************/
2017     /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON  */
2018     /******************************************************************/
2019     if (doIcon)
2020     {
2021         LONG state_width = 0;
2022
2023         if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2024             state_width = infoPtr->iconStateSize.cx;
2025
2026         if (uView == LVS_ICON)
2027         {
2028             Icon.left   = Box.left + state_width;
2029             if (infoPtr->himlNormal)
2030                 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2031             Icon.top    = Box.top + ICON_TOP_PADDING;
2032             Icon.right  = Icon.left;
2033             Icon.bottom = Icon.top;
2034             if (infoPtr->himlNormal)
2035             {
2036                 Icon.right  += infoPtr->iconSize.cx;
2037                 Icon.bottom += infoPtr->iconSize.cy;
2038             }
2039         }
2040         else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2041         {
2042             Icon.left   = Box.left + state_width;
2043
2044             if (uView == LVS_REPORT)
2045                 Icon.left += REPORT_MARGINX;
2046
2047             Icon.top    = Box.top;
2048             Icon.right  = Icon.left;
2049             if (infoPtr->himlSmall &&
2050                 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2051                  ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2052                 Icon.right += infoPtr->iconSize.cx;
2053             Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2054         }
2055         if(lprcIcon) *lprcIcon = Icon;
2056         TRACE("    - icon=%s\n", wine_dbgstr_rect(&Icon));
2057
2058         /* TODO: is this correct? */
2059         if (lprcStateIcon)
2060         {
2061             lprcStateIcon->left = Icon.left - state_width;
2062             lprcStateIcon->right = Icon.left;
2063             lprcStateIcon->top = Icon.top;
2064             lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2065             TRACE("    - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2066         }
2067      }
2068      else Icon.right = 0;
2069
2070     /************************************************************/
2071     /* compute LABEL bounding box (ala LVM_GETITEMRECT)         */
2072     /************************************************************/
2073     if (doLabel)
2074     {
2075         /* calculate how far to the right can the label stretch */
2076         Label.right = Box.right;
2077         if (uView == LVS_REPORT)
2078         {
2079             if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2080         }
2081
2082         if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2083         {
2084            labelSize.cx = infoPtr->nItemWidth;
2085            labelSize.cy = infoPtr->nItemHeight;
2086            goto calc_label;
2087         }
2088         
2089         /* we need the text in non owner draw mode */
2090         assert(lpLVItem->mask & LVIF_TEXT);
2091         if (is_textT(lpLVItem->pszText, TRUE))
2092         {
2093             HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2094             HDC hdc = GetDC(infoPtr->hwndSelf);
2095             HFONT hOldFont = SelectObject(hdc, hFont);
2096             UINT uFormat;
2097             RECT rcText;
2098
2099             /* compute rough rectangle where the label will go */
2100             SetRectEmpty(&rcText);
2101             rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2102             rcText.bottom = infoPtr->nItemHeight;
2103             if (uView == LVS_ICON) 
2104                 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2105
2106             /* now figure out the flags */
2107             if (uView == LVS_ICON)
2108                 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2109             else
2110                 uFormat = LV_SL_DT_FLAGS;
2111             
2112             DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2113
2114             labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2115             labelSize.cy = rcText.bottom - rcText.top;
2116
2117             SelectObject(hdc, hOldFont);
2118             ReleaseDC(infoPtr->hwndSelf, hdc);
2119         }
2120
2121 calc_label:
2122         if (uView == LVS_ICON)
2123         {
2124             Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2125             Label.top  = Box.top + ICON_TOP_PADDING_HITABLE +
2126                          infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2127             Label.right = Label.left + labelSize.cx;
2128             Label.bottom = Label.top + infoPtr->nItemHeight;
2129             if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2130             {
2131                 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2132                 labelSize.cy /= infoPtr->ntmHeight;
2133                 labelSize.cy = max(labelSize.cy, 1);
2134                 labelSize.cy *= infoPtr->ntmHeight;
2135              }
2136              Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2137         }
2138         else if (uView == LVS_REPORT)
2139         {
2140             Label.left = Icon.right;
2141             Label.top = Box.top;
2142             Label.right = lpColumnInfo->rcHeader.right;
2143             Label.bottom = Label.top + infoPtr->nItemHeight;
2144         }
2145         else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2146         {
2147             Label.left = Icon.right;
2148             Label.top = Box.top;
2149             Label.right = min(Label.left + labelSize.cx, Label.right);
2150             Label.bottom = Label.top + infoPtr->nItemHeight;
2151         }
2152   
2153         if (lprcLabel) *lprcLabel = Label;
2154         TRACE("    - label=%s\n", wine_dbgstr_rect(&Label));
2155     }
2156
2157     /************************************************************/
2158     /* compute STATEICON bounding box                           */
2159     /************************************************************/
2160     if (doSelectBox)
2161     {
2162         if (uView == LVS_REPORT)
2163         {
2164             SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2165             SelectBox.top = Box.top;
2166             SelectBox.bottom = Box.bottom;
2167             if (lpLVItem->iSubItem == 0)
2168             {
2169                 /* we need the indent in report mode */
2170                 assert(lpLVItem->mask & LVIF_INDENT);
2171                 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2172             }
2173             SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2174         }
2175         else
2176         {
2177             UnionRect(&SelectBox, &Icon, &Label);
2178         }
2179         if (lprcSelectBox) *lprcSelectBox = SelectBox;
2180         TRACE("    - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2181     }
2182
2183     /* Fix the Box if necessary */
2184     if (lprcBox)
2185     {
2186         if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2187         else *lprcBox = Box;
2188     }
2189     TRACE("    - box=%s\n", wine_dbgstr_rect(&Box));
2190 }
2191
2192 /***
2193  * DESCRIPTION:            [INTERNAL]
2194  *
2195  * PARAMETER(S):
2196  * [I] infoPtr : valid pointer to the listview structure
2197  * [I] nItem : item number
2198  * [O] lprcBox : ptr to Box rectangle
2199  *
2200  * RETURN:
2201  *   None.
2202  */
2203 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2204 {
2205     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2206     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2207     POINT Position, Origin;
2208     LVITEMW lvItem;
2209
2210     LISTVIEW_GetOrigin(infoPtr, &Origin);
2211     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2212
2213     /* Be smart and try to figure out the minimum we have to do */
2214     lvItem.mask = 0;
2215     if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2216         lvItem.mask |= LVIF_TEXT;
2217     lvItem.iItem = nItem;
2218     lvItem.iSubItem = 0;
2219     lvItem.pszText = szDispText;
2220     lvItem.cchTextMax = DISP_TEXT_SIZE;
2221     if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2222     if (uView == LVS_ICON)
2223     {
2224         lvItem.mask |= LVIF_STATE;
2225         lvItem.stateMask = LVIS_FOCUSED;
2226         lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2227     }
2228     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2229
2230     OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2231 }
2232
2233
2234 /***
2235  * DESCRIPTION:
2236  * Returns the current icon position, and advances it along the top.
2237  * The returned position is not offset by Origin.
2238  *
2239  * PARAMETER(S):
2240  * [I] infoPtr : valid pointer to the listview structure
2241  * [O] lpPos : will get the current icon position
2242  *
2243  * RETURN:
2244  * None
2245  */
2246 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2247 {
2248     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2249     
2250     *lpPos = infoPtr->currIconPos;
2251     
2252     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2253     if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2254
2255     infoPtr->currIconPos.x  = 0;
2256     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2257 }
2258
2259     
2260 /***
2261  * DESCRIPTION:
2262  * Returns the current icon position, and advances it down the left edge.
2263  * The returned position is not offset by Origin.
2264  *
2265  * PARAMETER(S):
2266  * [I] infoPtr : valid pointer to the listview structure
2267  * [O] lpPos : will get the current icon position
2268  *
2269  * RETURN:
2270  * None
2271  */
2272 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2273 {
2274     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2275     
2276     *lpPos = infoPtr->currIconPos;
2277     
2278     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2279     if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2280
2281     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2282     infoPtr->currIconPos.y  = 0;
2283 }
2284
2285     
2286 /***
2287  * DESCRIPTION:
2288  * Moves an icon to the specified position.
2289  * It takes care of invalidating the item, etc.
2290  *
2291  * PARAMETER(S):
2292  * [I] infoPtr : valid pointer to the listview structure
2293  * [I] nItem : the item to move
2294  * [I] lpPos : the new icon position
2295  * [I] isNew : flags the item as being new
2296  *
2297  * RETURN:
2298  *   Success: TRUE
2299  *   Failure: FALSE
2300  */
2301 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2302 {
2303     POINT old;
2304     
2305     if (!isNew)
2306     { 
2307         old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2308         old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2309     
2310         if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2311         LISTVIEW_InvalidateItem(infoPtr, nItem);
2312     }
2313
2314     /* Allocating a POINTER for every item is too resource intensive,
2315      * so we'll keep the (x,y) in different arrays */
2316     if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2317     if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2318
2319     LISTVIEW_InvalidateItem(infoPtr, nItem);
2320
2321     return TRUE;
2322 }
2323
2324 /***
2325  * DESCRIPTION:
2326  * Arranges listview items in icon display mode.
2327  *
2328  * PARAMETER(S):
2329  * [I] infoPtr : valid pointer to the listview structure
2330  * [I] nAlignCode : alignment code
2331  *
2332  * RETURN:
2333  *   SUCCESS : TRUE
2334  *   FAILURE : FALSE
2335  */
2336 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2337 {
2338     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2339     void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2340     POINT pos;
2341     INT i;
2342
2343     if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2344   
2345     TRACE("nAlignCode=%d\n", nAlignCode);
2346
2347     if (nAlignCode == LVA_DEFAULT)
2348     {
2349         if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2350         else nAlignCode = LVA_ALIGNTOP;
2351     }
2352    
2353     switch (nAlignCode)
2354     {
2355     case LVA_ALIGNLEFT:  next_pos = LISTVIEW_NextIconPosLeft; break;
2356     case LVA_ALIGNTOP:   next_pos = LISTVIEW_NextIconPosTop;  break;
2357     case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop;  break; /* FIXME */
2358     default: return FALSE;
2359     }
2360     
2361     infoPtr->bAutoarrange = TRUE;
2362     infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2363     for (i = 0; i < infoPtr->nItemCount; i++)
2364     {
2365         next_pos(infoPtr, &pos);
2366         LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2367     }
2368
2369     return TRUE;
2370 }
2371   
2372 /***
2373  * DESCRIPTION:
2374  * Retrieves the bounding rectangle of all the items, not offset by Origin.
2375  *
2376  * PARAMETER(S):
2377  * [I] infoPtr : valid pointer to the listview structure
2378  * [O] lprcView : bounding rectangle
2379  *
2380  * RETURN:
2381  *   SUCCESS : TRUE
2382  *   FAILURE : FALSE
2383  */
2384 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2385 {
2386     INT i, x, y;
2387
2388     SetRectEmpty(lprcView);
2389
2390     switch (infoPtr->dwStyle & LVS_TYPEMASK)
2391     {
2392     case LVS_ICON:
2393     case LVS_SMALLICON:
2394         for (i = 0; i < infoPtr->nItemCount; i++)
2395         {
2396             x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2397             y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2398             lprcView->right = max(lprcView->right, x);
2399             lprcView->bottom = max(lprcView->bottom, y);
2400         }
2401         if (infoPtr->nItemCount > 0)
2402         {
2403             lprcView->right += infoPtr->nItemWidth;
2404             lprcView->bottom += infoPtr->nItemHeight;
2405         }
2406         break;
2407
2408     case LVS_LIST:
2409         y = LISTVIEW_GetCountPerColumn(infoPtr);
2410         x = infoPtr->nItemCount / y;
2411         if (infoPtr->nItemCount % y) x++;
2412         lprcView->right = x * infoPtr->nItemWidth;
2413         lprcView->bottom = y * infoPtr->nItemHeight;
2414         break;
2415         
2416     case LVS_REPORT:        
2417         lprcView->right = infoPtr->nItemWidth;
2418         lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2419         break;
2420     }
2421 }
2422
2423 /***
2424  * DESCRIPTION:
2425  * Retrieves the bounding rectangle of all the items.
2426  *
2427  * PARAMETER(S):
2428  * [I] infoPtr : valid pointer to the listview structure
2429  * [O] lprcView : bounding rectangle
2430  *
2431  * RETURN:
2432  *   SUCCESS : TRUE
2433  *   FAILURE : FALSE
2434  */
2435 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2436 {
2437     POINT ptOrigin;
2438
2439     TRACE("(lprcView=%p)\n", lprcView);
2440
2441     if (!lprcView) return FALSE;
2442  
2443     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2444     LISTVIEW_GetAreaRect(infoPtr, lprcView); 
2445     OffsetRect(lprcView, ptOrigin.x, ptOrigin.y); 
2446
2447     TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2448
2449     return TRUE;
2450 }
2451
2452 /***
2453  * DESCRIPTION:
2454  * Retrieves the subitem pointer associated with the subitem index.
2455  *
2456  * PARAMETER(S):
2457  * [I] hdpaSubItems : DPA handle for a specific item
2458  * [I] nSubItem : index of subitem
2459  *
2460  * RETURN:
2461  *   SUCCESS : subitem pointer
2462  *   FAILURE : NULL
2463  */
2464 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2465 {
2466     SUBITEM_INFO *lpSubItem;
2467     INT i;
2468
2469     /* we should binary search here if need be */
2470     for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2471     {
2472         lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2473         if (lpSubItem->iSubItem == nSubItem)
2474             return lpSubItem;
2475     }
2476
2477     return NULL;
2478 }
2479
2480
2481 /***
2482  * DESCRIPTION:
2483  * Calculates the desired item width.
2484  *
2485  * PARAMETER(S):
2486  * [I] infoPtr : valid pointer to the listview structure
2487  *
2488  * RETURN:
2489  *  The desired item width.
2490  */
2491 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2492 {
2493     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2494     INT nItemWidth = 0;
2495
2496     TRACE("uView=%d\n", uView);
2497
2498     if (uView == LVS_ICON)
2499         nItemWidth = infoPtr->iconSpacing.cx;
2500     else if (uView == LVS_REPORT)
2501     {
2502         RECT rcHeader;
2503
2504         if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2505         {
2506             LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2507             nItemWidth = rcHeader.right;
2508         }
2509     }
2510     else /* LVS_SMALLICON, or LVS_LIST */
2511     {
2512         INT i;
2513         
2514         for (i = 0; i < infoPtr->nItemCount; i++)
2515             nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2516
2517         if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx; 
2518         if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2519
2520         nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2521     }
2522
2523     return max(nItemWidth, 1);
2524 }
2525
2526 /***
2527  * DESCRIPTION:
2528  * Calculates the desired item height.
2529  *
2530  * PARAMETER(S):
2531  * [I] infoPtr : valid pointer to the listview structure
2532  *
2533  * RETURN:
2534  *  The desired item height.
2535  */
2536 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2537 {
2538     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2539     INT nItemHeight;
2540
2541     TRACE("uView=%d\n", uView);
2542
2543     if (uView == LVS_ICON)
2544         nItemHeight = infoPtr->iconSpacing.cy;
2545     else
2546     {
2547         nItemHeight = infoPtr->ntmHeight; 
2548         if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2549             nItemHeight++;
2550         if (infoPtr->himlState)
2551             nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2552         if (infoPtr->himlSmall)
2553             nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2554         if (infoPtr->himlState || infoPtr->himlSmall)
2555             nItemHeight += HEIGHT_PADDING;
2556     if (infoPtr->nMeasureItemHeight > 0)
2557         nItemHeight = infoPtr->nMeasureItemHeight;
2558     }
2559
2560     return max(nItemHeight, 1);
2561 }
2562
2563 /***
2564  * DESCRIPTION:
2565  * Updates the width, and height of an item.
2566  *
2567  * PARAMETER(S):
2568  * [I] infoPtr : valid pointer to the listview structure
2569  *
2570  * RETURN:
2571  *  None.
2572  */
2573 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2574 {
2575     infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2576     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2577 }
2578
2579
2580 /***
2581  * DESCRIPTION:
2582  * Retrieves and saves important text metrics info for the current
2583  * Listview font.
2584  *
2585  * PARAMETER(S):
2586  * [I] infoPtr : valid pointer to the listview structure
2587  *
2588  */
2589 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2590 {
2591     HDC hdc = GetDC(infoPtr->hwndSelf);
2592     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2593     HFONT hOldFont = SelectObject(hdc, hFont);
2594     TEXTMETRICW tm;
2595     SIZE sz;
2596
2597     if (GetTextMetricsW(hdc, &tm))
2598     {
2599         infoPtr->ntmHeight = tm.tmHeight;
2600         infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2601     }
2602
2603     if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2604         infoPtr->nEllipsisWidth = sz.cx;
2605         
2606     SelectObject(hdc, hOldFont);
2607     ReleaseDC(infoPtr->hwndSelf, hdc);
2608     
2609     TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2610 }
2611
2612 /***
2613  * DESCRIPTION:
2614  * A compare function for ranges
2615  *
2616  * PARAMETER(S)
2617  * [I] range1 : pointer to range 1;
2618  * [I] range2 : pointer to range 2;
2619  * [I] flags : flags
2620  *
2621  * RETURNS:
2622  * > 0 : if range 1 > range 2
2623  * < 0 : if range 2 > range 1
2624  * = 0 : if range intersects range 2
2625  */
2626 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2627 {
2628     INT cmp;
2629     
2630     if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower) 
2631         cmp = -1;
2632     else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower) 
2633         cmp = 1;
2634     else 
2635         cmp = 0;
2636
2637     TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2638
2639     return cmp;
2640 }
2641
2642 #if DEBUG_RANGES
2643 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2644 #else
2645 #define ranges_check(ranges, desc) do { } while(0)
2646 #endif
2647
2648 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2649 {
2650     INT i;
2651     RANGE *prev, *curr;
2652     
2653     TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2654     assert (ranges);
2655     assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2656     ranges_dump(ranges);
2657     if (DPA_GetPtrCount(ranges->hdpa) > 0)
2658     {
2659         prev = DPA_GetPtr(ranges->hdpa, 0);
2660         assert (prev->lower >= 0 && prev->lower < prev->upper);
2661         for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2662         {
2663             curr = DPA_GetPtr(ranges->hdpa, i);
2664             assert (prev->upper <= curr->lower);
2665             assert (curr->lower < curr->upper);
2666             prev = curr;
2667         }
2668     }
2669     TRACE("--- Done checking---\n");
2670 }
2671
2672 static RANGES ranges_create(int count)
2673 {
2674     RANGES ranges = Alloc(sizeof(struct tagRANGES));
2675     if (!ranges) return NULL;
2676     ranges->hdpa = DPA_Create(count);
2677     if (ranges->hdpa) return ranges;
2678     Free(ranges);
2679     return NULL;
2680 }
2681
2682 static void ranges_clear(RANGES ranges)
2683 {
2684     INT i;
2685         
2686     for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2687         Free(DPA_GetPtr(ranges->hdpa, i));
2688     DPA_DeleteAllPtrs(ranges->hdpa);
2689 }
2690
2691
2692 static void ranges_destroy(RANGES ranges)
2693 {
2694     if (!ranges) return;
2695     ranges_clear(ranges);
2696     DPA_Destroy(ranges->hdpa);
2697     Free(ranges);
2698 }
2699
2700 static RANGES ranges_clone(RANGES ranges)
2701 {
2702     RANGES clone;
2703     INT i;
2704            
2705     if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2706
2707     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2708     {
2709         RANGE *newrng = Alloc(sizeof(RANGE));
2710         if (!newrng) goto fail;
2711         *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2712         DPA_SetPtr(clone->hdpa, i, newrng);
2713     }
2714     return clone;
2715     
2716 fail:
2717     TRACE ("clone failed\n");
2718     ranges_destroy(clone);
2719     return NULL;
2720 }
2721
2722 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2723 {
2724     INT i;
2725
2726     for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2727         ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2728
2729     return ranges;
2730 }
2731
2732 static void ranges_dump(RANGES ranges)
2733 {
2734     INT i;
2735
2736     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2737         TRACE("   %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2738 }
2739
2740 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2741 {
2742     RANGE srchrng = { nItem, nItem + 1 };
2743
2744     TRACE("(nItem=%d)\n", nItem);
2745     ranges_check(ranges, "before contain");
2746     return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2747 }
2748
2749 static INT ranges_itemcount(RANGES ranges)
2750 {
2751     INT i, count = 0;
2752     
2753     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2754     {
2755         RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2756         count += sel->upper - sel->lower;
2757     }
2758
2759     return count;
2760 }
2761
2762 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2763 {
2764     RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2765     INT index;
2766
2767     index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2768     if (index == -1) return TRUE;
2769
2770     for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2771     {
2772         chkrng = DPA_GetPtr(ranges->hdpa, index);
2773         if (chkrng->lower >= nItem)
2774             chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2775         if (chkrng->upper > nItem)
2776             chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2777     }
2778     return TRUE;
2779 }
2780
2781 static BOOL ranges_add(RANGES ranges, RANGE range)
2782 {
2783     RANGE srchrgn;
2784     INT index;
2785
2786     TRACE("(%s)\n", debugrange(&range));
2787     ranges_check(ranges, "before add");
2788
2789     /* try find overlapping regions first */
2790     srchrgn.lower = range.lower - 1;
2791     srchrgn.upper = range.upper + 1;
2792     index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2793    
2794     if (index == -1)
2795     {
2796         RANGE *newrgn;
2797
2798         TRACE("Adding new range\n");
2799
2800         /* create the brand new range to insert */      
2801         newrgn = Alloc(sizeof(RANGE));
2802         if(!newrgn) goto fail;
2803         *newrgn = range;
2804         
2805         /* figure out where to insert it */
2806         index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2807         TRACE("index=%d\n", index);
2808         if (index == -1) index = 0;
2809         
2810         /* and get it over with */
2811         if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2812         {
2813             Free(newrgn);
2814             goto fail;
2815         }
2816     }
2817     else
2818     {
2819         RANGE *chkrgn, *mrgrgn;
2820         INT fromindex, mergeindex;
2821
2822         chkrgn = DPA_GetPtr(ranges->hdpa, index);
2823         TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2824
2825         chkrgn->lower = min(range.lower, chkrgn->lower);
2826         chkrgn->upper = max(range.upper, chkrgn->upper);
2827         
2828         TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2829
2830         /* merge now common ranges */
2831         fromindex = 0;
2832         srchrgn.lower = chkrgn->lower - 1;
2833         srchrgn.upper = chkrgn->upper + 1;
2834             
2835         do
2836         {
2837             mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2838             if (mergeindex == -1) break;
2839             if (mergeindex == index) 
2840             {
2841                 fromindex = index + 1;
2842                 continue;
2843             }
2844           
2845             TRACE("Merge with index %i\n", mergeindex);
2846             
2847             mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2848             chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2849             chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2850             Free(mrgrgn);
2851             DPA_DeletePtr(ranges->hdpa, mergeindex);
2852             if (mergeindex < index) index --;
2853         } while(1);
2854     }
2855
2856     ranges_check(ranges, "after add");
2857     return TRUE;
2858     
2859 fail:
2860     ranges_check(ranges, "failed add");
2861     return FALSE;
2862 }
2863
2864 static BOOL ranges_del(RANGES ranges, RANGE range)
2865 {
2866     RANGE *chkrgn;
2867     INT index;
2868
2869     TRACE("(%s)\n", debugrange(&range));
2870     ranges_check(ranges, "before del");
2871     
2872     /* we don't use DPAS_SORTED here, since we need *
2873      * to find the first overlapping range          */
2874     index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2875     while(index != -1) 
2876     {
2877         chkrgn = DPA_GetPtr(ranges->hdpa, index);
2878         
2879         TRACE("Matches range %s @%d\n", debugrange(chkrgn), index); 
2880
2881         /* case 1: Same range */
2882         if ( (chkrgn->upper == range.upper) &&
2883              (chkrgn->lower == range.lower) )
2884         {
2885             DPA_DeletePtr(ranges->hdpa, index);
2886             break;
2887         }
2888         /* case 2: engulf */
2889         else if ( (chkrgn->upper <= range.upper) &&
2890                   (chkrgn->lower >= range.lower) ) 
2891         {
2892             DPA_DeletePtr(ranges->hdpa, index);
2893         }
2894         /* case 3: overlap upper */
2895         else if ( (chkrgn->upper <= range.upper) &&
2896                   (chkrgn->lower < range.lower) )
2897         {
2898             chkrgn->upper = range.lower;
2899         }
2900         /* case 4: overlap lower */
2901         else if ( (chkrgn->upper > range.upper) &&
2902                   (chkrgn->lower >= range.lower) )
2903         {
2904             chkrgn->lower = range.upper;
2905             break;
2906         }
2907         /* case 5: fully internal */
2908         else
2909         {
2910             RANGE tmprgn = *chkrgn, *newrgn;
2911
2912             if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2913             newrgn->lower = chkrgn->lower;
2914             newrgn->upper = range.lower;
2915             chkrgn->lower = range.upper;
2916             if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2917             {
2918                 Free(newrgn);
2919                 goto fail;
2920             }
2921             chkrgn = &tmprgn;
2922             break;
2923         }
2924
2925         index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2926     }
2927
2928     ranges_check(ranges, "after del");
2929     return TRUE;
2930
2931 fail:
2932     ranges_check(ranges, "failed del");
2933     return FALSE;
2934 }
2935
2936 /***
2937 * DESCRIPTION:
2938 * Removes all selection ranges
2939 *
2940 * Parameters(s):
2941 * [I] infoPtr : valid pointer to the listview structure
2942 * [I] toSkip : item range to skip removing the selection
2943 *
2944 * RETURNS:
2945 *   SUCCESS : TRUE
2946 *   FAILURE : FALSE
2947 */
2948 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2949 {
2950     LVITEMW lvItem;
2951     ITERATOR i;
2952     RANGES clone;
2953
2954     TRACE("()\n");
2955
2956     lvItem.state = 0;
2957     lvItem.stateMask = LVIS_SELECTED;
2958     
2959     /* need to clone the DPA because callbacks can change it */
2960     if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2961     iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2962     while(iterator_next(&i))
2963         LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2964     /* note that the iterator destructor will free the cloned range */
2965     iterator_destroy(&i);
2966
2967     return TRUE;
2968 }
2969
2970 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2971 {
2972     RANGES toSkip;
2973    
2974     if (!(toSkip = ranges_create(1))) return FALSE;
2975     if (nItem != -1) ranges_additem(toSkip, nItem);
2976     LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2977     ranges_destroy(toSkip);
2978     return TRUE;
2979 }
2980
2981 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2982 {
2983     return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2984 }
2985
2986 /***
2987  * DESCRIPTION:
2988  * Retrieves the number of items that are marked as selected.
2989  *
2990  * PARAMETER(S):
2991  * [I] infoPtr : valid pointer to the listview structure
2992  *
2993  * RETURN:
2994  * Number of items selected.
2995  */
2996 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2997 {
2998     INT nSelectedCount = 0;
2999
3000     if (infoPtr->uCallbackMask & LVIS_SELECTED)
3001     {
3002         INT i;
3003         for (i = 0; i < infoPtr->nItemCount; i++)
3004         {
3005             if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3006                 nSelectedCount++;
3007         }
3008     }
3009     else
3010         nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3011
3012     TRACE("nSelectedCount=%d\n", nSelectedCount);
3013     return nSelectedCount;
3014 }
3015
3016 /***
3017  * DESCRIPTION:
3018  * Manages the item focus.
3019  *
3020  * PARAMETER(S):
3021  * [I] infoPtr : valid pointer to the listview structure
3022  * [I] nItem : item index
3023  *
3024  * RETURN:
3025  *   TRUE : focused item changed
3026  *   FALSE : focused item has NOT changed
3027  */
3028 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3029 {
3030     INT oldFocus = infoPtr->nFocusedItem;
3031     LVITEMW lvItem;
3032
3033     if (nItem == infoPtr->nFocusedItem) return FALSE;
3034     
3035     lvItem.state =  nItem == -1 ? 0 : LVIS_FOCUSED;
3036     lvItem.stateMask = LVIS_FOCUSED;
3037     LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3038
3039     return oldFocus != infoPtr->nFocusedItem;
3040 }
3041
3042 /* Helper function for LISTVIEW_ShiftIndices *only* */
3043 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3044 {
3045     if (nShiftItem < nItem) return nShiftItem;
3046
3047     if (nShiftItem > nItem) return nShiftItem + direction;
3048
3049     if (direction > 0) return nShiftItem + direction;
3050
3051     return min(nShiftItem, infoPtr->nItemCount - 1);
3052 }
3053
3054 /**
3055 * DESCRIPTION:
3056 * Updates the various indices after an item has been inserted or deleted.
3057 *
3058 * PARAMETER(S):
3059 * [I] infoPtr : valid pointer to the listview structure
3060 * [I] nItem : item index
3061 * [I] direction : Direction of shift, +1 or -1.
3062 *
3063 * RETURN:
3064 * None
3065 */
3066 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3067 {
3068     INT nNewFocus;
3069     BOOL bOldChange;
3070
3071     /* temporarily disable change notification while shifting items */
3072     bOldChange = infoPtr->bDoChangeNotify;
3073     infoPtr->bDoChangeNotify = FALSE;
3074
3075     TRACE("Shifting %iu, %i steps\n", nItem, direction);
3076
3077     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3078
3079     assert(abs(direction) == 1);
3080
3081     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3082
3083     nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3084     if (nNewFocus != infoPtr->nFocusedItem)
3085         LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3086     
3087     /* But we are not supposed to modify nHotItem! */
3088
3089     infoPtr->bDoChangeNotify = bOldChange;
3090 }
3091
3092
3093 /**
3094  * DESCRIPTION:
3095  * Adds a block of selections.
3096  *
3097  * PARAMETER(S):
3098  * [I] infoPtr : valid pointer to the listview structure
3099  * [I] nItem : item index
3100  *
3101  * RETURN:
3102  * Whether the window is still valid.
3103  */
3104 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3105 {
3106     INT nFirst = min(infoPtr->nSelectionMark, nItem);
3107     INT nLast = max(infoPtr->nSelectionMark, nItem);
3108     HWND hwndSelf = infoPtr->hwndSelf;
3109     NMLVODSTATECHANGE nmlv;
3110     LVITEMW item;
3111     BOOL bOldChange;
3112     INT i;
3113
3114     /* Temporarily disable change notification
3115      * If the control is LVS_OWNERDATA, we need to send
3116      * only one LVN_ODSTATECHANGED notification.
3117      * See MSDN documentation for LVN_ITEMCHANGED.
3118      */
3119     bOldChange = infoPtr->bDoChangeNotify;
3120     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3121
3122     if (nFirst == -1) nFirst = nItem;
3123
3124     item.state = LVIS_SELECTED;
3125     item.stateMask = LVIS_SELECTED;
3126
3127     for (i = nFirst; i <= nLast; i++)
3128         LISTVIEW_SetItemState(infoPtr,i,&item);
3129
3130     ZeroMemory(&nmlv, sizeof(nmlv));
3131     nmlv.iFrom = nFirst;
3132     nmlv.iTo = nLast;
3133     nmlv.uNewState = 0;
3134     nmlv.uOldState = item.state;
3135
3136     notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3137     if (!IsWindow(hwndSelf))
3138         return FALSE;
3139     infoPtr->bDoChangeNotify = bOldChange;
3140     return TRUE;
3141 }
3142
3143
3144 /***
3145  * DESCRIPTION:
3146  * Sets a single group selection.
3147  *
3148  * PARAMETER(S):
3149  * [I] infoPtr : valid pointer to the listview structure
3150  * [I] nItem : item index
3151  *
3152  * RETURN:
3153  * None
3154  */
3155 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3156 {
3157     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3158     RANGES selection;
3159     LVITEMW item;
3160     ITERATOR i;
3161     BOOL bOldChange;
3162
3163     if (!(selection = ranges_create(100))) return;
3164
3165     item.state = LVIS_SELECTED; 
3166     item.stateMask = LVIS_SELECTED;
3167
3168     if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3169     {
3170         if (infoPtr->nSelectionMark == -1)
3171         {
3172             infoPtr->nSelectionMark = nItem;
3173             ranges_additem(selection, nItem);
3174         }
3175         else
3176         {
3177             RANGE sel;
3178             
3179             sel.lower = min(infoPtr->nSelectionMark, nItem);
3180             sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3181             ranges_add(selection, sel);
3182         }
3183     }
3184     else
3185     {
3186         RECT rcItem, rcSel, rcSelMark;
3187         POINT ptItem;
3188         
3189         rcItem.left = LVIR_BOUNDS;
3190         if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3191         rcSelMark.left = LVIR_BOUNDS;
3192         if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3193         UnionRect(&rcSel, &rcItem, &rcSelMark);
3194         iterator_frameditems(&i, infoPtr, &rcSel);
3195         while(iterator_next(&i))
3196         {
3197             LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3198             if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3199         }
3200         iterator_destroy(&i);
3201     }
3202
3203     /* disable per item notifications on LVS_OWNERDATA style
3204        FIXME: single LVN_ODSTATECHANGED should be used */
3205     bOldChange = infoPtr->bDoChangeNotify;
3206     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3207
3208     LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3209
3210
3211     iterator_rangesitems(&i, selection);
3212     while(iterator_next(&i))
3213         LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3214     /* this will also destroy the selection */
3215     iterator_destroy(&i);
3216
3217     infoPtr->bDoChangeNotify = bOldChange;
3218     
3219     LISTVIEW_SetItemFocus(infoPtr, nItem);
3220 }
3221
3222 /***
3223  * DESCRIPTION:
3224  * Sets a single selection.
3225  *
3226  * PARAMETER(S):
3227  * [I] infoPtr : valid pointer to the listview structure
3228  * [I] nItem : item index
3229  *
3230  * RETURN:
3231  * None
3232  */
3233 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3234 {
3235     LVITEMW lvItem;
3236
3237     TRACE("nItem=%d\n", nItem);
3238     
3239     LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3240
3241     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3242     lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3243     LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3244
3245     infoPtr->nSelectionMark = nItem;
3246 }
3247
3248 /***
3249  * DESCRIPTION:
3250  * Set selection(s) with keyboard.
3251  *
3252  * PARAMETER(S):
3253  * [I] infoPtr : valid pointer to the listview structure
3254  * [I] nItem : item index
3255  * [I] space : VK_SPACE code sent
3256  *
3257  * RETURN:
3258  *   SUCCESS : TRUE (needs to be repainted)
3259  *   FAILURE : FALSE (nothing has changed)
3260  */
3261 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3262 {
3263   /* FIXME: pass in the state */
3264   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3265   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3266   BOOL bResult = FALSE;
3267
3268   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3269   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3270   {
3271     if (infoPtr->dwStyle & LVS_SINGLESEL)
3272     {
3273       bResult = TRUE;
3274       LISTVIEW_SetSelection(infoPtr, nItem);
3275     }
3276     else
3277     {
3278       if (wShift)
3279       {
3280         bResult = TRUE;
3281         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3282       }
3283       else if (wCtrl)
3284       {
3285         LVITEMW lvItem;
3286         lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3287         lvItem.stateMask = LVIS_SELECTED;
3288         if (space)
3289         {
3290             LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3291             if (lvItem.state & LVIS_SELECTED)
3292                 infoPtr->nSelectionMark = nItem;
3293         }
3294         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3295       }
3296       else
3297       {
3298         bResult = TRUE;
3299         LISTVIEW_SetSelection(infoPtr, nItem);
3300       }
3301     }
3302     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3303   }
3304
3305   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3306   return bResult;
3307 }
3308
3309 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3310 {
3311     LVHITTESTINFO lvHitTestInfo;
3312
3313     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3314     lvHitTestInfo.pt.x = pt.x;
3315     lvHitTestInfo.pt.y = pt.y;
3316
3317     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3318
3319     lpLVItem->mask = LVIF_PARAM;
3320     lpLVItem->iItem = lvHitTestInfo.iItem;
3321     lpLVItem->iSubItem = 0;
3322
3323     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3324 }
3325
3326 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3327 {
3328     return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3329             (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3330             (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3331 }
3332
3333 /***
3334  * DESCRIPTION:
3335  * Called when the mouse is being actively tracked and has hovered for a specified
3336  * amount of time
3337  *
3338  * PARAMETER(S):
3339  * [I] infoPtr : valid pointer to the listview structure
3340  * [I] fwKeys : key indicator
3341  * [I] x,y : mouse position
3342  *
3343  * RETURN:
3344  *   0 if the message was processed, non-zero if there was an error
3345  *
3346  * INFO:
3347  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3348  * over the item for a certain period of time.
3349  *
3350  */
3351 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3352 {
3353     if (LISTVIEW_isHotTracking(infoPtr))
3354     {
3355         LVITEMW item;
3356         POINT pt;
3357
3358         pt.x = x;
3359         pt.y = y;
3360
3361         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3362             LISTVIEW_SetSelection(infoPtr, item.iItem);
3363     }
3364
3365     return 0;
3366 }
3367
3368 /***
3369  * DESCRIPTION:
3370  * Called whenever WM_MOUSEMOVE is received.
3371  *
3372  * PARAMETER(S):
3373  * [I] infoPtr : valid pointer to the listview structure
3374  * [I] fwKeys : key indicator
3375  * [I] x,y : mouse position
3376  *
3377  * RETURN:
3378  *   0 if the message is processed, non-zero if there was an error
3379  */
3380 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3381 {
3382     TRACKMOUSEEVENT trackinfo;
3383
3384     if (!(fwKeys & MK_LBUTTON))
3385         infoPtr->bLButtonDown = FALSE;
3386
3387     if (infoPtr->bLButtonDown)
3388     {
3389         POINT tmp;
3390         RECT rect;
3391         WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3392         WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3393
3394         rect.left = infoPtr->ptClickPos.x - wDragWidth;
3395         rect.right = infoPtr->ptClickPos.x + wDragWidth;
3396         rect.top = infoPtr->ptClickPos.y - wDragHeight;
3397         rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3398
3399         tmp.x = x;
3400         tmp.y = y;
3401
3402         if (!PtInRect(&rect, tmp))
3403         {
3404             LVHITTESTINFO lvHitTestInfo;
3405             NMLISTVIEW nmlv;
3406
3407             lvHitTestInfo.pt = infoPtr->ptClickPos;
3408             LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3409
3410             ZeroMemory(&nmlv, sizeof(nmlv));
3411             nmlv.iItem = lvHitTestInfo.iItem;
3412             nmlv.ptAction = infoPtr->ptClickPos;
3413
3414             if (!infoPtr->bDragging)
3415             {
3416                 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3417                 infoPtr->bDragging = TRUE;
3418             }
3419
3420             return 0;
3421         }
3422     }
3423     else
3424         infoPtr->bLButtonDown = FALSE;
3425
3426     /* see if we are supposed to be tracking mouse hovering */
3427     if (LISTVIEW_isHotTracking(infoPtr)) {
3428         /* fill in the trackinfo struct */
3429         trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3430         trackinfo.dwFlags = TME_QUERY;
3431         trackinfo.hwndTrack = infoPtr->hwndSelf;
3432         trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3433
3434         /* see if we are already tracking this hwnd */
3435         _TrackMouseEvent(&trackinfo);
3436
3437         if(!(trackinfo.dwFlags & TME_HOVER)) {
3438             trackinfo.dwFlags = TME_HOVER;
3439
3440             /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3441             _TrackMouseEvent(&trackinfo);
3442         }
3443     }
3444
3445     return 0;
3446 }
3447
3448
3449 /***
3450  * Tests whether the item is assignable to a list with style lStyle
3451  */
3452 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3453 {
3454     if ( (lpLVItem->mask & LVIF_TEXT) && 
3455         (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3456         (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3457     
3458     return TRUE;
3459 }
3460
3461
3462 /***
3463  * DESCRIPTION:
3464  * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3465  *
3466  * PARAMETER(S):
3467  * [I] infoPtr : valid pointer to the listview structure
3468  * [I] lpLVItem : valid pointer to new item attributes
3469  * [I] isNew : the item being set is being inserted
3470  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3471  * [O] bChanged : will be set to TRUE if the item really changed
3472  *
3473  * RETURN:
3474  *   SUCCESS : TRUE
3475  *   FAILURE : FALSE
3476  */
3477 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3478 {
3479     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3480     ITEM_INFO *lpItem;
3481     NMLISTVIEW nmlv;
3482     UINT uChanged = 0;
3483     LVITEMW item;
3484     /* stateMask is ignored for LVM_INSERTITEM */
3485     UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3486
3487     TRACE("()\n");
3488
3489     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3490     
3491     if (lpLVItem->mask == 0) return TRUE;   
3492
3493     if (infoPtr->dwStyle & LVS_OWNERDATA)
3494     {
3495         /* a virtual listview only stores selection and focus */
3496         if (lpLVItem->mask & ~LVIF_STATE)
3497             return FALSE;
3498         lpItem = NULL;
3499     }
3500     else
3501     {
3502         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3503         lpItem = DPA_GetPtr(hdpaSubItems, 0);
3504         assert (lpItem);
3505     }
3506
3507     /* we need to get the lParam and state of the item */
3508     item.iItem = lpLVItem->iItem;
3509     item.iSubItem = lpLVItem->iSubItem;
3510     item.mask = LVIF_STATE | LVIF_PARAM;
3511     item.stateMask = ~0;
3512     item.state = 0;
3513     item.lParam = 0;
3514     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3515
3516     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3517     /* determine what fields will change */    
3518     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3519         uChanged |= LVIF_STATE;
3520
3521     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3522         uChanged |= LVIF_IMAGE;
3523
3524     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3525         uChanged |= LVIF_PARAM;
3526
3527     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3528         uChanged |= LVIF_INDENT;
3529
3530     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3531         uChanged |= LVIF_TEXT;
3532    
3533     TRACE("uChanged=0x%x\n", uChanged); 
3534     if (!uChanged) return TRUE;
3535     *bChanged = TRUE;
3536     
3537     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3538     nmlv.iItem = lpLVItem->iItem;
3539     nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3540     nmlv.uOldState = item.state;
3541     nmlv.uChanged = uChanged;
3542     nmlv.lParam = item.lParam;
3543     
3544     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3545     /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3546     /* are enabled */
3547     if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3548     {
3549       HWND hwndSelf = infoPtr->hwndSelf;
3550
3551       if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3552         return FALSE;
3553       if (!IsWindow(hwndSelf))
3554         return FALSE;
3555     }
3556
3557     /* copy information */
3558     if (lpLVItem->mask & LVIF_TEXT)
3559         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3560
3561     if (lpLVItem->mask & LVIF_IMAGE)
3562         lpItem->hdr.iImage = lpLVItem->iImage;
3563
3564     if (lpLVItem->mask & LVIF_PARAM)
3565         lpItem->lParam = lpLVItem->lParam;
3566
3567     if (lpLVItem->mask & LVIF_INDENT)
3568         lpItem->iIndent = lpLVItem->iIndent;
3569
3570     if (uChanged & LVIF_STATE)
3571     {
3572         if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3573         {
3574             lpItem->state &= ~stateMask;
3575             lpItem->state |= (lpLVItem->state & stateMask);
3576         }
3577         if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3578         {
3579             if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3580             ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3581         }
3582         else if (stateMask & LVIS_SELECTED)
3583         {
3584             ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3585         }
3586         /* if we are asked to change focus, and we manage it, do it */
3587         if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3588         {
3589             if (lpLVItem->state & LVIS_FOCUSED)
3590             {
3591                 if (infoPtr->nFocusedItem != -1)
3592                 {
3593                     /* remove current focus */
3594                     item.mask  = LVIF_STATE;
3595                     item.state = 0;
3596                     item.stateMask = LVIS_FOCUSED;
3597
3598                     /* recurse with redrawing an item */
3599                     LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3600                 }
3601
3602                 infoPtr->nFocusedItem = lpLVItem->iItem;
3603                 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3604             }
3605             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3606             {
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     /* special case for header items */
5988     if (nItem == -1)
5989     {
5990         if (lprc->left != LVIR_BOUNDS)
5991         {
5992             FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
5993             return FALSE;
5994         }
5995
5996         if (infoPtr->hwndHeader)
5997             return Header_GetItemRect(infoPtr->hwndHeader, lprc->top, lprc);
5998         else
5999         {
6000             memset(lprc, 0, sizeof(RECT));
6001             return TRUE;
6002         }
6003     }
6004
6005     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6006
6007     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6008
6009     lvItem.mask = 0;
6010     lvItem.iItem = nItem;
6011     lvItem.iSubItem = nColumn;
6012     
6013     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6014     switch(lprc->left)
6015     {
6016     case LVIR_ICON:
6017         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6018         break;
6019
6020     case LVIR_LABEL:
6021     case LVIR_BOUNDS:
6022         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6023         break;
6024
6025     default:
6026         ERR("Unknown bounds=%d\n", lprc->left);
6027         return FALSE;
6028     }
6029
6030     OffsetRect(lprc, Position.x, Position.y);
6031     return TRUE;
6032 }
6033
6034
6035 /***
6036  * DESCRIPTION:
6037  * Retrieves the width of a label.
6038  *
6039  * PARAMETER(S):
6040  * [I] infoPtr : valid pointer to the listview structure
6041  *
6042  * RETURN:
6043  *   SUCCESS : string width (in pixels)
6044  *   FAILURE : zero
6045  */
6046 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6047 {
6048     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6049     LVITEMW lvItem;
6050
6051     TRACE("(nItem=%d)\n", nItem);
6052
6053     lvItem.mask = LVIF_TEXT;
6054     lvItem.iItem = nItem;
6055     lvItem.iSubItem = 0;
6056     lvItem.pszText = szDispText;
6057     lvItem.cchTextMax = DISP_TEXT_SIZE;
6058     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6059   
6060     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6061 }
6062
6063 /***
6064  * DESCRIPTION:
6065  * Retrieves the spacing between listview control items.
6066  *
6067  * PARAMETER(S):
6068  * [I] infoPtr : valid pointer to the listview structure
6069  * [I] bSmall : flag for small or large icon
6070  *
6071  * RETURN:
6072  * Horizontal + vertical spacing
6073  */
6074 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6075 {
6076   LONG lResult;
6077
6078   if (!bSmall)
6079   {
6080     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6081   }
6082   else
6083   {
6084     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6085       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6086     else
6087       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6088   }
6089   return lResult;
6090 }
6091
6092 /***
6093  * DESCRIPTION:
6094  * Retrieves the state of a listview control item.
6095  *
6096  * PARAMETER(S):
6097  * [I] infoPtr : valid pointer to the listview structure
6098  * [I] nItem : item index
6099  * [I] uMask : state mask
6100  *
6101  * RETURN:
6102  * State specified by the mask.
6103  */
6104 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6105 {
6106     LVITEMW lvItem;
6107
6108     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6109
6110     lvItem.iItem = nItem;
6111     lvItem.iSubItem = 0;
6112     lvItem.mask = LVIF_STATE;
6113     lvItem.stateMask = uMask;
6114     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6115
6116     return lvItem.state & uMask;
6117 }
6118
6119 /***
6120  * DESCRIPTION:
6121  * Retrieves the text of a listview control item or subitem.
6122  *
6123  * PARAMETER(S):
6124  * [I] hwnd : window handle
6125  * [I] nItem : item index
6126  * [IO] lpLVItem : item information
6127  * [I] isW :  TRUE if lpLVItem is Unicode
6128  *
6129  * RETURN:
6130  *   SUCCESS : string length
6131  *   FAILURE : 0
6132  */
6133 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6134 {
6135     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6136
6137     lpLVItem->mask = LVIF_TEXT;
6138     lpLVItem->iItem = nItem;
6139     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6140
6141     return textlenT(lpLVItem->pszText, isW);
6142 }
6143
6144 /***
6145  * DESCRIPTION:
6146  * Searches for an item based on properties + relationships.
6147  *
6148  * PARAMETER(S):
6149  * [I] infoPtr : valid pointer to the listview structure
6150  * [I] nItem : item index
6151  * [I] uFlags : relationship flag
6152  *
6153  * RETURN:
6154  *   SUCCESS : item index
6155  *   FAILURE : -1
6156  */
6157 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6158 {
6159     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6160     UINT uMask = 0;
6161     LVFINDINFOW lvFindInfo;
6162     INT nCountPerColumn;
6163     INT nCountPerRow;
6164     INT i;
6165
6166     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6167     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6168
6169     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6170
6171     if (uFlags & LVNI_CUT)
6172       uMask |= LVIS_CUT;
6173
6174     if (uFlags & LVNI_DROPHILITED)
6175       uMask |= LVIS_DROPHILITED;
6176
6177     if (uFlags & LVNI_FOCUSED)
6178       uMask |= LVIS_FOCUSED;
6179
6180     if (uFlags & LVNI_SELECTED)
6181       uMask |= LVIS_SELECTED;
6182
6183     /* if we're asked for the focused item, that's only one, 
6184      * so it's worth optimizing */
6185     if (uFlags & LVNI_FOCUSED)
6186     {
6187         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6188         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6189     }
6190     
6191     if (uFlags & LVNI_ABOVE)
6192     {
6193       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6194       {
6195         while (nItem >= 0)
6196         {
6197           nItem--;
6198           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6199             return nItem;
6200         }
6201       }
6202       else
6203       {
6204         /* Special case for autoarrange - move 'til the top of a list */
6205         if (is_autoarrange(infoPtr))
6206         {
6207           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6208           while (nItem - nCountPerRow >= 0)
6209           {
6210             nItem -= nCountPerRow;
6211             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6212               return nItem;
6213           }
6214           return -1;
6215         }
6216         lvFindInfo.flags = LVFI_NEARESTXY;
6217         lvFindInfo.vkDirection = VK_UP;
6218         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6219         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6220         {
6221           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6222             return nItem;
6223         }
6224       }
6225     }
6226     else if (uFlags & LVNI_BELOW)
6227     {
6228       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6229       {
6230         while (nItem < infoPtr->nItemCount)
6231         {
6232           nItem++;
6233           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6234             return nItem;
6235         }
6236       }
6237       else
6238       {
6239         /* Special case for autoarrange - move 'til the bottom of a list */
6240         if (is_autoarrange(infoPtr))
6241         {
6242           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6243           while (nItem + nCountPerRow < infoPtr->nItemCount )
6244           {
6245             nItem += nCountPerRow;
6246             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6247               return nItem;
6248           }
6249           return -1;
6250         }
6251         lvFindInfo.flags = LVFI_NEARESTXY;
6252         lvFindInfo.vkDirection = VK_DOWN;
6253         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6254         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6255         {
6256           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6257             return nItem;
6258         }
6259       }
6260     }
6261     else if (uFlags & LVNI_TOLEFT)
6262     {
6263       if (uView == LVS_LIST)
6264       {
6265         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6266         while (nItem - nCountPerColumn >= 0)
6267         {
6268           nItem -= nCountPerColumn;
6269           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6270             return nItem;
6271         }
6272       }
6273       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6274       {
6275         /* Special case for autoarrange - move 'til the beginning of a row */
6276         if (is_autoarrange(infoPtr))
6277         {
6278           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6279           while (nItem % nCountPerRow > 0)
6280           {
6281             nItem --;
6282             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6283               return nItem;
6284           }
6285           return -1;
6286         }
6287         lvFindInfo.flags = LVFI_NEARESTXY;
6288         lvFindInfo.vkDirection = VK_LEFT;
6289         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6290         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6291         {
6292           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6293             return nItem;
6294         }
6295       }
6296     }
6297     else if (uFlags & LVNI_TORIGHT)
6298     {
6299       if (uView == LVS_LIST)
6300       {
6301         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6302         while (nItem + nCountPerColumn < infoPtr->nItemCount)
6303         {
6304           nItem += nCountPerColumn;
6305           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6306             return nItem;
6307         }
6308       }
6309       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6310       {
6311         /* Special case for autoarrange - move 'til the end of a row */
6312         if (is_autoarrange(infoPtr))
6313         {
6314           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6315           while (nItem % nCountPerRow < nCountPerRow - 1 )
6316           {
6317             nItem ++;
6318             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6319               return nItem;
6320           }
6321           return -1;
6322         }
6323         lvFindInfo.flags = LVFI_NEARESTXY;
6324         lvFindInfo.vkDirection = VK_RIGHT;
6325         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6326         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6327         {
6328           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6329             return nItem;
6330         }
6331       }
6332     }
6333     else
6334     {
6335       nItem++;
6336
6337       /* search by index */
6338       for (i = nItem; i < infoPtr->nItemCount; i++)
6339       {
6340         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6341           return i;
6342       }
6343     }
6344
6345     return -1;
6346 }
6347
6348 /* LISTVIEW_GetNumberOfWorkAreas */
6349
6350 /***
6351  * DESCRIPTION:
6352  * Retrieves the origin coordinates when in icon or small icon display mode.
6353  *
6354  * PARAMETER(S):
6355  * [I] infoPtr : valid pointer to the listview structure
6356  * [O] lpptOrigin : coordinate information
6357  *
6358  * RETURN:
6359  *   None.
6360  */
6361 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6362 {
6363     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6364     INT nHorzPos = 0, nVertPos = 0;
6365     SCROLLINFO scrollInfo;
6366
6367     scrollInfo.cbSize = sizeof(SCROLLINFO);    
6368     scrollInfo.fMask = SIF_POS;
6369     
6370     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6371         nHorzPos = scrollInfo.nPos;
6372     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6373         nVertPos = scrollInfo.nPos;
6374
6375     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6376
6377     lpptOrigin->x = infoPtr->rcList.left;
6378     lpptOrigin->y = infoPtr->rcList.top;
6379     if (uView == LVS_LIST)
6380         nHorzPos *= infoPtr->nItemWidth;
6381     else if (uView == LVS_REPORT)
6382         nVertPos *= infoPtr->nItemHeight;
6383     
6384     lpptOrigin->x -= nHorzPos;
6385     lpptOrigin->y -= nVertPos;
6386
6387     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6388 }
6389
6390 /***
6391  * DESCRIPTION:
6392  * Retrieves the width of a string.
6393  *
6394  * PARAMETER(S):
6395  * [I] hwnd : window handle
6396  * [I] lpszText : text string to process
6397  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6398  *
6399  * RETURN:
6400  *   SUCCESS : string width (in pixels)
6401  *   FAILURE : zero
6402  */
6403 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6404 {
6405     SIZE stringSize;
6406     
6407     stringSize.cx = 0;    
6408     if (is_textT(lpszText, isW))
6409     {
6410         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6411         HDC hdc = GetDC(infoPtr->hwndSelf);
6412         HFONT hOldFont = SelectObject(hdc, hFont);
6413
6414         if (isW)
6415             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6416         else
6417             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6418         SelectObject(hdc, hOldFont);
6419         ReleaseDC(infoPtr->hwndSelf, hdc);
6420     }
6421     return stringSize.cx;
6422 }
6423
6424 /***
6425  * DESCRIPTION:
6426  * Determines which listview item is located at the specified position.
6427  *
6428  * PARAMETER(S):
6429  * [I] infoPtr : valid pointer to the listview structure
6430  * [IO] lpht : hit test information
6431  * [I] subitem : fill out iSubItem.
6432  * [I] select : return the index only if the hit selects the item
6433  *
6434  * NOTE:
6435  * (mm 20001022): We must not allow iSubItem to be touched, for
6436  * an app might pass only a structure with space up to iItem!
6437  * (MS Office 97 does that for instance in the file open dialog)
6438  * 
6439  * RETURN:
6440  *   SUCCESS : item index
6441  *   FAILURE : -1
6442  */
6443 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6444 {
6445     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6446     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6447     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6448     POINT Origin, Position, opt;
6449     LVITEMW lvItem;
6450     ITERATOR i;
6451     INT iItem;
6452     
6453     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6454     
6455     lpht->flags = 0;
6456     lpht->iItem = -1;
6457     if (subitem) lpht->iSubItem = 0;
6458
6459     if (infoPtr->rcList.left > lpht->pt.x)
6460         lpht->flags |= LVHT_TOLEFT;
6461     else if (infoPtr->rcList.right < lpht->pt.x)
6462         lpht->flags |= LVHT_TORIGHT;
6463     
6464     if (infoPtr->rcList.top > lpht->pt.y)
6465         lpht->flags |= LVHT_ABOVE;
6466     else if (infoPtr->rcList.bottom < lpht->pt.y)
6467         lpht->flags |= LVHT_BELOW;
6468
6469     TRACE("lpht->flags=0x%x\n", lpht->flags);
6470     if (lpht->flags) return -1;
6471
6472     lpht->flags |= LVHT_NOWHERE;
6473
6474     LISTVIEW_GetOrigin(infoPtr, &Origin);
6475    
6476     /* first deal with the large items */
6477     rcSearch.left = lpht->pt.x;
6478     rcSearch.top = lpht->pt.y;
6479     rcSearch.right = rcSearch.left + 1;
6480     rcSearch.bottom = rcSearch.top + 1;
6481     
6482     iterator_frameditems(&i, infoPtr, &rcSearch);
6483     iterator_next(&i); /* go to first item in the sequence */
6484     iItem = i.nItem;
6485     iterator_destroy(&i);
6486    
6487     TRACE("lpht->iItem=%d\n", iItem); 
6488     if (iItem == -1) return -1;
6489
6490     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6491     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6492     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6493     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6494     lvItem.iItem = iItem;
6495     lvItem.iSubItem = 0;
6496     lvItem.pszText = szDispText;
6497     lvItem.cchTextMax = DISP_TEXT_SIZE;
6498     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6499     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6500
6501     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6502     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6503     opt.x = lpht->pt.x - Position.x - Origin.x;
6504     opt.y = lpht->pt.y - Position.y - Origin.y;
6505     
6506     if (uView == LVS_REPORT)
6507         rcBounds = rcBox;
6508     else
6509     {
6510         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6511         UnionRect(&rcBounds, &rcBounds, &rcState);
6512     }
6513     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6514     if (!PtInRect(&rcBounds, opt)) return -1;
6515
6516     if (PtInRect(&rcIcon, opt))
6517         lpht->flags |= LVHT_ONITEMICON;
6518     else if (PtInRect(&rcLabel, opt))
6519         lpht->flags |= LVHT_ONITEMLABEL;
6520     else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6521         lpht->flags |= LVHT_ONITEMSTATEICON;
6522     if (lpht->flags & LVHT_ONITEM)
6523         lpht->flags &= ~LVHT_NOWHERE;
6524    
6525     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6526     if (uView == LVS_REPORT && subitem)
6527     {
6528         INT j;
6529
6530         rcBounds.right = rcBounds.left;
6531         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6532         {
6533             rcBounds.left = rcBounds.right;
6534             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6535             if (PtInRect(&rcBounds, opt))
6536             {
6537                 lpht->iSubItem = j;
6538                 break;
6539             }
6540         }
6541     }
6542
6543     if (select && !(uView == LVS_REPORT &&
6544                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6545                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6546     {
6547         if (uView == LVS_REPORT)
6548         {
6549             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6550             UnionRect(&rcBounds, &rcBounds, &rcState);
6551         }
6552         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6553     }
6554     return lpht->iItem = iItem;
6555 }
6556
6557
6558 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6559    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6560    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6561    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6562    their own sort proc. when sending LVM_SORTITEMS.
6563 */
6564 /* Platform SDK:
6565     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6566         if:
6567           LVS_SORTXXX must be specified,
6568           LVS_OWNERDRAW is not set,
6569           <item>.pszText is not LPSTR_TEXTCALLBACK.
6570
6571     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6572     are sorted based on item text..."
6573 */
6574 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6575 {
6576     ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6577     ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6578     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
6579
6580     /* if we're sorting descending, negate the return value */
6581     return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6582 }
6583
6584 /***
6585  * DESCRIPTION:
6586  * Inserts a new item in the listview control.
6587  *
6588  * PARAMETER(S):
6589  * [I] infoPtr : valid pointer to the listview structure
6590  * [I] lpLVItem : item information
6591  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6592  *
6593  * RETURN:
6594  *   SUCCESS : new item index
6595  *   FAILURE : -1
6596  */
6597 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6598 {
6599     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6600     INT nItem;
6601     HDPA hdpaSubItems;
6602     NMLISTVIEW nmlv;
6603     ITEM_INFO *lpItem;
6604     BOOL is_sorted, has_changed;
6605     LVITEMW item;
6606     HWND hwndSelf = infoPtr->hwndSelf;
6607
6608     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6609
6610     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6611
6612     /* make sure it's an item, and not a subitem; cannot insert a subitem */
6613     if (!lpLVItem || lpLVItem->iSubItem) return -1;
6614
6615     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6616
6617     if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6618     
6619     /* insert item in listview control data structure */
6620     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6621     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6622
6623     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6624                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6625
6626     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6627
6628     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6629     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6630     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6631     if (nItem == -1) goto fail;
6632     infoPtr->nItemCount++;
6633
6634     /* shift indices first so they don't get tangled */
6635     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6636
6637     /* set the item attributes */
6638     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6639     {
6640         /* full size structure expected - _WIN32IE >= 0x560 */
6641         item = *lpLVItem;
6642     }
6643     else if (lpLVItem->mask & LVIF_INDENT)
6644     {
6645         /* indent member expected - _WIN32IE >= 0x300 */
6646         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6647     }
6648     else
6649     {
6650         /* minimal structure expected */
6651         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6652     }
6653     item.iItem = nItem;
6654     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6655     {
6656         item.mask |= LVIF_STATE;
6657         item.stateMask |= LVIS_STATEIMAGEMASK;
6658         item.state &= ~LVIS_STATEIMAGEMASK;
6659         item.state |= INDEXTOSTATEIMAGEMASK(1);
6660     }
6661     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6662
6663     /* if we're sorted, sort the list, and update the index */
6664     if (is_sorted)
6665     {
6666         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6667         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6668         assert(nItem != -1);
6669     }
6670
6671     /* make room for the position, if we are in the right mode */
6672     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6673     {
6674         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6675             goto undo;
6676         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6677         {
6678             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6679             goto undo;
6680         }
6681     }
6682     
6683     /* send LVN_INSERTITEM notification */
6684     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6685     nmlv.iItem = nItem;
6686     nmlv.lParam = lpItem->lParam;
6687     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6688     if (!IsWindow(hwndSelf))
6689         return -1;
6690
6691     /* align items (set position of each item) */
6692     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6693     {
6694         POINT pt;
6695
6696         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6697             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6698         else
6699             LISTVIEW_NextIconPosTop(infoPtr, &pt);
6700
6701         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6702     }
6703
6704     /* now is the invalidation fun */
6705     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6706     return nItem;
6707
6708 undo:
6709     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6710     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6711     infoPtr->nItemCount--;
6712 fail:
6713     DPA_DeletePtr(hdpaSubItems, 0);
6714     DPA_Destroy (hdpaSubItems);
6715     Free (lpItem);
6716     return -1;
6717 }
6718
6719 /***
6720  * DESCRIPTION:
6721  * Redraws a range of items.
6722  *
6723  * PARAMETER(S):
6724  * [I] infoPtr : valid pointer to the listview structure
6725  * [I] nFirst : first item
6726  * [I] nLast : last item
6727  *
6728  * RETURN:
6729  *   SUCCESS : TRUE
6730  *   FAILURE : FALSE
6731  */
6732 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6733 {
6734     INT i;
6735  
6736     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
6737         max(nFirst, nLast) >= infoPtr->nItemCount)
6738         return FALSE;
6739     
6740     for (i = nFirst; i <= nLast; i++)
6741         LISTVIEW_InvalidateItem(infoPtr, i);
6742
6743     return TRUE;
6744 }
6745
6746 /***
6747  * DESCRIPTION:
6748  * Scroll the content of a listview.
6749  *
6750  * PARAMETER(S):
6751  * [I] infoPtr : valid pointer to the listview structure
6752  * [I] dx : horizontal scroll amount in pixels
6753  * [I] dy : vertical scroll amount in pixels
6754  *
6755  * RETURN:
6756  *   SUCCESS : TRUE
6757  *   FAILURE : FALSE
6758  *
6759  * COMMENTS:
6760  *  If the control is in report mode (LVS_REPORT) the control can
6761  *  be scrolled only in line increments. "dy" will be rounded to the
6762  *  nearest number of pixels that are a whole line. Ex: if line height
6763  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6764  *  is passed, then the scroll will be 0.  (per MSDN 7/2002)
6765  *
6766  *  For:  (per experimentation with native control and CSpy ListView)
6767  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
6768  *                    dx ignored
6769  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
6770  *                    dx ignored
6771  *     LVS_LIST       dx=1 = 1 column (horizontal only)
6772  *                           but will only scroll 1 column per message
6773  *                           no matter what the value.
6774  *                    dy must be 0 or FALSE returned.
6775  *     LVS_REPORT     dx=1 = 1 pixel
6776  *                    dy=  see above
6777  *
6778  */
6779 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6780 {
6781     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6782     case LVS_REPORT:
6783         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6784         dy /= infoPtr->nItemHeight;
6785         break;
6786     case LVS_LIST:
6787         if (dy != 0) return FALSE;
6788         break;
6789     default: /* icon */
6790         dx = 0;
6791         break;
6792     }   
6793
6794     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6795     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6796   
6797     return TRUE;
6798 }
6799
6800 /***
6801  * DESCRIPTION:
6802  * Sets the background color.
6803  *
6804  * PARAMETER(S):
6805  * [I] infoPtr : valid pointer to the listview structure
6806  * [I] clrBk : background color
6807  *
6808  * RETURN:
6809  *   SUCCESS : TRUE
6810  *   FAILURE : FALSE
6811  */
6812 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6813 {
6814     TRACE("(clrBk=%x)\n", clrBk);
6815
6816     if(infoPtr->clrBk != clrBk) {
6817         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6818         infoPtr->clrBk = clrBk;
6819         if (clrBk == CLR_NONE)
6820             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6821         else
6822             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6823         LISTVIEW_InvalidateList(infoPtr);
6824     }
6825
6826    return TRUE;
6827 }
6828
6829 /* LISTVIEW_SetBkImage */
6830
6831 /*** Helper for {Insert,Set}ColumnT *only* */
6832 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6833                                const LVCOLUMNW *lpColumn, BOOL isW)
6834 {
6835     if (lpColumn->mask & LVCF_FMT)
6836     {
6837         /* format member is valid */
6838         lphdi->mask |= HDI_FORMAT;
6839
6840         /* set text alignment (leftmost column must be left-aligned) */
6841         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6842             lphdi->fmt |= HDF_LEFT;
6843         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6844             lphdi->fmt |= HDF_RIGHT;
6845         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6846             lphdi->fmt |= HDF_CENTER;
6847
6848         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6849             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6850
6851         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6852         {
6853             lphdi->fmt |= HDF_IMAGE;
6854             lphdi->iImage = I_IMAGECALLBACK;
6855         }
6856     }
6857
6858     if (lpColumn->mask & LVCF_WIDTH)
6859     {
6860         lphdi->mask |= HDI_WIDTH;
6861         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6862         {
6863             /* make it fill the remainder of the controls width */
6864             RECT rcHeader;
6865             INT item_index;
6866
6867             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6868             {
6869                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6870                 lphdi->cxy += rcHeader.right - rcHeader.left;
6871             }
6872
6873             /* retrieve the layout of the header */
6874             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6875             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6876
6877             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6878         }
6879         else
6880             lphdi->cxy = lpColumn->cx;
6881     }
6882
6883     if (lpColumn->mask & LVCF_TEXT)
6884     {
6885         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6886         lphdi->fmt |= HDF_STRING;
6887         lphdi->pszText = lpColumn->pszText;
6888         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6889     }
6890
6891     if (lpColumn->mask & LVCF_IMAGE)
6892     {
6893         lphdi->mask |= HDI_IMAGE;
6894         lphdi->iImage = lpColumn->iImage;
6895     }
6896
6897     if (lpColumn->mask & LVCF_ORDER)
6898     {
6899         lphdi->mask |= HDI_ORDER;
6900         lphdi->iOrder = lpColumn->iOrder;
6901     }
6902 }
6903
6904
6905 /***
6906  * DESCRIPTION:
6907  * Inserts a new column.
6908  *
6909  * PARAMETER(S):
6910  * [I] infoPtr : valid pointer to the listview structure
6911  * [I] nColumn : column index
6912  * [I] lpColumn : column information
6913  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6914  *
6915  * RETURN:
6916  *   SUCCESS : new column index
6917  *   FAILURE : -1
6918  */
6919 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6920                                   const LVCOLUMNW *lpColumn, BOOL isW)
6921 {
6922     COLUMN_INFO *lpColumnInfo;
6923     INT nNewColumn;
6924     HDITEMW hdi;
6925     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6926
6927     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6928
6929     if (!lpColumn || nColumn < 0) return -1;
6930     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6931     
6932     ZeroMemory(&hdi, sizeof(HDITEMW));
6933     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6934
6935     /*
6936      * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6937      * (can be seen in SPY) otherwise column never gets added.
6938      */
6939     if (!(lpColumn->mask & LVCF_WIDTH)) {
6940         hdi.mask |= HDI_WIDTH;
6941         hdi.cxy = 10;
6942     }
6943
6944     /*
6945      * when the iSubItem is available Windows copies it to the header lParam. It seems
6946      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6947      */
6948     if (lpColumn->mask & LVCF_SUBITEM)
6949     {
6950         hdi.mask |= HDI_LPARAM;
6951         hdi.lParam = lpColumn->iSubItem;
6952     }
6953
6954     /* create header if not present */
6955     LISTVIEW_CreateHeader(infoPtr);
6956     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
6957          (LVS_REPORT == uView) && (WS_VISIBLE & infoPtr->dwStyle))
6958     {
6959         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6960     }
6961
6962     /* insert item in header control */
6963     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6964                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6965                               (WPARAM)nColumn, (LPARAM)&hdi);
6966     if (nNewColumn == -1) return -1;
6967     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6968    
6969     /* create our own column info */ 
6970     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6971     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6972
6973     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6974     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6975
6976     /* now we have to actually adjust the data */
6977     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6978     {
6979         SUBITEM_INFO *lpSubItem;
6980         HDPA hdpaSubItems;
6981         INT nItem, i;
6982         
6983         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6984         {
6985             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
6986             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6987             {
6988                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
6989                 if (lpSubItem->iSubItem >= nNewColumn)
6990                     lpSubItem->iSubItem++;
6991             }
6992         }
6993     }
6994
6995     /* make space for the new column */
6996     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6997     LISTVIEW_UpdateItemSize(infoPtr);
6998     
6999     return nNewColumn;
7000
7001 fail:
7002     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7003     if (lpColumnInfo)
7004     {
7005         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7006         Free(lpColumnInfo);
7007     }
7008     return -1;
7009 }
7010
7011 /***
7012  * DESCRIPTION:
7013  * Sets the attributes of a header item.
7014  *
7015  * PARAMETER(S):
7016  * [I] infoPtr : valid pointer to the listview structure
7017  * [I] nColumn : column index
7018  * [I] lpColumn : column attributes
7019  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7020  *
7021  * RETURN:
7022  *   SUCCESS : TRUE
7023  *   FAILURE : FALSE
7024  */
7025 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7026                                 const LVCOLUMNW *lpColumn, BOOL isW)
7027 {
7028     HDITEMW hdi, hdiget;
7029     BOOL bResult;
7030
7031     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7032     
7033     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7034
7035     ZeroMemory(&hdi, sizeof(HDITEMW));
7036     if (lpColumn->mask & LVCF_FMT)
7037     {
7038         hdi.mask |= HDI_FORMAT;
7039         hdiget.mask = HDI_FORMAT;
7040         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7041             hdi.fmt = hdiget.fmt & HDF_STRING;
7042     }
7043     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7044
7045     /* set header item attributes */
7046     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7047     if (!bResult) return FALSE;
7048
7049     if (lpColumn->mask & LVCF_FMT)
7050     {
7051         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7052         int oldFmt = lpColumnInfo->fmt;
7053         
7054         lpColumnInfo->fmt = lpColumn->fmt;
7055         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7056         {
7057             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7058             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7059         }
7060     }
7061
7062     return TRUE;
7063 }
7064
7065 /***
7066  * DESCRIPTION:
7067  * Sets the column order array
7068  *
7069  * PARAMETERS:
7070  * [I] infoPtr : valid pointer to the listview structure
7071  * [I] iCount : number of elements in column order array
7072  * [I] lpiArray : pointer to column order array
7073  *
7074  * RETURN:
7075  *   SUCCESS : TRUE
7076  *   FAILURE : FALSE
7077  */
7078 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7079 {
7080   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7081
7082   if (!lpiArray)
7083     return FALSE;
7084
7085   return TRUE;
7086 }
7087
7088
7089 /***
7090  * DESCRIPTION:
7091  * Sets the width of a column
7092  *
7093  * PARAMETERS:
7094  * [I] infoPtr : valid pointer to the listview structure
7095  * [I] nColumn : column index
7096  * [I] cx : column width
7097  *
7098  * RETURN:
7099  *   SUCCESS : TRUE
7100  *   FAILURE : FALSE
7101  */
7102 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7103 {
7104     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7105     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7106     INT max_cx = 0;
7107     HDITEMW hdi;
7108
7109     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7110
7111     /* set column width only if in report or list mode */
7112     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7113
7114     /* take care of invalid cx values */
7115     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7116     else if (uView == LVS_LIST && cx < 1) return FALSE;
7117
7118     /* resize all columns if in LVS_LIST mode */
7119     if(uView == LVS_LIST) 
7120     {
7121         infoPtr->nItemWidth = cx;
7122         LISTVIEW_InvalidateList(infoPtr);
7123         return TRUE;
7124     }
7125
7126     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7127
7128     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7129     {
7130         INT nLabelWidth;
7131         LVITEMW lvItem;
7132
7133         lvItem.mask = LVIF_TEXT;        
7134         lvItem.iItem = 0;
7135         lvItem.iSubItem = nColumn;
7136         lvItem.pszText = szDispText;
7137         lvItem.cchTextMax = DISP_TEXT_SIZE;
7138         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7139         {
7140             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7141             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7142             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7143         }
7144         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7145             max_cx += infoPtr->iconSize.cx;
7146         max_cx += TRAILING_LABEL_PADDING;
7147     }
7148
7149     /* autosize based on listview items width */
7150     if(cx == LVSCW_AUTOSIZE)
7151         cx = max_cx;
7152     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7153     {
7154         /* if iCol is the last column make it fill the remainder of the controls width */
7155         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
7156         {
7157             RECT rcHeader;
7158             POINT Origin;
7159
7160             LISTVIEW_GetOrigin(infoPtr, &Origin);
7161             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7162
7163             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7164         }
7165         else
7166         {
7167             /* Despite what the MS docs say, if this is not the last
7168                column, then MS resizes the column to the width of the
7169                largest text string in the column, including headers
7170                and items. This is different from LVSCW_AUTOSIZE in that
7171                LVSCW_AUTOSIZE ignores the header string length. */
7172             cx = 0;
7173
7174             /* retrieve header text */
7175             hdi.mask = HDI_TEXT;
7176             hdi.cchTextMax = DISP_TEXT_SIZE;
7177             hdi.pszText = szDispText;
7178             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7179             {
7180                 HDC hdc = GetDC(infoPtr->hwndSelf);
7181                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7182                 SIZE size;
7183
7184                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7185                     cx = size.cx + TRAILING_HEADER_PADDING;
7186                 /* FIXME: Take into account the header image, if one is present */
7187                 SelectObject(hdc, old_font);
7188                 ReleaseDC(infoPtr->hwndSelf, hdc);
7189             }
7190             cx = max (cx, max_cx);
7191         }
7192     }
7193
7194     if (cx < 0) return FALSE;
7195
7196     /* call header to update the column change */
7197     hdi.mask = HDI_WIDTH;
7198     hdi.cxy = cx;
7199     TRACE("hdi.cxy=%d\n", hdi.cxy);
7200     return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7201 }
7202
7203 /***
7204  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
7205  *
7206  */
7207 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7208 {
7209     HDC hdc_wnd, hdc;
7210     HBITMAP hbm_im, hbm_mask, hbm_orig;
7211     RECT rc;
7212     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7213     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7214     HIMAGELIST himl;
7215
7216     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7217                             ILC_COLOR | ILC_MASK, 2, 2);
7218     hdc_wnd = GetDC(infoPtr->hwndSelf);
7219     hdc = CreateCompatibleDC(hdc_wnd);
7220     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7221     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7222     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7223
7224     rc.left = rc.top = 0;
7225     rc.right = GetSystemMetrics(SM_CXSMICON);
7226     rc.bottom = GetSystemMetrics(SM_CYSMICON);
7227
7228     hbm_orig = SelectObject(hdc, hbm_mask);
7229     FillRect(hdc, &rc, hbr_white);
7230     InflateRect(&rc, -2, -2);
7231     FillRect(hdc, &rc, hbr_black);
7232
7233     SelectObject(hdc, hbm_im);
7234     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7235     SelectObject(hdc, hbm_orig);
7236     ImageList_Add(himl, hbm_im, hbm_mask); 
7237
7238     SelectObject(hdc, hbm_im);
7239     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7240     SelectObject(hdc, hbm_orig);
7241     ImageList_Add(himl, hbm_im, hbm_mask);
7242
7243     DeleteObject(hbm_mask);
7244     DeleteObject(hbm_im);
7245     DeleteDC(hdc);
7246
7247     return himl;
7248 }
7249
7250 /***
7251  * DESCRIPTION:
7252  * Sets the extended listview style.
7253  *
7254  * PARAMETERS:
7255  * [I] infoPtr : valid pointer to the listview structure
7256  * [I] dwMask : mask
7257  * [I] dwStyle : style
7258  *
7259  * RETURN:
7260  *   SUCCESS : previous style
7261  *   FAILURE : 0
7262  */
7263 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7264 {
7265     DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7266
7267     /* set new style */
7268     if (dwMask)
7269         infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7270     else
7271         infoPtr->dwLvExStyle = dwExStyle;
7272
7273     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7274     {
7275         HIMAGELIST himl = 0;
7276         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7277         {
7278             LVITEMW item;
7279             item.mask = LVIF_STATE;
7280             item.stateMask = LVIS_STATEIMAGEMASK;
7281             item.state = INDEXTOSTATEIMAGEMASK(1);
7282             LISTVIEW_SetItemState(infoPtr, -1, &item);
7283
7284             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7285         }
7286         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7287     }
7288     
7289     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7290     {
7291         DWORD dwStyle;
7292
7293         /* if not already created */
7294         LISTVIEW_CreateHeader(infoPtr);
7295
7296         dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7297         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7298             dwStyle |= HDS_DRAGDROP;
7299         else
7300             dwStyle &= ~HDS_DRAGDROP;
7301         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7302     }
7303
7304     /* GRIDLINES adds decoration at top so changes sizes */
7305     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7306     {
7307         LISTVIEW_UpdateSize(infoPtr);
7308     }
7309
7310
7311     LISTVIEW_InvalidateList(infoPtr);
7312     return dwOldExStyle;
7313 }
7314
7315 /***
7316  * DESCRIPTION:
7317  * Sets the new hot cursor used during hot tracking and hover selection.
7318  *
7319  * PARAMETER(S):
7320  * [I] infoPtr : valid pointer to the listview structure
7321  * [I] hCursor : the new hot cursor handle
7322  *
7323  * RETURN:
7324  * Returns the previous hot cursor
7325  */
7326 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7327 {
7328     HCURSOR oldCursor = infoPtr->hHotCursor;
7329     
7330     infoPtr->hHotCursor = hCursor;
7331
7332     return oldCursor;
7333 }
7334
7335
7336 /***
7337  * DESCRIPTION:
7338  * Sets the hot item index.
7339  *
7340  * PARAMETERS:
7341  * [I] infoPtr : valid pointer to the listview structure
7342  * [I] iIndex : index
7343  *
7344  * RETURN:
7345  *   SUCCESS : previous hot item index
7346  *   FAILURE : -1 (no hot item)
7347  */
7348 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7349 {
7350     INT iOldIndex = infoPtr->nHotItem;
7351     
7352     infoPtr->nHotItem = iIndex;
7353     
7354     return iOldIndex;
7355 }
7356
7357
7358 /***
7359  * DESCRIPTION:
7360  * Sets the amount of time the cursor must hover over an item before it is selected.
7361  *
7362  * PARAMETER(S):
7363  * [I] infoPtr : valid pointer to the listview structure
7364  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7365  *
7366  * RETURN:
7367  * Returns the previous hover time
7368  */
7369 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7370 {
7371     DWORD oldHoverTime = infoPtr->dwHoverTime;
7372     
7373     infoPtr->dwHoverTime = dwHoverTime;
7374     
7375     return oldHoverTime;
7376 }
7377
7378 /***
7379  * DESCRIPTION:
7380  * Sets spacing for icons of LVS_ICON style.
7381  *
7382  * PARAMETER(S):
7383  * [I] infoPtr : valid pointer to the listview structure
7384  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7385  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7386  *
7387  * RETURN:
7388  *   MAKELONG(oldcx, oldcy)
7389  */
7390 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7391 {
7392     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7393     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7394
7395     TRACE("requested=(%d,%d)\n", cx, cy);
7396     
7397     /* this is supported only for LVS_ICON style */
7398     if (uView != LVS_ICON) return oldspacing;
7399   
7400     /* set to defaults, if instructed to */
7401     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7402     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7403
7404     /* if 0 then compute width
7405      * FIXME: Should scan each item and determine max width of
7406      *        icon or label, then make that the width */
7407     if (cx == 0)
7408         cx = infoPtr->iconSpacing.cx;
7409
7410     /* if 0 then compute height */
7411     if (cy == 0) 
7412         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7413              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7414     
7415
7416     infoPtr->iconSpacing.cx = cx;
7417     infoPtr->iconSpacing.cy = cy;
7418
7419     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7420           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
7421           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7422           infoPtr->ntmHeight);
7423
7424     /* these depend on the iconSpacing */
7425     LISTVIEW_UpdateItemSize(infoPtr);
7426
7427     return oldspacing;
7428 }
7429
7430 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7431 {
7432     INT cx, cy;
7433     
7434     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7435     {
7436         size->cx = cx;
7437         size->cy = cy;
7438     }
7439     else
7440     {
7441         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7442         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7443     }
7444 }
7445
7446 /***
7447  * DESCRIPTION:
7448  * Sets image lists.
7449  *
7450  * PARAMETER(S):
7451  * [I] infoPtr : valid pointer to the listview structure
7452  * [I] nType : image list type
7453  * [I] himl : image list handle
7454  *
7455  * RETURN:
7456  *   SUCCESS : old image list
7457  *   FAILURE : NULL
7458  */
7459 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7460 {
7461     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7462     INT oldHeight = infoPtr->nItemHeight;
7463     HIMAGELIST himlOld = 0;
7464
7465     TRACE("(nType=%d, himl=%p\n", nType, himl);
7466
7467     switch (nType)
7468     {
7469     case LVSIL_NORMAL:
7470         himlOld = infoPtr->himlNormal;
7471         infoPtr->himlNormal = himl;
7472         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7473         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7474     break;
7475
7476     case LVSIL_SMALL:
7477         himlOld = infoPtr->himlSmall;
7478         infoPtr->himlSmall = himl;
7479          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7480     break;
7481
7482     case LVSIL_STATE:
7483         himlOld = infoPtr->himlState;
7484         infoPtr->himlState = himl;
7485         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7486         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7487     break;
7488
7489     default:
7490         ERR("Unknown icon type=%d\n", nType);
7491         return NULL;
7492     }
7493
7494     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7495     if (infoPtr->nItemHeight != oldHeight)
7496         LISTVIEW_UpdateScroll(infoPtr);
7497
7498     return himlOld;
7499 }
7500
7501 /***
7502  * DESCRIPTION:
7503  * Preallocates memory (does *not* set the actual count of items !)
7504  *
7505  * PARAMETER(S):
7506  * [I] infoPtr : valid pointer to the listview structure
7507  * [I] nItems : item count (projected number of items to allocate)
7508  * [I] dwFlags : update flags
7509  *
7510  * RETURN:
7511  *   SUCCESS : TRUE
7512  *   FAILURE : FALSE
7513  */
7514 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7515 {
7516     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7517
7518     if (infoPtr->dwStyle & LVS_OWNERDATA)
7519     {
7520         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7521         INT nOldCount = infoPtr->nItemCount;
7522
7523         if (nItems < nOldCount)
7524         {
7525             RANGE range = { nItems, nOldCount };
7526             ranges_del(infoPtr->selectionRanges, range);
7527             if (infoPtr->nFocusedItem >= nItems)
7528             {
7529                 LISTVIEW_SetItemFocus(infoPtr, -1);
7530                 SetRectEmpty(&infoPtr->rcFocus);
7531             }
7532         }
7533
7534         infoPtr->nItemCount = nItems;
7535         LISTVIEW_UpdateScroll(infoPtr);
7536
7537         /* the flags are valid only in ownerdata report and list modes */
7538         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7539
7540         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7541             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7542
7543         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7544             LISTVIEW_InvalidateList(infoPtr);
7545         else
7546         {
7547             INT nFrom, nTo;
7548             POINT Origin;
7549             RECT rcErase;
7550             
7551             LISTVIEW_GetOrigin(infoPtr, &Origin);
7552             nFrom = min(nOldCount, nItems);
7553             nTo = max(nOldCount, nItems);
7554     
7555             if (uView == LVS_REPORT)
7556             {
7557                 rcErase.left = 0;
7558                 rcErase.top = nFrom * infoPtr->nItemHeight;
7559                 rcErase.right = infoPtr->nItemWidth;
7560                 rcErase.bottom = nTo * infoPtr->nItemHeight;
7561                 OffsetRect(&rcErase, Origin.x, Origin.y);
7562                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7563                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7564             }
7565             else /* LVS_LIST */
7566             {
7567                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7568
7569                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7570                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7571                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7572                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7573                 OffsetRect(&rcErase, Origin.x, Origin.y);
7574                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7575                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7576
7577                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7578                 rcErase.top = 0;
7579                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7580                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7581                 OffsetRect(&rcErase, Origin.x, Origin.y);
7582                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7583                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7584             }
7585         }
7586     }
7587     else
7588     {
7589         /* According to MSDN for non-LVS_OWNERDATA this is just
7590          * a performance issue. The control allocates its internal
7591          * data structures for the number of items specified. It
7592          * cuts down on the number of memory allocations. Therefore
7593          * we will just issue a WARN here
7594          */
7595         WARN("for non-ownerdata performance option not implemented.\n");
7596     }
7597
7598     return TRUE;
7599 }
7600
7601 /***
7602  * DESCRIPTION:
7603  * Sets the position of an item.
7604  *
7605  * PARAMETER(S):
7606  * [I] infoPtr : valid pointer to the listview structure
7607  * [I] nItem : item index
7608  * [I] pt : coordinate
7609  *
7610  * RETURN:
7611  *   SUCCESS : TRUE
7612  *   FAILURE : FALSE
7613  */
7614 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7615 {
7616     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7617     POINT Origin;
7618
7619     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7620
7621     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7622         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7623
7624     LISTVIEW_GetOrigin(infoPtr, &Origin);
7625
7626     /* This point value seems to be an undocumented feature.
7627      * The best guess is that it means either at the origin, 
7628      * or at true beginning of the list. I will assume the origin. */
7629     if ((pt.x == -1) && (pt.y == -1))
7630         pt = Origin;
7631     
7632     if (uView == LVS_ICON)
7633     {
7634         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7635         pt.y -= ICON_TOP_PADDING;
7636     }
7637     pt.x -= Origin.x;
7638     pt.y -= Origin.y;
7639
7640     infoPtr->bAutoarrange = FALSE;
7641
7642     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7643 }
7644
7645 /***
7646  * DESCRIPTION:
7647  * Sets the state of one or many items.
7648  *
7649  * PARAMETER(S):
7650  * [I] infoPtr : valid pointer to the listview structure
7651  * [I] nItem : item index
7652  * [I] lpLVItem : item or subitem info
7653  *
7654  * RETURN:
7655  *   SUCCESS : TRUE
7656  *   FAILURE : FALSE
7657  */
7658 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7659 {
7660     BOOL bResult = TRUE;
7661     LVITEMW lvItem;
7662
7663     lvItem.iItem = nItem;
7664     lvItem.iSubItem = 0;
7665     lvItem.mask = LVIF_STATE;
7666     lvItem.state = lpLVItem->state;
7667     lvItem.stateMask = lpLVItem->stateMask;
7668     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7669
7670     if (nItem == -1)
7671     {
7672         /* apply to all items */
7673         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7674             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7675     }
7676     else
7677         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7678
7679     /*
7680      * Update selection mark
7681      *
7682      * Investigation on windows 2k showed that selection mark was updated
7683      * whenever a new selection was made, but if the selected item was
7684      * unselected it was not updated.
7685      *
7686      * we are probably still not 100% accurate, but this at least sets the
7687      * proper selection mark when it is needed
7688      */
7689
7690     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7691         (infoPtr->nSelectionMark == -1))
7692     {
7693         int i;
7694         for (i = 0; i < infoPtr->nItemCount; i++)
7695         {
7696             if (infoPtr->uCallbackMask & LVIS_SELECTED)
7697             {
7698                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7699                 {
7700                     infoPtr->nSelectionMark = i;
7701                     break;
7702                 }
7703             }
7704             else if (ranges_contain(infoPtr->selectionRanges, i))
7705             {
7706                 infoPtr->nSelectionMark = i;
7707                 break;
7708             }
7709         }
7710     }
7711
7712     return bResult;
7713 }
7714
7715 /***
7716  * DESCRIPTION:
7717  * Sets the text of an item or subitem.
7718  *
7719  * PARAMETER(S):
7720  * [I] hwnd : window handle
7721  * [I] nItem : item index
7722  * [I] lpLVItem : item or subitem info
7723  * [I] isW : TRUE if input is Unicode
7724  *
7725  * RETURN:
7726  *   SUCCESS : TRUE
7727  *   FAILURE : FALSE
7728  */
7729 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7730 {
7731     LVITEMW lvItem;
7732
7733     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7734     
7735     lvItem.iItem = nItem;
7736     lvItem.iSubItem = lpLVItem->iSubItem;
7737     lvItem.mask = LVIF_TEXT;
7738     lvItem.pszText = lpLVItem->pszText;
7739     lvItem.cchTextMax = lpLVItem->cchTextMax;
7740     
7741     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7742
7743     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
7744 }
7745
7746 /***
7747  * DESCRIPTION:
7748  * Set item index that marks the start of a multiple selection.
7749  *
7750  * PARAMETER(S):
7751  * [I] infoPtr : valid pointer to the listview structure
7752  * [I] nIndex : index
7753  *
7754  * RETURN:
7755  * Index number or -1 if there is no selection mark.
7756  */
7757 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7758 {
7759   INT nOldIndex = infoPtr->nSelectionMark;
7760
7761   TRACE("(nIndex=%d)\n", nIndex);
7762
7763   infoPtr->nSelectionMark = nIndex;
7764
7765   return nOldIndex;
7766 }
7767
7768 /***
7769  * DESCRIPTION:
7770  * Sets the text background color.
7771  *
7772  * PARAMETER(S):
7773  * [I] infoPtr : valid pointer to the listview structure
7774  * [I] clrTextBk : text background color
7775  *
7776  * RETURN:
7777  *   SUCCESS : TRUE
7778  *   FAILURE : FALSE
7779  */
7780 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7781 {
7782     TRACE("(clrTextBk=%x)\n", clrTextBk);
7783
7784     if (infoPtr->clrTextBk != clrTextBk)
7785     {
7786         infoPtr->clrTextBk = clrTextBk;
7787         LISTVIEW_InvalidateList(infoPtr);
7788     }
7789     
7790   return TRUE;
7791 }
7792
7793 /***
7794  * DESCRIPTION:
7795  * Sets the text foreground color.
7796  *
7797  * PARAMETER(S):
7798  * [I] infoPtr : valid pointer to the listview structure
7799  * [I] clrText : text color
7800  *
7801  * RETURN:
7802  *   SUCCESS : TRUE
7803  *   FAILURE : FALSE
7804  */
7805 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7806 {
7807     TRACE("(clrText=%x)\n", clrText);
7808
7809     if (infoPtr->clrText != clrText)
7810     {
7811         infoPtr->clrText = clrText;
7812         LISTVIEW_InvalidateList(infoPtr);
7813     }
7814
7815     return TRUE;
7816 }
7817
7818 /***
7819  * DESCRIPTION:
7820  * Determines which listview item is located at the specified position.
7821  *
7822  * PARAMETER(S):
7823  * [I] infoPtr        : valid pointer to the listview structure
7824  * [I] hwndNewToolTip : handle to new ToolTip
7825  *
7826  * RETURN:
7827  *   old tool tip
7828  */
7829 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7830 {
7831   HWND hwndOldToolTip = infoPtr->hwndToolTip;
7832   infoPtr->hwndToolTip = hwndNewToolTip;
7833   return hwndOldToolTip;
7834 }
7835
7836 /*
7837  * DESCRIPTION:
7838  *   sets the Unicode character format flag for the control
7839  * PARAMETER(S):
7840  *    [I] infoPtr         :valid pointer to the listview structure
7841  *    [I] fUnicode        :true to switch to UNICODE false to switch to ANSI
7842  *
7843  * RETURN:
7844  *    Old Unicode Format
7845  */
7846 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7847 {
7848   BOOL rc = infoPtr->notifyFormat;
7849   infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7850   return rc;
7851 }
7852
7853 /* LISTVIEW_SetWorkAreas */
7854
7855 /***
7856  * DESCRIPTION:
7857  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
7858  *
7859  * PARAMETER(S):
7860  * [I] first : pointer to first ITEM_INFO to compare
7861  * [I] second : pointer to second ITEM_INFO to compare
7862  * [I] lParam : HWND of control
7863  *
7864  * RETURN:
7865  *   if first comes before second : negative
7866  *   if first comes after second : positive
7867  *   if first and second are equivalent : zero
7868  */
7869 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7870 {
7871   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7872   ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7873   ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7874
7875   /* Forward the call to the client defined callback */
7876   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7877 }
7878
7879 /***
7880  * DESCRIPTION:
7881  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
7882  *
7883  * PARAMETER(S):
7884  * [I] first : pointer to first ITEM_INFO to compare
7885  * [I] second : pointer to second ITEM_INFO to compare
7886  * [I] lParam : HWND of control
7887  *
7888  * RETURN:
7889  *   if first comes before second : negative
7890  *   if first comes after second : positive
7891  *   if first and second are equivalent : zero
7892  */
7893 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
7894 {
7895   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7896   INT first_idx  = DPA_GetPtrIndex( infoPtr->hdpaItems, first  );
7897   INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
7898
7899   /* Forward the call to the client defined callback */
7900   return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
7901 }
7902
7903 /***
7904  * DESCRIPTION:
7905  * Sorts the listview items.
7906  *
7907  * PARAMETER(S):
7908  * [I] infoPtr : valid pointer to the listview structure
7909  * [I] pfnCompare : application-defined value
7910  * [I] lParamSort : pointer to comparison callback
7911  * [I] IsEx : TRUE when LVM_SORTITEMSEX used
7912  *
7913  * RETURN:
7914  *   SUCCESS : TRUE
7915  *   FAILURE : FALSE
7916  */
7917 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
7918                                LPARAM lParamSort, BOOL IsEx)
7919 {
7920     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7921     HDPA hdpaSubItems;
7922     ITEM_INFO *lpItem;
7923     LPVOID selectionMarkItem = NULL;
7924     LPVOID focusedItem = NULL;
7925     int i;
7926
7927     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7928
7929     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7930
7931     if (!pfnCompare) return FALSE;
7932     if (!infoPtr->hdpaItems) return FALSE;
7933
7934     /* if there are 0 or 1 items, there is no need to sort */
7935     if (infoPtr->nItemCount < 2) return TRUE;
7936
7937     /* clear selection */
7938     ranges_clear(infoPtr->selectionRanges);
7939
7940     /* save selection mark and focused item */
7941     if (infoPtr->nSelectionMark >= 0)
7942         selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
7943     if (infoPtr->nFocusedItem >= 0)
7944         focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7945
7946     infoPtr->pfnCompare = pfnCompare;
7947     infoPtr->lParamSort = lParamSort;
7948     if (IsEx)
7949         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
7950     else
7951         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7952
7953     /* restore selection ranges */
7954     for (i=0; i < infoPtr->nItemCount; i++)
7955     {
7956         hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7957         lpItem = DPA_GetPtr(hdpaSubItems, 0);
7958
7959         if (lpItem->state & LVIS_SELECTED)
7960             ranges_additem(infoPtr->selectionRanges, i);
7961     }
7962     /* restore selection mark and focused item */
7963     infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7964     infoPtr->nFocusedItem   = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
7965
7966     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7967
7968     /* refresh the display */
7969     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7970         LISTVIEW_InvalidateList(infoPtr);
7971
7972     return TRUE;
7973 }
7974
7975 /***
7976  * DESCRIPTION:
7977  * Update theme handle after a theme change.
7978  *
7979  * PARAMETER(S):
7980  * [I] infoPtr : valid pointer to the listview structure
7981  *
7982  * RETURN:
7983  *   SUCCESS : 0
7984  *   FAILURE : something else
7985  */
7986 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7987 {
7988     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7989     CloseThemeData(theme);
7990     OpenThemeData(infoPtr->hwndSelf, themeClass);
7991     return 0;
7992 }
7993
7994 /***
7995  * DESCRIPTION:
7996  * Updates an items or rearranges the listview control.
7997  *
7998  * PARAMETER(S):
7999  * [I] infoPtr : valid pointer to the listview structure
8000  * [I] nItem : item index
8001  *
8002  * RETURN:
8003  *   SUCCESS : TRUE
8004  *   FAILURE : FALSE
8005  */
8006 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8007 {
8008     TRACE("(nItem=%d)\n", nItem);
8009
8010     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8011
8012     /* rearrange with default alignment style */
8013     if (is_autoarrange(infoPtr))
8014         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8015     else
8016         LISTVIEW_InvalidateItem(infoPtr, nItem);
8017
8018     return TRUE;
8019 }
8020
8021 /***
8022  * DESCRIPTION:
8023  * Draw the track line at the place defined in the infoPtr structure.
8024  * The line is drawn with a XOR pen so drawing the line for the second time
8025  * in the same place erases the line.
8026  *
8027  * PARAMETER(S):
8028  * [I] infoPtr : valid pointer to the listview structure
8029  *
8030  * RETURN:
8031  *   SUCCESS : TRUE
8032  *   FAILURE : FALSE
8033  */
8034 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8035 {
8036     HPEN hOldPen;
8037     HDC hdc;
8038     INT oldROP;
8039
8040     if (infoPtr->xTrackLine == -1)
8041         return FALSE;
8042
8043     if (!(hdc = GetDC(infoPtr->hwndSelf)))
8044         return FALSE;
8045     hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8046     oldROP = SetROP2(hdc, R2_XORPEN);
8047     MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8048     LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8049     SetROP2(hdc, oldROP);
8050     SelectObject(hdc, hOldPen);
8051     ReleaseDC(infoPtr->hwndSelf, hdc);
8052     return TRUE;
8053 }
8054
8055 /***
8056  * DESCRIPTION:
8057  * Called when an edit control should be displayed. This function is called after
8058  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8059  *
8060  * PARAMETER(S):
8061  * [I] hwnd : Handle to the listview
8062  * [I] uMsg : WM_TIMER (ignored)
8063  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8064  * [I] dwTimer : The elapsed time (ignored)
8065  *
8066  * RETURN:
8067  *   None.
8068  */
8069 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8070 {
8071     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8072     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8073
8074     KillTimer(hwnd, idEvent);
8075     editItem->fEnabled = FALSE;
8076     /* check if the item is still selected */
8077     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8078         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8079 }
8080
8081 /***
8082  * DESCRIPTION:
8083  * Creates the listview control - the WM_NCCREATE phase.
8084  *
8085  * PARAMETER(S):
8086  * [I] hwnd : window handle
8087  * [I] lpcs : the create parameters
8088  *
8089  * RETURN:
8090  *   Success: TRUE
8091  *   Failure: FALSE
8092  */
8093 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8094 {
8095   LISTVIEW_INFO *infoPtr;
8096   LOGFONTW logFont;
8097
8098   TRACE("(lpcs=%p)\n", lpcs);
8099
8100   /* initialize info pointer */
8101   infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8102   if (!infoPtr) return FALSE;
8103
8104   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8105
8106   infoPtr->hwndSelf = hwnd;
8107   infoPtr->dwStyle = lpcs->style;    /* Note: may be changed in WM_CREATE */
8108   /* determine the type of structures to use */
8109   infoPtr->hwndNotify = lpcs->hwndParent;
8110   /* infoPtr->notifyFormat will be filled in WM_CREATE */
8111
8112   /* initialize color information  */
8113   infoPtr->clrBk = CLR_NONE;
8114   infoPtr->clrText = CLR_DEFAULT;
8115   infoPtr->clrTextBk = CLR_DEFAULT;
8116   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8117
8118   /* set default values */
8119   infoPtr->nFocusedItem = -1;
8120   infoPtr->nSelectionMark = -1;
8121   infoPtr->nHotItem = -1;
8122   infoPtr->bRedraw = TRUE;
8123   infoPtr->bNoItemMetrics = TRUE;
8124   infoPtr->bDoChangeNotify = TRUE;
8125   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8126   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8127   infoPtr->nEditLabelItem = -1;
8128   infoPtr->dwHoverTime = -1; /* default system hover time */
8129   infoPtr->nMeasureItemHeight = 0;
8130   infoPtr->xTrackLine = -1;  /* no track line */
8131   infoPtr->itemEdit.fEnabled = FALSE;
8132
8133   /* get default font (icon title) */
8134   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8135   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8136   infoPtr->hFont = infoPtr->hDefaultFont;
8137   LISTVIEW_SaveTextMetrics(infoPtr);
8138
8139   /* allocate memory for the data structure */
8140   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8141   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8142   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
8143   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
8144   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8145   return TRUE;
8146
8147 fail:
8148     DestroyWindow(infoPtr->hwndHeader);
8149     ranges_destroy(infoPtr->selectionRanges);
8150     DPA_Destroy(infoPtr->hdpaItems);
8151     DPA_Destroy(infoPtr->hdpaPosX);
8152     DPA_Destroy(infoPtr->hdpaPosY);
8153     DPA_Destroy(infoPtr->hdpaColumns);
8154     Free(infoPtr);
8155     return FALSE;
8156 }
8157
8158 /***
8159  * DESCRIPTION:
8160  * Creates the listview control - the WM_CREATE phase. Most of the data is
8161  * already set up in LISTVIEW_NCCreate
8162  *
8163  * PARAMETER(S):
8164  * [I] hwnd : window handle
8165  * [I] lpcs : the create parameters
8166  *
8167  * RETURN:
8168  *   Success: 0
8169  *   Failure: -1
8170  */
8171 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8172 {
8173   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8174   UINT uView = lpcs->style & LVS_TYPEMASK;
8175
8176   TRACE("(lpcs=%p)\n", lpcs);
8177
8178   infoPtr->dwStyle = lpcs->style;
8179   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8180                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8181
8182   if ((uView == LVS_REPORT) && (lpcs->style & WS_VISIBLE))
8183   {
8184     if (LISTVIEW_CreateHeader(infoPtr) < 0)  return -1;
8185   }
8186   else
8187     infoPtr->hwndHeader = 0;
8188
8189   /* init item size to avoid division by 0 */
8190   LISTVIEW_UpdateItemSize (infoPtr);
8191
8192   if (uView == LVS_REPORT)
8193   {
8194     if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8195     {
8196       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8197     }
8198     LISTVIEW_UpdateScroll(infoPtr);
8199   }
8200
8201   OpenThemeData(hwnd, themeClass);
8202
8203   /* initialize the icon sizes */
8204   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8205   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8206   return 0;
8207 }
8208
8209 /***
8210  * DESCRIPTION:
8211  * Destroys the listview control.
8212  *
8213  * PARAMETER(S):
8214  * [I] infoPtr : valid pointer to the listview structure
8215  *
8216  * RETURN:
8217  *   Success: 0
8218  *   Failure: -1
8219  */
8220 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8221 {
8222     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8223     CloseThemeData(theme);
8224     return 0;
8225 }
8226
8227 /***
8228  * DESCRIPTION:
8229  * Enables the listview control.
8230  *
8231  * PARAMETER(S):
8232  * [I] infoPtr : valid pointer to the listview structure
8233  * [I] bEnable : specifies whether to enable or disable the window
8234  *
8235  * RETURN:
8236  *   SUCCESS : TRUE
8237  *   FAILURE : FALSE
8238  */
8239 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8240 {
8241     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8242         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8243     return TRUE;
8244 }
8245
8246 /***
8247  * DESCRIPTION:
8248  * Erases the background of the listview control.
8249  *
8250  * PARAMETER(S):
8251  * [I] infoPtr : valid pointer to the listview structure
8252  * [I] hdc : device context handle
8253  *
8254  * RETURN:
8255  *   SUCCESS : TRUE
8256  *   FAILURE : FALSE
8257  */
8258 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8259 {
8260     RECT rc;
8261
8262     TRACE("(hdc=%p)\n", hdc);
8263
8264     if (!GetClipBox(hdc, &rc)) return FALSE;
8265
8266     /* for double buffered controls we need to do this during refresh */
8267     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8268
8269     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8270 }
8271         
8272
8273 /***
8274  * DESCRIPTION:
8275  * Helper function for LISTVIEW_[HV]Scroll *only*.
8276  * Performs vertical/horizontal scrolling by a give amount.
8277  *
8278  * PARAMETER(S):
8279  * [I] infoPtr : valid pointer to the listview structure
8280  * [I] dx : amount of horizontal scroll
8281  * [I] dy : amount of vertical scroll
8282  */
8283 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8284 {
8285     /* now we can scroll the list */
8286     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
8287                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8288     /* if we have focus, adjust rect */
8289     OffsetRect(&infoPtr->rcFocus, dx, dy);
8290     UpdateWindow(infoPtr->hwndSelf);
8291 }
8292
8293 /***
8294  * DESCRIPTION:
8295  * Performs vertical scrolling.
8296  *
8297  * PARAMETER(S):
8298  * [I] infoPtr : valid pointer to the listview structure
8299  * [I] nScrollCode : scroll code
8300  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8301  * [I] hScrollWnd  : scrollbar control window handle
8302  *
8303  * RETURN:
8304  * Zero
8305  *
8306  * NOTES:
8307  *   SB_LINEUP/SB_LINEDOWN:
8308  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
8309  *        for LVS_REPORT is 1 line
8310  *        for LVS_LIST cannot occur
8311  *
8312  */
8313 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
8314                                 INT nScrollDiff, HWND hScrollWnd)
8315 {
8316     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8317     INT nOldScrollPos, nNewScrollPos;
8318     SCROLLINFO scrollInfo;
8319     BOOL is_an_icon;
8320
8321     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8322         debugscrollcode(nScrollCode), nScrollDiff);
8323
8324     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8325
8326     scrollInfo.cbSize = sizeof(SCROLLINFO);
8327     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8328
8329     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8330
8331     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8332
8333     nOldScrollPos = scrollInfo.nPos;
8334     switch (nScrollCode)
8335     {
8336     case SB_INTERNAL:
8337         break;
8338
8339     case SB_LINEUP:
8340         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8341         break;
8342
8343     case SB_LINEDOWN:
8344         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8345         break;
8346
8347     case SB_PAGEUP:
8348         nScrollDiff = -scrollInfo.nPage;
8349         break;
8350
8351     case SB_PAGEDOWN:
8352         nScrollDiff = scrollInfo.nPage;
8353         break;
8354
8355     case SB_THUMBPOSITION:
8356     case SB_THUMBTRACK:
8357         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8358         break;
8359
8360     default:
8361         nScrollDiff = 0;
8362     }
8363
8364     /* quit right away if pos isn't changing */
8365     if (nScrollDiff == 0) return 0;
8366     
8367     /* calculate new position, and handle overflows */
8368     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8369     if (nScrollDiff > 0) {
8370         if (nNewScrollPos < nOldScrollPos ||
8371             nNewScrollPos > scrollInfo.nMax)
8372             nNewScrollPos = scrollInfo.nMax;
8373     } else {
8374         if (nNewScrollPos > nOldScrollPos ||
8375             nNewScrollPos < scrollInfo.nMin)
8376             nNewScrollPos = scrollInfo.nMin;
8377     }
8378
8379     /* set the new position, and reread in case it changed */
8380     scrollInfo.fMask = SIF_POS;
8381     scrollInfo.nPos = nNewScrollPos;
8382     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8383     
8384     /* carry on only if it really changed */
8385     if (nNewScrollPos == nOldScrollPos) return 0;
8386     
8387     /* now adjust to client coordinates */
8388     nScrollDiff = nOldScrollPos - nNewScrollPos;
8389     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8390    
8391     /* and scroll the window */ 
8392     scroll_list(infoPtr, 0, nScrollDiff);
8393
8394     return 0;
8395 }
8396
8397 /***
8398  * DESCRIPTION:
8399  * Performs horizontal scrolling.
8400  *
8401  * PARAMETER(S):
8402  * [I] infoPtr : valid pointer to the listview structure
8403  * [I] nScrollCode : scroll code
8404  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8405  * [I] hScrollWnd  : scrollbar control window handle
8406  *
8407  * RETURN:
8408  * Zero
8409  *
8410  * NOTES:
8411  *   SB_LINELEFT/SB_LINERIGHT:
8412  *        for LVS_ICON, LVS_SMALLICON  1 pixel
8413  *        for LVS_REPORT is 1 pixel
8414  *        for LVS_LIST  is 1 column --> which is a 1 because the
8415  *                                      scroll is based on columns not pixels
8416  *
8417  */
8418 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8419                                 INT nScrollDiff, HWND hScrollWnd)
8420 {
8421     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8422     INT nOldScrollPos, nNewScrollPos;
8423     SCROLLINFO scrollInfo;
8424
8425     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8426         debugscrollcode(nScrollCode), nScrollDiff);
8427
8428     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8429
8430     scrollInfo.cbSize = sizeof(SCROLLINFO);
8431     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8432
8433     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8434
8435     nOldScrollPos = scrollInfo.nPos;
8436
8437     switch (nScrollCode)
8438     {
8439     case SB_INTERNAL:
8440         break;
8441
8442     case SB_LINELEFT:
8443         nScrollDiff = -1;
8444         break;
8445
8446     case SB_LINERIGHT:
8447         nScrollDiff = 1;
8448         break;
8449
8450     case SB_PAGELEFT:
8451         nScrollDiff = -scrollInfo.nPage;
8452         break;
8453
8454     case SB_PAGERIGHT:
8455         nScrollDiff = scrollInfo.nPage;
8456         break;
8457
8458     case SB_THUMBPOSITION:
8459     case SB_THUMBTRACK:
8460         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8461         break;
8462
8463     default:
8464         nScrollDiff = 0;
8465     }
8466
8467     /* quit right away if pos isn't changing */
8468     if (nScrollDiff == 0) return 0;
8469     
8470     /* calculate new position, and handle overflows */
8471     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8472     if (nScrollDiff > 0) {
8473         if (nNewScrollPos < nOldScrollPos ||
8474             nNewScrollPos > scrollInfo.nMax)
8475             nNewScrollPos = scrollInfo.nMax;
8476     } else {
8477         if (nNewScrollPos > nOldScrollPos ||
8478             nNewScrollPos < scrollInfo.nMin)
8479             nNewScrollPos = scrollInfo.nMin;
8480     }
8481
8482     /* set the new position, and reread in case it changed */
8483     scrollInfo.fMask = SIF_POS;
8484     scrollInfo.nPos = nNewScrollPos;
8485     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8486     
8487     /* carry on only if it really changed */
8488     if (nNewScrollPos == nOldScrollPos) return 0;
8489     
8490     if(uView == LVS_REPORT)
8491         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8492       
8493     /* now adjust to client coordinates */
8494     nScrollDiff = nOldScrollPos - nNewScrollPos;
8495     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8496    
8497     /* and scroll the window */
8498     scroll_list(infoPtr, nScrollDiff, 0);
8499
8500   return 0;
8501 }
8502
8503 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8504 {
8505     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8506     INT gcWheelDelta = 0;
8507     INT pulScrollLines = 3;
8508     SCROLLINFO scrollInfo;
8509
8510     TRACE("(wheelDelta=%d)\n", wheelDelta);
8511
8512     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8513     gcWheelDelta -= wheelDelta;
8514
8515     scrollInfo.cbSize = sizeof(SCROLLINFO);
8516     scrollInfo.fMask = SIF_POS;
8517
8518     switch(uView)
8519     {
8520     case LVS_ICON:
8521     case LVS_SMALLICON:
8522        /*
8523         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8524         *  should be fixed in the future.
8525         */
8526         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8527                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8528         break;
8529
8530     case LVS_REPORT:
8531         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8532         {
8533             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8534             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8535             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8536         }
8537         break;
8538
8539     case LVS_LIST:
8540         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8541         break;
8542     }
8543     return 0;
8544 }
8545
8546 /***
8547  * DESCRIPTION:
8548  * ???
8549  *
8550  * PARAMETER(S):
8551  * [I] infoPtr : valid pointer to the listview structure
8552  * [I] nVirtualKey : virtual key
8553  * [I] lKeyData : key data
8554  *
8555  * RETURN:
8556  * Zero
8557  */
8558 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8559 {
8560   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8561   HWND hwndSelf = infoPtr->hwndSelf;
8562   INT nItem = -1;
8563   NMLVKEYDOWN nmKeyDown;
8564
8565   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8566
8567   /* send LVN_KEYDOWN notification */
8568   nmKeyDown.wVKey = nVirtualKey;
8569   nmKeyDown.flags = 0;
8570   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8571   if (!IsWindow(hwndSelf))
8572     return 0;
8573
8574   switch (nVirtualKey)
8575   {
8576   case VK_SPACE:
8577     nItem = infoPtr->nFocusedItem;
8578     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8579         toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8580     break;
8581
8582   case VK_RETURN:
8583     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8584     {
8585         if (!notify(infoPtr, NM_RETURN)) return 0;
8586         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8587     }
8588     break;
8589
8590   case VK_HOME:
8591     if (infoPtr->nItemCount > 0)
8592       nItem = 0;
8593     break;
8594
8595   case VK_END:
8596     if (infoPtr->nItemCount > 0)
8597       nItem = infoPtr->nItemCount - 1;
8598     break;
8599
8600   case VK_LEFT:
8601     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8602     break;
8603
8604   case VK_UP:
8605     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8606     break;
8607
8608   case VK_RIGHT:
8609     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8610     break;
8611
8612   case VK_DOWN:
8613     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8614     break;
8615
8616   case VK_PRIOR:
8617     if (uView == LVS_REPORT)
8618     {
8619       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8620       if (infoPtr->nFocusedItem == topidx)
8621         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8622       else
8623         nItem = topidx;
8624     }
8625     else
8626       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8627                                     * LISTVIEW_GetCountPerRow(infoPtr);
8628     if(nItem < 0) nItem = 0;
8629     break;
8630
8631   case VK_NEXT:
8632     if (uView == LVS_REPORT)
8633     {
8634       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8635       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8636       if (infoPtr->nFocusedItem == topidx + cnt - 1)
8637         nItem = infoPtr->nFocusedItem + cnt - 1;
8638       else
8639         nItem = topidx + cnt - 1;
8640     }
8641     else
8642       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8643                                     * LISTVIEW_GetCountPerRow(infoPtr);
8644     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8645     break;
8646   }
8647
8648   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8649       LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8650
8651   return 0;
8652 }
8653
8654 /***
8655  * DESCRIPTION:
8656  * Kills the focus.
8657  *
8658  * PARAMETER(S):
8659  * [I] infoPtr : valid pointer to the listview structure
8660  *
8661  * RETURN:
8662  * Zero
8663  */
8664 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8665 {
8666     TRACE("()\n");
8667
8668     /* if we did not have the focus, there's nothing to do */
8669     if (!infoPtr->bFocus) return 0;
8670    
8671     /* send NM_KILLFOCUS notification */
8672     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8673
8674     /* if we have a focus rectangle, get rid of it */
8675     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8676     
8677     /* set window focus flag */
8678     infoPtr->bFocus = FALSE;
8679
8680     /* invalidate the selected items before resetting focus flag */
8681     LISTVIEW_InvalidateSelectedItems(infoPtr);
8682     
8683     return 0;
8684 }
8685
8686 /***
8687  * DESCRIPTION:
8688  * Processes double click messages (left mouse button).
8689  *
8690  * PARAMETER(S):
8691  * [I] infoPtr : valid pointer to the listview structure
8692  * [I] wKey : key flag
8693  * [I] x,y : mouse coordinate
8694  *
8695  * RETURN:
8696  * Zero
8697  */
8698 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8699 {
8700     LVHITTESTINFO htInfo;
8701
8702     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8703     
8704     /* Cancel the item edition if any */
8705     if (infoPtr->itemEdit.fEnabled)
8706     {
8707       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8708       infoPtr->itemEdit.fEnabled = FALSE;
8709     }
8710
8711     /* send NM_RELEASEDCAPTURE notification */
8712     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8713
8714     htInfo.pt.x = x;
8715     htInfo.pt.y = y;
8716
8717     /* send NM_DBLCLK notification */
8718     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8719     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8720
8721     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8722     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8723
8724     return 0;
8725 }
8726
8727 /***
8728  * DESCRIPTION:
8729  * Processes mouse down messages (left mouse button).
8730  *
8731  * PARAMETERS:
8732  *   infoPtr  [I ] valid pointer to the listview structure
8733  *   wKey     [I ] key flag
8734  *   x,y      [I ] mouse coordinate
8735  *
8736  * RETURN:
8737  *   Zero
8738  */
8739 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8740 {
8741   LVHITTESTINFO lvHitTestInfo;
8742   static BOOL bGroupSelect = TRUE;
8743   POINT pt = { x, y };
8744   INT nItem;
8745
8746   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8747
8748   /* send NM_RELEASEDCAPTURE notification */
8749   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8750
8751   /* set left button down flag and record the click position */
8752   infoPtr->bLButtonDown = TRUE;
8753   infoPtr->ptClickPos = pt;
8754   infoPtr->bDragging = FALSE;
8755
8756   lvHitTestInfo.pt.x = x;
8757   lvHitTestInfo.pt.y = y;
8758
8759   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8760   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8761   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8762   {
8763     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8764     {
8765         toggle_checkbox_state(infoPtr, nItem);
8766         return 0;
8767     }
8768
8769     if (infoPtr->dwStyle & LVS_SINGLESEL)
8770     {
8771       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8772         infoPtr->nEditLabelItem = nItem;
8773       else
8774         LISTVIEW_SetSelection(infoPtr, nItem);
8775     }
8776     else
8777     {
8778       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8779       {
8780         if (bGroupSelect)
8781         {
8782           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8783           LISTVIEW_SetItemFocus(infoPtr, nItem);
8784           infoPtr->nSelectionMark = nItem;
8785         }
8786         else
8787         {
8788           LVITEMW item;
8789
8790           item.state = LVIS_SELECTED | LVIS_FOCUSED;
8791           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8792
8793           LISTVIEW_SetItemState(infoPtr,nItem,&item);
8794           infoPtr->nSelectionMark = nItem;
8795         }
8796       }
8797       else if (wKey & MK_CONTROL)
8798       {
8799         LVITEMW item;
8800
8801         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8802         
8803         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8804         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8805         LISTVIEW_SetItemState(infoPtr, nItem, &item);
8806         infoPtr->nSelectionMark = nItem;
8807       }
8808       else  if (wKey & MK_SHIFT)
8809       {
8810         LISTVIEW_SetGroupSelection(infoPtr, nItem);
8811       }
8812       else
8813       {
8814         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8815           infoPtr->nEditLabelItem = nItem;
8816
8817         /* set selection (clears other pre-existing selections) */
8818         LISTVIEW_SetSelection(infoPtr, nItem);
8819       }
8820     }
8821
8822     if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8823         if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8824   }
8825   else
8826   {
8827     /* remove all selections */
8828     LISTVIEW_DeselectAll(infoPtr);
8829     ReleaseCapture();
8830   }
8831   
8832   return 0;
8833 }
8834
8835 /***
8836  * DESCRIPTION:
8837  * Processes mouse up messages (left mouse button).
8838  *
8839  * PARAMETERS:
8840  *   infoPtr [I ] valid pointer to the listview structure
8841  *   wKey    [I ] key flag
8842  *   x,y     [I ] mouse coordinate
8843  *
8844  * RETURN:
8845  *   Zero
8846  */
8847 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8848 {
8849     LVHITTESTINFO lvHitTestInfo;
8850     
8851     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8852
8853     if (!infoPtr->bLButtonDown) return 0;
8854
8855     lvHitTestInfo.pt.x = x;
8856     lvHitTestInfo.pt.y = y;
8857
8858     /* send NM_CLICK notification */
8859     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8860     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8861
8862     /* set left button flag */
8863     infoPtr->bLButtonDown = FALSE;
8864
8865     if (infoPtr->bDragging)
8866     {
8867         infoPtr->bDragging = FALSE;
8868         return 0;
8869     }
8870
8871     /* if we clicked on a selected item, edit the label */
8872     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8873     {
8874         /* we want to make sure the user doesn't want to do a double click. So we will
8875          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8876          */
8877         infoPtr->itemEdit.fEnabled = TRUE;
8878         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8879         SetTimer(infoPtr->hwndSelf,
8880             (UINT_PTR)&infoPtr->itemEdit,
8881             GetDoubleClickTime(),
8882             LISTVIEW_DelayedEditItem);
8883     }
8884
8885     if (!infoPtr->bFocus)
8886         SetFocus(infoPtr->hwndSelf);
8887
8888     return 0;
8889 }
8890
8891 /***
8892  * DESCRIPTION:
8893  * Destroys the listview control (called after WM_DESTROY).
8894  *
8895  * PARAMETER(S):
8896  * [I] infoPtr : valid pointer to the listview structure
8897  *
8898  * RETURN:
8899  * Zero
8900  */
8901 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8902 {
8903   TRACE("()\n");
8904
8905   /* delete all items */
8906   LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8907
8908   /* destroy data structure */
8909   DPA_Destroy(infoPtr->hdpaItems);
8910   DPA_Destroy(infoPtr->hdpaPosX);
8911   DPA_Destroy(infoPtr->hdpaPosY);
8912   DPA_Destroy(infoPtr->hdpaColumns);
8913   ranges_destroy(infoPtr->selectionRanges);
8914
8915   /* destroy image lists */
8916   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8917   {
8918       if (infoPtr->himlNormal)
8919           ImageList_Destroy(infoPtr->himlNormal);
8920       if (infoPtr->himlSmall)
8921           ImageList_Destroy(infoPtr->himlSmall);
8922       if (infoPtr->himlState)
8923           ImageList_Destroy(infoPtr->himlState);
8924   }
8925
8926   /* destroy font, bkgnd brush */
8927   infoPtr->hFont = 0;
8928   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8929   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8930
8931   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8932
8933   /* free listview info pointer*/
8934   Free(infoPtr);
8935
8936   return 0;
8937 }
8938
8939 /***
8940  * DESCRIPTION:
8941  * Handles notifications from header.
8942  *
8943  * PARAMETER(S):
8944  * [I] infoPtr : valid pointer to the listview structure
8945  * [I] nCtrlId : control identifier
8946  * [I] lpnmh : notification information
8947  *
8948  * RETURN:
8949  * Zero
8950  */
8951 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8952 {
8953     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8954     HWND hwndSelf = infoPtr->hwndSelf;
8955     
8956     TRACE("(lpnmh=%p)\n", lpnmh);
8957
8958     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8959     
8960     switch (lpnmh->hdr.code)
8961     {    
8962         case HDN_TRACKW:
8963         case HDN_TRACKA:
8964         {
8965             COLUMN_INFO *lpColumnInfo;
8966             POINT ptOrigin;
8967             INT x;
8968             
8969             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8970                 break;
8971
8972             /* remove the old line (if any) */
8973             LISTVIEW_DrawTrackLine(infoPtr);
8974             
8975             /* compute & draw the new line */
8976             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8977             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8978             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8979             infoPtr->xTrackLine = x + ptOrigin.x;
8980             LISTVIEW_DrawTrackLine(infoPtr);
8981             break;
8982         }
8983         
8984         case HDN_ENDTRACKA:
8985         case HDN_ENDTRACKW:
8986             /* remove the track line (if any) */
8987             LISTVIEW_DrawTrackLine(infoPtr);
8988             infoPtr->xTrackLine = -1;
8989             break;
8990             
8991         case HDN_ENDDRAG:
8992             FIXME("Changing column order not implemented\n");
8993             return TRUE;
8994             
8995         case HDN_ITEMCHANGINGW:
8996         case HDN_ITEMCHANGINGA:
8997             return notify_forward_header(infoPtr, lpnmh);
8998             
8999         case HDN_ITEMCHANGEDW:
9000         case HDN_ITEMCHANGEDA:
9001         {
9002             COLUMN_INFO *lpColumnInfo;
9003             INT dx, cxy;
9004             
9005             notify_forward_header(infoPtr, lpnmh);
9006             if (!IsWindow(hwndSelf))
9007                 break;
9008
9009             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9010             {
9011                 HDITEMW hdi;
9012     
9013                 hdi.mask = HDI_WIDTH;
9014                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
9015                 cxy = hdi.cxy;
9016             }
9017             else
9018                 cxy = lpnmh->pitem->cxy;
9019             
9020             /* determine how much we change since the last know position */
9021             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9022             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9023             if (dx != 0)
9024             {
9025                 lpColumnInfo->rcHeader.right += dx;
9026                 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9027                     LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9028                 else
9029                 {
9030                     /* only needs to update the scrolls */
9031                     infoPtr->nItemWidth += dx;
9032                     LISTVIEW_UpdateScroll(infoPtr);
9033                 }
9034                 LISTVIEW_UpdateItemSize(infoPtr);
9035                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
9036                 {
9037                     POINT ptOrigin;
9038                     RECT rcCol = lpColumnInfo->rcHeader;
9039                     
9040                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9041                     OffsetRect(&rcCol, ptOrigin.x, 0);
9042                     
9043                     rcCol.top = infoPtr->rcList.top;
9044                     rcCol.bottom = infoPtr->rcList.bottom;
9045
9046                     /* resizing left-aligned columns leaves most of the left side untouched */
9047                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9048                     {
9049                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9050                         if (dx > 0)
9051                             nMaxDirty += dx;
9052                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9053                     }
9054
9055                     /* when shrinking the last column clear the now unused field */
9056                     if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
9057                         rcCol.right -= dx;
9058
9059                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9060                 }
9061             }
9062         }
9063         break;
9064
9065         case HDN_ITEMCLICKW:
9066         case HDN_ITEMCLICKA:
9067         {
9068             /* Handle sorting by Header Column */
9069             NMLISTVIEW nmlv;
9070
9071             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9072             nmlv.iItem = -1;
9073             nmlv.iSubItem = lpnmh->iItem;
9074             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9075             notify_forward_header(infoPtr, lpnmh);
9076         }
9077         break;
9078
9079         case HDN_DIVIDERDBLCLICKW:
9080         case HDN_DIVIDERDBLCLICKA:
9081             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9082             break;
9083     }
9084
9085     return 0;
9086 }
9087
9088 /***
9089  * DESCRIPTION:
9090  * Paint non-client area of control.
9091  *
9092  * PARAMETER(S):
9093  * [I] infoPtr : valid pointer to the listview structureof the sender
9094  * [I] region : update region
9095  *
9096  * RETURN:
9097  *  TRUE  - frame was painted
9098  *  FALSE - call default window proc
9099  */
9100 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9101 {
9102     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9103     HDC dc;
9104     RECT r;
9105     HRGN cliprgn;
9106     int cxEdge = GetSystemMetrics (SM_CXEDGE),
9107         cyEdge = GetSystemMetrics (SM_CYEDGE);
9108
9109     if (!theme) return FALSE;
9110
9111     GetWindowRect(infoPtr->hwndSelf, &r);
9112
9113     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9114         r.right - cxEdge, r.bottom - cyEdge);
9115     if (region != (HRGN)1)
9116         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9117     OffsetRect(&r, -r.left, -r.top);
9118
9119     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9120     OffsetRect(&r, -r.left, -r.top);
9121
9122     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9123         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9124     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9125     ReleaseDC(infoPtr->hwndSelf, dc);
9126
9127     /* Call default proc to get the scrollbars etc. painted */
9128     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9129
9130     return TRUE;
9131 }
9132
9133 /***
9134  * DESCRIPTION:
9135  * Determines the type of structure to use.
9136  *
9137  * PARAMETER(S):
9138  * [I] infoPtr : valid pointer to the listview structureof the sender
9139  * [I] hwndFrom : listview window handle
9140  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9141  *
9142  * RETURN:
9143  * Zero
9144  */
9145 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9146 {
9147     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9148
9149     if (nCommand == NF_REQUERY)
9150         infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9151
9152     return infoPtr->notifyFormat;
9153 }
9154
9155 /***
9156  * DESCRIPTION:
9157  * Paints/Repaints the listview control.
9158  *
9159  * PARAMETER(S):
9160  * [I] infoPtr : valid pointer to the listview structure
9161  * [I] hdc : device context handle
9162  *
9163  * RETURN:
9164  * Zero
9165  */
9166 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9167 {
9168     TRACE("(hdc=%p)\n", hdc);
9169
9170     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9171     {
9172         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
9173         
9174         infoPtr->bNoItemMetrics = FALSE;
9175         LISTVIEW_UpdateItemSize(infoPtr);
9176         if (uView == LVS_ICON || uView == LVS_SMALLICON)
9177             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9178         LISTVIEW_UpdateScroll(infoPtr);
9179     }
9180
9181     if (infoPtr->hwndHeader)  UpdateWindow(infoPtr->hwndHeader);
9182
9183     if (hdc) 
9184         LISTVIEW_Refresh(infoPtr, hdc, NULL);
9185     else
9186     {
9187         PAINTSTRUCT ps;
9188
9189         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9190         if (!hdc) return 1;
9191         LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9192         EndPaint(infoPtr->hwndSelf, &ps);
9193     }
9194
9195     return 0;
9196 }
9197
9198
9199 /***
9200  * DESCRIPTION:
9201  * Paints/Repaints the listview control.
9202  *
9203  * PARAMETER(S):
9204  * [I] infoPtr : valid pointer to the listview structure
9205  * [I] hdc : device context handle
9206  * [I] options : drawing options
9207  *
9208  * RETURN:
9209  * Zero
9210  */
9211 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9212 {
9213     FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9214
9215     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9216         return 0;
9217
9218     if (options & PRF_ERASEBKGND)
9219         LISTVIEW_EraseBkgnd(infoPtr, hdc);
9220
9221     if (options & PRF_CLIENT)
9222         LISTVIEW_Paint(infoPtr, hdc);
9223
9224     return 0;
9225 }
9226
9227
9228 /***
9229  * DESCRIPTION:
9230  * Processes double click messages (right mouse button).
9231  *
9232  * PARAMETER(S):
9233  * [I] infoPtr : valid pointer to the listview structure
9234  * [I] wKey : key flag
9235  * [I] x,y : mouse coordinate
9236  *
9237  * RETURN:
9238  * Zero
9239  */
9240 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9241 {
9242     LVHITTESTINFO lvHitTestInfo;
9243     
9244     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9245
9246     /* send NM_RELEASEDCAPTURE notification */
9247     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9248
9249     /* send NM_RDBLCLK notification */
9250     lvHitTestInfo.pt.x = x;
9251     lvHitTestInfo.pt.y = y;
9252     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9253     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9254
9255     return 0;
9256 }
9257
9258 /***
9259  * DESCRIPTION:
9260  * Processes mouse down messages (right mouse button).
9261  *
9262  * PARAMETER(S):
9263  * [I] infoPtr : valid pointer to the listview structure
9264  * [I] wKey : key flag
9265  * [I] x,y : mouse coordinate
9266  *
9267  * RETURN:
9268  * Zero
9269  */
9270 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9271 {
9272     LVHITTESTINFO lvHitTestInfo;
9273     INT nItem;
9274
9275     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9276
9277     /* send NM_RELEASEDCAPTURE notification */
9278     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9279
9280     /* make sure the listview control window has the focus */
9281     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9282
9283     /* set right button down flag */
9284     infoPtr->bRButtonDown = TRUE;
9285
9286     /* determine the index of the selected item */
9287     lvHitTestInfo.pt.x = x;
9288     lvHitTestInfo.pt.y = y;
9289     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9290   
9291     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9292     {
9293         LISTVIEW_SetItemFocus(infoPtr, nItem);
9294         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9295             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9296             LISTVIEW_SetSelection(infoPtr, nItem);
9297     }
9298     else
9299     {
9300         LISTVIEW_DeselectAll(infoPtr);
9301     }
9302
9303     return 0;
9304 }
9305
9306 /***
9307  * DESCRIPTION:
9308  * Processes mouse up messages (right mouse button).
9309  *
9310  * PARAMETER(S):
9311  * [I] infoPtr : valid pointer to the listview structure
9312  * [I] wKey : key flag
9313  * [I] x,y : mouse coordinate
9314  *
9315  * RETURN:
9316  * Zero
9317  */
9318 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9319 {
9320     LVHITTESTINFO lvHitTestInfo;
9321     POINT pt;
9322
9323     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9324
9325     if (!infoPtr->bRButtonDown) return 0;
9326  
9327     /* set button flag */
9328     infoPtr->bRButtonDown = FALSE;
9329
9330     /* Send NM_RCLICK notification */
9331     lvHitTestInfo.pt.x = x;
9332     lvHitTestInfo.pt.y = y;
9333     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9334     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9335
9336     /* Change to screen coordinate for WM_CONTEXTMENU */
9337     pt = lvHitTestInfo.pt;
9338     ClientToScreen(infoPtr->hwndSelf, &pt);
9339
9340     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9341     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9342                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9343
9344     return 0;
9345 }
9346
9347
9348 /***
9349  * DESCRIPTION:
9350  * Sets the cursor.
9351  *
9352  * PARAMETER(S):
9353  * [I] infoPtr : valid pointer to the listview structure
9354  * [I] hwnd : window handle of window containing the cursor
9355  * [I] nHittest : hit-test code
9356  * [I] wMouseMsg : ideintifier of the mouse message
9357  *
9358  * RETURN:
9359  * TRUE if cursor is set
9360  * FALSE otherwise
9361  */
9362 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9363 {
9364     LVHITTESTINFO lvHitTestInfo;
9365
9366     if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9367
9368     if(!infoPtr->hHotCursor)  return FALSE;
9369
9370     GetCursorPos(&lvHitTestInfo.pt);
9371     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9372
9373     SetCursor(infoPtr->hHotCursor);
9374
9375     return TRUE;
9376 }
9377
9378 /***
9379  * DESCRIPTION:
9380  * Sets the focus.
9381  *
9382  * PARAMETER(S):
9383  * [I] infoPtr : valid pointer to the listview structure
9384  * [I] hwndLoseFocus : handle of previously focused window
9385  *
9386  * RETURN:
9387  * Zero
9388  */
9389 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9390 {
9391     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9392
9393     /* if we have the focus already, there's nothing to do */
9394     if (infoPtr->bFocus) return 0;
9395    
9396     /* send NM_SETFOCUS notification */
9397     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9398
9399     /* set window focus flag */
9400     infoPtr->bFocus = TRUE;
9401
9402     /* put the focus rect back on */
9403     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9404
9405     /* redraw all visible selected items */
9406     LISTVIEW_InvalidateSelectedItems(infoPtr);
9407
9408     return 0;
9409 }
9410
9411 /***
9412  * DESCRIPTION:
9413  * Sets the font.
9414  *
9415  * PARAMETER(S):
9416  * [I] infoPtr : valid pointer to the listview structure
9417  * [I] fRedraw : font handle
9418  * [I] fRedraw : redraw flag
9419  *
9420  * RETURN:
9421  * Zero
9422  */
9423 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9424 {
9425     HFONT oldFont = infoPtr->hFont;
9426
9427     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9428
9429     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9430     if (infoPtr->hFont == oldFont) return 0;
9431     
9432     LISTVIEW_SaveTextMetrics(infoPtr);
9433
9434     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9435     {
9436         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9437         LISTVIEW_UpdateSize(infoPtr);
9438         LISTVIEW_UpdateScroll(infoPtr);
9439     }
9440
9441     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9442
9443     return 0;
9444 }
9445
9446 /***
9447  * DESCRIPTION:
9448  * Message handling for WM_SETREDRAW.
9449  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9450  *
9451  * PARAMETER(S):
9452  * [I] infoPtr : valid pointer to the listview structure
9453  * [I] bRedraw: state of redraw flag
9454  *
9455  * RETURN:
9456  * DefWinProc return value
9457  */
9458 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9459 {
9460     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9461
9462     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9463     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9464
9465     infoPtr->bRedraw = bRedraw;
9466
9467     if(!bRedraw) return 0;
9468     
9469     if (is_autoarrange(infoPtr))
9470         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9471     LISTVIEW_UpdateScroll(infoPtr);
9472
9473     /* despite what the WM_SETREDRAW docs says, apps expect us
9474      * to invalidate the listview here... stupid! */
9475     LISTVIEW_InvalidateList(infoPtr);
9476
9477     return 0;
9478 }
9479
9480 /***
9481  * DESCRIPTION:
9482  * Resizes the listview control. This function processes WM_SIZE
9483  * messages.  At this time, the width and height are not used.
9484  *
9485  * PARAMETER(S):
9486  * [I] infoPtr : valid pointer to the listview structure
9487  * [I] Width : new width
9488  * [I] Height : new height
9489  *
9490  * RETURN:
9491  * Zero
9492  */
9493 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9494 {
9495     RECT rcOld = infoPtr->rcList;
9496
9497     TRACE("(width=%d, height=%d)\n", Width, Height);
9498
9499     LISTVIEW_UpdateSize(infoPtr);
9500     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9501   
9502     /* do not bother with display related stuff if we're not redrawing */ 
9503     if (!is_redrawing(infoPtr)) return 0;
9504     
9505     if (is_autoarrange(infoPtr)) 
9506         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9507
9508     LISTVIEW_UpdateScroll(infoPtr);
9509
9510     /* refresh all only for lists whose height changed significantly */
9511     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
9512         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9513         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9514         LISTVIEW_InvalidateList(infoPtr);
9515
9516   return 0;
9517 }
9518
9519 /***
9520  * DESCRIPTION:
9521  * Sets the size information.
9522  *
9523  * PARAMETER(S):
9524  * [I] infoPtr : valid pointer to the listview structure
9525  *
9526  * RETURN:
9527  *  None
9528  */
9529 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9530 {
9531     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9532
9533     TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9534     
9535     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9536
9537     if (uView == LVS_LIST)
9538     {
9539         /* Apparently the "LIST" style is supposed to have the same
9540          * number of items in a column even if there is no scroll bar.
9541          * Since if a scroll bar already exists then the bottom is already
9542          * reduced, only reduce if the scroll bar does not currently exist.
9543          * The "2" is there to mimic the native control. I think it may be
9544          * related to either padding or edges.  (GLA 7/2002)
9545          */
9546         if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9547             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9548         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9549     }
9550     else if (uView == LVS_REPORT)
9551     {
9552         HDLAYOUT hl;
9553         WINDOWPOS wp;
9554
9555         hl.prc = &infoPtr->rcList;
9556         hl.pwpos = &wp;
9557         SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9558         TRACE("  wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9559         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9560                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9561                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9562         TRACE("  after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9563
9564         infoPtr->rcList.top = max(wp.cy, 0);
9565         infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9566     }
9567
9568     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9569 }
9570
9571 /***
9572  * DESCRIPTION:
9573  * Processes WM_STYLECHANGED messages.
9574  *
9575  * PARAMETER(S):
9576  * [I] infoPtr : valid pointer to the listview structure
9577  * [I] wStyleType : window style type (normal or extended)
9578  * [I] lpss : window style information
9579  *
9580  * RETURN:
9581  * Zero
9582  */
9583 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9584                                  const STYLESTRUCT *lpss)
9585 {
9586     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9587     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9588     UINT style;
9589
9590     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9591           wStyleType, lpss->styleOld, lpss->styleNew);
9592
9593     if (wStyleType != GWL_STYLE) return 0;
9594   
9595     /* FIXME: if LVS_NOSORTHEADER changed, update header */
9596     /*        or LVS_SORT{AS,DES}CENDING */
9597
9598     infoPtr->dwStyle = lpss->styleNew;
9599
9600     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9601         ((lpss->styleNew & WS_HSCROLL) == 0))
9602        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9603
9604     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9605         ((lpss->styleNew & WS_VSCROLL) == 0))
9606        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9607
9608     if (uNewView != uOldView)
9609     {
9610         SIZE oldIconSize = infoPtr->iconSize;
9611         HIMAGELIST himl;
9612     
9613         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9614         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9615
9616         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9617         SetRectEmpty(&infoPtr->rcFocus);
9618
9619         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9620         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9621     
9622         if (uNewView == LVS_ICON)
9623         {
9624             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9625             {
9626                 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9627                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9628                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9629             }
9630         }
9631         else if (uNewView == LVS_REPORT)
9632         {
9633             HDLAYOUT hl;
9634             WINDOWPOS wp;
9635
9636             LISTVIEW_CreateHeader( infoPtr );
9637
9638             hl.prc = &infoPtr->rcList;
9639             hl.pwpos = &wp;
9640             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9641             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9642                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9643                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9644         }
9645
9646         LISTVIEW_UpdateItemSize(infoPtr);
9647     }
9648
9649     if (uNewView == LVS_REPORT)
9650     {
9651         if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9652         {
9653             if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9654             {
9655                 /* Turn off the header control */
9656                 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9657                 TRACE("Hide header control, was 0x%08x\n", style);
9658                 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9659             } else {
9660                 /* Turn on the header control */
9661                 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9662                 {
9663                     TRACE("Show header control, was 0x%08x\n", style);
9664                     SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9665                 }
9666             }
9667         }
9668     }
9669
9670     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9671          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9672          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9673
9674     /* update the size of the client area */
9675     LISTVIEW_UpdateSize(infoPtr);
9676
9677     /* add scrollbars if needed */
9678     LISTVIEW_UpdateScroll(infoPtr);
9679
9680     /* invalidate client area + erase background */
9681     LISTVIEW_InvalidateList(infoPtr);
9682
9683     return 0;
9684 }
9685
9686 /***
9687  * DESCRIPTION:
9688  * Processes WM_STYLECHANGING messages.
9689  *
9690  * PARAMETER(S):
9691  * [I] infoPtr : valid pointer to the listview structure
9692  * [I] wStyleType : window style type (normal or extended)
9693  * [I0] lpss : window style information
9694  *
9695  * RETURN:
9696  * Zero
9697  */
9698 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9699                                   STYLESTRUCT *lpss)
9700 {
9701     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9702           wStyleType, lpss->styleOld, lpss->styleNew);
9703
9704     /* don't forward LVS_OWNERDATA only if not already set to */
9705     if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
9706     {
9707         if (lpss->styleOld & LVS_OWNERDATA)
9708             lpss->styleNew |= LVS_OWNERDATA;
9709         else
9710             lpss->styleNew &= ~LVS_OWNERDATA;
9711     }
9712
9713     return 0;
9714 }
9715
9716 /***
9717  * DESCRIPTION:
9718  * Processes WM_SHOWWINDOW messages.
9719  *
9720  * PARAMETER(S):
9721  * [I] infoPtr : valid pointer to the listview structure
9722  * [I] bShown  : window is being shown (FALSE when hidden)
9723  * [I] iStatus : window show status
9724  *
9725  * RETURN:
9726  * Zero
9727  */
9728 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9729 {
9730   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9731
9732   /* header delayed creation */
9733   if ((uView == LVS_REPORT) && bShown)
9734   {
9735     LISTVIEW_CreateHeader(infoPtr);
9736
9737     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9738       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9739   }
9740
9741   return 0;
9742 }
9743
9744 /***
9745  * DESCRIPTION:
9746  * Window procedure of the listview control.
9747  *
9748  */
9749 static LRESULT WINAPI
9750 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9751 {
9752   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9753
9754   TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9755
9756   if (!infoPtr && (uMsg != WM_NCCREATE))
9757     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9758
9759   switch (uMsg)
9760   {
9761   case LVM_APPROXIMATEVIEWRECT:
9762     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9763                                         LOWORD(lParam), HIWORD(lParam));
9764   case LVM_ARRANGE:
9765     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9766
9767 /* case LVM_CANCELEDITLABEL: */
9768
9769   case LVM_CREATEDRAGIMAGE:
9770     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9771
9772   case LVM_DELETEALLITEMS:
9773     return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9774
9775   case LVM_DELETECOLUMN:
9776     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9777
9778   case LVM_DELETEITEM:
9779     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9780
9781   case LVM_EDITLABELW:
9782     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9783
9784   case LVM_EDITLABELA:
9785     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9786
9787   /* case LVM_ENABLEGROUPVIEW: */
9788
9789   case LVM_ENSUREVISIBLE:
9790     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9791
9792   case LVM_FINDITEMW:
9793     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9794
9795   case LVM_FINDITEMA:
9796     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9797
9798   case LVM_GETBKCOLOR:
9799     return infoPtr->clrBk;
9800
9801   /* case LVM_GETBKIMAGE: */
9802
9803   case LVM_GETCALLBACKMASK:
9804     return infoPtr->uCallbackMask;
9805
9806   case LVM_GETCOLUMNA:
9807     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9808
9809   case LVM_GETCOLUMNW:
9810     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9811
9812   case LVM_GETCOLUMNORDERARRAY:
9813     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9814
9815   case LVM_GETCOLUMNWIDTH:
9816     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9817
9818   case LVM_GETCOUNTPERPAGE:
9819     return LISTVIEW_GetCountPerPage(infoPtr);
9820
9821   case LVM_GETEDITCONTROL:
9822     return (LRESULT)infoPtr->hwndEdit;
9823
9824   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9825     return infoPtr->dwLvExStyle;
9826
9827   /* case LVM_GETGROUPINFO: */
9828
9829   /* case LVM_GETGROUPMETRICS: */
9830
9831   case LVM_GETHEADER:
9832     return (LRESULT)infoPtr->hwndHeader;
9833
9834   case LVM_GETHOTCURSOR:
9835     return (LRESULT)infoPtr->hHotCursor;
9836
9837   case LVM_GETHOTITEM:
9838     return infoPtr->nHotItem;
9839
9840   case LVM_GETHOVERTIME:
9841     return infoPtr->dwHoverTime;
9842
9843   case LVM_GETIMAGELIST:
9844     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9845
9846   /* case LVM_GETINSERTMARK: */
9847
9848   /* case LVM_GETINSERTMARKCOLOR: */
9849
9850   /* case LVM_GETINSERTMARKRECT: */
9851
9852   case LVM_GETISEARCHSTRINGA:
9853   case LVM_GETISEARCHSTRINGW:
9854     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9855     return FALSE;
9856
9857   case LVM_GETITEMA:
9858     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9859
9860   case LVM_GETITEMW:
9861     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9862
9863   case LVM_GETITEMCOUNT:
9864     return infoPtr->nItemCount;
9865
9866   case LVM_GETITEMPOSITION:
9867     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9868
9869   case LVM_GETITEMRECT:
9870     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9871
9872   case LVM_GETITEMSPACING:
9873     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9874
9875   case LVM_GETITEMSTATE:
9876     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9877
9878   case LVM_GETITEMTEXTA:
9879     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9880
9881   case LVM_GETITEMTEXTW:
9882     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9883
9884   case LVM_GETNEXTITEM:
9885     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9886
9887   case LVM_GETNUMBEROFWORKAREAS:
9888     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9889     return 1;
9890
9891   case LVM_GETORIGIN:
9892     if (!lParam) return FALSE;
9893     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9894         (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9895     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9896     return TRUE;
9897
9898   /* case LVM_GETOUTLINECOLOR: */
9899
9900   /* case LVM_GETSELECTEDCOLUMN: */
9901
9902   case LVM_GETSELECTEDCOUNT:
9903     return LISTVIEW_GetSelectedCount(infoPtr);
9904
9905   case LVM_GETSELECTIONMARK:
9906     return infoPtr->nSelectionMark;
9907
9908   case LVM_GETSTRINGWIDTHA:
9909     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9910
9911   case LVM_GETSTRINGWIDTHW:
9912     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9913
9914   case LVM_GETSUBITEMRECT:
9915     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9916
9917   case LVM_GETTEXTBKCOLOR:
9918     return infoPtr->clrTextBk;
9919
9920   case LVM_GETTEXTCOLOR:
9921     return infoPtr->clrText;
9922
9923   /* case LVM_GETTILEINFO: */
9924
9925   /* case LVM_GETTILEVIEWINFO: */
9926
9927   case LVM_GETTOOLTIPS:
9928     if( !infoPtr->hwndToolTip )
9929         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9930     return (LRESULT)infoPtr->hwndToolTip;
9931
9932   case LVM_GETTOPINDEX:
9933     return LISTVIEW_GetTopIndex(infoPtr);
9934
9935   case LVM_GETUNICODEFORMAT:
9936     return (infoPtr->notifyFormat == NFR_UNICODE);
9937
9938   /* case LVM_GETVIEW: */
9939
9940   case LVM_GETVIEWRECT:
9941     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9942
9943   case LVM_GETWORKAREAS:
9944     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9945     return FALSE;
9946
9947   /* case LVM_HASGROUP: */
9948
9949   case LVM_HITTEST:
9950     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9951
9952   case LVM_INSERTCOLUMNA:
9953     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9954
9955   case LVM_INSERTCOLUMNW:
9956     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9957
9958   /* case LVM_INSERTGROUP: */
9959
9960   /* case LVM_INSERTGROUPSORTED: */
9961
9962   case LVM_INSERTITEMA:
9963     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9964
9965   case LVM_INSERTITEMW:
9966     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9967
9968   /* case LVM_INSERTMARKHITTEST: */
9969
9970   /* case LVM_ISGROUPVIEWENABLED: */
9971
9972   /* case LVM_MAPIDTOINDEX: */
9973
9974   /* case LVM_MAPINDEXTOID: */
9975
9976   /* case LVM_MOVEGROUP: */
9977
9978   /* case LVM_MOVEITEMTOGROUP: */
9979
9980   case LVM_REDRAWITEMS:
9981     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9982
9983   /* case LVM_REMOVEALLGROUPS: */
9984
9985   /* case LVM_REMOVEGROUP: */
9986
9987   case LVM_SCROLL:
9988     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9989
9990   case LVM_SETBKCOLOR:
9991     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9992
9993   /* case LVM_SETBKIMAGE: */
9994
9995   case LVM_SETCALLBACKMASK:
9996     infoPtr->uCallbackMask = (UINT)wParam;
9997     return TRUE;
9998
9999   case LVM_SETCOLUMNA:
10000     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10001
10002   case LVM_SETCOLUMNW:
10003     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10004
10005   case LVM_SETCOLUMNORDERARRAY:
10006     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10007
10008   case LVM_SETCOLUMNWIDTH:
10009     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10010
10011   case LVM_SETEXTENDEDLISTVIEWSTYLE:
10012     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10013
10014   /* case LVM_SETGROUPINFO: */
10015
10016   /* case LVM_SETGROUPMETRICS: */
10017
10018   case LVM_SETHOTCURSOR:
10019     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10020
10021   case LVM_SETHOTITEM:
10022     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10023
10024   case LVM_SETHOVERTIME:
10025     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10026
10027   case LVM_SETICONSPACING:
10028     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10029
10030   case LVM_SETIMAGELIST:
10031     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10032
10033   /* case LVM_SETINFOTIP: */
10034
10035   /* case LVM_SETINSERTMARK: */
10036
10037   /* case LVM_SETINSERTMARKCOLOR: */
10038
10039   case LVM_SETITEMA:
10040   case LVM_SETITEMW:
10041     {
10042         if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10043         return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10044     }
10045
10046   case LVM_SETITEMCOUNT:
10047     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10048
10049   case LVM_SETITEMPOSITION:
10050     {
10051         POINT pt;
10052         pt.x = (short)LOWORD(lParam);
10053         pt.y = (short)HIWORD(lParam);
10054         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10055     }
10056
10057   case LVM_SETITEMPOSITION32:
10058     if (lParam == 0) return FALSE;
10059     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10060
10061   case LVM_SETITEMSTATE:
10062     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10063
10064   case LVM_SETITEMTEXTA:
10065     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10066
10067   case LVM_SETITEMTEXTW:
10068     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10069
10070   /* case LVM_SETOUTLINECOLOR: */
10071
10072   /* case LVM_SETSELECTEDCOLUMN: */
10073
10074   case LVM_SETSELECTIONMARK:
10075     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10076
10077   case LVM_SETTEXTBKCOLOR:
10078     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10079
10080   case LVM_SETTEXTCOLOR:
10081     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10082
10083   /* case LVM_SETTILEINFO: */
10084
10085   /* case LVM_SETTILEVIEWINFO: */
10086
10087   /* case LVM_SETTILEWIDTH: */
10088
10089   case LVM_SETTOOLTIPS:
10090     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10091
10092   case LVM_SETUNICODEFORMAT:
10093     return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10094
10095   /* case LVM_SETVIEW: */
10096
10097   /* case LVM_SETWORKAREAS: */
10098
10099   /* case LVM_SORTGROUPS: */
10100
10101   case LVM_SORTITEMS:
10102     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10103
10104   case LVM_SORTITEMSEX:
10105     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10106
10107   case LVM_SUBITEMHITTEST:
10108     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10109
10110   case LVM_UPDATE:
10111     return LISTVIEW_Update(infoPtr, (INT)wParam);
10112
10113   case WM_CHAR:
10114     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10115
10116   case WM_COMMAND:
10117     return LISTVIEW_Command(infoPtr, wParam, lParam);
10118
10119   case WM_NCCREATE:
10120     return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10121
10122   case WM_CREATE:
10123     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10124
10125   case WM_DESTROY:
10126     return LISTVIEW_Destroy(infoPtr);
10127
10128   case WM_ENABLE:
10129     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10130
10131   case WM_ERASEBKGND:
10132     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10133
10134   case WM_GETDLGCODE:
10135     return DLGC_WANTCHARS | DLGC_WANTARROWS;
10136
10137   case WM_GETFONT:
10138     return (LRESULT)infoPtr->hFont;
10139
10140   case WM_HSCROLL:
10141     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10142
10143   case WM_KEYDOWN:
10144     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10145
10146   case WM_KILLFOCUS:
10147     return LISTVIEW_KillFocus(infoPtr);
10148
10149   case WM_LBUTTONDBLCLK:
10150     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10151
10152   case WM_LBUTTONDOWN:
10153     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10154
10155   case WM_LBUTTONUP:
10156     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10157
10158   case WM_MOUSEMOVE:
10159     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10160
10161   case WM_MOUSEHOVER:
10162     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10163
10164   case WM_NCDESTROY:
10165     return LISTVIEW_NCDestroy(infoPtr);
10166
10167   case WM_NCPAINT:
10168     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10169         return 0;
10170     goto fwd_msg;
10171
10172   case WM_NOTIFY:
10173     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10174         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10175     else return 0;
10176
10177   case WM_NOTIFYFORMAT:
10178     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10179
10180   case WM_PRINTCLIENT:
10181     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10182
10183   case WM_PAINT:
10184     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10185
10186   case WM_RBUTTONDBLCLK:
10187     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10188
10189   case WM_RBUTTONDOWN:
10190     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10191
10192   case WM_RBUTTONUP:
10193     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10194
10195   case WM_SETCURSOR:
10196     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10197       return TRUE;
10198     goto fwd_msg;
10199
10200   case WM_SETFOCUS:
10201     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10202
10203   case WM_SETFONT:
10204     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10205
10206   case WM_SETREDRAW:
10207     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10208
10209   case WM_SHOWWINDOW:
10210     LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10211     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10212
10213   case WM_SIZE:
10214     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10215
10216   case WM_STYLECHANGED:
10217     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10218
10219   case WM_STYLECHANGING:
10220     return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10221
10222   case WM_SYSCOLORCHANGE:
10223     COMCTL32_RefreshSysColors();
10224     return 0;
10225
10226 /*      case WM_TIMER: */
10227   case WM_THEMECHANGED:
10228     return LISTVIEW_ThemeChanged(infoPtr);
10229
10230   case WM_VSCROLL:
10231     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10232
10233   case WM_MOUSEWHEEL:
10234       if (wParam & (MK_SHIFT | MK_CONTROL))
10235           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10236       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10237
10238   case WM_WINDOWPOSCHANGED:
10239       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
10240       {
10241       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10242           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10243                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10244
10245       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10246       {
10247           MEASUREITEMSTRUCT mis;
10248           mis.CtlType = ODT_LISTVIEW;
10249           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10250           mis.itemID = -1;
10251           mis.itemWidth = 0;
10252           mis.itemData = 0;
10253           mis.itemHeight= infoPtr->nItemHeight;
10254           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10255           if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10256               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10257       }
10258
10259           LISTVIEW_UpdateSize(infoPtr);
10260           LISTVIEW_UpdateScroll(infoPtr);
10261       }
10262       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10263
10264 /*      case WM_WININICHANGE: */
10265
10266   default:
10267     if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10268       ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10269
10270   fwd_msg:
10271     /* call default window procedure */
10272     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10273   }
10274
10275 }
10276
10277 /***
10278  * DESCRIPTION:
10279  * Registers the window class.
10280  *
10281  * PARAMETER(S):
10282  * None
10283  *
10284  * RETURN:
10285  * None
10286  */
10287 void LISTVIEW_Register(void)
10288 {
10289     WNDCLASSW wndClass;
10290
10291     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10292     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10293     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10294     wndClass.cbClsExtra = 0;
10295     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10296     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10297     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10298     wndClass.lpszClassName = WC_LISTVIEWW;
10299     RegisterClassW(&wndClass);
10300 }
10301
10302 /***
10303  * DESCRIPTION:
10304  * Unregisters the window class.
10305  *
10306  * PARAMETER(S):
10307  * None
10308  *
10309  * RETURN:
10310  * None
10311  */
10312 void LISTVIEW_Unregister(void)
10313 {
10314     UnregisterClassW(WC_LISTVIEWW, NULL);
10315 }
10316
10317 /***
10318  * DESCRIPTION:
10319  * Handle any WM_COMMAND messages
10320  *
10321  * PARAMETER(S):
10322  * [I] infoPtr : valid pointer to the listview structure
10323  * [I] wParam : the first message parameter
10324  * [I] lParam : the second message parameter
10325  *
10326  * RETURN:
10327  *   Zero.
10328  */
10329 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10330 {
10331     switch (HIWORD(wParam))
10332     {
10333         case EN_UPDATE:
10334         {
10335             /*
10336              * Adjust the edit window size
10337              */
10338             WCHAR buffer[1024];
10339             HDC           hdc = GetDC(infoPtr->hwndEdit);
10340             HFONT         hFont, hOldFont = 0;
10341             RECT          rect;
10342             SIZE          sz;
10343
10344             if (!infoPtr->hwndEdit || !hdc) return 0;
10345             GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10346             GetWindowRect(infoPtr->hwndEdit, &rect);
10347
10348             /* Select font to get the right dimension of the string */
10349             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10350             if(hFont != 0)
10351             {
10352                 hOldFont = SelectObject(hdc, hFont);
10353             }
10354
10355             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10356             {
10357                 TEXTMETRICW textMetric;
10358
10359                 /* Add Extra spacing for the next character */
10360                 GetTextMetricsW(hdc, &textMetric);
10361                 sz.cx += (textMetric.tmMaxCharWidth * 2);
10362
10363                 SetWindowPos (
10364                     infoPtr->hwndEdit,
10365                     HWND_TOP,
10366                     0,
10367                     0,
10368                     sz.cx,
10369                     rect.bottom - rect.top,
10370                     SWP_DRAWFRAME|SWP_NOMOVE);
10371             }
10372             if(hFont != 0)
10373                 SelectObject(hdc, hOldFont);
10374
10375             ReleaseDC(infoPtr->hwndEdit, hdc);
10376
10377             break;
10378         }
10379
10380         default:
10381           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10382     }
10383
10384     return 0;
10385 }
10386
10387
10388 /***
10389  * DESCRIPTION:
10390  * Subclassed edit control windproc function
10391  *
10392  * PARAMETER(S):
10393  * [I] hwnd : the edit window handle
10394  * [I] uMsg : the message that is to be processed
10395  * [I] wParam : first message parameter
10396  * [I] lParam : second message parameter
10397  * [I] isW : TRUE if input is Unicode
10398  *
10399  * RETURN:
10400  *   Zero.
10401  */
10402 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10403 {
10404     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10405     BOOL cancel = FALSE;
10406
10407     TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10408           hwnd, uMsg, wParam, lParam, isW);
10409
10410     switch (uMsg)
10411     {
10412         case WM_GETDLGCODE:
10413           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10414
10415         case WM_KILLFOCUS:
10416             break;
10417
10418         case WM_DESTROY:
10419         {
10420             WNDPROC editProc = infoPtr->EditWndProc;
10421             infoPtr->EditWndProc = 0;
10422             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10423             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10424         }
10425
10426         case WM_KEYDOWN:
10427             if (VK_ESCAPE == (INT)wParam)
10428             {
10429                 cancel = TRUE;
10430                 break;
10431             }
10432             else if (VK_RETURN == (INT)wParam)
10433                 break;
10434
10435         default:
10436             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10437     }
10438
10439     /* kill the edit */
10440     if (infoPtr->hwndEdit)
10441     {
10442         LPWSTR buffer = NULL;
10443
10444         infoPtr->hwndEdit = 0;
10445         if (!cancel)
10446         {
10447             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10448
10449             if (len)
10450             {
10451                 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10452                 {
10453                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
10454                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10455                 }
10456             }
10457         }
10458         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10459
10460         Free(buffer);
10461     }
10462
10463     SendMessageW(hwnd, WM_CLOSE, 0, 0);
10464     return 0;
10465 }
10466
10467 /***
10468  * DESCRIPTION:
10469  * Subclassed edit control Unicode windproc function
10470  *
10471  * PARAMETER(S):
10472  * [I] hwnd : the edit window handle
10473  * [I] uMsg : the message that is to be processed
10474  * [I] wParam : first message parameter
10475  * [I] lParam : second message parameter
10476  *
10477  * RETURN:
10478  */
10479 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10480 {
10481     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10482 }
10483
10484 /***
10485  * DESCRIPTION:
10486  * Subclassed edit control ANSI windproc function
10487  *
10488  * PARAMETER(S):
10489  * [I] hwnd : the edit window handle
10490  * [I] uMsg : the message that is to be processed
10491  * [I] wParam : first message parameter
10492  * [I] lParam : second message parameter
10493  *
10494  * RETURN:
10495  */
10496 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10497 {
10498     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10499 }
10500
10501 /***
10502  * DESCRIPTION:
10503  * Creates a subclassed edit control
10504  *
10505  * PARAMETER(S):
10506  * [I] infoPtr : valid pointer to the listview structure
10507  * [I] text : initial text for the edit
10508  * [I] style : the window style
10509  * [I] isW : TRUE if input is Unicode
10510  *
10511  * RETURN:
10512  */
10513 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10514         INT x, INT y, INT width, INT height, BOOL isW)
10515 {
10516     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10517     HWND hedit;
10518     SIZE sz;
10519     HDC hdc;
10520     HDC hOldFont=0;
10521     TEXTMETRICW textMetric;
10522     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10523
10524     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10525
10526     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10527     hdc = GetDC(infoPtr->hwndSelf);
10528
10529     /* Select the font to get appropriate metric dimensions */
10530     if(infoPtr->hFont != 0)
10531         hOldFont = SelectObject(hdc, infoPtr->hFont);
10532
10533     /*Get String Length in pixels */
10534     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10535
10536     /*Add Extra spacing for the next character */
10537     GetTextMetricsW(hdc, &textMetric);
10538     sz.cx += (textMetric.tmMaxCharWidth * 2);
10539
10540     if(infoPtr->hFont != 0)
10541         SelectObject(hdc, hOldFont);
10542
10543     ReleaseDC(infoPtr->hwndSelf, hdc);
10544     if (isW)
10545         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10546     else
10547         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10548
10549     if (!hedit) return 0;
10550
10551     infoPtr->EditWndProc = (WNDPROC)
10552         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10553                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10554
10555     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10556
10557     return hedit;
10558 }