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