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