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