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