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