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