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