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