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