comctl32/listview: Don't offset to column bound calculating LVIR_LABEL for items.
[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
6262     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6263
6264     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6265
6266     LISTVIEW_GetOrigin(infoPtr, &Origin);
6267     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6268
6269     /* Be smart and try to figure out the minimum we have to do */
6270     if (lprc->left == LVIR_ICON) doLabel = FALSE;
6271     if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6272     if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6273         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6274         oversizedBox = TRUE;
6275
6276     /* get what we need from the item before hand, so we make
6277      * only one request. This can speed up things, if data
6278      * is stored on the app side */
6279     lvItem.mask = 0;
6280     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6281     if (doLabel) lvItem.mask |= LVIF_TEXT;
6282     lvItem.iItem = nItem;
6283     lvItem.iSubItem = 0;
6284     lvItem.pszText = szDispText;
6285     lvItem.cchTextMax = DISP_TEXT_SIZE;
6286     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6287     /* we got the state already up, simulate it here, to avoid a reget */
6288     if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6289     {
6290         lvItem.mask |= LVIF_STATE;
6291         lvItem.stateMask = LVIS_FOCUSED;
6292         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6293     }
6294
6295     if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6296         lprc->left = LVIR_BOUNDS;
6297     switch(lprc->left)
6298     {
6299     case LVIR_ICON:
6300         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6301         break;
6302
6303     case LVIR_LABEL:
6304         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6305         break;
6306
6307     case LVIR_BOUNDS:
6308         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6309         break;
6310
6311     case LVIR_SELECTBOUNDS:
6312         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6313         break;
6314
6315     default:
6316         WARN("Unknown value: %d\n", lprc->left);
6317         return FALSE;
6318     }
6319
6320     if (infoPtr->uView == LV_VIEW_DETAILS)
6321         OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6322     else
6323         OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6324
6325     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6326
6327     return TRUE;
6328 }
6329
6330 /***
6331  * DESCRIPTION:
6332  * Retrieves the spacing between listview control items.
6333  *
6334  * PARAMETER(S):
6335  * [I] infoPtr : valid pointer to the listview structure
6336  * [IO] lprc : rectangle to receive the output
6337  *             on input, lprc->top = nSubItem
6338  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6339  * 
6340  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6341  *       not only those of the first column.
6342  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6343  * 
6344  * RETURN:
6345  *     TRUE: success
6346  *     FALSE: failure
6347  */
6348 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6349 {
6350     POINT Position;
6351     LVITEMW lvItem;
6352     INT nColumn;
6353     
6354     if (!lprc) return FALSE;
6355
6356     nColumn = lprc->top;
6357
6358     TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6359     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6360     if (lprc->top == 0)
6361         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6362
6363     if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6364
6365     /* special case for header items */
6366     if (nItem == -1)
6367     {
6368         if (lprc->left != LVIR_BOUNDS)
6369         {
6370             FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6371             return FALSE;
6372         }
6373
6374         if (infoPtr->hwndHeader)
6375             return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6376         else
6377         {
6378             memset(lprc, 0, sizeof(RECT));
6379             return TRUE;
6380         }
6381     }
6382
6383     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6384
6385     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6386
6387     lvItem.mask = 0;
6388     lvItem.iItem = nItem;
6389     lvItem.iSubItem = nColumn;
6390     
6391     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6392     switch(lprc->left)
6393     {
6394     case LVIR_ICON:
6395         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6396         break;
6397
6398     case LVIR_LABEL:
6399     case LVIR_BOUNDS:
6400         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6401         break;
6402
6403     default:
6404         ERR("Unknown bounds=%d\n", lprc->left);
6405         return FALSE;
6406     }
6407
6408     OffsetRect(lprc, 0, Position.y);
6409     return TRUE;
6410 }
6411
6412
6413 /***
6414  * DESCRIPTION:
6415  * Retrieves the width of a label.
6416  *
6417  * PARAMETER(S):
6418  * [I] infoPtr : valid pointer to the listview structure
6419  *
6420  * RETURN:
6421  *   SUCCESS : string width (in pixels)
6422  *   FAILURE : zero
6423  */
6424 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6425 {
6426     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6427     LVITEMW lvItem;
6428
6429     TRACE("(nItem=%d)\n", nItem);
6430
6431     lvItem.mask = LVIF_TEXT;
6432     lvItem.iItem = nItem;
6433     lvItem.iSubItem = 0;
6434     lvItem.pszText = szDispText;
6435     lvItem.cchTextMax = DISP_TEXT_SIZE;
6436     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6437   
6438     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6439 }
6440
6441 /***
6442  * DESCRIPTION:
6443  * Retrieves the spacing between listview control items.
6444  *
6445  * PARAMETER(S):
6446  * [I] infoPtr : valid pointer to the listview structure
6447  * [I] bSmall : flag for small or large icon
6448  *
6449  * RETURN:
6450  * Horizontal + vertical spacing
6451  */
6452 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6453 {
6454   LONG lResult;
6455
6456   if (!bSmall)
6457   {
6458     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6459   }
6460   else
6461   {
6462     if (infoPtr->uView == LV_VIEW_ICON)
6463       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6464     else
6465       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6466   }
6467   return lResult;
6468 }
6469
6470 /***
6471  * DESCRIPTION:
6472  * Retrieves the state of a listview control item.
6473  *
6474  * PARAMETER(S):
6475  * [I] infoPtr : valid pointer to the listview structure
6476  * [I] nItem : item index
6477  * [I] uMask : state mask
6478  *
6479  * RETURN:
6480  * State specified by the mask.
6481  */
6482 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6483 {
6484     LVITEMW lvItem;
6485
6486     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6487
6488     lvItem.iItem = nItem;
6489     lvItem.iSubItem = 0;
6490     lvItem.mask = LVIF_STATE;
6491     lvItem.stateMask = uMask;
6492     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6493
6494     return lvItem.state & uMask;
6495 }
6496
6497 /***
6498  * DESCRIPTION:
6499  * Retrieves the text of a listview control item or subitem.
6500  *
6501  * PARAMETER(S):
6502  * [I] hwnd : window handle
6503  * [I] nItem : item index
6504  * [IO] lpLVItem : item information
6505  * [I] isW :  TRUE if lpLVItem is Unicode
6506  *
6507  * RETURN:
6508  *   SUCCESS : string length
6509  *   FAILURE : 0
6510  */
6511 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6512 {
6513     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6514
6515     lpLVItem->mask = LVIF_TEXT;
6516     lpLVItem->iItem = nItem;
6517     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6518
6519     return textlenT(lpLVItem->pszText, isW);
6520 }
6521
6522 /***
6523  * DESCRIPTION:
6524  * Searches for an item based on properties + relationships.
6525  *
6526  * PARAMETER(S):
6527  * [I] infoPtr : valid pointer to the listview structure
6528  * [I] nItem : item index
6529  * [I] uFlags : relationship flag
6530  *
6531  * RETURN:
6532  *   SUCCESS : item index
6533  *   FAILURE : -1
6534  */
6535 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6536 {
6537     UINT uMask = 0;
6538     LVFINDINFOW lvFindInfo;
6539     INT nCountPerColumn;
6540     INT nCountPerRow;
6541     INT i;
6542
6543     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6544     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6545
6546     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6547
6548     if (uFlags & LVNI_CUT)
6549       uMask |= LVIS_CUT;
6550
6551     if (uFlags & LVNI_DROPHILITED)
6552       uMask |= LVIS_DROPHILITED;
6553
6554     if (uFlags & LVNI_FOCUSED)
6555       uMask |= LVIS_FOCUSED;
6556
6557     if (uFlags & LVNI_SELECTED)
6558       uMask |= LVIS_SELECTED;
6559
6560     /* if we're asked for the focused item, that's only one, 
6561      * so it's worth optimizing */
6562     if (uFlags & LVNI_FOCUSED)
6563     {
6564         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6565         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6566     }
6567     
6568     if (uFlags & LVNI_ABOVE)
6569     {
6570       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6571       {
6572         while (nItem >= 0)
6573         {
6574           nItem--;
6575           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6576             return nItem;
6577         }
6578       }
6579       else
6580       {
6581         /* Special case for autoarrange - move 'til the top of a list */
6582         if (is_autoarrange(infoPtr))
6583         {
6584           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6585           while (nItem - nCountPerRow >= 0)
6586           {
6587             nItem -= nCountPerRow;
6588             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6589               return nItem;
6590           }
6591           return -1;
6592         }
6593         lvFindInfo.flags = LVFI_NEARESTXY;
6594         lvFindInfo.vkDirection = VK_UP;
6595         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6596         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6597         {
6598           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6599             return nItem;
6600         }
6601       }
6602     }
6603     else if (uFlags & LVNI_BELOW)
6604     {
6605       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6606       {
6607         while (nItem < infoPtr->nItemCount)
6608         {
6609           nItem++;
6610           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6611             return nItem;
6612         }
6613       }
6614       else
6615       {
6616         /* Special case for autoarrange - move 'til the bottom of a list */
6617         if (is_autoarrange(infoPtr))
6618         {
6619           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6620           while (nItem + nCountPerRow < infoPtr->nItemCount )
6621           {
6622             nItem += nCountPerRow;
6623             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6624               return nItem;
6625           }
6626           return -1;
6627         }
6628         lvFindInfo.flags = LVFI_NEARESTXY;
6629         lvFindInfo.vkDirection = VK_DOWN;
6630         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6631         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6632         {
6633           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6634             return nItem;
6635         }
6636       }
6637     }
6638     else if (uFlags & LVNI_TOLEFT)
6639     {
6640       if (infoPtr->uView == LV_VIEW_LIST)
6641       {
6642         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6643         while (nItem - nCountPerColumn >= 0)
6644         {
6645           nItem -= nCountPerColumn;
6646           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6647             return nItem;
6648         }
6649       }
6650       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6651       {
6652         /* Special case for autoarrange - move 'til the beginning of a row */
6653         if (is_autoarrange(infoPtr))
6654         {
6655           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6656           while (nItem % nCountPerRow > 0)
6657           {
6658             nItem --;
6659             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6660               return nItem;
6661           }
6662           return -1;
6663         }
6664         lvFindInfo.flags = LVFI_NEARESTXY;
6665         lvFindInfo.vkDirection = VK_LEFT;
6666         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6667         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6668         {
6669           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6670             return nItem;
6671         }
6672       }
6673     }
6674     else if (uFlags & LVNI_TORIGHT)
6675     {
6676       if (infoPtr->uView == LV_VIEW_LIST)
6677       {
6678         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6679         while (nItem + nCountPerColumn < infoPtr->nItemCount)
6680         {
6681           nItem += nCountPerColumn;
6682           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6683             return nItem;
6684         }
6685       }
6686       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6687       {
6688         /* Special case for autoarrange - move 'til the end of a row */
6689         if (is_autoarrange(infoPtr))
6690         {
6691           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6692           while (nItem % nCountPerRow < nCountPerRow - 1 )
6693           {
6694             nItem ++;
6695             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6696               return nItem;
6697           }
6698           return -1;
6699         }
6700         lvFindInfo.flags = LVFI_NEARESTXY;
6701         lvFindInfo.vkDirection = VK_RIGHT;
6702         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6703         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6704         {
6705           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6706             return nItem;
6707         }
6708       }
6709     }
6710     else
6711     {
6712       nItem++;
6713
6714       /* search by index */
6715       for (i = nItem; i < infoPtr->nItemCount; i++)
6716       {
6717         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6718           return i;
6719       }
6720     }
6721
6722     return -1;
6723 }
6724
6725 /* LISTVIEW_GetNumberOfWorkAreas */
6726
6727 /***
6728  * DESCRIPTION:
6729  * Retrieves the origin coordinates when in icon or small icon display mode.
6730  *
6731  * PARAMETER(S):
6732  * [I] infoPtr : valid pointer to the listview structure
6733  * [O] lpptOrigin : coordinate information
6734  *
6735  * RETURN:
6736  *   None.
6737  */
6738 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6739 {
6740     INT nHorzPos = 0, nVertPos = 0;
6741     SCROLLINFO scrollInfo;
6742
6743     scrollInfo.cbSize = sizeof(SCROLLINFO);    
6744     scrollInfo.fMask = SIF_POS;
6745     
6746     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6747         nHorzPos = scrollInfo.nPos;
6748     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6749         nVertPos = scrollInfo.nPos;
6750
6751     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6752
6753     lpptOrigin->x = infoPtr->rcList.left;
6754     lpptOrigin->y = infoPtr->rcList.top;
6755     if (infoPtr->uView == LV_VIEW_LIST)
6756         nHorzPos *= infoPtr->nItemWidth;
6757     else if (infoPtr->uView == LV_VIEW_DETAILS)
6758         nVertPos *= infoPtr->nItemHeight;
6759     
6760     lpptOrigin->x -= nHorzPos;
6761     lpptOrigin->y -= nVertPos;
6762
6763     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6764 }
6765
6766 /***
6767  * DESCRIPTION:
6768  * Retrieves the width of a string.
6769  *
6770  * PARAMETER(S):
6771  * [I] hwnd : window handle
6772  * [I] lpszText : text string to process
6773  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6774  *
6775  * RETURN:
6776  *   SUCCESS : string width (in pixels)
6777  *   FAILURE : zero
6778  */
6779 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6780 {
6781     SIZE stringSize;
6782     
6783     stringSize.cx = 0;    
6784     if (is_textT(lpszText, isW))
6785     {
6786         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6787         HDC hdc = GetDC(infoPtr->hwndSelf);
6788         HFONT hOldFont = SelectObject(hdc, hFont);
6789
6790         if (isW)
6791             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6792         else
6793             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6794         SelectObject(hdc, hOldFont);
6795         ReleaseDC(infoPtr->hwndSelf, hdc);
6796     }
6797     return stringSize.cx;
6798 }
6799
6800 /***
6801  * DESCRIPTION:
6802  * Determines which listview item is located at the specified position.
6803  *
6804  * PARAMETER(S):
6805  * [I] infoPtr : valid pointer to the listview structure
6806  * [IO] lpht : hit test information
6807  * [I] subitem : fill out iSubItem.
6808  * [I] select : return the index only if the hit selects the item
6809  *
6810  * NOTE:
6811  * (mm 20001022): We must not allow iSubItem to be touched, for
6812  * an app might pass only a structure with space up to iItem!
6813  * (MS Office 97 does that for instance in the file open dialog)
6814  * 
6815  * RETURN:
6816  *   SUCCESS : item index
6817  *   FAILURE : -1
6818  */
6819 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6820 {
6821     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6822     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6823     POINT Origin, Position, opt;
6824     LVITEMW lvItem;
6825     ITERATOR i;
6826     INT iItem;
6827     
6828     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6829     
6830     lpht->flags = 0;
6831     lpht->iItem = -1;
6832     if (subitem) lpht->iSubItem = 0;
6833
6834     LISTVIEW_GetOrigin(infoPtr, &Origin);
6835
6836     /* set whole list relation flags */
6837     if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
6838     {
6839         /* LVM_SUBITEMHITTEST checks left bound of possible client area */
6840         if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
6841             lpht->flags |= LVHT_TOLEFT;
6842
6843         if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
6844             opt.y = lpht->pt.y + infoPtr->rcList.top;
6845         else
6846             opt.y = lpht->pt.y;
6847
6848         if (infoPtr->rcList.bottom < opt.y)
6849             lpht->flags |= LVHT_BELOW;
6850     }
6851     else
6852     {
6853         if (infoPtr->rcList.left > lpht->pt.x)
6854             lpht->flags |= LVHT_TOLEFT;
6855         else if (infoPtr->rcList.right < lpht->pt.x)
6856             lpht->flags |= LVHT_TORIGHT;
6857
6858         if (infoPtr->rcList.top > lpht->pt.y)
6859             lpht->flags |= LVHT_ABOVE;
6860         else if (infoPtr->rcList.bottom < lpht->pt.y)
6861             lpht->flags |= LVHT_BELOW;
6862     }
6863
6864     /* even if item is invalid try to find subitem */
6865     if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
6866     {
6867         RECT *pRect;
6868         INT j;
6869
6870         opt.x = lpht->pt.x - Origin.x;
6871
6872         lpht->iSubItem = -1;
6873         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6874         {
6875             pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
6876
6877             if ((opt.x >= pRect->left) && (opt.x < pRect->right))
6878             {
6879                 lpht->iSubItem = j;
6880                 break;
6881             }
6882         }
6883         TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
6884     }
6885
6886     TRACE("lpht->flags=0x%x\n", lpht->flags);
6887     if (lpht->flags) return -1;
6888
6889     lpht->flags |= LVHT_NOWHERE;
6890
6891     /* first deal with the large items */
6892     rcSearch.left = lpht->pt.x;
6893     rcSearch.top = lpht->pt.y;
6894     rcSearch.right = rcSearch.left + 1;
6895     rcSearch.bottom = rcSearch.top + 1;
6896
6897     iterator_frameditems(&i, infoPtr, &rcSearch);
6898     iterator_next(&i); /* go to first item in the sequence */
6899     iItem = i.nItem;
6900     iterator_destroy(&i);
6901
6902     TRACE("lpht->iItem=%d\n", iItem);
6903     if (iItem == -1) return -1;
6904
6905     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6906     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6907     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6908     if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6909     lvItem.iItem = iItem;
6910     lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
6911     lvItem.pszText = szDispText;
6912     lvItem.cchTextMax = DISP_TEXT_SIZE;
6913     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6914     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6915
6916     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6917     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6918     opt.x = lpht->pt.x - Position.x - Origin.x;
6919
6920     if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
6921         opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
6922     else
6923         opt.y = lpht->pt.y - Position.y - Origin.y;
6924
6925     if (infoPtr->uView == LV_VIEW_DETAILS)
6926     {
6927         rcBounds = rcBox;
6928         if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
6929             opt.x = lpht->pt.x - Origin.x;
6930     }
6931     else
6932     {
6933         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6934         UnionRect(&rcBounds, &rcBounds, &rcState);
6935     }
6936     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6937     if (!PtInRect(&rcBounds, opt)) return -1;
6938
6939     if (PtInRect(&rcIcon, opt))
6940         lpht->flags |= LVHT_ONITEMICON;
6941     else if (PtInRect(&rcLabel, opt))
6942         lpht->flags |= LVHT_ONITEMLABEL;
6943     else if (infoPtr->himlState && PtInRect(&rcState, opt))
6944         lpht->flags |= LVHT_ONITEMSTATEICON;
6945     /* special case for LVS_EX_FULLROWSELECT */
6946     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
6947       !(lpht->flags & LVHT_ONITEM))
6948     {
6949         lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
6950     }
6951     if (lpht->flags & LVHT_ONITEM)
6952         lpht->flags &= ~LVHT_NOWHERE;
6953     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6954
6955     if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
6956                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6957                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6958     {
6959         if (infoPtr->uView == LV_VIEW_DETAILS)
6960         {
6961             /* get main item bounds */
6962             lvItem.iSubItem = 0;
6963             LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6964             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6965             UnionRect(&rcBounds, &rcBounds, &rcState);
6966         }
6967         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6968     }
6969     return lpht->iItem = iItem;
6970 }
6971
6972 /***
6973  * DESCRIPTION:
6974  * Inserts a new item in the listview control.
6975  *
6976  * PARAMETER(S):
6977  * [I] infoPtr : valid pointer to the listview structure
6978  * [I] lpLVItem : item information
6979  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6980  *
6981  * RETURN:
6982  *   SUCCESS : new item index
6983  *   FAILURE : -1
6984  */
6985 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6986 {
6987     INT nItem;
6988     HDPA hdpaSubItems;
6989     NMLISTVIEW nmlv;
6990     ITEM_INFO *lpItem;
6991     ITEM_ID *lpID;
6992     BOOL is_sorted, has_changed;
6993     LVITEMW item;
6994     HWND hwndSelf = infoPtr->hwndSelf;
6995
6996     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6997
6998     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6999
7000     /* make sure it's an item, and not a subitem; cannot insert a subitem */
7001     if (!lpLVItem || lpLVItem->iSubItem) return -1;
7002
7003     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7004
7005     if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
7006     
7007     /* insert item in listview control data structure */
7008     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7009     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7010
7011     /* link with id struct */
7012     if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
7013     lpItem->id = lpID;
7014     lpID->item = hdpaSubItems;
7015     lpID->id = get_next_itemid(infoPtr);
7016     if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7017
7018     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7019                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7020
7021     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7022
7023     /* calculate new item index */
7024     if (is_sorted)
7025     {
7026         HDPA hItem;
7027         ITEM_INFO *item_s;
7028         INT i = 0, cmpv;
7029
7030         while (i < infoPtr->nItemCount)
7031         {
7032             hItem  = DPA_GetPtr( infoPtr->hdpaItems, i);
7033             item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
7034
7035             cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, TRUE);
7036             if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7037
7038             if (cmpv >= 0) break;
7039             i++;
7040         }
7041         nItem = i;
7042     }
7043     else
7044         nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7045
7046     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7047     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7048     if (nItem == -1) goto fail;
7049     infoPtr->nItemCount++;
7050
7051     /* shift indices first so they don't get tangled */
7052     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7053
7054     /* set the item attributes */
7055     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7056     {
7057         /* full size structure expected - _WIN32IE >= 0x560 */
7058         item = *lpLVItem;
7059     }
7060     else if (lpLVItem->mask & LVIF_INDENT)
7061     {
7062         /* indent member expected - _WIN32IE >= 0x300 */
7063         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7064     }
7065     else
7066     {
7067         /* minimal structure expected */
7068         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7069     }
7070     item.iItem = nItem;
7071     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7072     {
7073         item.mask |= LVIF_STATE;
7074         item.stateMask |= LVIS_STATEIMAGEMASK;
7075         item.state &= ~LVIS_STATEIMAGEMASK;
7076         item.state |= INDEXTOSTATEIMAGEMASK(1);
7077     }
7078     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7079
7080     /* make room for the position, if we are in the right mode */
7081     if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7082     {
7083         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7084             goto undo;
7085         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7086         {
7087             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7088             goto undo;
7089         }
7090     }
7091     
7092     /* send LVN_INSERTITEM notification */
7093     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7094     nmlv.iItem = nItem;
7095     nmlv.lParam = lpItem->lParam;
7096     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7097     if (!IsWindow(hwndSelf))
7098         return -1;
7099
7100     /* align items (set position of each item) */
7101     if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7102     {
7103         POINT pt;
7104
7105         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7106             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7107         else
7108             LISTVIEW_NextIconPosTop(infoPtr, &pt);
7109
7110         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7111     }
7112
7113     /* now is the invalidation fun */
7114     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7115     return nItem;
7116
7117 undo:
7118     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7119     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7120     infoPtr->nItemCount--;
7121 fail:
7122     DPA_DeletePtr(hdpaSubItems, 0);
7123     DPA_Destroy (hdpaSubItems);
7124     Free (lpItem);
7125     return -1;
7126 }
7127
7128 /***
7129  * DESCRIPTION:
7130  * Checks item visibility.
7131  *
7132  * PARAMETER(S):
7133  * [I] infoPtr : valid pointer to the listview structure
7134  * [I] nFirst : item index to check for
7135  *
7136  * RETURN:
7137  *   Item visible : TRUE
7138  *   Item invisible or failure : FALSE
7139  */
7140 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7141 {
7142     POINT Origin, Position;
7143     RECT rcItem;
7144     HDC hdc;
7145     BOOL ret;
7146
7147     TRACE("nItem=%d\n", nItem);
7148
7149     if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7150
7151     LISTVIEW_GetOrigin(infoPtr, &Origin);
7152     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7153     rcItem.left = Position.x + Origin.x;
7154     rcItem.top  = Position.y + Origin.y;
7155     rcItem.right  = rcItem.left + infoPtr->nItemWidth;
7156     rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7157
7158     hdc = GetDC(infoPtr->hwndSelf);
7159     if (!hdc) return FALSE;
7160     ret = RectVisible(hdc, &rcItem);
7161     ReleaseDC(infoPtr->hwndSelf, hdc);
7162
7163     return ret;
7164 }
7165
7166 /***
7167  * DESCRIPTION:
7168  * Redraws a range of items.
7169  *
7170  * PARAMETER(S):
7171  * [I] infoPtr : valid pointer to the listview structure
7172  * [I] nFirst : first item
7173  * [I] nLast : last item
7174  *
7175  * RETURN:
7176  *   SUCCESS : TRUE
7177  *   FAILURE : FALSE
7178  */
7179 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
7180 {
7181     INT i;
7182  
7183     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
7184         max(nFirst, nLast) >= infoPtr->nItemCount)
7185         return FALSE;
7186     
7187     for (i = nFirst; i <= nLast; i++)
7188         LISTVIEW_InvalidateItem(infoPtr, i);
7189
7190     return TRUE;
7191 }
7192
7193 /***
7194  * DESCRIPTION:
7195  * Scroll the content of a listview.
7196  *
7197  * PARAMETER(S):
7198  * [I] infoPtr : valid pointer to the listview structure
7199  * [I] dx : horizontal scroll amount in pixels
7200  * [I] dy : vertical scroll amount in pixels
7201  *
7202  * RETURN:
7203  *   SUCCESS : TRUE
7204  *   FAILURE : FALSE
7205  *
7206  * COMMENTS:
7207  *  If the control is in report view (LV_VIEW_DETAILS) the control can
7208  *  be scrolled only in line increments. "dy" will be rounded to the
7209  *  nearest number of pixels that are a whole line. Ex: if line height
7210  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7211  *  is passed, then the scroll will be 0.  (per MSDN 7/2002)
7212  *
7213  *  For:  (per experimentation with native control and CSpy ListView)
7214  *     LV_VIEW_ICON       dy=1 = 1 pixel  (vertical only)
7215  *                        dx ignored
7216  *     LV_VIEW_SMALLICON  dy=1 = 1 pixel  (vertical only)
7217  *                        dx ignored
7218  *     LV_VIEW_LIST       dx=1 = 1 column (horizontal only)
7219  *                           but will only scroll 1 column per message
7220  *                           no matter what the value.
7221  *                        dy must be 0 or FALSE returned.
7222  *     LV_VIEW_DETAILS    dx=1 = 1 pixel
7223  *                        dy=  see above
7224  *
7225  */
7226 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7227 {
7228     switch(infoPtr->uView) {
7229     case LV_VIEW_DETAILS:
7230         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
7231         dy /= infoPtr->nItemHeight;
7232         break;
7233     case LV_VIEW_LIST:
7234         if (dy != 0) return FALSE;
7235         break;
7236     default: /* icon */
7237         dx = 0;
7238         break;
7239     }   
7240
7241     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
7242     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
7243   
7244     return TRUE;
7245 }
7246
7247 /***
7248  * DESCRIPTION:
7249  * Sets the background color.
7250  *
7251  * PARAMETER(S):
7252  * [I] infoPtr : valid pointer to the listview structure
7253  * [I] clrBk : background color
7254  *
7255  * RETURN:
7256  *   SUCCESS : TRUE
7257  *   FAILURE : FALSE
7258  */
7259 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
7260 {
7261     TRACE("(clrBk=%x)\n", clrBk);
7262
7263     if(infoPtr->clrBk != clrBk) {
7264         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7265         infoPtr->clrBk = clrBk;
7266         if (clrBk == CLR_NONE)
7267             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
7268         else
7269             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
7270         LISTVIEW_InvalidateList(infoPtr);
7271     }
7272
7273    return TRUE;
7274 }
7275
7276 /* LISTVIEW_SetBkImage */
7277
7278 /*** Helper for {Insert,Set}ColumnT *only* */
7279 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
7280                                const LVCOLUMNW *lpColumn, BOOL isW)
7281 {
7282     if (lpColumn->mask & LVCF_FMT)
7283     {
7284         /* format member is valid */
7285         lphdi->mask |= HDI_FORMAT;
7286
7287         /* set text alignment (leftmost column must be left-aligned) */
7288         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7289             lphdi->fmt |= HDF_LEFT;
7290         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
7291             lphdi->fmt |= HDF_RIGHT;
7292         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
7293             lphdi->fmt |= HDF_CENTER;
7294
7295         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7296             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
7297
7298         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7299         {
7300             lphdi->fmt |= HDF_IMAGE;
7301             lphdi->iImage = I_IMAGECALLBACK;
7302         }
7303     }
7304
7305     if (lpColumn->mask & LVCF_WIDTH)
7306     {
7307         lphdi->mask |= HDI_WIDTH;
7308         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7309         {
7310             /* make it fill the remainder of the controls width */
7311             RECT rcHeader;
7312             INT item_index;
7313
7314             for(item_index = 0; item_index < (nColumn - 1); item_index++)
7315             {
7316                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7317                 lphdi->cxy += rcHeader.right - rcHeader.left;
7318             }
7319
7320             /* retrieve the layout of the header */
7321             GetClientRect(infoPtr->hwndSelf, &rcHeader);
7322             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7323
7324             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7325         }
7326         else
7327             lphdi->cxy = lpColumn->cx;
7328     }
7329
7330     if (lpColumn->mask & LVCF_TEXT)
7331     {
7332         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7333         lphdi->fmt |= HDF_STRING;
7334         lphdi->pszText = lpColumn->pszText;
7335         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7336     }
7337
7338     if (lpColumn->mask & LVCF_IMAGE)
7339     {
7340         lphdi->mask |= HDI_IMAGE;
7341         lphdi->iImage = lpColumn->iImage;
7342     }
7343
7344     if (lpColumn->mask & LVCF_ORDER)
7345     {
7346         lphdi->mask |= HDI_ORDER;
7347         lphdi->iOrder = lpColumn->iOrder;
7348     }
7349 }
7350
7351
7352 /***
7353  * DESCRIPTION:
7354  * Inserts a new column.
7355  *
7356  * PARAMETER(S):
7357  * [I] infoPtr : valid pointer to the listview structure
7358  * [I] nColumn : column index
7359  * [I] lpColumn : column information
7360  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7361  *
7362  * RETURN:
7363  *   SUCCESS : new column index
7364  *   FAILURE : -1
7365  */
7366 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7367                                   const LVCOLUMNW *lpColumn, BOOL isW)
7368 {
7369     COLUMN_INFO *lpColumnInfo;
7370     INT nNewColumn;
7371     HDITEMW hdi;
7372
7373     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7374
7375     if (!lpColumn || nColumn < 0) return -1;
7376     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7377     
7378     ZeroMemory(&hdi, sizeof(HDITEMW));
7379     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7380
7381     /*
7382      * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7383      * (can be seen in SPY) otherwise column never gets added.
7384      */
7385     if (!(lpColumn->mask & LVCF_WIDTH)) {
7386         hdi.mask |= HDI_WIDTH;
7387         hdi.cxy = 10;
7388     }
7389
7390     /*
7391      * when the iSubItem is available Windows copies it to the header lParam. It seems
7392      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7393      */
7394     if (lpColumn->mask & LVCF_SUBITEM)
7395     {
7396         hdi.mask |= HDI_LPARAM;
7397         hdi.lParam = lpColumn->iSubItem;
7398     }
7399
7400     /* create header if not present */
7401     LISTVIEW_CreateHeader(infoPtr);
7402     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7403          (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7404     {
7405         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7406     }
7407
7408     /* insert item in header control */
7409     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
7410                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7411                               (WPARAM)nColumn, (LPARAM)&hdi);
7412     if (nNewColumn == -1) return -1;
7413     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7414    
7415     /* create our own column info */ 
7416     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7417     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7418
7419     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7420     if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7421         goto fail;
7422
7423     /* now we have to actually adjust the data */
7424     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7425     {
7426         SUBITEM_INFO *lpSubItem;
7427         HDPA hdpaSubItems;
7428         INT nItem, i;
7429         
7430         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7431         {
7432             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7433             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7434             {
7435                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7436                 if (lpSubItem->iSubItem >= nNewColumn)
7437                     lpSubItem->iSubItem++;
7438             }
7439         }
7440     }
7441
7442     /* make space for the new column */
7443     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7444     LISTVIEW_UpdateItemSize(infoPtr);
7445     
7446     return nNewColumn;
7447
7448 fail:
7449     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7450     if (lpColumnInfo)
7451     {
7452         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7453         Free(lpColumnInfo);
7454     }
7455     return -1;
7456 }
7457
7458 /***
7459  * DESCRIPTION:
7460  * Sets the attributes of a header item.
7461  *
7462  * PARAMETER(S):
7463  * [I] infoPtr : valid pointer to the listview structure
7464  * [I] nColumn : column index
7465  * [I] lpColumn : column attributes
7466  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7467  *
7468  * RETURN:
7469  *   SUCCESS : TRUE
7470  *   FAILURE : FALSE
7471  */
7472 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7473                                 const LVCOLUMNW *lpColumn, BOOL isW)
7474 {
7475     HDITEMW hdi, hdiget;
7476     BOOL bResult;
7477
7478     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7479     
7480     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7481
7482     ZeroMemory(&hdi, sizeof(HDITEMW));
7483     if (lpColumn->mask & LVCF_FMT)
7484     {
7485         hdi.mask |= HDI_FORMAT;
7486         hdiget.mask = HDI_FORMAT;
7487         if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
7488             hdi.fmt = hdiget.fmt & HDF_STRING;
7489     }
7490     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7491
7492     /* set header item attributes */
7493     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7494     if (!bResult) return FALSE;
7495
7496     if (lpColumn->mask & LVCF_FMT)
7497     {
7498         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7499         int oldFmt = lpColumnInfo->fmt;
7500         
7501         lpColumnInfo->fmt = lpColumn->fmt;
7502         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7503         {
7504             if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7505         }
7506     }
7507
7508     return TRUE;
7509 }
7510
7511 /***
7512  * DESCRIPTION:
7513  * Sets the column order array
7514  *
7515  * PARAMETERS:
7516  * [I] infoPtr : valid pointer to the listview structure
7517  * [I] iCount : number of elements in column order array
7518  * [I] lpiArray : pointer to column order array
7519  *
7520  * RETURN:
7521  *   SUCCESS : TRUE
7522  *   FAILURE : FALSE
7523  */
7524 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7525 {
7526   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7527
7528   if (!lpiArray)
7529     return FALSE;
7530
7531   return TRUE;
7532 }
7533
7534
7535 /***
7536  * DESCRIPTION:
7537  * Sets the width of a column
7538  *
7539  * PARAMETERS:
7540  * [I] infoPtr : valid pointer to the listview structure
7541  * [I] nColumn : column index
7542  * [I] cx : column width
7543  *
7544  * RETURN:
7545  *   SUCCESS : TRUE
7546  *   FAILURE : FALSE
7547  */
7548 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7549 {
7550     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7551     INT max_cx = 0;
7552     HDITEMW hdi;
7553
7554     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7555
7556     /* set column width only if in report or list mode */
7557     if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7558
7559     /* take care of invalid cx values */
7560     if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
7561     else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
7562
7563     /* resize all columns if in LV_VIEW_LIST mode */
7564     if(infoPtr->uView == LV_VIEW_LIST)
7565     {
7566         infoPtr->nItemWidth = cx;
7567         LISTVIEW_InvalidateList(infoPtr);
7568         return TRUE;
7569     }
7570
7571     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7572
7573     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7574     {
7575         INT nLabelWidth;
7576         LVITEMW lvItem;
7577
7578         lvItem.mask = LVIF_TEXT;        
7579         lvItem.iItem = 0;
7580         lvItem.iSubItem = nColumn;
7581         lvItem.pszText = szDispText;
7582         lvItem.cchTextMax = DISP_TEXT_SIZE;
7583         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7584         {
7585             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7586             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7587             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7588         }
7589         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7590             max_cx += infoPtr->iconSize.cx;
7591         max_cx += TRAILING_LABEL_PADDING;
7592     }
7593
7594     /* autosize based on listview items width */
7595     if(cx == LVSCW_AUTOSIZE)
7596         cx = max_cx;
7597     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7598     {
7599         /* if iCol is the last column make it fill the remainder of the controls width */
7600         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
7601         {
7602             RECT rcHeader;
7603             POINT Origin;
7604
7605             LISTVIEW_GetOrigin(infoPtr, &Origin);
7606             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7607
7608             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7609         }
7610         else
7611         {
7612             /* Despite what the MS docs say, if this is not the last
7613                column, then MS resizes the column to the width of the
7614                largest text string in the column, including headers
7615                and items. This is different from LVSCW_AUTOSIZE in that
7616                LVSCW_AUTOSIZE ignores the header string length. */
7617             cx = 0;
7618
7619             /* retrieve header text */
7620             hdi.mask = HDI_TEXT;
7621             hdi.cchTextMax = DISP_TEXT_SIZE;
7622             hdi.pszText = szDispText;
7623             if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
7624             {
7625                 HDC hdc = GetDC(infoPtr->hwndSelf);
7626                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7627                 SIZE size;
7628
7629                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7630                     cx = size.cx + TRAILING_HEADER_PADDING;
7631                 /* FIXME: Take into account the header image, if one is present */
7632                 SelectObject(hdc, old_font);
7633                 ReleaseDC(infoPtr->hwndSelf, hdc);
7634             }
7635             cx = max (cx, max_cx);
7636         }
7637     }
7638
7639     if (cx < 0) return FALSE;
7640
7641     /* call header to update the column change */
7642     hdi.mask = HDI_WIDTH;
7643     hdi.cxy = cx;
7644     TRACE("hdi.cxy=%d\n", hdi.cxy);
7645     return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
7646 }
7647
7648 /***
7649  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
7650  *
7651  */
7652 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7653 {
7654     HDC hdc_wnd, hdc;
7655     HBITMAP hbm_im, hbm_mask, hbm_orig;
7656     RECT rc;
7657     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7658     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7659     HIMAGELIST himl;
7660
7661     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7662                             ILC_COLOR | ILC_MASK, 2, 2);
7663     hdc_wnd = GetDC(infoPtr->hwndSelf);
7664     hdc = CreateCompatibleDC(hdc_wnd);
7665     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7666     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7667     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7668
7669     rc.left = rc.top = 0;
7670     rc.right = GetSystemMetrics(SM_CXSMICON);
7671     rc.bottom = GetSystemMetrics(SM_CYSMICON);
7672
7673     hbm_orig = SelectObject(hdc, hbm_mask);
7674     FillRect(hdc, &rc, hbr_white);
7675     InflateRect(&rc, -2, -2);
7676     FillRect(hdc, &rc, hbr_black);
7677
7678     SelectObject(hdc, hbm_im);
7679     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7680     SelectObject(hdc, hbm_orig);
7681     ImageList_Add(himl, hbm_im, hbm_mask); 
7682
7683     SelectObject(hdc, hbm_im);
7684     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7685     SelectObject(hdc, hbm_orig);
7686     ImageList_Add(himl, hbm_im, hbm_mask);
7687
7688     DeleteObject(hbm_mask);
7689     DeleteObject(hbm_im);
7690     DeleteDC(hdc);
7691
7692     return himl;
7693 }
7694
7695 /***
7696  * DESCRIPTION:
7697  * Sets the extended listview style.
7698  *
7699  * PARAMETERS:
7700  * [I] infoPtr : valid pointer to the listview structure
7701  * [I] dwMask : mask
7702  * [I] dwStyle : style
7703  *
7704  * RETURN:
7705  *   SUCCESS : previous style
7706  *   FAILURE : 0
7707  */
7708 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7709 {
7710     DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7711
7712     /* set new style */
7713     if (dwMask)
7714         infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7715     else
7716         infoPtr->dwLvExStyle = dwExStyle;
7717
7718     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7719     {
7720         HIMAGELIST himl = 0;
7721         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7722         {
7723             LVITEMW item;
7724             item.mask = LVIF_STATE;
7725             item.stateMask = LVIS_STATEIMAGEMASK;
7726             item.state = INDEXTOSTATEIMAGEMASK(1);
7727             LISTVIEW_SetItemState(infoPtr, -1, &item);
7728
7729             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7730         }
7731         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7732     }
7733     
7734     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7735     {
7736         DWORD dwStyle;
7737
7738         /* if not already created */
7739         LISTVIEW_CreateHeader(infoPtr);
7740
7741         dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7742         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7743             dwStyle |= HDS_DRAGDROP;
7744         else
7745             dwStyle &= ~HDS_DRAGDROP;
7746         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7747     }
7748
7749     /* GRIDLINES adds decoration at top so changes sizes */
7750     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7751     {
7752         LISTVIEW_UpdateSize(infoPtr);
7753     }
7754
7755
7756     LISTVIEW_InvalidateList(infoPtr);
7757     return dwOldExStyle;
7758 }
7759
7760 /***
7761  * DESCRIPTION:
7762  * Sets the new hot cursor used during hot tracking and hover selection.
7763  *
7764  * PARAMETER(S):
7765  * [I] infoPtr : valid pointer to the listview structure
7766  * [I] hCursor : the new hot cursor handle
7767  *
7768  * RETURN:
7769  * Returns the previous hot cursor
7770  */
7771 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7772 {
7773     HCURSOR oldCursor = infoPtr->hHotCursor;
7774     
7775     infoPtr->hHotCursor = hCursor;
7776
7777     return oldCursor;
7778 }
7779
7780
7781 /***
7782  * DESCRIPTION:
7783  * Sets the hot item index.
7784  *
7785  * PARAMETERS:
7786  * [I] infoPtr : valid pointer to the listview structure
7787  * [I] iIndex : index
7788  *
7789  * RETURN:
7790  *   SUCCESS : previous hot item index
7791  *   FAILURE : -1 (no hot item)
7792  */
7793 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7794 {
7795     INT iOldIndex = infoPtr->nHotItem;
7796     
7797     infoPtr->nHotItem = iIndex;
7798     
7799     return iOldIndex;
7800 }
7801
7802
7803 /***
7804  * DESCRIPTION:
7805  * Sets the amount of time the cursor must hover over an item before it is selected.
7806  *
7807  * PARAMETER(S):
7808  * [I] infoPtr : valid pointer to the listview structure
7809  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7810  *
7811  * RETURN:
7812  * Returns the previous hover time
7813  */
7814 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7815 {
7816     DWORD oldHoverTime = infoPtr->dwHoverTime;
7817     
7818     infoPtr->dwHoverTime = dwHoverTime;
7819     
7820     return oldHoverTime;
7821 }
7822
7823 /***
7824  * DESCRIPTION:
7825  * Sets spacing for icons of LVS_ICON style.
7826  *
7827  * PARAMETER(S):
7828  * [I] infoPtr : valid pointer to the listview structure
7829  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7830  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7831  *
7832  * RETURN:
7833  *   MAKELONG(oldcx, oldcy)
7834  */
7835 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7836 {
7837     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7838
7839     TRACE("requested=(%d,%d)\n", cx, cy);
7840     
7841     /* this is supported only for LVS_ICON style */
7842     if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
7843   
7844     /* set to defaults, if instructed to */
7845     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7846     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7847
7848     /* if 0 then compute width
7849      * FIXME: Should scan each item and determine max width of
7850      *        icon or label, then make that the width */
7851     if (cx == 0)
7852         cx = infoPtr->iconSpacing.cx;
7853
7854     /* if 0 then compute height */
7855     if (cy == 0) 
7856         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7857              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7858     
7859
7860     infoPtr->iconSpacing.cx = cx;
7861     infoPtr->iconSpacing.cy = cy;
7862
7863     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7864           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
7865           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7866           infoPtr->ntmHeight);
7867
7868     /* these depend on the iconSpacing */
7869     LISTVIEW_UpdateItemSize(infoPtr);
7870
7871     return oldspacing;
7872 }
7873
7874 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7875 {
7876     INT cx, cy;
7877     
7878     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7879     {
7880         size->cx = cx;
7881         size->cy = cy;
7882     }
7883     else
7884     {
7885         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7886         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7887     }
7888 }
7889
7890 /***
7891  * DESCRIPTION:
7892  * Sets image lists.
7893  *
7894  * PARAMETER(S):
7895  * [I] infoPtr : valid pointer to the listview structure
7896  * [I] nType : image list type
7897  * [I] himl : image list handle
7898  *
7899  * RETURN:
7900  *   SUCCESS : old image list
7901  *   FAILURE : NULL
7902  */
7903 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7904 {
7905     INT oldHeight = infoPtr->nItemHeight;
7906     HIMAGELIST himlOld = 0;
7907
7908     TRACE("(nType=%d, himl=%p\n", nType, himl);
7909
7910     switch (nType)
7911     {
7912     case LVSIL_NORMAL:
7913         himlOld = infoPtr->himlNormal;
7914         infoPtr->himlNormal = himl;
7915         if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7916         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7917     break;
7918
7919     case LVSIL_SMALL:
7920         himlOld = infoPtr->himlSmall;
7921         infoPtr->himlSmall = himl;
7922         if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7923     break;
7924
7925     case LVSIL_STATE:
7926         himlOld = infoPtr->himlState;
7927         infoPtr->himlState = himl;
7928         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7929         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7930     break;
7931
7932     default:
7933         ERR("Unknown icon type=%d\n", nType);
7934         return NULL;
7935     }
7936
7937     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7938     if (infoPtr->nItemHeight != oldHeight)
7939         LISTVIEW_UpdateScroll(infoPtr);
7940
7941     return himlOld;
7942 }
7943
7944 /***
7945  * DESCRIPTION:
7946  * Preallocates memory (does *not* set the actual count of items !)
7947  *
7948  * PARAMETER(S):
7949  * [I] infoPtr : valid pointer to the listview structure
7950  * [I] nItems : item count (projected number of items to allocate)
7951  * [I] dwFlags : update flags
7952  *
7953  * RETURN:
7954  *   SUCCESS : TRUE
7955  *   FAILURE : FALSE
7956  */
7957 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7958 {
7959     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7960
7961     if (infoPtr->dwStyle & LVS_OWNERDATA)
7962     {
7963         INT nOldCount = infoPtr->nItemCount;
7964
7965         if (nItems < nOldCount)
7966         {
7967             RANGE range = { nItems, nOldCount };
7968             ranges_del(infoPtr->selectionRanges, range);
7969             if (infoPtr->nFocusedItem >= nItems)
7970             {
7971                 LISTVIEW_SetItemFocus(infoPtr, -1);
7972                 SetRectEmpty(&infoPtr->rcFocus);
7973             }
7974         }
7975
7976         infoPtr->nItemCount = nItems;
7977         LISTVIEW_UpdateScroll(infoPtr);
7978
7979         /* the flags are valid only in ownerdata report and list modes */
7980         if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
7981
7982         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7983             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7984
7985         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7986             LISTVIEW_InvalidateList(infoPtr);
7987         else
7988         {
7989             INT nFrom, nTo;
7990             POINT Origin;
7991             RECT rcErase;
7992             
7993             LISTVIEW_GetOrigin(infoPtr, &Origin);
7994             nFrom = min(nOldCount, nItems);
7995             nTo = max(nOldCount, nItems);
7996     
7997             if (infoPtr->uView == LV_VIEW_DETAILS)
7998             {
7999                 rcErase.left = 0;
8000                 rcErase.top = nFrom * infoPtr->nItemHeight;
8001                 rcErase.right = infoPtr->nItemWidth;
8002                 rcErase.bottom = nTo * infoPtr->nItemHeight;
8003                 OffsetRect(&rcErase, Origin.x, Origin.y);
8004                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8005                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8006             }
8007             else /* LV_VIEW_LIST */
8008             {
8009                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
8010
8011                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
8012                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
8013                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
8014                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8015                 OffsetRect(&rcErase, Origin.x, Origin.y);
8016                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8017                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8018
8019                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
8020                 rcErase.top = 0;
8021                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
8022                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8023                 OffsetRect(&rcErase, Origin.x, Origin.y);
8024                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8025                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8026             }
8027         }
8028     }
8029     else
8030     {
8031         /* According to MSDN for non-LVS_OWNERDATA this is just
8032          * a performance issue. The control allocates its internal
8033          * data structures for the number of items specified. It
8034          * cuts down on the number of memory allocations. Therefore
8035          * we will just issue a WARN here
8036          */
8037         WARN("for non-ownerdata performance option not implemented.\n");
8038     }
8039
8040     return TRUE;
8041 }
8042
8043 /***
8044  * DESCRIPTION:
8045  * Sets the position of an item.
8046  *
8047  * PARAMETER(S):
8048  * [I] infoPtr : valid pointer to the listview structure
8049  * [I] nItem : item index
8050  * [I] pt : coordinate
8051  *
8052  * RETURN:
8053  *   SUCCESS : TRUE
8054  *   FAILURE : FALSE
8055  */
8056 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
8057 {
8058     POINT Origin;
8059
8060     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
8061
8062     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
8063         !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
8064
8065     LISTVIEW_GetOrigin(infoPtr, &Origin);
8066
8067     /* This point value seems to be an undocumented feature.
8068      * The best guess is that it means either at the origin, 
8069      * or at true beginning of the list. I will assume the origin. */
8070     if ((pt.x == -1) && (pt.y == -1))
8071         pt = Origin;
8072     
8073     if (infoPtr->uView == LV_VIEW_ICON)
8074     {
8075         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
8076         pt.y -= ICON_TOP_PADDING;
8077     }
8078     pt.x -= Origin.x;
8079     pt.y -= Origin.y;
8080
8081     infoPtr->bAutoarrange = FALSE;
8082
8083     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
8084 }
8085
8086 /***
8087  * DESCRIPTION:
8088  * Sets the state of one or many items.
8089  *
8090  * PARAMETER(S):
8091  * [I] infoPtr : valid pointer to the listview structure
8092  * [I] nItem : item index
8093  * [I] lpLVItem : item or subitem info
8094  *
8095  * RETURN:
8096  *   SUCCESS : TRUE
8097  *   FAILURE : FALSE
8098  */
8099 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
8100 {
8101     BOOL bResult = TRUE;
8102     LVITEMW lvItem;
8103
8104     lvItem.iItem = nItem;
8105     lvItem.iSubItem = 0;
8106     lvItem.mask = LVIF_STATE;
8107     lvItem.state = lpLVItem->state;
8108     lvItem.stateMask = lpLVItem->stateMask;
8109     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
8110
8111     if (nItem == -1)
8112     {
8113         /* select all isn't allowed in LVS_SINGLESEL */
8114         if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
8115             return FALSE;
8116
8117         /* focus all isn't allowed */
8118         if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
8119
8120         /* apply to all items */
8121         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8122             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
8123     }
8124     else
8125         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
8126
8127     return bResult;
8128 }
8129
8130 /***
8131  * DESCRIPTION:
8132  * Sets the text of an item or subitem.
8133  *
8134  * PARAMETER(S):
8135  * [I] hwnd : window handle
8136  * [I] nItem : item index
8137  * [I] lpLVItem : item or subitem info
8138  * [I] isW : TRUE if input is Unicode
8139  *
8140  * RETURN:
8141  *   SUCCESS : TRUE
8142  *   FAILURE : FALSE
8143  */
8144 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
8145 {
8146     LVITEMW lvItem;
8147
8148     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
8149     
8150     lvItem.iItem = nItem;
8151     lvItem.iSubItem = lpLVItem->iSubItem;
8152     lvItem.mask = LVIF_TEXT;
8153     lvItem.pszText = lpLVItem->pszText;
8154     lvItem.cchTextMax = lpLVItem->cchTextMax;
8155     
8156     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
8157
8158     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
8159 }
8160
8161 /***
8162  * DESCRIPTION:
8163  * Set item index that marks the start of a multiple selection.
8164  *
8165  * PARAMETER(S):
8166  * [I] infoPtr : valid pointer to the listview structure
8167  * [I] nIndex : index
8168  *
8169  * RETURN:
8170  * Index number or -1 if there is no selection mark.
8171  */
8172 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
8173 {
8174   INT nOldIndex = infoPtr->nSelectionMark;
8175
8176   TRACE("(nIndex=%d)\n", nIndex);
8177
8178   infoPtr->nSelectionMark = nIndex;
8179
8180   return nOldIndex;
8181 }
8182
8183 /***
8184  * DESCRIPTION:
8185  * Sets the text background color.
8186  *
8187  * PARAMETER(S):
8188  * [I] infoPtr : valid pointer to the listview structure
8189  * [I] clrTextBk : text background color
8190  *
8191  * RETURN:
8192  *   SUCCESS : TRUE
8193  *   FAILURE : FALSE
8194  */
8195 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
8196 {
8197     TRACE("(clrTextBk=%x)\n", clrTextBk);
8198
8199     if (infoPtr->clrTextBk != clrTextBk)
8200     {
8201         infoPtr->clrTextBk = clrTextBk;
8202         LISTVIEW_InvalidateList(infoPtr);
8203     }
8204     
8205   return TRUE;
8206 }
8207
8208 /***
8209  * DESCRIPTION:
8210  * Sets the text foreground color.
8211  *
8212  * PARAMETER(S):
8213  * [I] infoPtr : valid pointer to the listview structure
8214  * [I] clrText : text color
8215  *
8216  * RETURN:
8217  *   SUCCESS : TRUE
8218  *   FAILURE : FALSE
8219  */
8220 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
8221 {
8222     TRACE("(clrText=%x)\n", clrText);
8223
8224     if (infoPtr->clrText != clrText)
8225     {
8226         infoPtr->clrText = clrText;
8227         LISTVIEW_InvalidateList(infoPtr);
8228     }
8229
8230     return TRUE;
8231 }
8232
8233 /***
8234  * DESCRIPTION:
8235  * Sets new ToolTip window to ListView control.
8236  *
8237  * PARAMETER(S):
8238  * [I] infoPtr        : valid pointer to the listview structure
8239  * [I] hwndNewToolTip : handle to new ToolTip
8240  *
8241  * RETURN:
8242  *   old tool tip
8243  */
8244 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
8245 {
8246   HWND hwndOldToolTip = infoPtr->hwndToolTip;
8247   infoPtr->hwndToolTip = hwndNewToolTip;
8248   return hwndOldToolTip;
8249 }
8250
8251 /*
8252  * DESCRIPTION:
8253  *   sets the Unicode character format flag for the control
8254  * PARAMETER(S):
8255  *    [I] infoPtr         :valid pointer to the listview structure
8256  *    [I] fUnicode        :true to switch to UNICODE false to switch to ANSI
8257  *
8258  * RETURN:
8259  *    Old Unicode Format
8260  */
8261 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
8262 {
8263   SHORT rc = infoPtr->notifyFormat;
8264   infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
8265   return rc == NFR_UNICODE;
8266 }
8267
8268 /*
8269  * DESCRIPTION:
8270  *   sets the control view mode
8271  * PARAMETER(S):
8272  *    [I] infoPtr         :valid pointer to the listview structure
8273  *    [I] nView           :new view mode value
8274  *
8275  * RETURN:
8276  *    SUCCESS:  1
8277  *    FAILURE: -1
8278  */
8279 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8280 {
8281   SIZE oldIconSize = infoPtr->iconSize;
8282   HIMAGELIST himl;
8283
8284   if (infoPtr->uView == nView) return 1;
8285
8286   if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8287   if (nView == LV_VIEW_TILE)
8288   {
8289       FIXME("View LV_VIEW_TILE unimplemented\n");
8290       return -1;
8291   }
8292
8293   infoPtr->uView = nView;
8294
8295   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8296   ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8297
8298   ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8299   SetRectEmpty(&infoPtr->rcFocus);
8300
8301   himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8302   set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8303
8304   switch (nView)
8305   {
8306   case LV_VIEW_ICON:
8307       if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8308       {
8309             TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8310                    oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8311             LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8312       }
8313       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8314       break;
8315   case LV_VIEW_SMALLICON:
8316       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8317       break;
8318   case LV_VIEW_DETAILS:
8319   {
8320       HDLAYOUT hl;
8321       WINDOWPOS wp;
8322
8323       LISTVIEW_CreateHeader( infoPtr );
8324
8325       hl.prc = &infoPtr->rcList;
8326       hl.pwpos = &wp;
8327       SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8328       SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8329                    wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8330       break;
8331   }
8332   case LV_VIEW_LIST:
8333       break;
8334   }
8335
8336   LISTVIEW_UpdateItemSize(infoPtr);
8337   LISTVIEW_UpdateSize(infoPtr);
8338   LISTVIEW_UpdateScroll(infoPtr);
8339   LISTVIEW_InvalidateList(infoPtr);
8340
8341   TRACE("nView=%d\n", nView);
8342
8343   return 1;
8344 }
8345
8346 /* LISTVIEW_SetWorkAreas */
8347
8348 /***
8349  * DESCRIPTION:
8350  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8351  *
8352  * PARAMETER(S):
8353  * [I] first : pointer to first ITEM_INFO to compare
8354  * [I] second : pointer to second ITEM_INFO to compare
8355  * [I] lParam : HWND of control
8356  *
8357  * RETURN:
8358  *   if first comes before second : negative
8359  *   if first comes after second : positive
8360  *   if first and second are equivalent : zero
8361  */
8362 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8363 {
8364   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8365   ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8366   ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8367
8368   /* Forward the call to the client defined callback */
8369   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8370 }
8371
8372 /***
8373  * DESCRIPTION:
8374  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8375  *
8376  * PARAMETER(S):
8377  * [I] first : pointer to first ITEM_INFO to compare
8378  * [I] second : pointer to second ITEM_INFO to compare
8379  * [I] lParam : HWND of control
8380  *
8381  * RETURN:
8382  *   if first comes before second : negative
8383  *   if first comes after second : positive
8384  *   if first and second are equivalent : zero
8385  */
8386 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8387 {
8388   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8389   INT first_idx  = DPA_GetPtrIndex( infoPtr->hdpaItems, first  );
8390   INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8391
8392   /* Forward the call to the client defined callback */
8393   return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8394 }
8395
8396 /***
8397  * DESCRIPTION:
8398  * Sorts the listview items.
8399  *
8400  * PARAMETER(S):
8401  * [I] infoPtr : valid pointer to the listview structure
8402  * [I] pfnCompare : application-defined value
8403  * [I] lParamSort : pointer to comparison callback
8404  * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8405  *
8406  * RETURN:
8407  *   SUCCESS : TRUE
8408  *   FAILURE : FALSE
8409  */
8410 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8411                                LPARAM lParamSort, BOOL IsEx)
8412 {
8413     HDPA hdpaSubItems;
8414     ITEM_INFO *lpItem;
8415     LPVOID selectionMarkItem = NULL;
8416     LPVOID focusedItem = NULL;
8417     int i;
8418
8419     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8420
8421     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8422
8423     if (!pfnCompare) return FALSE;
8424     if (!infoPtr->hdpaItems) return FALSE;
8425
8426     /* if there are 0 or 1 items, there is no need to sort */
8427     if (infoPtr->nItemCount < 2) return TRUE;
8428
8429     /* clear selection */
8430     ranges_clear(infoPtr->selectionRanges);
8431
8432     /* save selection mark and focused item */
8433     if (infoPtr->nSelectionMark >= 0)
8434         selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8435     if (infoPtr->nFocusedItem >= 0)
8436         focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8437
8438     infoPtr->pfnCompare = pfnCompare;
8439     infoPtr->lParamSort = lParamSort;
8440     if (IsEx)
8441         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8442     else
8443         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8444
8445     /* restore selection ranges */
8446     for (i=0; i < infoPtr->nItemCount; i++)
8447     {
8448         hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8449         lpItem = DPA_GetPtr(hdpaSubItems, 0);
8450
8451         if (lpItem->state & LVIS_SELECTED)
8452             ranges_additem(infoPtr->selectionRanges, i);
8453     }
8454     /* restore selection mark and focused item */
8455     infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8456     infoPtr->nFocusedItem   = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8457
8458     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8459
8460     /* refresh the display */
8461     if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8462         LISTVIEW_InvalidateList(infoPtr);
8463
8464     return TRUE;
8465 }
8466
8467 /***
8468  * DESCRIPTION:
8469  * Update theme handle after a theme change.
8470  *
8471  * PARAMETER(S):
8472  * [I] infoPtr : valid pointer to the listview structure
8473  *
8474  * RETURN:
8475  *   SUCCESS : 0
8476  *   FAILURE : something else
8477  */
8478 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8479 {
8480     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8481     CloseThemeData(theme);
8482     OpenThemeData(infoPtr->hwndSelf, themeClass);
8483     return 0;
8484 }
8485
8486 /***
8487  * DESCRIPTION:
8488  * Updates an items or rearranges the listview control.
8489  *
8490  * PARAMETER(S):
8491  * [I] infoPtr : valid pointer to the listview structure
8492  * [I] nItem : item index
8493  *
8494  * RETURN:
8495  *   SUCCESS : TRUE
8496  *   FAILURE : FALSE
8497  */
8498 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8499 {
8500     TRACE("(nItem=%d)\n", nItem);
8501
8502     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8503
8504     /* rearrange with default alignment style */
8505     if (is_autoarrange(infoPtr))
8506         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8507     else
8508         LISTVIEW_InvalidateItem(infoPtr, nItem);
8509
8510     return TRUE;
8511 }
8512
8513 /***
8514  * DESCRIPTION:
8515  * Draw the track line at the place defined in the infoPtr structure.
8516  * The line is drawn with a XOR pen so drawing the line for the second time
8517  * in the same place erases the line.
8518  *
8519  * PARAMETER(S):
8520  * [I] infoPtr : valid pointer to the listview structure
8521  *
8522  * RETURN:
8523  *   SUCCESS : TRUE
8524  *   FAILURE : FALSE
8525  */
8526 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8527 {
8528     HPEN hOldPen;
8529     HDC hdc;
8530     INT oldROP;
8531
8532     if (infoPtr->xTrackLine == -1)
8533         return FALSE;
8534
8535     if (!(hdc = GetDC(infoPtr->hwndSelf)))
8536         return FALSE;
8537     hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8538     oldROP = SetROP2(hdc, R2_XORPEN);
8539     MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8540     LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8541     SetROP2(hdc, oldROP);
8542     SelectObject(hdc, hOldPen);
8543     ReleaseDC(infoPtr->hwndSelf, hdc);
8544     return TRUE;
8545 }
8546
8547 /***
8548  * DESCRIPTION:
8549  * Called when an edit control should be displayed. This function is called after
8550  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8551  *
8552  * PARAMETER(S):
8553  * [I] hwnd : Handle to the listview
8554  * [I] uMsg : WM_TIMER (ignored)
8555  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8556  * [I] dwTimer : The elapsed time (ignored)
8557  *
8558  * RETURN:
8559  *   None.
8560  */
8561 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8562 {
8563     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8564     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8565
8566     KillTimer(hwnd, idEvent);
8567     editItem->fEnabled = FALSE;
8568     /* check if the item is still selected */
8569     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8570         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8571 }
8572
8573 /***
8574  * DESCRIPTION:
8575  * Creates the listview control - the WM_NCCREATE phase.
8576  *
8577  * PARAMETER(S):
8578  * [I] hwnd : window handle
8579  * [I] lpcs : the create parameters
8580  *
8581  * RETURN:
8582  *   Success: TRUE
8583  *   Failure: FALSE
8584  */
8585 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8586 {
8587   LISTVIEW_INFO *infoPtr;
8588   LOGFONTW logFont;
8589
8590   TRACE("(lpcs=%p)\n", lpcs);
8591
8592   /* initialize info pointer */
8593   infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8594   if (!infoPtr) return FALSE;
8595
8596   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8597
8598   infoPtr->hwndSelf = hwnd;
8599   infoPtr->dwStyle = lpcs->style;    /* Note: may be changed in WM_CREATE */
8600   map_style_view(infoPtr);
8601   /* determine the type of structures to use */
8602   infoPtr->hwndNotify = lpcs->hwndParent;
8603   /* infoPtr->notifyFormat will be filled in WM_CREATE */
8604
8605   /* initialize color information  */
8606   infoPtr->clrBk = CLR_NONE;
8607   infoPtr->clrText = CLR_DEFAULT;
8608   infoPtr->clrTextBk = CLR_DEFAULT;
8609   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8610
8611   /* set default values */
8612   infoPtr->nFocusedItem = -1;
8613   infoPtr->nSelectionMark = -1;
8614   infoPtr->nHotItem = -1;
8615   infoPtr->bRedraw = TRUE;
8616   infoPtr->bNoItemMetrics = TRUE;
8617   infoPtr->bDoChangeNotify = TRUE;
8618   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8619   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8620   infoPtr->nEditLabelItem = -1;
8621   infoPtr->nLButtonDownItem = -1;
8622   infoPtr->dwHoverTime = -1; /* default system hover time */
8623   infoPtr->nMeasureItemHeight = 0;
8624   infoPtr->xTrackLine = -1;  /* no track line */
8625   infoPtr->itemEdit.fEnabled = FALSE;
8626   infoPtr->iVersion = COMCTL32_VERSION;
8627
8628   /* get default font (icon title) */
8629   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8630   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8631   infoPtr->hFont = infoPtr->hDefaultFont;
8632   LISTVIEW_SaveTextMetrics(infoPtr);
8633
8634   /* allocate memory for the data structure */
8635   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8636   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8637   if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
8638   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
8639   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
8640   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8641   return TRUE;
8642
8643 fail:
8644     DestroyWindow(infoPtr->hwndHeader);
8645     ranges_destroy(infoPtr->selectionRanges);
8646     DPA_Destroy(infoPtr->hdpaItems);
8647     DPA_Destroy(infoPtr->hdpaItemIds);
8648     DPA_Destroy(infoPtr->hdpaPosX);
8649     DPA_Destroy(infoPtr->hdpaPosY);
8650     DPA_Destroy(infoPtr->hdpaColumns);
8651     Free(infoPtr);
8652     return FALSE;
8653 }
8654
8655 /***
8656  * DESCRIPTION:
8657  * Creates the listview control - the WM_CREATE phase. Most of the data is
8658  * already set up in LISTVIEW_NCCreate
8659  *
8660  * PARAMETER(S):
8661  * [I] hwnd : window handle
8662  * [I] lpcs : the create parameters
8663  *
8664  * RETURN:
8665  *   Success: 0
8666  *   Failure: -1
8667  */
8668 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8669 {
8670   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8671
8672   TRACE("(lpcs=%p)\n", lpcs);
8673
8674   infoPtr->dwStyle = lpcs->style;
8675   map_style_view(infoPtr);
8676
8677   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8678                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8679   /* on error defaulting to ANSI notifications */
8680   if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
8681
8682   if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
8683   {
8684     if (LISTVIEW_CreateHeader(infoPtr) < 0)  return -1;
8685   }
8686   else
8687     infoPtr->hwndHeader = 0;
8688
8689   /* init item size to avoid division by 0 */
8690   LISTVIEW_UpdateItemSize (infoPtr);
8691
8692   if (infoPtr->uView == LV_VIEW_DETAILS)
8693   {
8694     if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8695     {
8696       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8697     }
8698     LISTVIEW_UpdateScroll(infoPtr);
8699     /* send WM_MEASUREITEM notification */
8700     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
8701   }
8702
8703   OpenThemeData(hwnd, themeClass);
8704
8705   /* initialize the icon sizes */
8706   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
8707   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8708   return 0;
8709 }
8710
8711 /***
8712  * DESCRIPTION:
8713  * Destroys the listview control.
8714  *
8715  * PARAMETER(S):
8716  * [I] infoPtr : valid pointer to the listview structure
8717  *
8718  * RETURN:
8719  *   Success: 0
8720  *   Failure: -1
8721  */
8722 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8723 {
8724     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8725     CloseThemeData(theme);
8726     return 0;
8727 }
8728
8729 /***
8730  * DESCRIPTION:
8731  * Enables the listview control.
8732  *
8733  * PARAMETER(S):
8734  * [I] infoPtr : valid pointer to the listview structure
8735  * [I] bEnable : specifies whether to enable or disable the window
8736  *
8737  * RETURN:
8738  *   SUCCESS : TRUE
8739  *   FAILURE : FALSE
8740  */
8741 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8742 {
8743     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8744         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8745     return TRUE;
8746 }
8747
8748 /***
8749  * DESCRIPTION:
8750  * Erases the background of the listview control.
8751  *
8752  * PARAMETER(S):
8753  * [I] infoPtr : valid pointer to the listview structure
8754  * [I] hdc : device context handle
8755  *
8756  * RETURN:
8757  *   SUCCESS : TRUE
8758  *   FAILURE : FALSE
8759  */
8760 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8761 {
8762     RECT rc;
8763
8764     TRACE("(hdc=%p)\n", hdc);
8765
8766     if (!GetClipBox(hdc, &rc)) return FALSE;
8767
8768     if (infoPtr->clrBk == CLR_NONE)
8769         return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
8770
8771     /* for double buffered controls we need to do this during refresh */
8772     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8773
8774     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8775 }
8776         
8777
8778 /***
8779  * DESCRIPTION:
8780  * Helper function for LISTVIEW_[HV]Scroll *only*.
8781  * Performs vertical/horizontal scrolling by a give amount.
8782  *
8783  * PARAMETER(S):
8784  * [I] infoPtr : valid pointer to the listview structure
8785  * [I] dx : amount of horizontal scroll
8786  * [I] dy : amount of vertical scroll
8787  */
8788 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8789 {
8790     /* now we can scroll the list */
8791     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
8792                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8793     /* if we have focus, adjust rect */
8794     OffsetRect(&infoPtr->rcFocus, dx, dy);
8795     UpdateWindow(infoPtr->hwndSelf);
8796 }
8797
8798 /***
8799  * DESCRIPTION:
8800  * Performs vertical scrolling.
8801  *
8802  * PARAMETER(S):
8803  * [I] infoPtr : valid pointer to the listview structure
8804  * [I] nScrollCode : scroll code
8805  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8806  * [I] hScrollWnd  : scrollbar control window handle
8807  *
8808  * RETURN:
8809  * Zero
8810  *
8811  * NOTES:
8812  *   SB_LINEUP/SB_LINEDOWN:
8813  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
8814  *        for LVS_REPORT is 1 line
8815  *        for LVS_LIST cannot occur
8816  *
8817  */
8818 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
8819                                 INT nScrollDiff, HWND hScrollWnd)
8820 {
8821     INT nOldScrollPos, nNewScrollPos;
8822     SCROLLINFO scrollInfo;
8823     BOOL is_an_icon;
8824
8825     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8826         debugscrollcode(nScrollCode), nScrollDiff);
8827
8828     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8829
8830     scrollInfo.cbSize = sizeof(SCROLLINFO);
8831     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8832
8833     is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
8834
8835     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8836
8837     nOldScrollPos = scrollInfo.nPos;
8838     switch (nScrollCode)
8839     {
8840     case SB_INTERNAL:
8841         break;
8842
8843     case SB_LINEUP:
8844         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8845         break;
8846
8847     case SB_LINEDOWN:
8848         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8849         break;
8850
8851     case SB_PAGEUP:
8852         nScrollDiff = -scrollInfo.nPage;
8853         break;
8854
8855     case SB_PAGEDOWN:
8856         nScrollDiff = scrollInfo.nPage;
8857         break;
8858
8859     case SB_THUMBPOSITION:
8860     case SB_THUMBTRACK:
8861         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8862         break;
8863
8864     default:
8865         nScrollDiff = 0;
8866     }
8867
8868     /* quit right away if pos isn't changing */
8869     if (nScrollDiff == 0) return 0;
8870     
8871     /* calculate new position, and handle overflows */
8872     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8873     if (nScrollDiff > 0) {
8874         if (nNewScrollPos < nOldScrollPos ||
8875             nNewScrollPos > scrollInfo.nMax)
8876             nNewScrollPos = scrollInfo.nMax;
8877     } else {
8878         if (nNewScrollPos > nOldScrollPos ||
8879             nNewScrollPos < scrollInfo.nMin)
8880             nNewScrollPos = scrollInfo.nMin;
8881     }
8882
8883     /* set the new position, and reread in case it changed */
8884     scrollInfo.fMask = SIF_POS;
8885     scrollInfo.nPos = nNewScrollPos;
8886     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8887     
8888     /* carry on only if it really changed */
8889     if (nNewScrollPos == nOldScrollPos) return 0;
8890     
8891     /* now adjust to client coordinates */
8892     nScrollDiff = nOldScrollPos - nNewScrollPos;
8893     if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
8894    
8895     /* and scroll the window */ 
8896     scroll_list(infoPtr, 0, nScrollDiff);
8897
8898     return 0;
8899 }
8900
8901 /***
8902  * DESCRIPTION:
8903  * Performs horizontal scrolling.
8904  *
8905  * PARAMETER(S):
8906  * [I] infoPtr : valid pointer to the listview structure
8907  * [I] nScrollCode : scroll code
8908  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8909  * [I] hScrollWnd  : scrollbar control window handle
8910  *
8911  * RETURN:
8912  * Zero
8913  *
8914  * NOTES:
8915  *   SB_LINELEFT/SB_LINERIGHT:
8916  *        for LVS_ICON, LVS_SMALLICON  1 pixel
8917  *        for LVS_REPORT is 1 pixel
8918  *        for LVS_LIST  is 1 column --> which is a 1 because the
8919  *                                      scroll is based on columns not pixels
8920  *
8921  */
8922 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8923                                 INT nScrollDiff, HWND hScrollWnd)
8924 {
8925     INT nOldScrollPos, nNewScrollPos;
8926     SCROLLINFO scrollInfo;
8927
8928     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8929         debugscrollcode(nScrollCode), nScrollDiff);
8930
8931     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8932
8933     scrollInfo.cbSize = sizeof(SCROLLINFO);
8934     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8935
8936     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8937
8938     nOldScrollPos = scrollInfo.nPos;
8939
8940     switch (nScrollCode)
8941     {
8942     case SB_INTERNAL:
8943         break;
8944
8945     case SB_LINELEFT:
8946         nScrollDiff = -1;
8947         break;
8948
8949     case SB_LINERIGHT:
8950         nScrollDiff = 1;
8951         break;
8952
8953     case SB_PAGELEFT:
8954         nScrollDiff = -scrollInfo.nPage;
8955         break;
8956
8957     case SB_PAGERIGHT:
8958         nScrollDiff = scrollInfo.nPage;
8959         break;
8960
8961     case SB_THUMBPOSITION:
8962     case SB_THUMBTRACK:
8963         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8964         break;
8965
8966     default:
8967         nScrollDiff = 0;
8968     }
8969
8970     /* quit right away if pos isn't changing */
8971     if (nScrollDiff == 0) return 0;
8972     
8973     /* calculate new position, and handle overflows */
8974     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8975     if (nScrollDiff > 0) {
8976         if (nNewScrollPos < nOldScrollPos ||
8977             nNewScrollPos > scrollInfo.nMax)
8978             nNewScrollPos = scrollInfo.nMax;
8979     } else {
8980         if (nNewScrollPos > nOldScrollPos ||
8981             nNewScrollPos < scrollInfo.nMin)
8982             nNewScrollPos = scrollInfo.nMin;
8983     }
8984
8985     /* set the new position, and reread in case it changed */
8986     scrollInfo.fMask = SIF_POS;
8987     scrollInfo.nPos = nNewScrollPos;
8988     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8989     
8990     /* carry on only if it really changed */
8991     if (nNewScrollPos == nOldScrollPos) return 0;
8992     
8993     if (infoPtr->uView == LV_VIEW_DETAILS)
8994         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8995       
8996     /* now adjust to client coordinates */
8997     nScrollDiff = nOldScrollPos - nNewScrollPos;
8998     if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
8999    
9000     /* and scroll the window */
9001     scroll_list(infoPtr, nScrollDiff, 0);
9002
9003   return 0;
9004 }
9005
9006 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
9007 {
9008     INT gcWheelDelta = 0;
9009     INT pulScrollLines = 3;
9010     SCROLLINFO scrollInfo;
9011
9012     TRACE("(wheelDelta=%d)\n", wheelDelta);
9013
9014     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
9015     gcWheelDelta -= wheelDelta;
9016
9017     scrollInfo.cbSize = sizeof(SCROLLINFO);
9018     scrollInfo.fMask = SIF_POS;
9019
9020     switch(infoPtr->uView)
9021     {
9022     case LV_VIEW_ICON:
9023     case LV_VIEW_SMALLICON:
9024        /*
9025         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9026         *  should be fixed in the future.
9027         */
9028         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
9029                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
9030         break;
9031
9032     case LV_VIEW_DETAILS:
9033         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
9034         {
9035             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
9036             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
9037             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
9038         }
9039         break;
9040
9041     case LV_VIEW_LIST:
9042         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
9043         break;
9044     }
9045     return 0;
9046 }
9047
9048 /***
9049  * DESCRIPTION:
9050  * ???
9051  *
9052  * PARAMETER(S):
9053  * [I] infoPtr : valid pointer to the listview structure
9054  * [I] nVirtualKey : virtual key
9055  * [I] lKeyData : key data
9056  *
9057  * RETURN:
9058  * Zero
9059  */
9060 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
9061 {
9062   HWND hwndSelf = infoPtr->hwndSelf;
9063   INT nItem = -1;
9064   NMLVKEYDOWN nmKeyDown;
9065
9066   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
9067
9068   /* send LVN_KEYDOWN notification */
9069   nmKeyDown.wVKey = nVirtualKey;
9070   nmKeyDown.flags = 0;
9071   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
9072   if (!IsWindow(hwndSelf))
9073     return 0;
9074
9075   switch (nVirtualKey)
9076   {
9077   case VK_SPACE:
9078     nItem = infoPtr->nFocusedItem;
9079     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
9080         toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
9081     break;
9082
9083   case VK_RETURN:
9084     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
9085     {
9086         if (!notify(infoPtr, NM_RETURN)) return 0;
9087         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
9088     }
9089     break;
9090
9091   case VK_HOME:
9092     if (infoPtr->nItemCount > 0)
9093       nItem = 0;
9094     break;
9095
9096   case VK_END:
9097     if (infoPtr->nItemCount > 0)
9098       nItem = infoPtr->nItemCount - 1;
9099     break;
9100
9101   case VK_LEFT:
9102     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
9103     break;
9104
9105   case VK_UP:
9106     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
9107     break;
9108
9109   case VK_RIGHT:
9110     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
9111     break;
9112
9113   case VK_DOWN:
9114     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
9115     break;
9116
9117   case VK_PRIOR:
9118     if (infoPtr->uView == LV_VIEW_DETAILS)
9119     {
9120       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9121       if (infoPtr->nFocusedItem == topidx)
9122         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
9123       else
9124         nItem = topidx;
9125     }
9126     else
9127       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
9128                                     * LISTVIEW_GetCountPerRow(infoPtr);
9129     if(nItem < 0) nItem = 0;
9130     break;
9131
9132   case VK_NEXT:
9133     if (infoPtr->uView == LV_VIEW_DETAILS)
9134     {
9135       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9136       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
9137       if (infoPtr->nFocusedItem == topidx + cnt - 1)
9138         nItem = infoPtr->nFocusedItem + cnt - 1;
9139       else
9140         nItem = topidx + cnt - 1;
9141     }
9142     else
9143       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
9144                                     * LISTVIEW_GetCountPerRow(infoPtr);
9145     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
9146     break;
9147   }
9148
9149   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
9150       LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
9151
9152   return 0;
9153 }
9154
9155 /***
9156  * DESCRIPTION:
9157  * Kills the focus.
9158  *
9159  * PARAMETER(S):
9160  * [I] infoPtr : valid pointer to the listview structure
9161  *
9162  * RETURN:
9163  * Zero
9164  */
9165 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
9166 {
9167     TRACE("()\n");
9168
9169     /* if we did not have the focus, there's nothing to do */
9170     if (!infoPtr->bFocus) return 0;
9171    
9172     /* send NM_KILLFOCUS notification */
9173     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
9174
9175     /* if we have a focus rectangle, get rid of it */
9176     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
9177     
9178     /* set window focus flag */
9179     infoPtr->bFocus = FALSE;
9180
9181     /* invalidate the selected items before resetting focus flag */
9182     LISTVIEW_InvalidateSelectedItems(infoPtr);
9183     
9184     return 0;
9185 }
9186
9187 /***
9188  * DESCRIPTION:
9189  * Processes double click messages (left mouse button).
9190  *
9191  * PARAMETER(S):
9192  * [I] infoPtr : valid pointer to the listview structure
9193  * [I] wKey : key flag
9194  * [I] x,y : mouse coordinate
9195  *
9196  * RETURN:
9197  * Zero
9198  */
9199 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9200 {
9201     LVHITTESTINFO htInfo;
9202
9203     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9204     
9205     /* Cancel the item edition if any */
9206     if (infoPtr->itemEdit.fEnabled)
9207     {
9208       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
9209       infoPtr->itemEdit.fEnabled = FALSE;
9210     }
9211
9212     /* send NM_RELEASEDCAPTURE notification */
9213     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9214
9215     htInfo.pt.x = x;
9216     htInfo.pt.y = y;
9217
9218     /* send NM_DBLCLK notification */
9219     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
9220     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
9221
9222     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
9223     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
9224
9225     return 0;
9226 }
9227
9228 /***
9229  * DESCRIPTION:
9230  * Processes mouse down messages (left mouse button).
9231  *
9232  * PARAMETERS:
9233  *   infoPtr  [I ] valid pointer to the listview structure
9234  *   wKey     [I ] key flag
9235  *   x,y      [I ] mouse coordinate
9236  *
9237  * RETURN:
9238  *   Zero
9239  */
9240 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9241 {
9242   LVHITTESTINFO lvHitTestInfo;
9243   static BOOL bGroupSelect = TRUE;
9244   POINT pt = { x, y };
9245   INT nItem;
9246
9247   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9248
9249   /* send NM_RELEASEDCAPTURE notification */
9250   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9251
9252   /* set left button down flag and record the click position */
9253   infoPtr->bLButtonDown = TRUE;
9254   infoPtr->ptClickPos = pt;
9255   infoPtr->bDragging = FALSE;
9256
9257   lvHitTestInfo.pt.x = x;
9258   lvHitTestInfo.pt.y = y;
9259
9260   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9261   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
9262   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9263   {
9264     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
9265     {
9266         toggle_checkbox_state(infoPtr, nItem);
9267         return 0;
9268     }
9269
9270     if (infoPtr->dwStyle & LVS_SINGLESEL)
9271     {
9272       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9273         infoPtr->nEditLabelItem = nItem;
9274       else
9275         LISTVIEW_SetSelection(infoPtr, nItem);
9276     }
9277     else
9278     {
9279       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9280       {
9281         if (bGroupSelect)
9282         {
9283           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9284           LISTVIEW_SetItemFocus(infoPtr, nItem);
9285           infoPtr->nSelectionMark = nItem;
9286         }
9287         else
9288         {
9289           LVITEMW item;
9290
9291           item.state = LVIS_SELECTED | LVIS_FOCUSED;
9292           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9293
9294           LISTVIEW_SetItemState(infoPtr,nItem,&item);
9295           infoPtr->nSelectionMark = nItem;
9296         }
9297       }
9298       else if (wKey & MK_CONTROL)
9299       {
9300         LVITEMW item;
9301
9302         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9303         
9304         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9305         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9306         LISTVIEW_SetItemState(infoPtr, nItem, &item);
9307         infoPtr->nSelectionMark = nItem;
9308       }
9309       else  if (wKey & MK_SHIFT)
9310       {
9311         LISTVIEW_SetGroupSelection(infoPtr, nItem);
9312       }
9313       else
9314       {
9315         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9316         {
9317           infoPtr->nEditLabelItem = nItem;
9318           infoPtr->nLButtonDownItem = nItem;
9319
9320           LISTVIEW_SetItemFocus(infoPtr, nItem);
9321         }
9322         else
9323           /* set selection (clears other pre-existing selections) */
9324           LISTVIEW_SetSelection(infoPtr, nItem);
9325       }
9326     }
9327
9328     if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9329         if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9330   }
9331   else
9332   {
9333     /* remove all selections */
9334     if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9335         LISTVIEW_DeselectAll(infoPtr);
9336     ReleaseCapture();
9337   }
9338   
9339   return 0;
9340 }
9341
9342 /***
9343  * DESCRIPTION:
9344  * Processes mouse up messages (left mouse button).
9345  *
9346  * PARAMETERS:
9347  *   infoPtr [I ] valid pointer to the listview structure
9348  *   wKey    [I ] key flag
9349  *   x,y     [I ] mouse coordinate
9350  *
9351  * RETURN:
9352  *   Zero
9353  */
9354 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9355 {
9356     LVHITTESTINFO lvHitTestInfo;
9357     
9358     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9359
9360     if (!infoPtr->bLButtonDown) return 0;
9361
9362     lvHitTestInfo.pt.x = x;
9363     lvHitTestInfo.pt.y = y;
9364
9365     /* send NM_CLICK notification */
9366     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9367     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9368
9369     /* set left button flag */
9370     infoPtr->bLButtonDown = FALSE;
9371
9372     /* set a single selection, reset others */
9373     if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9374         LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9375     infoPtr->nLButtonDownItem = -1;
9376
9377     if (infoPtr->bDragging)
9378     {
9379         infoPtr->bDragging = FALSE;
9380         return 0;
9381     }
9382
9383     /* if we clicked on a selected item, edit the label */
9384     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9385     {
9386         /* we want to make sure the user doesn't want to do a double click. So we will
9387          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9388          */
9389         infoPtr->itemEdit.fEnabled = TRUE;
9390         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9391         SetTimer(infoPtr->hwndSelf,
9392             (UINT_PTR)&infoPtr->itemEdit,
9393             GetDoubleClickTime(),
9394             LISTVIEW_DelayedEditItem);
9395     }
9396
9397     if (!infoPtr->bFocus)
9398         SetFocus(infoPtr->hwndSelf);
9399
9400     return 0;
9401 }
9402
9403 /***
9404  * DESCRIPTION:
9405  * Destroys the listview control (called after WM_DESTROY).
9406  *
9407  * PARAMETER(S):
9408  * [I] infoPtr : valid pointer to the listview structure
9409  *
9410  * RETURN:
9411  * Zero
9412  */
9413 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9414 {
9415   TRACE("()\n");
9416
9417   /* delete all items */
9418   LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9419
9420   /* destroy data structure */
9421   DPA_Destroy(infoPtr->hdpaItems);
9422   DPA_Destroy(infoPtr->hdpaItemIds);
9423   DPA_Destroy(infoPtr->hdpaPosX);
9424   DPA_Destroy(infoPtr->hdpaPosY);
9425   DPA_Destroy(infoPtr->hdpaColumns);
9426   ranges_destroy(infoPtr->selectionRanges);
9427
9428   /* destroy image lists */
9429   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9430   {
9431       if (infoPtr->himlNormal)
9432           ImageList_Destroy(infoPtr->himlNormal);
9433       if (infoPtr->himlSmall)
9434           ImageList_Destroy(infoPtr->himlSmall);
9435       if (infoPtr->himlState)
9436           ImageList_Destroy(infoPtr->himlState);
9437   }
9438
9439   /* destroy font, bkgnd brush */
9440   infoPtr->hFont = 0;
9441   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9442   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9443
9444   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9445
9446   /* free listview info pointer*/
9447   Free(infoPtr);
9448
9449   return 0;
9450 }
9451
9452 /***
9453  * DESCRIPTION:
9454  * Handles notifications from header.
9455  *
9456  * PARAMETER(S):
9457  * [I] infoPtr : valid pointer to the listview structure
9458  * [I] nCtrlId : control identifier
9459  * [I] lpnmh : notification information
9460  *
9461  * RETURN:
9462  * Zero
9463  */
9464 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9465 {
9466     HWND hwndSelf = infoPtr->hwndSelf;
9467     
9468     TRACE("(lpnmh=%p)\n", lpnmh);
9469
9470     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9471     
9472     switch (lpnmh->hdr.code)
9473     {    
9474         case HDN_TRACKW:
9475         case HDN_TRACKA:
9476         {
9477             COLUMN_INFO *lpColumnInfo;
9478             POINT ptOrigin;
9479             INT x;
9480             
9481             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9482                 break;
9483
9484             /* remove the old line (if any) */
9485             LISTVIEW_DrawTrackLine(infoPtr);
9486             
9487             /* compute & draw the new line */
9488             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9489             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9490             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9491             infoPtr->xTrackLine = x + ptOrigin.x;
9492             LISTVIEW_DrawTrackLine(infoPtr);
9493             break;
9494         }
9495         
9496         case HDN_ENDTRACKA:
9497         case HDN_ENDTRACKW:
9498             /* remove the track line (if any) */
9499             LISTVIEW_DrawTrackLine(infoPtr);
9500             infoPtr->xTrackLine = -1;
9501             break;
9502
9503         case HDN_BEGINDRAG:
9504             notify_forward_header(infoPtr, lpnmh);
9505             return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
9506
9507         case HDN_ENDDRAG:
9508             FIXME("Changing column order not implemented\n");
9509             notify_forward_header(infoPtr, lpnmh);
9510             return TRUE;
9511
9512         case HDN_ITEMCHANGINGW:
9513         case HDN_ITEMCHANGINGA:
9514             return notify_forward_header(infoPtr, lpnmh);
9515             
9516         case HDN_ITEMCHANGEDW:
9517         case HDN_ITEMCHANGEDA:
9518         {
9519             COLUMN_INFO *lpColumnInfo;
9520             INT dx, cxy;
9521             
9522             notify_forward_header(infoPtr, lpnmh);
9523             if (!IsWindow(hwndSelf))
9524                 break;
9525
9526             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9527             {
9528                 HDITEMW hdi;
9529     
9530                 hdi.mask = HDI_WIDTH;
9531                 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
9532                 cxy = hdi.cxy;
9533             }
9534             else
9535                 cxy = lpnmh->pitem->cxy;
9536             
9537             /* determine how much we change since the last know position */
9538             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9539             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9540             if (dx != 0)
9541             {
9542                 lpColumnInfo->rcHeader.right += dx;
9543                 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9544                     LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9545                 else
9546                 {
9547                     /* only needs to update the scrolls */
9548                     infoPtr->nItemWidth += dx;
9549                     LISTVIEW_UpdateScroll(infoPtr);
9550                 }
9551                 LISTVIEW_UpdateItemSize(infoPtr);
9552                 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
9553                 {
9554                     POINT ptOrigin;
9555                     RECT rcCol = lpColumnInfo->rcHeader;
9556                     
9557                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9558                     OffsetRect(&rcCol, ptOrigin.x, 0);
9559                     
9560                     rcCol.top = infoPtr->rcList.top;
9561                     rcCol.bottom = infoPtr->rcList.bottom;
9562
9563                     /* resizing left-aligned columns leaves most of the left side untouched */
9564                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9565                     {
9566                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9567                         if (dx > 0)
9568                             nMaxDirty += dx;
9569                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9570                     }
9571
9572                     /* when shrinking the last column clear the now unused field */
9573                     if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9574                     {
9575                         RECT right;
9576
9577                         rcCol.right -= dx;
9578
9579                         /* deal with right from rightmost column area */
9580                         right.left = rcCol.right;
9581                         right.top  = rcCol.top;
9582                         right.bottom = rcCol.bottom;
9583                         right.right = infoPtr->rcList.right;
9584
9585                         LISTVIEW_InvalidateRect(infoPtr, &right);
9586                     }
9587
9588                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9589                 }
9590             }
9591         }
9592         break;
9593
9594         case HDN_ITEMCLICKW:
9595         case HDN_ITEMCLICKA:
9596         {
9597             /* Handle sorting by Header Column */
9598             NMLISTVIEW nmlv;
9599
9600             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9601             nmlv.iItem = -1;
9602             nmlv.iSubItem = lpnmh->iItem;
9603             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9604             notify_forward_header(infoPtr, lpnmh);
9605         }
9606         break;
9607
9608         case HDN_DIVIDERDBLCLICKW:
9609         case HDN_DIVIDERDBLCLICKA:
9610             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9611             notify_forward_header(infoPtr, lpnmh);
9612             break;
9613     }
9614
9615     return 0;
9616 }
9617
9618 /***
9619  * DESCRIPTION:
9620  * Paint non-client area of control.
9621  *
9622  * PARAMETER(S):
9623  * [I] infoPtr : valid pointer to the listview structureof the sender
9624  * [I] region : update region
9625  *
9626  * RETURN:
9627  *  TRUE  - frame was painted
9628  *  FALSE - call default window proc
9629  */
9630 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9631 {
9632     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9633     HDC dc;
9634     RECT r;
9635     HRGN cliprgn;
9636     int cxEdge = GetSystemMetrics (SM_CXEDGE),
9637         cyEdge = GetSystemMetrics (SM_CYEDGE);
9638
9639     if (!theme) return FALSE;
9640
9641     GetWindowRect(infoPtr->hwndSelf, &r);
9642
9643     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9644         r.right - cxEdge, r.bottom - cyEdge);
9645     if (region != (HRGN)1)
9646         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9647     OffsetRect(&r, -r.left, -r.top);
9648
9649     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9650     OffsetRect(&r, -r.left, -r.top);
9651
9652     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9653         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9654     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9655     ReleaseDC(infoPtr->hwndSelf, dc);
9656
9657     /* Call default proc to get the scrollbars etc. painted */
9658     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9659
9660     return TRUE;
9661 }
9662
9663 /***
9664  * DESCRIPTION:
9665  * Determines the type of structure to use.
9666  *
9667  * PARAMETER(S):
9668  * [I] infoPtr : valid pointer to the listview structureof the sender
9669  * [I] hwndFrom : listview window handle
9670  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9671  *
9672  * RETURN:
9673  * Zero
9674  */
9675 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9676 {
9677     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9678
9679     if (nCommand == NF_REQUERY)
9680         infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9681
9682     return infoPtr->notifyFormat;
9683 }
9684
9685 /***
9686  * DESCRIPTION:
9687  * Paints/Repaints the listview control.
9688  *
9689  * PARAMETER(S):
9690  * [I] infoPtr : valid pointer to the listview structure
9691  * [I] hdc : device context handle
9692  *
9693  * RETURN:
9694  * Zero
9695  */
9696 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9697 {
9698     TRACE("(hdc=%p)\n", hdc);
9699
9700     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9701     {
9702         infoPtr->bNoItemMetrics = FALSE;
9703         LISTVIEW_UpdateItemSize(infoPtr);
9704         if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
9705             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9706         LISTVIEW_UpdateScroll(infoPtr);
9707     }
9708
9709     if (infoPtr->hwndHeader)  UpdateWindow(infoPtr->hwndHeader);
9710
9711     if (hdc) 
9712         LISTVIEW_Refresh(infoPtr, hdc, NULL);
9713     else
9714     {
9715         PAINTSTRUCT ps;
9716
9717         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9718         if (!hdc) return 1;
9719         LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9720         EndPaint(infoPtr->hwndSelf, &ps);
9721     }
9722
9723     return 0;
9724 }
9725
9726
9727 /***
9728  * DESCRIPTION:
9729  * Paints/Repaints the listview control.
9730  *
9731  * PARAMETER(S):
9732  * [I] infoPtr : valid pointer to the listview structure
9733  * [I] hdc : device context handle
9734  * [I] options : drawing options
9735  *
9736  * RETURN:
9737  * Zero
9738  */
9739 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9740 {
9741     FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9742
9743     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9744         return 0;
9745
9746     if (options & PRF_ERASEBKGND)
9747         LISTVIEW_EraseBkgnd(infoPtr, hdc);
9748
9749     if (options & PRF_CLIENT)
9750         LISTVIEW_Paint(infoPtr, hdc);
9751
9752     return 0;
9753 }
9754
9755
9756 /***
9757  * DESCRIPTION:
9758  * Processes double click messages (right mouse button).
9759  *
9760  * PARAMETER(S):
9761  * [I] infoPtr : valid pointer to the listview structure
9762  * [I] wKey : key flag
9763  * [I] x,y : mouse coordinate
9764  *
9765  * RETURN:
9766  * Zero
9767  */
9768 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9769 {
9770     LVHITTESTINFO lvHitTestInfo;
9771     
9772     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9773
9774     /* send NM_RELEASEDCAPTURE notification */
9775     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9776
9777     /* send NM_RDBLCLK notification */
9778     lvHitTestInfo.pt.x = x;
9779     lvHitTestInfo.pt.y = y;
9780     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9781     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9782
9783     return 0;
9784 }
9785
9786 /***
9787  * DESCRIPTION:
9788  * Processes mouse down messages (right mouse button).
9789  *
9790  * PARAMETER(S):
9791  * [I] infoPtr : valid pointer to the listview structure
9792  * [I] wKey : key flag
9793  * [I] x,y : mouse coordinate
9794  *
9795  * RETURN:
9796  * Zero
9797  */
9798 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9799 {
9800     LVHITTESTINFO lvHitTestInfo;
9801     INT nItem;
9802
9803     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9804
9805     /* send NM_RELEASEDCAPTURE notification */
9806     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9807
9808     /* make sure the listview control window has the focus */
9809     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9810
9811     /* set right button down flag */
9812     infoPtr->bRButtonDown = TRUE;
9813
9814     /* determine the index of the selected item */
9815     lvHitTestInfo.pt.x = x;
9816     lvHitTestInfo.pt.y = y;
9817     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9818   
9819     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9820     {
9821         LISTVIEW_SetItemFocus(infoPtr, nItem);
9822         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9823             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9824             LISTVIEW_SetSelection(infoPtr, nItem);
9825     }
9826     else
9827     {
9828         LISTVIEW_DeselectAll(infoPtr);
9829     }
9830
9831     return 0;
9832 }
9833
9834 /***
9835  * DESCRIPTION:
9836  * Processes mouse up messages (right mouse button).
9837  *
9838  * PARAMETER(S):
9839  * [I] infoPtr : valid pointer to the listview structure
9840  * [I] wKey : key flag
9841  * [I] x,y : mouse coordinate
9842  *
9843  * RETURN:
9844  * Zero
9845  */
9846 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9847 {
9848     LVHITTESTINFO lvHitTestInfo;
9849     POINT pt;
9850
9851     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9852
9853     if (!infoPtr->bRButtonDown) return 0;
9854  
9855     /* set button flag */
9856     infoPtr->bRButtonDown = FALSE;
9857
9858     /* Send NM_RCLICK notification */
9859     lvHitTestInfo.pt.x = x;
9860     lvHitTestInfo.pt.y = y;
9861     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9862     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9863
9864     /* Change to screen coordinate for WM_CONTEXTMENU */
9865     pt = lvHitTestInfo.pt;
9866     ClientToScreen(infoPtr->hwndSelf, &pt);
9867
9868     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9869     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9870                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9871
9872     return 0;
9873 }
9874
9875
9876 /***
9877  * DESCRIPTION:
9878  * Sets the cursor.
9879  *
9880  * PARAMETER(S):
9881  * [I] infoPtr : valid pointer to the listview structure
9882  * [I] hwnd : window handle of window containing the cursor
9883  * [I] nHittest : hit-test code
9884  * [I] wMouseMsg : ideintifier of the mouse message
9885  *
9886  * RETURN:
9887  * TRUE if cursor is set
9888  * FALSE otherwise
9889  */
9890 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9891 {
9892     LVHITTESTINFO lvHitTestInfo;
9893
9894     if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9895
9896     if(!infoPtr->hHotCursor)  return FALSE;
9897
9898     GetCursorPos(&lvHitTestInfo.pt);
9899     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9900
9901     SetCursor(infoPtr->hHotCursor);
9902
9903     return TRUE;
9904 }
9905
9906 /***
9907  * DESCRIPTION:
9908  * Sets the focus.
9909  *
9910  * PARAMETER(S):
9911  * [I] infoPtr : valid pointer to the listview structure
9912  * [I] hwndLoseFocus : handle of previously focused window
9913  *
9914  * RETURN:
9915  * Zero
9916  */
9917 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9918 {
9919     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9920
9921     /* if we have the focus already, there's nothing to do */
9922     if (infoPtr->bFocus) return 0;
9923    
9924     /* send NM_SETFOCUS notification */
9925     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9926
9927     /* set window focus flag */
9928     infoPtr->bFocus = TRUE;
9929
9930     /* put the focus rect back on */
9931     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9932
9933     /* redraw all visible selected items */
9934     LISTVIEW_InvalidateSelectedItems(infoPtr);
9935
9936     return 0;
9937 }
9938
9939 /***
9940  * DESCRIPTION:
9941  * Sets the font.
9942  *
9943  * PARAMETER(S):
9944  * [I] infoPtr : valid pointer to the listview structure
9945  * [I] fRedraw : font handle
9946  * [I] fRedraw : redraw flag
9947  *
9948  * RETURN:
9949  * Zero
9950  */
9951 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9952 {
9953     HFONT oldFont = infoPtr->hFont;
9954
9955     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9956
9957     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9958     if (infoPtr->hFont == oldFont) return 0;
9959     
9960     LISTVIEW_SaveTextMetrics(infoPtr);
9961
9962     if (infoPtr->uView == LV_VIEW_DETAILS)
9963     {
9964         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9965         LISTVIEW_UpdateSize(infoPtr);
9966         LISTVIEW_UpdateScroll(infoPtr);
9967     }
9968
9969     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9970
9971     return 0;
9972 }
9973
9974 /***
9975  * DESCRIPTION:
9976  * Message handling for WM_SETREDRAW.
9977  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9978  *
9979  * PARAMETER(S):
9980  * [I] infoPtr : valid pointer to the listview structure
9981  * [I] bRedraw: state of redraw flag
9982  *
9983  * RETURN:
9984  * DefWinProc return value
9985  */
9986 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9987 {
9988     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9989
9990     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9991     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9992
9993     infoPtr->bRedraw = bRedraw;
9994
9995     if(!bRedraw) return 0;
9996     
9997     if (is_autoarrange(infoPtr))
9998         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9999     LISTVIEW_UpdateScroll(infoPtr);
10000
10001     /* despite what the WM_SETREDRAW docs says, apps expect us
10002      * to invalidate the listview here... stupid! */
10003     LISTVIEW_InvalidateList(infoPtr);
10004
10005     return 0;
10006 }
10007
10008 /***
10009  * DESCRIPTION:
10010  * Resizes the listview control. This function processes WM_SIZE
10011  * messages.  At this time, the width and height are not used.
10012  *
10013  * PARAMETER(S):
10014  * [I] infoPtr : valid pointer to the listview structure
10015  * [I] Width : new width
10016  * [I] Height : new height
10017  *
10018  * RETURN:
10019  * Zero
10020  */
10021 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
10022 {
10023     RECT rcOld = infoPtr->rcList;
10024
10025     TRACE("(width=%d, height=%d)\n", Width, Height);
10026
10027     LISTVIEW_UpdateSize(infoPtr);
10028     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
10029   
10030     /* do not bother with display related stuff if we're not redrawing */ 
10031     if (!is_redrawing(infoPtr)) return 0;
10032     
10033     if (is_autoarrange(infoPtr)) 
10034         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10035
10036     LISTVIEW_UpdateScroll(infoPtr);
10037
10038     /* refresh all only for lists whose height changed significantly */
10039     if ((infoPtr->uView == LV_VIEW_LIST) &&
10040         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
10041         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
10042         LISTVIEW_InvalidateList(infoPtr);
10043
10044   return 0;
10045 }
10046
10047 /***
10048  * DESCRIPTION:
10049  * Sets the size information.
10050  *
10051  * PARAMETER(S):
10052  * [I] infoPtr : valid pointer to the listview structure
10053  *
10054  * RETURN:
10055  *  None
10056  */
10057 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
10058 {
10059     TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
10060     
10061     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
10062
10063     if (infoPtr->uView == LV_VIEW_LIST)
10064     {
10065         /* Apparently the "LIST" style is supposed to have the same
10066          * number of items in a column even if there is no scroll bar.
10067          * Since if a scroll bar already exists then the bottom is already
10068          * reduced, only reduce if the scroll bar does not currently exist.
10069          * The "2" is there to mimic the native control. I think it may be
10070          * related to either padding or edges.  (GLA 7/2002)
10071          */
10072         if (!(infoPtr->dwStyle & WS_HSCROLL))
10073             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
10074         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
10075     }
10076     else if (infoPtr->uView == LV_VIEW_DETAILS)
10077     {
10078         HDLAYOUT hl;
10079         WINDOWPOS wp;
10080
10081         hl.prc = &infoPtr->rcList;
10082         hl.pwpos = &wp;
10083         SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10084         TRACE("  wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
10085         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
10086                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10087                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10088         TRACE("  after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
10089
10090         infoPtr->rcList.top = max(wp.cy, 0);
10091         infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
10092     }
10093
10094     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
10095 }
10096
10097 /***
10098  * DESCRIPTION:
10099  * Processes WM_STYLECHANGED messages.
10100  *
10101  * PARAMETER(S):
10102  * [I] infoPtr : valid pointer to the listview structure
10103  * [I] wStyleType : window style type (normal or extended)
10104  * [I] lpss : window style information
10105  *
10106  * RETURN:
10107  * Zero
10108  */
10109 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10110                                  const STYLESTRUCT *lpss)
10111 {
10112     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
10113     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
10114     UINT style;
10115
10116     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10117           wStyleType, lpss->styleOld, lpss->styleNew);
10118
10119     if (wStyleType != GWL_STYLE) return 0;
10120
10121     infoPtr->dwStyle = lpss->styleNew;
10122     map_style_view(infoPtr);
10123
10124     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
10125         ((lpss->styleNew & WS_HSCROLL) == 0))
10126        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
10127
10128     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
10129         ((lpss->styleNew & WS_VSCROLL) == 0))
10130        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
10131
10132     if (uNewView != uOldView)
10133     {
10134         SIZE oldIconSize = infoPtr->iconSize;
10135         HIMAGELIST himl;
10136     
10137         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
10138         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
10139
10140         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
10141         SetRectEmpty(&infoPtr->rcFocus);
10142
10143         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
10144         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
10145     
10146         if (uNewView == LVS_ICON)
10147         {
10148             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
10149             {
10150                 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
10151                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
10152                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
10153             }
10154         }
10155         else if (uNewView == LVS_REPORT)
10156         {
10157             HDLAYOUT hl;
10158             WINDOWPOS wp;
10159
10160             LISTVIEW_CreateHeader( infoPtr );
10161
10162             hl.prc = &infoPtr->rcList;
10163             hl.pwpos = &wp;
10164             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10165             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
10166                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10167                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10168         }
10169
10170         LISTVIEW_UpdateItemSize(infoPtr);
10171     }
10172
10173     if (uNewView == LVS_REPORT)
10174     {
10175         if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
10176         {
10177             if (lpss->styleNew & LVS_NOCOLUMNHEADER)
10178             {
10179                 /* Turn off the header control */
10180                 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
10181                 TRACE("Hide header control, was 0x%08x\n", style);
10182                 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
10183             } else {
10184                 /* Turn on the header control */
10185                 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
10186                 {
10187                     TRACE("Show header control, was 0x%08x\n", style);
10188                     SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
10189                 }
10190             }
10191         }
10192     }
10193
10194     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
10195          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
10196          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10197
10198     /* update the size of the client area */
10199     LISTVIEW_UpdateSize(infoPtr);
10200
10201     /* add scrollbars if needed */
10202     LISTVIEW_UpdateScroll(infoPtr);
10203
10204     /* invalidate client area + erase background */
10205     LISTVIEW_InvalidateList(infoPtr);
10206
10207     return 0;
10208 }
10209
10210 /***
10211  * DESCRIPTION:
10212  * Processes WM_STYLECHANGING messages.
10213  *
10214  * PARAMETER(S):
10215  * [I] infoPtr : valid pointer to the listview structure
10216  * [I] wStyleType : window style type (normal or extended)
10217  * [I0] lpss : window style information
10218  *
10219  * RETURN:
10220  * Zero
10221  */
10222 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10223                                   STYLESTRUCT *lpss)
10224 {
10225     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10226           wStyleType, lpss->styleOld, lpss->styleNew);
10227
10228     /* don't forward LVS_OWNERDATA only if not already set to */
10229     if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
10230     {
10231         if (lpss->styleOld & LVS_OWNERDATA)
10232             lpss->styleNew |= LVS_OWNERDATA;
10233         else
10234             lpss->styleNew &= ~LVS_OWNERDATA;
10235     }
10236
10237     return 0;
10238 }
10239
10240 /***
10241  * DESCRIPTION:
10242  * Processes WM_SHOWWINDOW messages.
10243  *
10244  * PARAMETER(S):
10245  * [I] infoPtr : valid pointer to the listview structure
10246  * [I] bShown  : window is being shown (FALSE when hidden)
10247  * [I] iStatus : window show status
10248  *
10249  * RETURN:
10250  * Zero
10251  */
10252 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
10253 {
10254   /* header delayed creation */
10255   if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
10256   {
10257     LISTVIEW_CreateHeader(infoPtr);
10258
10259     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
10260       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
10261   }
10262
10263   return 0;
10264 }
10265
10266 /***
10267  * DESCRIPTION:
10268  * Processes CCM_GETVERSION messages.
10269  *
10270  * PARAMETER(S):
10271  * [I] infoPtr : valid pointer to the listview structure
10272  *
10273  * RETURN:
10274  * Current version
10275  */
10276 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
10277 {
10278   return infoPtr->iVersion;
10279 }
10280
10281 /***
10282  * DESCRIPTION:
10283  * Processes CCM_SETVERSION messages.
10284  *
10285  * PARAMETER(S):
10286  * [I] infoPtr  : valid pointer to the listview structure
10287  * [I] iVersion : version to be set
10288  *
10289  * RETURN:
10290  * -1 when requested version is greater than DLL version;
10291  * previous version otherwise
10292  */
10293 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10294 {
10295   INT iOldVersion = infoPtr->iVersion;
10296
10297   if (iVersion > COMCTL32_VERSION)
10298     return -1;
10299
10300   infoPtr->iVersion = iVersion;
10301
10302   TRACE("new version %d\n", iVersion);
10303
10304   return iOldVersion;
10305 }
10306
10307 /***
10308  * DESCRIPTION:
10309  * Window procedure of the listview control.
10310  *
10311  */
10312 static LRESULT WINAPI
10313 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10314 {
10315   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10316
10317   TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10318
10319   if (!infoPtr && (uMsg != WM_NCCREATE))
10320     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10321
10322   switch (uMsg)
10323   {
10324   case LVM_APPROXIMATEVIEWRECT:
10325     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10326                                         LOWORD(lParam), HIWORD(lParam));
10327   case LVM_ARRANGE:
10328     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10329
10330   case LVM_CANCELEDITLABEL:
10331     return LISTVIEW_CancelEditLabel(infoPtr);
10332
10333   case LVM_CREATEDRAGIMAGE:
10334     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10335
10336   case LVM_DELETEALLITEMS:
10337     return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10338
10339   case LVM_DELETECOLUMN:
10340     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10341
10342   case LVM_DELETEITEM:
10343     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10344
10345   case LVM_EDITLABELW:
10346     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
10347
10348   case LVM_EDITLABELA:
10349     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
10350
10351   /* case LVM_ENABLEGROUPVIEW: */
10352
10353   case LVM_ENSUREVISIBLE:
10354     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10355
10356   case LVM_FINDITEMW:
10357     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10358
10359   case LVM_FINDITEMA:
10360     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10361
10362   case LVM_GETBKCOLOR:
10363     return infoPtr->clrBk;
10364
10365   /* case LVM_GETBKIMAGE: */
10366
10367   case LVM_GETCALLBACKMASK:
10368     return infoPtr->uCallbackMask;
10369
10370   case LVM_GETCOLUMNA:
10371     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10372
10373   case LVM_GETCOLUMNW:
10374     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10375
10376   case LVM_GETCOLUMNORDERARRAY:
10377     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10378
10379   case LVM_GETCOLUMNWIDTH:
10380     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10381
10382   case LVM_GETCOUNTPERPAGE:
10383     return LISTVIEW_GetCountPerPage(infoPtr);
10384
10385   case LVM_GETEDITCONTROL:
10386     return (LRESULT)infoPtr->hwndEdit;
10387
10388   case LVM_GETEXTENDEDLISTVIEWSTYLE:
10389     return infoPtr->dwLvExStyle;
10390
10391   /* case LVM_GETGROUPINFO: */
10392
10393   /* case LVM_GETGROUPMETRICS: */
10394
10395   case LVM_GETHEADER:
10396     return (LRESULT)infoPtr->hwndHeader;
10397
10398   case LVM_GETHOTCURSOR:
10399     return (LRESULT)infoPtr->hHotCursor;
10400
10401   case LVM_GETHOTITEM:
10402     return infoPtr->nHotItem;
10403
10404   case LVM_GETHOVERTIME:
10405     return infoPtr->dwHoverTime;
10406
10407   case LVM_GETIMAGELIST:
10408     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10409
10410   /* case LVM_GETINSERTMARK: */
10411
10412   /* case LVM_GETINSERTMARKCOLOR: */
10413
10414   /* case LVM_GETINSERTMARKRECT: */
10415
10416   case LVM_GETISEARCHSTRINGA:
10417   case LVM_GETISEARCHSTRINGW:
10418     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10419     return FALSE;
10420
10421   case LVM_GETITEMA:
10422     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
10423
10424   case LVM_GETITEMW:
10425     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
10426
10427   case LVM_GETITEMCOUNT:
10428     return infoPtr->nItemCount;
10429
10430   case LVM_GETITEMPOSITION:
10431     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10432
10433   case LVM_GETITEMRECT:
10434     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10435
10436   case LVM_GETITEMSPACING:
10437     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10438
10439   case LVM_GETITEMSTATE:
10440     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10441
10442   case LVM_GETITEMTEXTA:
10443     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10444
10445   case LVM_GETITEMTEXTW:
10446     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10447
10448   case LVM_GETNEXTITEM:
10449     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10450
10451   case LVM_GETNUMBEROFWORKAREAS:
10452     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10453     return 1;
10454
10455   case LVM_GETORIGIN:
10456     if (!lParam) return FALSE;
10457     if (infoPtr->uView == LV_VIEW_DETAILS ||
10458         infoPtr->uView == LV_VIEW_LIST) return FALSE;
10459     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
10460     return TRUE;
10461
10462   /* case LVM_GETOUTLINECOLOR: */
10463
10464   /* case LVM_GETSELECTEDCOLUMN: */
10465
10466   case LVM_GETSELECTEDCOUNT:
10467     return LISTVIEW_GetSelectedCount(infoPtr);
10468
10469   case LVM_GETSELECTIONMARK:
10470     return infoPtr->nSelectionMark;
10471
10472   case LVM_GETSTRINGWIDTHA:
10473     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10474
10475   case LVM_GETSTRINGWIDTHW:
10476     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10477
10478   case LVM_GETSUBITEMRECT:
10479     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10480
10481   case LVM_GETTEXTBKCOLOR:
10482     return infoPtr->clrTextBk;
10483
10484   case LVM_GETTEXTCOLOR:
10485     return infoPtr->clrText;
10486
10487   /* case LVM_GETTILEINFO: */
10488
10489   /* case LVM_GETTILEVIEWINFO: */
10490
10491   case LVM_GETTOOLTIPS:
10492     if( !infoPtr->hwndToolTip )
10493         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10494     return (LRESULT)infoPtr->hwndToolTip;
10495
10496   case LVM_GETTOPINDEX:
10497     return LISTVIEW_GetTopIndex(infoPtr);
10498
10499   case LVM_GETUNICODEFORMAT:
10500     return (infoPtr->notifyFormat == NFR_UNICODE);
10501
10502   case LVM_GETVIEW:
10503     return infoPtr->uView;
10504
10505   case LVM_GETVIEWRECT:
10506     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10507
10508   case LVM_GETWORKAREAS:
10509     FIXME("LVM_GETWORKAREAS: unimplemented\n");
10510     return FALSE;
10511
10512   /* case LVM_HASGROUP: */
10513
10514   case LVM_HITTEST:
10515     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
10516
10517   case LVM_INSERTCOLUMNA:
10518     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10519
10520   case LVM_INSERTCOLUMNW:
10521     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10522
10523   /* case LVM_INSERTGROUP: */
10524
10525   /* case LVM_INSERTGROUPSORTED: */
10526
10527   case LVM_INSERTITEMA:
10528     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10529
10530   case LVM_INSERTITEMW:
10531     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10532
10533   /* case LVM_INSERTMARKHITTEST: */
10534
10535   /* case LVM_ISGROUPVIEWENABLED: */
10536
10537   case LVM_ISITEMVISIBLE:
10538     return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
10539
10540   case LVM_MAPIDTOINDEX:
10541     return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
10542
10543   case LVM_MAPINDEXTOID:
10544     return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
10545
10546   /* case LVM_MOVEGROUP: */
10547
10548   /* case LVM_MOVEITEMTOGROUP: */
10549
10550   case LVM_REDRAWITEMS:
10551     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10552
10553   /* case LVM_REMOVEALLGROUPS: */
10554
10555   /* case LVM_REMOVEGROUP: */
10556
10557   case LVM_SCROLL:
10558     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10559
10560   case LVM_SETBKCOLOR:
10561     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10562
10563   /* case LVM_SETBKIMAGE: */
10564
10565   case LVM_SETCALLBACKMASK:
10566     infoPtr->uCallbackMask = (UINT)wParam;
10567     return TRUE;
10568
10569   case LVM_SETCOLUMNA:
10570     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10571
10572   case LVM_SETCOLUMNW:
10573     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10574
10575   case LVM_SETCOLUMNORDERARRAY:
10576     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10577
10578   case LVM_SETCOLUMNWIDTH:
10579     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10580
10581   case LVM_SETEXTENDEDLISTVIEWSTYLE:
10582     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10583
10584   /* case LVM_SETGROUPINFO: */
10585
10586   /* case LVM_SETGROUPMETRICS: */
10587
10588   case LVM_SETHOTCURSOR:
10589     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10590
10591   case LVM_SETHOTITEM:
10592     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10593
10594   case LVM_SETHOVERTIME:
10595     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10596
10597   case LVM_SETICONSPACING:
10598     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10599
10600   case LVM_SETIMAGELIST:
10601     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10602
10603   /* case LVM_SETINFOTIP: */
10604
10605   /* case LVM_SETINSERTMARK: */
10606
10607   /* case LVM_SETINSERTMARKCOLOR: */
10608
10609   case LVM_SETITEMA:
10610   case LVM_SETITEMW:
10611     {
10612         if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10613         return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10614     }
10615
10616   case LVM_SETITEMCOUNT:
10617     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10618
10619   case LVM_SETITEMPOSITION:
10620     {
10621         POINT pt;
10622         pt.x = (short)LOWORD(lParam);
10623         pt.y = (short)HIWORD(lParam);
10624         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10625     }
10626
10627   case LVM_SETITEMPOSITION32:
10628     if (lParam == 0) return FALSE;
10629     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10630
10631   case LVM_SETITEMSTATE:
10632     if (lParam == 0) return FALSE;
10633     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10634
10635   case LVM_SETITEMTEXTA:
10636     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10637
10638   case LVM_SETITEMTEXTW:
10639     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10640
10641   /* case LVM_SETOUTLINECOLOR: */
10642
10643   /* case LVM_SETSELECTEDCOLUMN: */
10644
10645   case LVM_SETSELECTIONMARK:
10646     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10647
10648   case LVM_SETTEXTBKCOLOR:
10649     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10650
10651   case LVM_SETTEXTCOLOR:
10652     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10653
10654   /* case LVM_SETTILEINFO: */
10655
10656   /* case LVM_SETTILEVIEWINFO: */
10657
10658   /* case LVM_SETTILEWIDTH: */
10659
10660   case LVM_SETTOOLTIPS:
10661     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10662
10663   case LVM_SETUNICODEFORMAT:
10664     return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10665
10666   case LVM_SETVIEW:
10667     return LISTVIEW_SetView(infoPtr, wParam);
10668
10669   /* case LVM_SETWORKAREAS: */
10670
10671   /* case LVM_SORTGROUPS: */
10672
10673   case LVM_SORTITEMS:
10674     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10675
10676   case LVM_SORTITEMSEX:
10677     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10678
10679   case LVM_SUBITEMHITTEST:
10680     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10681
10682   case LVM_UPDATE:
10683     return LISTVIEW_Update(infoPtr, (INT)wParam);
10684
10685   case CCM_GETVERSION:
10686     return LISTVIEW_GetVersion(infoPtr);
10687
10688   case CCM_SETVERSION:
10689     return LISTVIEW_SetVersion(infoPtr, wParam);
10690
10691   case WM_CHAR:
10692     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10693
10694   case WM_COMMAND:
10695     return LISTVIEW_Command(infoPtr, wParam, lParam);
10696
10697   case WM_NCCREATE:
10698     return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10699
10700   case WM_CREATE:
10701     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10702
10703   case WM_DESTROY:
10704     return LISTVIEW_Destroy(infoPtr);
10705
10706   case WM_ENABLE:
10707     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10708
10709   case WM_ERASEBKGND:
10710     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10711
10712   case WM_GETDLGCODE:
10713     return DLGC_WANTCHARS | DLGC_WANTARROWS;
10714
10715   case WM_GETFONT:
10716     return (LRESULT)infoPtr->hFont;
10717
10718   case WM_HSCROLL:
10719     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10720
10721   case WM_KEYDOWN:
10722     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10723
10724   case WM_KILLFOCUS:
10725     return LISTVIEW_KillFocus(infoPtr);
10726
10727   case WM_LBUTTONDBLCLK:
10728     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10729
10730   case WM_LBUTTONDOWN:
10731     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10732
10733   case WM_LBUTTONUP:
10734     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10735
10736   case WM_MOUSEMOVE:
10737     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10738
10739   case WM_MOUSEHOVER:
10740     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10741
10742   case WM_NCDESTROY:
10743     return LISTVIEW_NCDestroy(infoPtr);
10744
10745   case WM_NCPAINT:
10746     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10747         return 0;
10748     goto fwd_msg;
10749
10750   case WM_NOTIFY:
10751     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10752         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10753     else return 0;
10754
10755   case WM_NOTIFYFORMAT:
10756     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10757
10758   case WM_PRINTCLIENT:
10759     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10760
10761   case WM_PAINT:
10762     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10763
10764   case WM_RBUTTONDBLCLK:
10765     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10766
10767   case WM_RBUTTONDOWN:
10768     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10769
10770   case WM_RBUTTONUP:
10771     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10772
10773   case WM_SETCURSOR:
10774     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10775       return TRUE;
10776     goto fwd_msg;
10777
10778   case WM_SETFOCUS:
10779     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10780
10781   case WM_SETFONT:
10782     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10783
10784   case WM_SETREDRAW:
10785     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10786
10787   case WM_SHOWWINDOW:
10788     LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10789     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10790
10791   case WM_SIZE:
10792     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10793
10794   case WM_STYLECHANGED:
10795     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10796
10797   case WM_STYLECHANGING:
10798     return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10799
10800   case WM_SYSCOLORCHANGE:
10801     COMCTL32_RefreshSysColors();
10802     return 0;
10803
10804 /*      case WM_TIMER: */
10805   case WM_THEMECHANGED:
10806     return LISTVIEW_ThemeChanged(infoPtr);
10807
10808   case WM_VSCROLL:
10809     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10810
10811   case WM_MOUSEWHEEL:
10812       if (wParam & (MK_SHIFT | MK_CONTROL))
10813           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10814       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10815
10816   case WM_WINDOWPOSCHANGED:
10817       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
10818       {
10819       SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10820                    SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10821
10822       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
10823       {
10824           if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
10825       }
10826
10827           LISTVIEW_UpdateSize(infoPtr);
10828           LISTVIEW_UpdateScroll(infoPtr);
10829       }
10830       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10831
10832 /*      case WM_WININICHANGE: */
10833
10834   default:
10835     if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10836       ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10837
10838   fwd_msg:
10839     /* call default window procedure */
10840     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10841   }
10842
10843 }
10844
10845 /***
10846  * DESCRIPTION:
10847  * Registers the window class.
10848  *
10849  * PARAMETER(S):
10850  * None
10851  *
10852  * RETURN:
10853  * None
10854  */
10855 void LISTVIEW_Register(void)
10856 {
10857     WNDCLASSW wndClass;
10858
10859     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10860     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10861     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10862     wndClass.cbClsExtra = 0;
10863     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10864     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10865     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10866     wndClass.lpszClassName = WC_LISTVIEWW;
10867     RegisterClassW(&wndClass);
10868 }
10869
10870 /***
10871  * DESCRIPTION:
10872  * Unregisters the window class.
10873  *
10874  * PARAMETER(S):
10875  * None
10876  *
10877  * RETURN:
10878  * None
10879  */
10880 void LISTVIEW_Unregister(void)
10881 {
10882     UnregisterClassW(WC_LISTVIEWW, NULL);
10883 }
10884
10885 /***
10886  * DESCRIPTION:
10887  * Handle any WM_COMMAND messages
10888  *
10889  * PARAMETER(S):
10890  * [I] infoPtr : valid pointer to the listview structure
10891  * [I] wParam : the first message parameter
10892  * [I] lParam : the second message parameter
10893  *
10894  * RETURN:
10895  *   Zero.
10896  */
10897 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10898 {
10899     switch (HIWORD(wParam))
10900     {
10901         case EN_UPDATE:
10902         {
10903             /*
10904              * Adjust the edit window size
10905              */
10906             WCHAR buffer[1024];
10907             HDC           hdc = GetDC(infoPtr->hwndEdit);
10908             HFONT         hFont, hOldFont = 0;
10909             RECT          rect;
10910             SIZE          sz;
10911
10912             if (!infoPtr->hwndEdit || !hdc) return 0;
10913             GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10914             GetWindowRect(infoPtr->hwndEdit, &rect);
10915
10916             /* Select font to get the right dimension of the string */
10917             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10918             if(hFont != 0)
10919             {
10920                 hOldFont = SelectObject(hdc, hFont);
10921             }
10922
10923             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10924             {
10925                 TEXTMETRICW textMetric;
10926
10927                 /* Add Extra spacing for the next character */
10928                 GetTextMetricsW(hdc, &textMetric);
10929                 sz.cx += (textMetric.tmMaxCharWidth * 2);
10930
10931                 SetWindowPos (
10932                     infoPtr->hwndEdit,
10933                     HWND_TOP,
10934                     0,
10935                     0,
10936                     sz.cx,
10937                     rect.bottom - rect.top,
10938                     SWP_DRAWFRAME|SWP_NOMOVE);
10939             }
10940             if(hFont != 0)
10941                 SelectObject(hdc, hOldFont);
10942
10943             ReleaseDC(infoPtr->hwndEdit, hdc);
10944
10945             break;
10946         }
10947         case EN_KILLFOCUS:
10948         {
10949             LISTVIEW_CancelEditLabel(infoPtr);
10950         }
10951
10952         default:
10953           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10954     }
10955
10956     return 0;
10957 }
10958
10959
10960 /***
10961  * DESCRIPTION:
10962  * Subclassed edit control windproc function
10963  *
10964  * PARAMETER(S):
10965  * [I] hwnd : the edit window handle
10966  * [I] uMsg : the message that is to be processed
10967  * [I] wParam : first message parameter
10968  * [I] lParam : second message parameter
10969  * [I] isW : TRUE if input is Unicode
10970  *
10971  * RETURN:
10972  *   Zero.
10973  */
10974 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10975 {
10976     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10977     BOOL save = TRUE;
10978
10979     TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10980           hwnd, uMsg, wParam, lParam, isW);
10981
10982     switch (uMsg)
10983     {
10984         case WM_GETDLGCODE:
10985           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10986
10987         case WM_DESTROY:
10988         {
10989             WNDPROC editProc = infoPtr->EditWndProc;
10990             infoPtr->EditWndProc = 0;
10991             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10992             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10993         }
10994
10995         case WM_KEYDOWN:
10996             if (VK_ESCAPE == (INT)wParam)
10997             {
10998                 save = FALSE;
10999                 break;
11000             }
11001             else if (VK_RETURN == (INT)wParam)
11002                 break;
11003
11004         default:
11005             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
11006     }
11007
11008     /* kill the edit */
11009     if (infoPtr->hwndEdit)
11010         LISTVIEW_EndEditLabelT(infoPtr, save, isW);
11011
11012     SendMessageW(hwnd, WM_CLOSE, 0, 0);
11013     return 0;
11014 }
11015
11016 /***
11017  * DESCRIPTION:
11018  * Subclassed edit control Unicode windproc function
11019  *
11020  * PARAMETER(S):
11021  * [I] hwnd : the edit window handle
11022  * [I] uMsg : the message that is to be processed
11023  * [I] wParam : first message parameter
11024  * [I] lParam : second message parameter
11025  *
11026  * RETURN:
11027  */
11028 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11029 {
11030     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
11031 }
11032
11033 /***
11034  * DESCRIPTION:
11035  * Subclassed edit control ANSI windproc function
11036  *
11037  * PARAMETER(S):
11038  * [I] hwnd : the edit window handle
11039  * [I] uMsg : the message that is to be processed
11040  * [I] wParam : first message parameter
11041  * [I] lParam : second message parameter
11042  *
11043  * RETURN:
11044  */
11045 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11046 {
11047     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
11048 }
11049
11050 /***
11051  * DESCRIPTION:
11052  * Creates a subclassed edit control
11053  *
11054  * PARAMETER(S):
11055  * [I] infoPtr : valid pointer to the listview structure
11056  * [I] text : initial text for the edit
11057  * [I] style : the window style
11058  * [I] isW : TRUE if input is Unicode
11059  *
11060  * RETURN:
11061  */
11062 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
11063 {
11064     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
11065     HWND hedit;
11066     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
11067
11068     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
11069
11070     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
11071
11072     /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
11073     if (isW)
11074         hedit = CreateWindowW(editName, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11075     else
11076         hedit = CreateWindowA("Edit", (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11077
11078     if (!hedit) return 0;
11079
11080     infoPtr->EditWndProc = (WNDPROC)
11081         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
11082                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
11083
11084     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
11085
11086     return hedit;
11087 }