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