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