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