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