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