rsaenh: Fix padding bytes check for 0-byte payload.
[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     return nItemWidth;
2838 }
2839
2840 /***
2841  * DESCRIPTION:
2842  * Calculates the desired item height.
2843  *
2844  * PARAMETER(S):
2845  * [I] infoPtr : valid pointer to the listview structure
2846  *
2847  * RETURN:
2848  *  The desired item height.
2849  */
2850 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2851 {
2852     INT nItemHeight;
2853
2854     TRACE("uView=%d\n", infoPtr->uView);
2855
2856     if (infoPtr->uView == LV_VIEW_ICON)
2857         nItemHeight = infoPtr->iconSpacing.cy;
2858     else
2859     {
2860         nItemHeight = infoPtr->ntmHeight; 
2861         if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2862             nItemHeight++;
2863         if (infoPtr->himlState)
2864             nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2865         if (infoPtr->himlSmall)
2866             nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2867         if (infoPtr->himlState || infoPtr->himlSmall)
2868             nItemHeight += HEIGHT_PADDING;
2869     if (infoPtr->nMeasureItemHeight > 0)
2870         nItemHeight = infoPtr->nMeasureItemHeight;
2871     }
2872
2873     return max(nItemHeight, 1);
2874 }
2875
2876 /***
2877  * DESCRIPTION:
2878  * Updates the width, and height of an item.
2879  *
2880  * PARAMETER(S):
2881  * [I] infoPtr : valid pointer to the listview structure
2882  *
2883  * RETURN:
2884  *  None.
2885  */
2886 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2887 {
2888     infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2889     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2890 }
2891
2892
2893 /***
2894  * DESCRIPTION:
2895  * Retrieves and saves important text metrics info for the current
2896  * Listview font.
2897  *
2898  * PARAMETER(S):
2899  * [I] infoPtr : valid pointer to the listview structure
2900  *
2901  */
2902 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2903 {
2904     HDC hdc = GetDC(infoPtr->hwndSelf);
2905     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2906     HFONT hOldFont = SelectObject(hdc, hFont);
2907     TEXTMETRICW tm;
2908     SIZE sz;
2909
2910     if (GetTextMetricsW(hdc, &tm))
2911     {
2912         infoPtr->ntmHeight = tm.tmHeight;
2913         infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2914     }
2915
2916     if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2917         infoPtr->nEllipsisWidth = sz.cx;
2918         
2919     SelectObject(hdc, hOldFont);
2920     ReleaseDC(infoPtr->hwndSelf, hdc);
2921     
2922     TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2923 }
2924
2925 /***
2926  * DESCRIPTION:
2927  * A compare function for ranges
2928  *
2929  * PARAMETER(S)
2930  * [I] range1 : pointer to range 1;
2931  * [I] range2 : pointer to range 2;
2932  * [I] flags : flags
2933  *
2934  * RETURNS:
2935  * > 0 : if range 1 > range 2
2936  * < 0 : if range 2 > range 1
2937  * = 0 : if range intersects range 2
2938  */
2939 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2940 {
2941     INT cmp;
2942     
2943     if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower) 
2944         cmp = -1;
2945     else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower) 
2946         cmp = 1;
2947     else 
2948         cmp = 0;
2949
2950     TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2951
2952     return cmp;
2953 }
2954
2955 #if DEBUG_RANGES
2956 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2957 #else
2958 #define ranges_check(ranges, desc) do { } while(0)
2959 #endif
2960
2961 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2962 {
2963     INT i;
2964     RANGE *prev, *curr;
2965     
2966     TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2967     assert (ranges);
2968     assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2969     ranges_dump(ranges);
2970     if (DPA_GetPtrCount(ranges->hdpa) > 0)
2971     {
2972         prev = DPA_GetPtr(ranges->hdpa, 0);
2973         assert (prev->lower >= 0 && prev->lower < prev->upper);
2974         for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2975         {
2976             curr = DPA_GetPtr(ranges->hdpa, i);
2977             assert (prev->upper <= curr->lower);
2978             assert (curr->lower < curr->upper);
2979             prev = curr;
2980         }
2981     }
2982     TRACE("--- Done checking---\n");
2983 }
2984
2985 static RANGES ranges_create(int count)
2986 {
2987     RANGES ranges = Alloc(sizeof(struct tagRANGES));
2988     if (!ranges) return NULL;
2989     ranges->hdpa = DPA_Create(count);
2990     if (ranges->hdpa) return ranges;
2991     Free(ranges);
2992     return NULL;
2993 }
2994
2995 static void ranges_clear(RANGES ranges)
2996 {
2997     INT i;
2998         
2999     for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3000         Free(DPA_GetPtr(ranges->hdpa, i));
3001     DPA_DeleteAllPtrs(ranges->hdpa);
3002 }
3003
3004
3005 static void ranges_destroy(RANGES ranges)
3006 {
3007     if (!ranges) return;
3008     ranges_clear(ranges);
3009     DPA_Destroy(ranges->hdpa);
3010     Free(ranges);
3011 }
3012
3013 static RANGES ranges_clone(RANGES ranges)
3014 {
3015     RANGES clone;
3016     INT i;
3017            
3018     if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
3019
3020     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3021     {
3022         RANGE *newrng = Alloc(sizeof(RANGE));
3023         if (!newrng) goto fail;
3024         *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
3025         DPA_SetPtr(clone->hdpa, i, newrng);
3026     }
3027     return clone;
3028     
3029 fail:
3030     TRACE ("clone failed\n");
3031     ranges_destroy(clone);
3032     return NULL;
3033 }
3034
3035 static RANGES ranges_diff(RANGES ranges, RANGES sub)
3036 {
3037     INT i;
3038
3039     for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
3040         ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
3041
3042     return ranges;
3043 }
3044
3045 static void ranges_dump(RANGES ranges)
3046 {
3047     INT i;
3048
3049     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3050         TRACE("   %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
3051 }
3052
3053 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
3054 {
3055     RANGE srchrng = { nItem, nItem + 1 };
3056
3057     TRACE("(nItem=%d)\n", nItem);
3058     ranges_check(ranges, "before contain");
3059     return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
3060 }
3061
3062 static INT ranges_itemcount(RANGES ranges)
3063 {
3064     INT i, count = 0;
3065     
3066     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3067     {
3068         RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
3069         count += sel->upper - sel->lower;
3070     }
3071
3072     return count;
3073 }
3074
3075 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
3076 {
3077     RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
3078     INT index;
3079
3080     index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3081     if (index == -1) return TRUE;
3082
3083     for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
3084     {
3085         chkrng = DPA_GetPtr(ranges->hdpa, index);
3086         if (chkrng->lower >= nItem)
3087             chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
3088         if (chkrng->upper > nItem)
3089             chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
3090     }
3091     return TRUE;
3092 }
3093
3094 static BOOL ranges_add(RANGES ranges, RANGE range)
3095 {
3096     RANGE srchrgn;
3097     INT index;
3098
3099     TRACE("(%s)\n", debugrange(&range));
3100     ranges_check(ranges, "before add");
3101
3102     /* try find overlapping regions first */
3103     srchrgn.lower = range.lower - 1;
3104     srchrgn.upper = range.upper + 1;
3105     index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
3106    
3107     if (index == -1)
3108     {
3109         RANGE *newrgn;
3110
3111         TRACE("Adding new range\n");
3112
3113         /* create the brand new range to insert */      
3114         newrgn = Alloc(sizeof(RANGE));
3115         if(!newrgn) goto fail;
3116         *newrgn = range;
3117         
3118         /* figure out where to insert it */
3119         index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3120         TRACE("index=%d\n", index);
3121         if (index == -1) index = 0;
3122         
3123         /* and get it over with */
3124         if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3125         {
3126             Free(newrgn);
3127             goto fail;
3128         }
3129     }
3130     else
3131     {
3132         RANGE *chkrgn, *mrgrgn;
3133         INT fromindex, mergeindex;
3134
3135         chkrgn = DPA_GetPtr(ranges->hdpa, index);
3136         TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3137
3138         chkrgn->lower = min(range.lower, chkrgn->lower);
3139         chkrgn->upper = max(range.upper, chkrgn->upper);
3140         
3141         TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3142
3143         /* merge now common ranges */
3144         fromindex = 0;
3145         srchrgn.lower = chkrgn->lower - 1;
3146         srchrgn.upper = chkrgn->upper + 1;
3147             
3148         do
3149         {
3150             mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3151             if (mergeindex == -1) break;
3152             if (mergeindex == index) 
3153             {
3154                 fromindex = index + 1;
3155                 continue;
3156             }
3157           
3158             TRACE("Merge with index %i\n", mergeindex);
3159             
3160             mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3161             chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3162             chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3163             Free(mrgrgn);
3164             DPA_DeletePtr(ranges->hdpa, mergeindex);
3165             if (mergeindex < index) index --;
3166         } while(1);
3167     }
3168
3169     ranges_check(ranges, "after add");
3170     return TRUE;
3171     
3172 fail:
3173     ranges_check(ranges, "failed add");
3174     return FALSE;
3175 }
3176
3177 static BOOL ranges_del(RANGES ranges, RANGE range)
3178 {
3179     RANGE *chkrgn;
3180     INT index;
3181
3182     TRACE("(%s)\n", debugrange(&range));
3183     ranges_check(ranges, "before del");
3184     
3185     /* we don't use DPAS_SORTED here, since we need *
3186      * to find the first overlapping range          */
3187     index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3188     while(index != -1) 
3189     {
3190         chkrgn = DPA_GetPtr(ranges->hdpa, index);
3191         
3192         TRACE("Matches range %s @%d\n", debugrange(chkrgn), index); 
3193
3194         /* case 1: Same range */
3195         if ( (chkrgn->upper == range.upper) &&
3196              (chkrgn->lower == range.lower) )
3197         {
3198             DPA_DeletePtr(ranges->hdpa, index);
3199             break;
3200         }
3201         /* case 2: engulf */
3202         else if ( (chkrgn->upper <= range.upper) &&
3203                   (chkrgn->lower >= range.lower) ) 
3204         {
3205             DPA_DeletePtr(ranges->hdpa, index);
3206         }
3207         /* case 3: overlap upper */
3208         else if ( (chkrgn->upper <= range.upper) &&
3209                   (chkrgn->lower < range.lower) )
3210         {
3211             chkrgn->upper = range.lower;
3212         }
3213         /* case 4: overlap lower */
3214         else if ( (chkrgn->upper > range.upper) &&
3215                   (chkrgn->lower >= range.lower) )
3216         {
3217             chkrgn->lower = range.upper;
3218             break;
3219         }
3220         /* case 5: fully internal */
3221         else
3222         {
3223             RANGE tmprgn = *chkrgn, *newrgn;
3224
3225             if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
3226             newrgn->lower = chkrgn->lower;
3227             newrgn->upper = range.lower;
3228             chkrgn->lower = range.upper;
3229             if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3230             {
3231                 Free(newrgn);
3232                 goto fail;
3233             }
3234             chkrgn = &tmprgn;
3235             break;
3236         }
3237
3238         index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3239     }
3240
3241     ranges_check(ranges, "after del");
3242     return TRUE;
3243
3244 fail:
3245     ranges_check(ranges, "failed del");
3246     return FALSE;
3247 }
3248
3249 /***
3250 * DESCRIPTION:
3251 * Removes all selection ranges
3252 *
3253 * Parameters(s):
3254 * [I] infoPtr : valid pointer to the listview structure
3255 * [I] toSkip : item range to skip removing the selection
3256 *
3257 * RETURNS:
3258 *   SUCCESS : TRUE
3259 *   FAILURE : FALSE
3260 */
3261 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3262 {
3263     LVITEMW lvItem;
3264     ITERATOR i;
3265     RANGES clone;
3266
3267     TRACE("()\n");
3268
3269     lvItem.state = 0;
3270     lvItem.stateMask = LVIS_SELECTED;
3271     
3272     /* need to clone the DPA because callbacks can change it */
3273     if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3274     iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3275     while(iterator_next(&i))
3276         LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3277     /* note that the iterator destructor will free the cloned range */
3278     iterator_destroy(&i);
3279
3280     return TRUE;
3281 }
3282
3283 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3284 {
3285     RANGES toSkip;
3286    
3287     if (!(toSkip = ranges_create(1))) return FALSE;
3288     if (nItem != -1) ranges_additem(toSkip, nItem);
3289     LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3290     ranges_destroy(toSkip);
3291     return TRUE;
3292 }
3293
3294 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3295 {
3296     return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3297 }
3298
3299 /***
3300  * DESCRIPTION:
3301  * Retrieves the number of items that are marked as selected.
3302  *
3303  * PARAMETER(S):
3304  * [I] infoPtr : valid pointer to the listview structure
3305  *
3306  * RETURN:
3307  * Number of items selected.
3308  */
3309 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3310 {
3311     INT nSelectedCount = 0;
3312
3313     if (infoPtr->uCallbackMask & LVIS_SELECTED)
3314     {
3315         INT i;
3316         for (i = 0; i < infoPtr->nItemCount; i++)
3317         {
3318             if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3319                 nSelectedCount++;
3320         }
3321     }
3322     else
3323         nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3324
3325     TRACE("nSelectedCount=%d\n", nSelectedCount);
3326     return nSelectedCount;
3327 }
3328
3329 /***
3330  * DESCRIPTION:
3331  * Manages the item focus.
3332  *
3333  * PARAMETER(S):
3334  * [I] infoPtr : valid pointer to the listview structure
3335  * [I] nItem : item index
3336  *
3337  * RETURN:
3338  *   TRUE : focused item changed
3339  *   FALSE : focused item has NOT changed
3340  */
3341 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3342 {
3343     INT oldFocus = infoPtr->nFocusedItem;
3344     LVITEMW lvItem;
3345
3346     if (nItem == infoPtr->nFocusedItem) return FALSE;
3347     
3348     lvItem.state =  nItem == -1 ? 0 : LVIS_FOCUSED;
3349     lvItem.stateMask = LVIS_FOCUSED;
3350     LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3351
3352     return oldFocus != infoPtr->nFocusedItem;
3353 }
3354
3355 /* Helper function for LISTVIEW_ShiftIndices *only* */
3356 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3357 {
3358     if (nShiftItem < nItem) return nShiftItem;
3359
3360     if (nShiftItem > nItem) return nShiftItem + direction;
3361
3362     if (direction > 0) return nShiftItem + direction;
3363
3364     return min(nShiftItem, infoPtr->nItemCount - 1);
3365 }
3366
3367 /**
3368 * DESCRIPTION:
3369 * Updates the various indices after an item has been inserted or deleted.
3370 *
3371 * PARAMETER(S):
3372 * [I] infoPtr : valid pointer to the listview structure
3373 * [I] nItem : item index
3374 * [I] direction : Direction of shift, +1 or -1.
3375 *
3376 * RETURN:
3377 * None
3378 */
3379 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3380 {
3381     INT nNewFocus;
3382     BOOL bOldChange;
3383
3384     /* temporarily disable change notification while shifting items */
3385     bOldChange = infoPtr->bDoChangeNotify;
3386     infoPtr->bDoChangeNotify = FALSE;
3387
3388     TRACE("Shifting %iu, %i steps\n", nItem, direction);
3389
3390     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3391
3392     assert(abs(direction) == 1);
3393
3394     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3395
3396     nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3397     if (nNewFocus != infoPtr->nFocusedItem)
3398         LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3399     
3400     /* But we are not supposed to modify nHotItem! */
3401
3402     infoPtr->bDoChangeNotify = bOldChange;
3403 }
3404
3405
3406 /**
3407  * DESCRIPTION:
3408  * Adds a block of selections.
3409  *
3410  * PARAMETER(S):
3411  * [I] infoPtr : valid pointer to the listview structure
3412  * [I] nItem : item index
3413  *
3414  * RETURN:
3415  * Whether the window is still valid.
3416  */
3417 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3418 {
3419     INT nFirst = min(infoPtr->nSelectionMark, nItem);
3420     INT nLast = max(infoPtr->nSelectionMark, nItem);
3421     HWND hwndSelf = infoPtr->hwndSelf;
3422     NMLVODSTATECHANGE nmlv;
3423     LVITEMW item;
3424     BOOL bOldChange;
3425     INT i;
3426
3427     /* Temporarily disable change notification
3428      * If the control is LVS_OWNERDATA, we need to send
3429      * only one LVN_ODSTATECHANGED notification.
3430      * See MSDN documentation for LVN_ITEMCHANGED.
3431      */
3432     bOldChange = infoPtr->bDoChangeNotify;
3433     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3434
3435     if (nFirst == -1) nFirst = nItem;
3436
3437     item.state = LVIS_SELECTED;
3438     item.stateMask = LVIS_SELECTED;
3439
3440     for (i = nFirst; i <= nLast; i++)
3441         LISTVIEW_SetItemState(infoPtr,i,&item);
3442
3443     ZeroMemory(&nmlv, sizeof(nmlv));
3444     nmlv.iFrom = nFirst;
3445     nmlv.iTo = nLast;
3446     nmlv.uNewState = 0;
3447     nmlv.uOldState = item.state;
3448
3449     notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3450     if (!IsWindow(hwndSelf))
3451         return FALSE;
3452     infoPtr->bDoChangeNotify = bOldChange;
3453     return TRUE;
3454 }
3455
3456
3457 /***
3458  * DESCRIPTION:
3459  * Sets a single group selection.
3460  *
3461  * PARAMETER(S):
3462  * [I] infoPtr : valid pointer to the listview structure
3463  * [I] nItem : item index
3464  *
3465  * RETURN:
3466  * None
3467  */
3468 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3469 {
3470     RANGES selection;
3471     LVITEMW item;
3472     ITERATOR i;
3473     BOOL bOldChange;
3474
3475     if (!(selection = ranges_create(100))) return;
3476
3477     item.state = LVIS_SELECTED; 
3478     item.stateMask = LVIS_SELECTED;
3479
3480     if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3481     {
3482         if (infoPtr->nSelectionMark == -1)
3483         {
3484             infoPtr->nSelectionMark = nItem;
3485             ranges_additem(selection, nItem);
3486         }
3487         else
3488         {
3489             RANGE sel;
3490             
3491             sel.lower = min(infoPtr->nSelectionMark, nItem);
3492             sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3493             ranges_add(selection, sel);
3494         }
3495     }
3496     else
3497     {
3498         RECT rcItem, rcSel, rcSelMark;
3499         POINT ptItem;
3500         
3501         rcItem.left = LVIR_BOUNDS;
3502         if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3503         rcSelMark.left = LVIR_BOUNDS;
3504         if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3505         UnionRect(&rcSel, &rcItem, &rcSelMark);
3506         iterator_frameditems(&i, infoPtr, &rcSel);
3507         while(iterator_next(&i))
3508         {
3509             LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3510             if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3511         }
3512         iterator_destroy(&i);
3513     }
3514
3515     /* disable per item notifications on LVS_OWNERDATA style
3516        FIXME: single LVN_ODSTATECHANGED should be used */
3517     bOldChange = infoPtr->bDoChangeNotify;
3518     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3519
3520     LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3521
3522
3523     iterator_rangesitems(&i, selection);
3524     while(iterator_next(&i))
3525         LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3526     /* this will also destroy the selection */
3527     iterator_destroy(&i);
3528
3529     infoPtr->bDoChangeNotify = bOldChange;
3530     
3531     LISTVIEW_SetItemFocus(infoPtr, nItem);
3532 }
3533
3534 /***
3535  * DESCRIPTION:
3536  * Sets a single selection.
3537  *
3538  * PARAMETER(S):
3539  * [I] infoPtr : valid pointer to the listview structure
3540  * [I] nItem : item index
3541  *
3542  * RETURN:
3543  * None
3544  */
3545 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3546 {
3547     LVITEMW lvItem;
3548
3549     TRACE("nItem=%d\n", nItem);
3550     
3551     LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3552
3553     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3554     lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3555     LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3556
3557     infoPtr->nSelectionMark = nItem;
3558 }
3559
3560 /***
3561  * DESCRIPTION:
3562  * Set selection(s) with keyboard.
3563  *
3564  * PARAMETER(S):
3565  * [I] infoPtr : valid pointer to the listview structure
3566  * [I] nItem : item index
3567  * [I] space : VK_SPACE code sent
3568  *
3569  * RETURN:
3570  *   SUCCESS : TRUE (needs to be repainted)
3571  *   FAILURE : FALSE (nothing has changed)
3572  */
3573 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3574 {
3575   /* FIXME: pass in the state */
3576   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3577   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3578   BOOL bResult = FALSE;
3579
3580   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3581   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3582   {
3583     bResult = TRUE;
3584
3585     if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3586       LISTVIEW_SetSelection(infoPtr, nItem);
3587     else
3588     {
3589       if (wShift)
3590         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3591       else if (wCtrl)
3592       {
3593         LVITEMW lvItem;
3594         lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3595         lvItem.stateMask = LVIS_SELECTED;
3596         if (space)
3597         {
3598             LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3599             if (lvItem.state & LVIS_SELECTED)
3600                 infoPtr->nSelectionMark = nItem;
3601         }
3602         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3603       }
3604     }
3605     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3606   }
3607
3608   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3609   return bResult;
3610 }
3611
3612 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3613 {
3614     LVHITTESTINFO lvHitTestInfo;
3615
3616     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3617     lvHitTestInfo.pt.x = pt.x;
3618     lvHitTestInfo.pt.y = pt.y;
3619
3620     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3621
3622     lpLVItem->mask = LVIF_PARAM;
3623     lpLVItem->iItem = lvHitTestInfo.iItem;
3624     lpLVItem->iSubItem = 0;
3625
3626     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3627 }
3628
3629 static inline BOOL LISTVIEW_IsHotTracking(const LISTVIEW_INFO *infoPtr)
3630 {
3631     return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3632             (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3633             (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3634 }
3635
3636 /***
3637  * DESCRIPTION:
3638  * Called when the mouse is being actively tracked and has hovered for a specified
3639  * amount of time
3640  *
3641  * PARAMETER(S):
3642  * [I] infoPtr : valid pointer to the listview structure
3643  * [I] fwKeys : key indicator
3644  * [I] x,y : mouse position
3645  *
3646  * RETURN:
3647  *   0 if the message was processed, non-zero if there was an error
3648  *
3649  * INFO:
3650  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3651  * over the item for a certain period of time.
3652  *
3653  */
3654 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3655 {
3656     NMHDR hdr;
3657
3658     if (notify_hdr(infoPtr, NM_HOVER, &hdr)) return 0;
3659
3660     if (LISTVIEW_IsHotTracking(infoPtr))
3661     {
3662         LVITEMW item;
3663         POINT pt;
3664
3665         pt.x = x;
3666         pt.y = y;
3667
3668         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3669             LISTVIEW_SetSelection(infoPtr, item.iItem);
3670
3671         SetFocus(infoPtr->hwndSelf);
3672     }
3673
3674     return 0;
3675 }
3676
3677 #define SCROLL_LEFT   0x1
3678 #define SCROLL_RIGHT  0x2
3679 #define SCROLL_UP     0x4
3680 #define SCROLL_DOWN   0x8
3681
3682 /***
3683  * DESCRIPTION:
3684  * Utility routine to draw and highlight items within a marquee selection rectangle.
3685  *
3686  * PARAMETER(S):
3687  * [I] infoPtr     : valid pointer to the listview structure
3688  * [I] coords_orig : original co-ordinates of the cursor
3689  * [I] coords_offs : offsetted coordinates of the cursor
3690  * [I] offset      : offset amount
3691  * [I] scroll      : Bitmask of which directions we should scroll, if at all
3692  *
3693  * RETURN:
3694  *   None.
3695  */
3696 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, LPPOINT coords_orig, LPPOINT coords_offs, LPPOINT offset, INT scroll)
3697 {
3698     BOOL controlDown = FALSE;
3699     LVITEMW item;
3700     ITERATOR i;
3701     RECT rect;
3702
3703     if (coords_offs->x > infoPtr->marqueeOrigin.x)
3704     {
3705         rect.left = infoPtr->marqueeOrigin.x;
3706         rect.right = coords_offs->x;
3707     }
3708     else
3709     {
3710         rect.left = coords_offs->x;
3711         rect.right = infoPtr->marqueeOrigin.x;
3712     }
3713
3714     if (coords_offs->y > infoPtr->marqueeOrigin.y)
3715     {
3716         rect.top = infoPtr->marqueeOrigin.y;
3717         rect.bottom = coords_offs->y;
3718     }
3719     else
3720     {
3721         rect.top = coords_offs->y;
3722         rect.bottom = infoPtr->marqueeOrigin.y;
3723     }
3724
3725     /* Cancel out the old marquee rectangle and draw the new one */
3726     LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3727
3728     /* Scroll by the appropriate distance if applicable - speed up scrolling as
3729        the cursor is further away */
3730
3731     if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0))
3732         LISTVIEW_Scroll(infoPtr, coords_orig->x, 0);
3733
3734     if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right))
3735         LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0);
3736
3737     if ((scroll & SCROLL_UP) && (coords_orig->y <= 0))
3738         LISTVIEW_Scroll(infoPtr, 0, coords_orig->y);
3739
3740     if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom))
3741         LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom));
3742
3743     /* Invert the items in the old marquee rectangle */
3744     iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3745
3746     while (iterator_next(&i))
3747     {
3748         if (i.nItem > -1)
3749         {
3750             if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED) == LVIS_SELECTED)
3751                 item.state = 0;
3752             else
3753                 item.state = LVIS_SELECTED;
3754
3755             item.stateMask = LVIS_SELECTED;
3756
3757             LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3758         }
3759     }
3760
3761     iterator_destroy(&i);
3762
3763     CopyRect(&infoPtr->marqueeRect, &rect);
3764
3765     CopyRect(&infoPtr->marqueeDrawRect, &rect);
3766     OffsetRect(&infoPtr->marqueeDrawRect, offset->x, offset->y);
3767
3768     /* Iterate over the items within our marquee rectangle */
3769     iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3770
3771     if (GetKeyState(VK_CONTROL) & 0x8000)
3772         controlDown = TRUE;
3773
3774     while (iterator_next(&i))
3775     {
3776         if (i.nItem > -1)
3777         {
3778             /* If CTRL is pressed, invert. If not, always select the item. */
3779             if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)))
3780                 item.state = 0;
3781             else
3782                 item.state = LVIS_SELECTED;
3783
3784             item.stateMask = LVIS_SELECTED;
3785
3786             LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3787         }
3788     }
3789
3790     iterator_destroy(&i);
3791     LISTVIEW_InvalidateRect(infoPtr, &rect);
3792 }
3793
3794 /***
3795  * DESCRIPTION:
3796  * Called when we are in a marquee selection that involves scrolling the listview (ie,
3797  * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3798  *
3799  * PARAMETER(S):
3800  * [I] hwnd : Handle to the listview
3801  * [I] uMsg : WM_TIMER (ignored)
3802  * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3803  * [I] dwTimer : The elapsed time (ignored)
3804  *
3805  * RETURN:
3806  *   None.
3807  */
3808 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
3809 {
3810     LISTVIEW_INFO *infoPtr;
3811     SCROLLINFO scrollInfo;
3812     POINT coords_orig;
3813     POINT coords_offs;
3814     POINT offset;
3815     INT scroll = 0;
3816
3817     infoPtr = (LISTVIEW_INFO *) idEvent;
3818
3819     if (!infoPtr)
3820         return;
3821
3822     /* Get the current cursor position and convert to client coordinates */
3823     GetCursorPos(&coords_orig);
3824     ScreenToClient(hWnd, &coords_orig);
3825
3826     /* Ensure coordinates are within client bounds */
3827     coords_offs.x = max(min(coords_orig.x, infoPtr->rcList.right), 0);
3828     coords_offs.y = max(min(coords_orig.y, infoPtr->rcList.bottom), 0);
3829
3830     /* Get offset */
3831     LISTVIEW_GetOrigin(infoPtr, &offset);
3832
3833     /* Offset coordinates by the appropriate amount */
3834     coords_offs.x -= offset.x;
3835     coords_offs.y -= offset.y;
3836
3837     scrollInfo.cbSize = sizeof(SCROLLINFO);
3838     scrollInfo.fMask = SIF_ALL;
3839
3840     /* Work out in which directions we can scroll */
3841     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3842     {
3843         if (scrollInfo.nPos != scrollInfo.nMin)
3844             scroll |= SCROLL_UP;
3845
3846         if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3847             scroll |= SCROLL_DOWN;
3848     }
3849
3850     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3851     {
3852         if (scrollInfo.nPos != scrollInfo.nMin)
3853             scroll |= SCROLL_LEFT;
3854
3855         if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3856             scroll |= SCROLL_RIGHT;
3857     }
3858
3859     if (((coords_orig.x <= 0) && (scroll & SCROLL_LEFT)) ||
3860         ((coords_orig.y <= 0) && (scroll & SCROLL_UP))   ||
3861         ((coords_orig.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) ||
3862         ((coords_orig.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN)))
3863     {
3864         LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, scroll);
3865     }
3866 }
3867
3868 /***
3869  * DESCRIPTION:
3870  * Called whenever WM_MOUSEMOVE is received.
3871  *
3872  * PARAMETER(S):
3873  * [I] infoPtr : valid pointer to the listview structure
3874  * [I] fwKeys : key indicator
3875  * [I] x,y : mouse position
3876  *
3877  * RETURN:
3878  *   0 if the message is processed, non-zero if there was an error
3879  */
3880 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3881 {
3882     if (!(fwKeys & MK_LBUTTON))
3883         infoPtr->bLButtonDown = FALSE;
3884
3885     if (infoPtr->bLButtonDown)
3886     {
3887         POINT tmp;
3888         RECT rect;
3889         LVHITTESTINFO lvHitTestInfo;
3890         WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3891         WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3892
3893         if (infoPtr->bMarqueeSelect)
3894         {
3895             POINT coords_orig;
3896             POINT coords_offs;
3897             POINT offset;
3898
3899             coords_orig.x = x;
3900             coords_orig.y = y;
3901
3902             /* Get offset */
3903             LISTVIEW_GetOrigin(infoPtr, &offset);
3904
3905             /* Ensure coordinates are within client bounds */
3906             coords_offs.x = max(min(x, infoPtr->rcList.right), 0);
3907             coords_offs.y = max(min(y, infoPtr->rcList.bottom), 0);
3908
3909             /* Offset coordinates by the appropriate amount */
3910             coords_offs.x -= offset.x;
3911             coords_offs.y -= offset.y;
3912
3913             /* Enable the timer if we're going outside our bounds, in case the user doesn't
3914                move the mouse again */
3915
3916             if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) ||
3917                 (y >= infoPtr->rcList.bottom))
3918             {
3919                 if (!infoPtr->bScrolling)
3920                 {
3921                     infoPtr->bScrolling = TRUE;
3922                     SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer);
3923                 }
3924             }
3925             else
3926             {
3927                 infoPtr->bScrolling = FALSE;
3928                 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
3929             }
3930
3931             LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, 0);
3932             return 0;
3933         }
3934
3935         rect.left = infoPtr->ptClickPos.x - wDragWidth;
3936         rect.right = infoPtr->ptClickPos.x + wDragWidth;
3937         rect.top = infoPtr->ptClickPos.y - wDragHeight;
3938         rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3939
3940         tmp.x = x;
3941         tmp.y = y;
3942
3943         lvHitTestInfo.pt = tmp;
3944         LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3945
3946         /* reset item marker */
3947         if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3948             infoPtr->nLButtonDownItem = -1;
3949
3950         if (!PtInRect(&rect, tmp))
3951         {
3952             /* this path covers the following:
3953                1. WM_LBUTTONDOWN over selected item (sets focus on it)
3954                2. change focus with keys
3955                3. move mouse over item from step 1 selects it and moves focus on it */
3956             if (infoPtr->nLButtonDownItem != -1 &&
3957                !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3958             {
3959                 LVITEMW lvItem;
3960
3961                 lvItem.state =  LVIS_FOCUSED | LVIS_SELECTED;
3962                 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3963
3964                 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3965                 infoPtr->nLButtonDownItem = -1;
3966             }
3967
3968             if (!infoPtr->bDragging)
3969             {
3970                 lvHitTestInfo.pt = infoPtr->ptClickPos;
3971                 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3972
3973                 /* If the click is outside the range of an item, begin a
3974                    highlight. If not, begin an item drag. */
3975                 if (lvHitTestInfo.iItem == -1)
3976                 {
3977                     NMHDR hdr;
3978
3979                     /* If we're allowing multiple selections, send notification.
3980                        If return value is non-zero, cancel. */
3981                     if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0))
3982                     {
3983                         /* Store the absolute coordinates of the click */
3984                         POINT offset;
3985                         LISTVIEW_GetOrigin(infoPtr, &offset);
3986
3987                         infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x;
3988                         infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y;
3989
3990                         /* Begin selection and capture mouse */
3991                         infoPtr->bMarqueeSelect = TRUE;
3992                         SetCapture(infoPtr->hwndSelf);
3993                     }
3994                 }
3995                 else
3996                 {
3997                     NMLISTVIEW nmlv;
3998
3999                     ZeroMemory(&nmlv, sizeof(nmlv));
4000                     nmlv.iItem = lvHitTestInfo.iItem;
4001                     nmlv.ptAction = infoPtr->ptClickPos;
4002
4003                     notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
4004                     infoPtr->bDragging = TRUE;
4005                 }
4006             }
4007
4008             return 0;
4009         }
4010     }
4011
4012     /* see if we are supposed to be tracking mouse hovering */
4013     if (LISTVIEW_IsHotTracking(infoPtr)) {
4014         TRACKMOUSEEVENT trackinfo;
4015
4016         trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
4017         trackinfo.dwFlags = TME_QUERY;
4018
4019         /* see if we are already tracking this hwnd */
4020         _TrackMouseEvent(&trackinfo);
4021
4022         if(!(trackinfo.dwFlags & TME_HOVER) || trackinfo.hwndTrack != infoPtr->hwndSelf) {
4023             trackinfo.dwFlags     = TME_HOVER;
4024             trackinfo.dwHoverTime = infoPtr->dwHoverTime;
4025             trackinfo.hwndTrack   = infoPtr->hwndSelf;
4026
4027             /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4028             _TrackMouseEvent(&trackinfo);
4029         }
4030     }
4031
4032     return 0;
4033 }
4034
4035
4036 /***
4037  * Tests whether the item is assignable to a list with style lStyle
4038  */
4039 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
4040 {
4041     if ( (lpLVItem->mask & LVIF_TEXT) && 
4042         (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
4043         (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
4044     
4045     return TRUE;
4046 }
4047
4048
4049 /***
4050  * DESCRIPTION:
4051  * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
4052  *
4053  * PARAMETER(S):
4054  * [I] infoPtr : valid pointer to the listview structure
4055  * [I] lpLVItem : valid pointer to new item attributes
4056  * [I] isNew : the item being set is being inserted
4057  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4058  * [O] bChanged : will be set to TRUE if the item really changed
4059  *
4060  * RETURN:
4061  *   SUCCESS : TRUE
4062  *   FAILURE : FALSE
4063  */
4064 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
4065 {
4066     ITEM_INFO *lpItem;
4067     NMLISTVIEW nmlv;
4068     UINT uChanged = 0;
4069     LVITEMW item;
4070     /* stateMask is ignored for LVM_INSERTITEM */
4071     UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
4072
4073     TRACE("()\n");
4074
4075     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
4076     
4077     if (lpLVItem->mask == 0) return TRUE;   
4078
4079     if (infoPtr->dwStyle & LVS_OWNERDATA)
4080     {
4081         /* a virtual listview only stores selection and focus */
4082         if (lpLVItem->mask & ~LVIF_STATE)
4083             return FALSE;
4084         lpItem = NULL;
4085     }
4086     else
4087     {
4088         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4089         lpItem = DPA_GetPtr(hdpaSubItems, 0);
4090         assert (lpItem);
4091     }
4092
4093     /* we need to get the lParam and state of the item */
4094     item.iItem = lpLVItem->iItem;
4095     item.iSubItem = lpLVItem->iSubItem;
4096     item.mask = LVIF_STATE | LVIF_PARAM;
4097     item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
4098
4099     item.state = 0;
4100     item.lParam = 0;
4101     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
4102
4103     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
4104     /* determine what fields will change */    
4105     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
4106         uChanged |= LVIF_STATE;
4107
4108     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
4109         uChanged |= LVIF_IMAGE;
4110
4111     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
4112         uChanged |= LVIF_PARAM;
4113
4114     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
4115         uChanged |= LVIF_INDENT;
4116
4117     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
4118         uChanged |= LVIF_TEXT;
4119    
4120     TRACE("uChanged=0x%x\n", uChanged); 
4121     if (!uChanged) return TRUE;
4122     *bChanged = TRUE;
4123     
4124     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4125     nmlv.iItem = lpLVItem->iItem;
4126     nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
4127     nmlv.uOldState = item.state;
4128     nmlv.uChanged = uChanged;
4129     nmlv.lParam = item.lParam;
4130     
4131     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
4132     /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
4133     /* are enabled */
4134     if(lpItem && !isNew && infoPtr->bDoChangeNotify)
4135     {
4136       HWND hwndSelf = infoPtr->hwndSelf;
4137
4138       if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
4139         return FALSE;
4140       if (!IsWindow(hwndSelf))
4141         return FALSE;
4142     }
4143
4144     /* copy information */
4145     if (lpLVItem->mask & LVIF_TEXT)
4146         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
4147
4148     if (lpLVItem->mask & LVIF_IMAGE)
4149         lpItem->hdr.iImage = lpLVItem->iImage;
4150
4151     if (lpLVItem->mask & LVIF_PARAM)
4152         lpItem->lParam = lpLVItem->lParam;
4153
4154     if (lpLVItem->mask & LVIF_INDENT)
4155         lpItem->iIndent = lpLVItem->iIndent;
4156
4157     if (uChanged & LVIF_STATE)
4158     {
4159         if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
4160         {
4161             lpItem->state &= ~stateMask;
4162             lpItem->state |= (lpLVItem->state & stateMask);
4163         }
4164         if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
4165         {
4166             if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
4167             ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
4168         }
4169         else if (stateMask & LVIS_SELECTED)
4170         {
4171             ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
4172         }
4173         /* if we are asked to change focus, and we manage it, do it */
4174         if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
4175         {
4176             if (lpLVItem->state & LVIS_FOCUSED)
4177             {
4178                 if (infoPtr->nFocusedItem != -1)
4179                 {
4180                     /* remove current focus */
4181                     item.mask  = LVIF_STATE;
4182                     item.state = 0;
4183                     item.stateMask = LVIS_FOCUSED;
4184
4185                     /* recurse with redrawing an item */
4186                     LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
4187                 }
4188
4189                 infoPtr->nFocusedItem = lpLVItem->iItem;
4190                 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
4191             }
4192             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
4193             {
4194                 infoPtr->nFocusedItem = -1;
4195             }
4196         }
4197     }
4198
4199     /* if we're inserting the item, we're done */
4200     if (isNew) return TRUE;
4201     
4202     /* send LVN_ITEMCHANGED notification */
4203     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
4204     if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
4205
4206     return TRUE;
4207 }
4208
4209 /***
4210  * DESCRIPTION:
4211  * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4212  *
4213  * PARAMETER(S):
4214  * [I] infoPtr : valid pointer to the listview structure
4215  * [I] lpLVItem : valid pointer to new subitem attributes
4216  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4217  * [O] bChanged : will be set to TRUE if the item really changed
4218  *
4219  * RETURN:
4220  *   SUCCESS : TRUE
4221  *   FAILURE : FALSE
4222  */
4223 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
4224 {
4225     HDPA hdpaSubItems;
4226     SUBITEM_INFO *lpSubItem;
4227
4228     /* we do not support subitems for virtual listviews */
4229     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
4230     
4231     /* set subitem only if column is present */
4232     if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4233    
4234     /* First do some sanity checks */
4235     /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4236        particularly useful. We currently do not actually do anything with
4237        the flag on subitems.
4238     */
4239     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
4240     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
4241    
4242     /* get the subitem structure, and create it if not there */
4243     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4244     assert (hdpaSubItems);
4245     
4246     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4247     if (!lpSubItem)
4248     {
4249         SUBITEM_INFO *tmpSubItem;
4250         INT i;
4251
4252         lpSubItem = Alloc(sizeof(SUBITEM_INFO));
4253         if (!lpSubItem) return FALSE;
4254         /* we could binary search here, if need be...*/
4255         for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4256         {
4257             tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
4258             if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
4259         }
4260         if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
4261         {
4262             Free(lpSubItem);
4263             return FALSE;
4264         }
4265         lpSubItem->iSubItem = lpLVItem->iSubItem;
4266         lpSubItem->hdr.iImage = I_IMAGECALLBACK;
4267         *bChanged = TRUE;
4268     }
4269     
4270     if (lpLVItem->mask & LVIF_IMAGE)
4271         if (lpSubItem->hdr.iImage != lpLVItem->iImage)
4272         {
4273             lpSubItem->hdr.iImage = lpLVItem->iImage;
4274             *bChanged = TRUE;
4275         }
4276
4277     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
4278     {
4279         textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
4280         *bChanged = TRUE;
4281     }
4282
4283     return TRUE;
4284 }
4285
4286 /***
4287  * DESCRIPTION:
4288  * Sets item attributes.
4289  *
4290  * PARAMETER(S):
4291  * [I] infoPtr : valid pointer to the listview structure
4292  * [I] lpLVItem : new item attributes
4293  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4294  *
4295  * RETURN:
4296  *   SUCCESS : TRUE
4297  *   FAILURE : FALSE
4298  */
4299 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
4300 {
4301     HWND hwndSelf = infoPtr->hwndSelf;
4302     LPWSTR pszText = NULL;
4303     BOOL bResult, bChanged = FALSE;
4304
4305     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4306
4307     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4308         return FALSE;
4309
4310     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4311     if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
4312     {
4313         pszText = lpLVItem->pszText;
4314         lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
4315     }
4316
4317     /* actually set the fields */
4318     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
4319
4320     if (lpLVItem->iSubItem)
4321         bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
4322     else
4323         bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
4324     if (!IsWindow(hwndSelf))
4325         return FALSE;
4326
4327     /* redraw item, if necessary */
4328     if (bChanged && !infoPtr->bIsDrawing)
4329     {
4330         /* this little optimization eliminates some nasty flicker */
4331         if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
4332              !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
4333              lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
4334             LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
4335         else
4336             LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
4337     }
4338     /* restore text */
4339     if (pszText)
4340     {
4341         textfreeT(lpLVItem->pszText, isW);
4342         lpLVItem->pszText = pszText;
4343     }
4344
4345     return bResult;
4346 }
4347
4348 /***
4349  * DESCRIPTION:
4350  * Retrieves the index of the item at coordinate (0, 0) of the client area.
4351  *
4352  * PARAMETER(S):
4353  * [I] infoPtr : valid pointer to the listview structure
4354  *
4355  * RETURN:
4356  * item index
4357  */
4358 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
4359 {
4360     INT nItem = 0;
4361     SCROLLINFO scrollInfo;
4362
4363     scrollInfo.cbSize = sizeof(SCROLLINFO);
4364     scrollInfo.fMask = SIF_POS;
4365
4366     if (infoPtr->uView == LV_VIEW_LIST)
4367     {
4368         if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4369             nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
4370     }
4371     else if (infoPtr->uView == LV_VIEW_DETAILS)
4372     {
4373         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4374             nItem = scrollInfo.nPos;
4375     } 
4376     else
4377     {
4378         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4379             nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
4380     }
4381
4382     TRACE("nItem=%d\n", nItem);
4383     
4384     return nItem;
4385 }
4386
4387
4388 /***
4389  * DESCRIPTION:
4390  * Erases the background of the given rectangle
4391  *
4392  * PARAMETER(S):
4393  * [I] infoPtr : valid pointer to the listview structure
4394  * [I] hdc : device context handle
4395  * [I] lprcBox : clipping rectangle
4396  *
4397  * RETURN:
4398  *   Success: TRUE
4399  *   Failure: FALSE
4400  */
4401 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4402 {
4403     if (!infoPtr->hBkBrush) return FALSE;
4404
4405     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
4406
4407     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
4408 }
4409
4410 /***
4411  * DESCRIPTION:
4412  * Draws an item.
4413  *
4414  * PARAMETER(S):
4415  * [I] infoPtr : valid pointer to the listview structure
4416  * [I] hdc : device context handle
4417  * [I] nItem : item index
4418  * [I] nSubItem : subitem index
4419  * [I] pos : item position in client coordinates
4420  * [I] cdmode : custom draw mode
4421  *
4422  * RETURN:
4423  *   Success: TRUE
4424  *   Failure: FALSE
4425  */
4426 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
4427 {
4428     UINT uFormat;
4429     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4430     static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4431     DWORD cdsubitemmode = CDRF_DODEFAULT;
4432     LPRECT lprcFocus;
4433     RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
4434     NMLVCUSTOMDRAW nmlvcd;
4435     HIMAGELIST himl;
4436     LVITEMW lvItem;
4437     HFONT hOldFont;
4438
4439     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
4440
4441     /* get information needed for drawing the item */
4442     lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
4443     if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
4444     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
4445     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT;
4446     lvItem.iItem = nItem;
4447     lvItem.iSubItem = nSubItem;
4448     lvItem.state = 0;
4449     lvItem.lParam = 0;
4450     lvItem.cchTextMax = DISP_TEXT_SIZE;
4451     lvItem.pszText = szDispText;
4452     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4453     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
4454         lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4455     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
4456     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4457
4458     /* now check if we need to update the focus rectangle */
4459     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4460
4461     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
4462     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
4463     OffsetRect(&rcBox, pos.x, pos.y);
4464     OffsetRect(&rcSelect, pos.x, pos.y);
4465     OffsetRect(&rcIcon, pos.x, pos.y);
4466     OffsetRect(&rcStateIcon, pos.x, pos.y);
4467     OffsetRect(&rcLabel, pos.x, pos.y);
4468     TRACE("    rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
4469         wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
4470         wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
4471
4472     /* fill in the custom draw structure */
4473     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
4474
4475     hOldFont = GetCurrentObject(hdc, OBJ_FONT);
4476     if (nSubItem > 0) cdmode = infoPtr->cditemmode;
4477     if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
4478     if (cdmode & CDRF_NOTIFYITEMDRAW)
4479         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4480     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
4481     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4482     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
4483     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
4484     {
4485         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
4486         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4487     }
4488     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
4489         prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4490     else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
4491         prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
4492
4493     /* in full row select, subitems, will just use main item's colors */
4494     if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4495         nmlvcd.clrTextBk = CLR_NONE;
4496
4497     /* FIXME: temporary hack */
4498     rcSelect.left = rcLabel.left;
4499
4500     /* draw the selection background, if we're drawing the main item */
4501     if (nSubItem == 0)
4502     {
4503         /* in icon mode, the label rect is really what we want to draw the
4504          * background for */
4505         if (infoPtr->uView == LV_VIEW_ICON)
4506             rcSelect = rcLabel;
4507
4508         if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4509         {
4510             /* we have to update left focus bound too if item isn't in leftmost column
4511                and reduce right box bound */
4512             if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4513             {
4514                 INT leftmost;
4515
4516                 if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
4517                 {
4518                     INT Originx = pos.x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
4519                     INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4520                                 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4521
4522                     rcBox.right   = LISTVIEW_GetColumnInfo(infoPtr, index)->rcHeader.right + Originx;
4523                     rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx;
4524                 }
4525             }
4526
4527             rcSelect.right = rcBox.right;
4528         }
4529
4530         if (nmlvcd.clrTextBk != CLR_NONE)
4531             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
4532         /* store new focus rectangle */
4533         if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
4534     }
4535
4536     /* state icons */
4537     if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
4538     {
4539         UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
4540         if (uStateImage)
4541         {
4542              TRACE("uStateImage=%d\n", uStateImage);
4543              ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
4544                  rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4545         }
4546     }
4547
4548     /* item icons */
4549     himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4550     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
4551     {
4552         UINT style;
4553
4554         TRACE("iImage=%d\n", lvItem.iImage);
4555
4556         if (lvItem.state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
4557             style = ILD_SELECTED;
4558         else
4559             style = ILD_NORMAL;
4560
4561         ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
4562                          rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk,
4563                          lvItem.state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
4564                          style);
4565     }
4566
4567     /* Don't bother painting item being edited */
4568     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
4569    
4570     /* figure out the text drawing flags */
4571     uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4572     if (infoPtr->uView == LV_VIEW_ICON)
4573         uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4574     else if (nSubItem)
4575     {
4576         switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4577         {
4578         case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
4579         case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
4580         default:            uFormat |= DT_LEFT;
4581         }
4582     }
4583     if (!(uFormat & (DT_RIGHT | DT_CENTER)))
4584     {
4585         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4586         else rcLabel.left += LABEL_HOR_PADDING;
4587     }
4588     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4589
4590     /* for GRIDLINES reduce the bottom so the text formats correctly */
4591     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4592         rcLabel.bottom--;
4593
4594     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
4595
4596 postpaint:
4597     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4598         notify_postpaint(infoPtr, &nmlvcd);
4599     if (cdsubitemmode & CDRF_NEWFONT)
4600         SelectObject(hdc, hOldFont);
4601     return TRUE;
4602 }
4603
4604 /***
4605  * DESCRIPTION:
4606  * Draws listview items when in owner draw mode.
4607  *
4608  * PARAMETER(S):
4609  * [I] infoPtr : valid pointer to the listview structure
4610  * [I] hdc : device context handle
4611  *
4612  * RETURN:
4613  * None
4614  */
4615 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4616 {
4617     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4618     DWORD cditemmode = CDRF_DODEFAULT;
4619     NMLVCUSTOMDRAW nmlvcd;
4620     POINT Origin, Position;
4621     DRAWITEMSTRUCT dis;
4622     LVITEMW item;
4623     
4624     TRACE("()\n");
4625
4626     ZeroMemory(&dis, sizeof(dis));
4627     
4628     /* Get scroll info once before loop */
4629     LISTVIEW_GetOrigin(infoPtr, &Origin);
4630     
4631     /* iterate through the invalidated rows */
4632     while(iterator_next(i))
4633     {
4634         item.iItem = i->nItem;
4635         item.iSubItem = 0;
4636         item.mask = LVIF_PARAM | LVIF_STATE;
4637         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4638         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4639            
4640         dis.CtlType = ODT_LISTVIEW;
4641         dis.CtlID = uID;
4642         dis.itemID = item.iItem;
4643         dis.itemAction = ODA_DRAWENTIRE;
4644         dis.itemState = 0;
4645         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4646         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4647         dis.hwndItem = infoPtr->hwndSelf;
4648         dis.hDC = hdc;
4649         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4650         dis.rcItem.left = Position.x + Origin.x;
4651         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4652         dis.rcItem.top = Position.y + Origin.y;
4653         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4654         dis.itemData = item.lParam;
4655
4656         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4657
4658     /*
4659      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4660      * structure for the rest. of the paint cycle
4661      */
4662         customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4663         if (cdmode & CDRF_NOTIFYITEMDRAW)
4664             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4665     
4666         if (!(cditemmode & CDRF_SKIPDEFAULT))
4667         {
4668             prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4669             SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4670         }
4671
4672         if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4673             notify_postpaint(infoPtr, &nmlvcd);
4674     }
4675 }
4676
4677 /***
4678  * DESCRIPTION:
4679  * Draws listview items when in report display mode.
4680  *
4681  * PARAMETER(S):
4682  * [I] infoPtr : valid pointer to the listview structure
4683  * [I] hdc : device context handle
4684  * [I] cdmode : custom draw mode
4685  *
4686  * RETURN:
4687  * None
4688  */
4689 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4690 {
4691     INT rgntype;
4692     RECT rcClip, rcItem;
4693     POINT Origin, Position;
4694     RANGES colRanges;
4695     INT col, index;
4696     ITERATOR j;
4697
4698     TRACE("()\n");
4699
4700     /* figure out what to draw */
4701     rgntype = GetClipBox(hdc, &rcClip);
4702     if (rgntype == NULLREGION) return;
4703     
4704     /* Get scroll info once before loop */
4705     LISTVIEW_GetOrigin(infoPtr, &Origin);
4706
4707     colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4708
4709     /* narrow down the columns we need to paint */
4710     for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4711     {
4712         index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4713
4714         LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4715         if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4716             ranges_additem(colRanges, index);
4717     }
4718     iterator_rangesitems(&j, colRanges);
4719
4720     /* in full row select, we _have_ to draw the main item */
4721     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4722         j.nSpecial = 0;
4723
4724     /* iterate through the invalidated rows */
4725     while(iterator_next(i))
4726     {
4727         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4728         Position.y += Origin.y;
4729
4730         /* iterate through the invalidated columns */
4731         while(iterator_next(&j))
4732         {
4733             LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4734             Position.x = (j.nItem == 0) ? rcItem.left + Origin.x : Origin.x;
4735
4736             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4737             {
4738                 rcItem.top = 0;
4739                 rcItem.bottom = infoPtr->nItemHeight;
4740                 OffsetRect(&rcItem, Origin.x, Position.y);
4741                 if (!RectVisible(hdc, &rcItem)) continue;
4742             }
4743
4744             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4745         }
4746     }
4747     iterator_destroy(&j);
4748 }
4749
4750 /***
4751  * DESCRIPTION:
4752  * Draws the gridlines if necessary when in report display mode.
4753  *
4754  * PARAMETER(S):
4755  * [I] infoPtr : valid pointer to the listview structure
4756  * [I] hdc : device context handle
4757  *
4758  * RETURN:
4759  * None
4760  */
4761 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4762 {
4763     INT rgntype;
4764     INT y, itemheight;
4765     INT col, index;
4766     HPEN hPen, hOldPen;
4767     RECT rcClip, rcItem = {0};
4768     POINT Origin;
4769     RANGES colRanges;
4770     ITERATOR j;
4771     BOOL rmost = FALSE;
4772
4773     TRACE("()\n");
4774
4775     /* figure out what to draw */
4776     rgntype = GetClipBox(hdc, &rcClip);
4777     if (rgntype == NULLREGION) return;
4778
4779     /* Get scroll info once before loop */
4780     LISTVIEW_GetOrigin(infoPtr, &Origin);
4781
4782     colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4783
4784     /* narrow down the columns we need to paint */
4785     for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4786     {
4787         index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4788
4789         LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4790         if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4791             ranges_additem(colRanges, index);
4792     }
4793
4794     /* is right most vertical line visible? */
4795     if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4796     {
4797         index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4798         LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4799         rmost = (rcItem.right + Origin.x < rcClip.right);
4800     }
4801
4802     if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4803     {
4804         hOldPen = SelectObject ( hdc, hPen );
4805
4806         /* draw the vertical lines for the columns */
4807         iterator_rangesitems(&j, colRanges);
4808         while(iterator_next(&j))
4809         {
4810             LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4811             if (rcItem.left == 0) continue; /* skip leftmost column */
4812             rcItem.left += Origin.x;
4813             rcItem.right += Origin.x;
4814             rcItem.top = infoPtr->rcList.top;
4815             rcItem.bottom = infoPtr->rcList.bottom;
4816             TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4817             MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4818             LineTo (hdc, rcItem.left, rcItem.bottom);
4819         }
4820         iterator_destroy(&j);
4821         /* draw rightmost grid line if visible */
4822         if (rmost)
4823         {
4824             index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4825                                  DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4826             LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4827
4828             rcItem.right += Origin.x;
4829
4830             MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
4831             LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
4832         }
4833
4834         /* draw the horizontial lines for the rows */
4835         itemheight =  LISTVIEW_CalculateItemHeight(infoPtr);
4836         rcItem.left   = infoPtr->rcList.left;
4837         rcItem.right  = infoPtr->rcList.right;
4838         rcItem.bottom = rcItem.top = Origin.y - 1;
4839         MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4840         LineTo(hdc, rcItem.right, rcItem.top);
4841         for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4842         {
4843             rcItem.bottom = rcItem.top = y;
4844             TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4845             MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4846             LineTo (hdc, rcItem.right, rcItem.top);
4847         }
4848
4849         SelectObject( hdc, hOldPen );
4850         DeleteObject( hPen );
4851     }
4852 }
4853
4854 /***
4855  * DESCRIPTION:
4856  * Draws listview items when in list display mode.
4857  *
4858  * PARAMETER(S):
4859  * [I] infoPtr : valid pointer to the listview structure
4860  * [I] hdc : device context handle
4861  * [I] cdmode : custom draw mode
4862  *
4863  * RETURN:
4864  * None
4865  */
4866 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4867 {
4868     POINT Origin, Position;
4869
4870     /* Get scroll info once before loop */
4871     LISTVIEW_GetOrigin(infoPtr, &Origin);
4872     
4873     while(iterator_prev(i))
4874     {
4875         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4876         Position.x += Origin.x;
4877         Position.y += Origin.y;
4878
4879         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4880     }
4881 }
4882
4883
4884 /***
4885  * DESCRIPTION:
4886  * Draws listview items.
4887  *
4888  * PARAMETER(S):
4889  * [I] infoPtr : valid pointer to the listview structure
4890  * [I] hdc : device context handle
4891  * [I] prcErase : rect to be erased before refresh (may be NULL)
4892  *
4893  * RETURN:
4894  * NoneX
4895  */
4896 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4897 {
4898     COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4899     NMLVCUSTOMDRAW nmlvcd;
4900     HFONT hOldFont = 0;
4901     DWORD cdmode;
4902     INT oldBkMode = 0;
4903     RECT rcClient;
4904     ITERATOR i;
4905     HDC hdcOrig = hdc;
4906     HBITMAP hbmp = NULL;
4907     RANGE range;
4908
4909     LISTVIEW_DUMP(infoPtr);
4910
4911     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4912         TRACE("double buffering\n");
4913
4914         hdc = CreateCompatibleDC(hdcOrig);
4915         if (!hdc) {
4916             ERR("Failed to create DC for backbuffer\n");
4917             return;
4918         }
4919         hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4920                                       infoPtr->rcList.bottom);
4921         if (!hbmp) {
4922             ERR("Failed to create bitmap for backbuffer\n");
4923             DeleteDC(hdc);
4924             return;
4925         }
4926
4927         SelectObject(hdc, hbmp);
4928         SelectObject(hdc, infoPtr->hFont);
4929     } else {
4930         /* Save dc values we're gonna trash while drawing
4931          * FIXME: Should be done in LISTVIEW_DrawItem() */
4932         hOldFont = SelectObject(hdc, infoPtr->hFont);
4933         oldBkMode = GetBkMode(hdc);
4934         oldBkColor = GetBkColor(hdc);
4935         oldTextColor = GetTextColor(hdc);
4936     }
4937
4938     infoPtr->bIsDrawing = TRUE;
4939
4940     if (prcErase) {
4941         LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4942     } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4943         /* If no erasing was done (usually because RedrawWindow was called
4944          * with RDW_INVALIDATE only) we need to copy the old contents into
4945          * the backbuffer before continuing. */
4946         BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4947                infoPtr->rcList.right - infoPtr->rcList.left,
4948                infoPtr->rcList.bottom - infoPtr->rcList.top,
4949                hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4950     }
4951
4952     /* FIXME: Shouldn't need to do this */
4953     oldClrTextBk = infoPtr->clrTextBk;
4954     oldClrText   = infoPtr->clrText;
4955    
4956     infoPtr->cditemmode = CDRF_DODEFAULT;
4957
4958     GetClientRect(infoPtr->hwndSelf, &rcClient);
4959     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4960     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4961     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4962     prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4963
4964     /* Use these colors to draw the items */
4965     infoPtr->clrTextBk = nmlvcd.clrTextBk;
4966     infoPtr->clrText = nmlvcd.clrText;
4967
4968     /* nothing to draw */
4969     if(infoPtr->nItemCount == 0) goto enddraw;
4970
4971     /* figure out what we need to draw */
4972     iterator_visibleitems(&i, infoPtr, hdc);
4973     range = iterator_range(&i);
4974
4975     /* send cache hint notification */
4976     if (infoPtr->dwStyle & LVS_OWNERDATA)
4977     {
4978         NMLVCACHEHINT nmlv;
4979         
4980         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4981         nmlv.iFrom = range.lower;
4982         nmlv.iTo   = range.upper - 1;
4983         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4984     }
4985
4986     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
4987         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4988     else
4989     {
4990         if (infoPtr->uView == LV_VIEW_DETAILS)
4991             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4992         else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
4993             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4994
4995         /* if we have a focus rect and it's visible, draw it */
4996         if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
4997                         (range.upper - 1) >= infoPtr->nFocusedItem)
4998             LISTVIEW_DrawFocusRect(infoPtr, hdc);
4999     }
5000     iterator_destroy(&i);
5001     
5002 enddraw:
5003     /* For LVS_EX_GRIDLINES go and draw lines */
5004     /*  This includes the case where there were *no* items */
5005     if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
5006         LISTVIEW_RefreshReportGrid(infoPtr, hdc);
5007
5008     /* Draw marquee rectangle if appropriate */
5009     if (infoPtr->bMarqueeSelect)
5010         DrawFocusRect(hdc, &infoPtr->marqueeDrawRect);
5011
5012     if (cdmode & CDRF_NOTIFYPOSTPAINT)
5013         notify_postpaint(infoPtr, &nmlvcd);
5014
5015     infoPtr->clrTextBk = oldClrTextBk;
5016     infoPtr->clrText = oldClrText;
5017
5018     if(hbmp) {
5019         BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
5020                infoPtr->rcList.right - infoPtr->rcList.left,
5021                infoPtr->rcList.bottom - infoPtr->rcList.top,
5022                hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5023
5024         DeleteObject(hbmp);
5025         DeleteDC(hdc);
5026     } else {
5027         SelectObject(hdc, hOldFont);
5028         SetBkMode(hdc, oldBkMode);
5029         SetBkColor(hdc, oldBkColor);
5030         SetTextColor(hdc, oldTextColor);
5031     }
5032
5033     infoPtr->bIsDrawing = FALSE;
5034 }
5035
5036
5037 /***
5038  * DESCRIPTION:
5039  * Calculates the approximate width and height of a given number of items.
5040  *
5041  * PARAMETER(S):
5042  * [I] infoPtr : valid pointer to the listview structure
5043  * [I] nItemCount : number of items
5044  * [I] wWidth : width
5045  * [I] wHeight : height
5046  *
5047  * RETURN:
5048  * Returns a DWORD. The width in the low word and the height in high word.
5049  */
5050 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
5051                                             WORD wWidth, WORD wHeight)
5052 {
5053   INT nItemCountPerColumn = 1;
5054   INT nColumnCount = 0;
5055   DWORD dwViewRect = 0;
5056
5057   if (nItemCount == -1)
5058     nItemCount = infoPtr->nItemCount;
5059
5060   if (infoPtr->uView == LV_VIEW_LIST)
5061   {
5062     if (wHeight == 0xFFFF)
5063     {
5064       /* use current height */
5065       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5066     }
5067
5068     if (wHeight < infoPtr->nItemHeight)
5069       wHeight = infoPtr->nItemHeight;
5070
5071     if (nItemCount > 0)
5072     {
5073       if (infoPtr->nItemHeight > 0)
5074       {
5075         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
5076         if (nItemCountPerColumn == 0)
5077           nItemCountPerColumn = 1;
5078
5079         if (nItemCount % nItemCountPerColumn != 0)
5080           nColumnCount = nItemCount / nItemCountPerColumn;
5081         else
5082           nColumnCount = nItemCount / nItemCountPerColumn + 1;
5083       }
5084     }
5085
5086     /* Microsoft padding magic */
5087     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
5088     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
5089
5090     dwViewRect = MAKELONG(wWidth, wHeight);
5091   }
5092   else if (infoPtr->uView == LV_VIEW_DETAILS)
5093   {
5094     RECT rcBox;
5095
5096     if (infoPtr->nItemCount > 0)
5097     {
5098       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
5099       wWidth = rcBox.right - rcBox.left;
5100       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
5101     }
5102     else
5103     {
5104       /* use current height and width */
5105       if (wHeight == 0xffff)
5106           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5107       if (wWidth == 0xffff)
5108           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5109     }
5110
5111     dwViewRect = MAKELONG(wWidth, wHeight);
5112   }
5113   else if (infoPtr->uView == LV_VIEW_ICON)
5114   {
5115     UINT rows,cols;
5116     UINT nItemWidth;
5117     UINT nItemHeight;
5118
5119     nItemWidth = infoPtr->iconSpacing.cx;
5120     nItemHeight = infoPtr->iconSpacing.cy;
5121
5122     if (nItemCount == -1)
5123       nItemCount = infoPtr->nItemCount;
5124
5125     if (wWidth == 0xffff)
5126       wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5127
5128     if (wWidth < nItemWidth)
5129       wWidth = nItemWidth;
5130
5131     cols = wWidth / nItemWidth;
5132     if (cols > nItemCount)
5133       cols = nItemCount;
5134     if (cols < 1)
5135         cols = 1;
5136
5137     if (nItemCount)
5138     {
5139       rows = nItemCount / cols;
5140       if (nItemCount % cols)
5141         rows++;
5142     }
5143     else
5144       rows = 0;
5145
5146     wHeight = (nItemHeight * rows)+2;
5147     wWidth = (nItemWidth * cols)+2;
5148
5149     dwViewRect = MAKELONG(wWidth, wHeight);
5150   }
5151   else if (infoPtr->uView == LV_VIEW_SMALLICON)
5152     FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5153
5154   return dwViewRect;
5155 }
5156
5157 /***
5158  * DESCRIPTION:
5159  * Cancel edit label with saving item text.
5160  *
5161  * PARAMETER(S):
5162  * [I] infoPtr : valid pointer to the listview structure
5163  *
5164  * RETURN:
5165  * Always returns TRUE.
5166  */
5167 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr)
5168 {
5169     if (infoPtr->hwndEdit)
5170     {
5171         /* handle value will be lost after LISTVIEW_EndEditLabelT */
5172         HWND edit = infoPtr->hwndEdit;
5173
5174         LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
5175         SendMessageW(edit, WM_CLOSE, 0, 0);
5176     }
5177
5178     return TRUE;
5179 }
5180
5181 /***
5182  * DESCRIPTION:
5183  * Create a drag image list for the specified item.
5184  *
5185  * PARAMETER(S):
5186  * [I] infoPtr : valid pointer to the listview structure
5187  * [I] iItem   : index of item
5188  * [O] lppt    : Upper-left corner of the image
5189  *
5190  * RETURN:
5191  * Returns a handle to the image list if successful, NULL otherwise.
5192  */
5193 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
5194 {
5195     RECT rcItem;
5196     SIZE size;
5197     POINT pos;
5198     HDC hdc, hdcOrig;
5199     HBITMAP hbmp, hOldbmp;
5200     HIMAGELIST dragList = 0;
5201     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
5202
5203     if (iItem < 0 || iItem >= infoPtr->nItemCount)
5204         return 0;
5205
5206     rcItem.left = LVIR_BOUNDS;
5207     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
5208         return 0;
5209
5210     lppt->x = rcItem.left;
5211     lppt->y = rcItem.top;
5212
5213     size.cx = rcItem.right - rcItem.left;
5214     size.cy = rcItem.bottom - rcItem.top;
5215
5216     hdcOrig = GetDC(infoPtr->hwndSelf);
5217     hdc = CreateCompatibleDC(hdcOrig);
5218     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
5219     hOldbmp = SelectObject(hdc, hbmp);
5220
5221     rcItem.left = rcItem.top = 0;
5222     rcItem.right = size.cx;
5223     rcItem.bottom = size.cy;
5224     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
5225     
5226     pos.x = pos.y = 0;
5227     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
5228     {
5229         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
5230         SelectObject(hdc, hOldbmp);
5231         ImageList_Add(dragList, hbmp, 0);
5232     }
5233     else
5234         SelectObject(hdc, hOldbmp);
5235
5236     DeleteObject(hbmp);
5237     DeleteDC(hdc);
5238     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
5239
5240     TRACE("ret=%p\n", dragList);
5241
5242     return dragList;
5243 }
5244
5245
5246 /***
5247  * DESCRIPTION:
5248  * Removes all listview items and subitems.
5249  *
5250  * PARAMETER(S):
5251  * [I] infoPtr : valid pointer to the listview structure
5252  *
5253  * RETURN:
5254  *   SUCCESS : TRUE
5255  *   FAILURE : FALSE
5256  */
5257 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
5258 {
5259     NMLISTVIEW nmlv;
5260     HDPA hdpaSubItems = NULL;
5261     BOOL bSuppress;
5262     ITEMHDR *hdrItem;
5263     ITEM_INFO *lpItem;
5264     ITEM_ID *lpID;
5265     INT i, j;
5266
5267     TRACE("()\n");
5268
5269     /* we do it directly, to avoid notifications */
5270     ranges_clear(infoPtr->selectionRanges);
5271     infoPtr->nSelectionMark = -1;
5272     infoPtr->nFocusedItem = -1;
5273     SetRectEmpty(&infoPtr->rcFocus);
5274     /* But we are supposed to leave nHotItem as is! */
5275
5276
5277     /* send LVN_DELETEALLITEMS notification */
5278     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5279     nmlv.iItem = -1;
5280     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
5281
5282     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
5283     {
5284         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5285         {
5286             /* send LVN_DELETEITEM notification, if not suppressed
5287                and if it is not a virtual listview */
5288             if (!bSuppress) notify_deleteitem(infoPtr, i);
5289             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
5290             lpItem = DPA_GetPtr(hdpaSubItems, 0);
5291             /* free id struct */
5292             j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5293             lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j);
5294             DPA_DeletePtr(infoPtr->hdpaItemIds, j);
5295             Free(lpID);
5296             /* both item and subitem start with ITEMHDR header */
5297             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
5298             {
5299                 hdrItem = DPA_GetPtr(hdpaSubItems, j);
5300                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5301                 Free(hdrItem);
5302             }
5303             DPA_Destroy(hdpaSubItems);
5304             DPA_DeletePtr(infoPtr->hdpaItems, i);
5305         }
5306         DPA_DeletePtr(infoPtr->hdpaPosX, i);
5307         DPA_DeletePtr(infoPtr->hdpaPosY, i);
5308         infoPtr->nItemCount --;
5309     }
5310     
5311     if (!destroy)
5312     {
5313         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5314         LISTVIEW_UpdateScroll(infoPtr);
5315     }
5316     LISTVIEW_InvalidateList(infoPtr);
5317     
5318     return TRUE;
5319 }
5320
5321 /***
5322  * DESCRIPTION:
5323  * Scrolls, and updates the columns, when a column is changing width.
5324  *
5325  * PARAMETER(S):
5326  * [I] infoPtr : valid pointer to the listview structure
5327  * [I] nColumn : column to scroll
5328  * [I] dx : amount of scroll, in pixels
5329  *
5330  * RETURN:
5331  *   None.
5332  */
5333 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
5334 {
5335     COLUMN_INFO *lpColumnInfo;
5336     RECT rcOld, rcCol;
5337     POINT ptOrigin;
5338     INT nCol;
5339     HDITEMW hdi;
5340
5341     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
5342     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
5343     rcCol = lpColumnInfo->rcHeader;
5344     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5345         rcCol.left = rcCol.right;
5346
5347     /* adjust the other columns */
5348     hdi.mask = HDI_ORDER;
5349     if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
5350     {
5351         INT nOrder = hdi.iOrder;
5352         for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
5353         {
5354             hdi.mask = HDI_ORDER;
5355             SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi);
5356             if (hdi.iOrder >= nOrder) {
5357                 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
5358                 lpColumnInfo->rcHeader.left  += dx;
5359                 lpColumnInfo->rcHeader.right += dx;
5360             }
5361         }
5362     }
5363
5364     /* do not update screen if not in report mode */
5365     if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
5366     
5367     /* Need to reset the item width when inserting a new column */
5368     infoPtr->nItemWidth += dx;
5369
5370     LISTVIEW_UpdateScroll(infoPtr);
5371     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
5372
5373     /* scroll to cover the deleted column, and invalidate for redraw */
5374     rcOld = infoPtr->rcList;
5375     rcOld.left = ptOrigin.x + rcCol.left + dx;
5376     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5377 }
5378
5379 /***
5380  * DESCRIPTION:
5381  * Removes a column from the listview control.
5382  *
5383  * PARAMETER(S):
5384  * [I] infoPtr : valid pointer to the listview structure
5385  * [I] nColumn : column index
5386  *
5387  * RETURN:
5388  *   SUCCESS : TRUE
5389  *   FAILURE : FALSE
5390  */
5391 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
5392 {
5393     RECT rcCol;
5394     
5395     TRACE("nColumn=%d\n", nColumn);
5396
5397     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
5398            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5399
5400     /* While the MSDN specifically says that column zero should not be deleted,
5401        what actually happens is that the column itself is deleted but no items or subitems
5402        are removed.
5403      */
5404
5405     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
5406     
5407     if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
5408         return FALSE;
5409
5410     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
5411     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
5412   
5413     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
5414     {
5415         SUBITEM_INFO *lpSubItem, *lpDelItem;
5416         HDPA hdpaSubItems;
5417         INT nItem, nSubItem, i;
5418         
5419         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5420         {
5421             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
5422             nSubItem = 0;
5423             lpDelItem = 0;
5424             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
5425             {
5426                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
5427                 if (lpSubItem->iSubItem == nColumn)
5428                 {
5429                     nSubItem = i;
5430                     lpDelItem = lpSubItem;
5431                 }
5432                 else if (lpSubItem->iSubItem > nColumn) 
5433                 {
5434                     lpSubItem->iSubItem--;
5435                 }
5436             }
5437
5438             /* if we found our subitem, zapp it */      
5439             if (nSubItem > 0)
5440             {
5441                 /* free string */
5442                 if (is_textW(lpDelItem->hdr.pszText))
5443                     Free(lpDelItem->hdr.pszText);
5444
5445                 /* free item */
5446                 Free(lpDelItem);
5447
5448                 /* free dpa memory */
5449                 DPA_DeletePtr(hdpaSubItems, nSubItem);
5450             }
5451         }
5452     }
5453
5454     /* update the other column info */
5455     LISTVIEW_UpdateItemSize(infoPtr);
5456     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
5457         LISTVIEW_InvalidateList(infoPtr);
5458     else
5459         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
5460
5461     return TRUE;
5462 }
5463
5464 /***
5465  * DESCRIPTION:
5466  * Invalidates the listview after an item's insertion or deletion.
5467  *
5468  * PARAMETER(S):
5469  * [I] infoPtr : valid pointer to the listview structure
5470  * [I] nItem : item index
5471  * [I] dir : -1 if deleting, 1 if inserting
5472  *
5473  * RETURN:
5474  *   None
5475  */
5476 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5477 {
5478     INT nPerCol, nItemCol, nItemRow;
5479     RECT rcScroll;
5480     POINT Origin;
5481
5482     /* if we don't refresh, what's the point of scrolling? */
5483     if (!is_redrawing(infoPtr)) return;
5484     
5485     assert (abs(dir) == 1);
5486
5487     /* arrange icons if autoarrange is on */
5488     if (is_autoarrange(infoPtr))
5489     {
5490         BOOL arrange = TRUE;
5491         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5492         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5493         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5494     }
5495
5496     /* scrollbars need updating */
5497     LISTVIEW_UpdateScroll(infoPtr);
5498
5499     /* figure out the item's position */ 
5500     if (infoPtr->uView == LV_VIEW_DETAILS)
5501         nPerCol = infoPtr->nItemCount + 1;
5502     else if (infoPtr->uView == LV_VIEW_LIST)
5503         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5504     else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5505         return;
5506     
5507     nItemCol = nItem / nPerCol;
5508     nItemRow = nItem % nPerCol;
5509     LISTVIEW_GetOrigin(infoPtr, &Origin);
5510
5511     /* move the items below up a slot */
5512     rcScroll.left = nItemCol * infoPtr->nItemWidth;
5513     rcScroll.top = nItemRow * infoPtr->nItemHeight;
5514     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5515     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5516     OffsetRect(&rcScroll, Origin.x, Origin.y);
5517     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5518     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5519     {
5520         TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5521         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
5522                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5523     }
5524
5525     /* report has only that column, so we're done */
5526     if (infoPtr->uView == LV_VIEW_DETAILS) return;
5527
5528     /* now for LISTs, we have to deal with the columns to the right */
5529     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
5530     rcScroll.top = 0;
5531     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
5532     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5533     OffsetRect(&rcScroll, Origin.x, Origin.y);
5534     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5535         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5536                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5537 }
5538
5539 /***
5540  * DESCRIPTION:
5541  * Removes an item from the listview control.
5542  *
5543  * PARAMETER(S):
5544  * [I] infoPtr : valid pointer to the listview structure
5545  * [I] nItem : item index
5546  *
5547  * RETURN:
5548  *   SUCCESS : TRUE
5549  *   FAILURE : FALSE
5550  */
5551 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5552 {
5553     LVITEMW item;
5554     const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5555
5556     TRACE("(nItem=%d)\n", nItem);
5557
5558     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5559     
5560     /* remove selection, and focus */
5561     item.state = 0;
5562     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5563     LISTVIEW_SetItemState(infoPtr, nItem, &item);
5564             
5565     /* send LVN_DELETEITEM notification. */
5566     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5567
5568     /* we need to do this here, because we'll be deleting stuff */  
5569     if (is_icon)
5570         LISTVIEW_InvalidateItem(infoPtr, nItem);
5571     
5572     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5573     {
5574         HDPA hdpaSubItems;
5575         ITEMHDR *hdrItem;
5576         ITEM_INFO *lpItem;
5577         ITEM_ID *lpID;
5578         INT i;
5579
5580         hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5581         lpItem = DPA_GetPtr(hdpaSubItems, 0);
5582
5583         /* free id struct */
5584         i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5585         lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5586         DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5587         Free(lpID);
5588         for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5589         {
5590             hdrItem = DPA_GetPtr(hdpaSubItems, i);
5591             if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5592             Free(hdrItem);
5593         }
5594         DPA_Destroy(hdpaSubItems);
5595     }
5596
5597     if (is_icon)
5598     {
5599         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5600         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5601     }
5602
5603     infoPtr->nItemCount--;
5604     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5605
5606     /* now is the invalidation fun */
5607     if (!is_icon)
5608         LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5609     return TRUE;
5610 }
5611
5612
5613 /***
5614  * DESCRIPTION:
5615  * Callback implementation for editlabel control
5616  *
5617  * PARAMETER(S):
5618  * [I] infoPtr : valid pointer to the listview structure
5619  * [I] storeText : store edit box text as item text
5620  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5621  *
5622  * RETURN:
5623  *   SUCCESS : TRUE
5624  *   FAILURE : FALSE
5625  */
5626 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5627 {
5628     HWND hwndSelf = infoPtr->hwndSelf;
5629     NMLVDISPINFOW dispInfo;
5630     INT editedItem = infoPtr->nEditLabelItem;
5631     BOOL bSame;
5632     WCHAR *pszText = NULL;
5633     BOOL res;
5634
5635     if (storeText)
5636     {
5637         DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
5638
5639         if (len)
5640         {
5641             if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5642             {
5643                 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
5644                 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
5645             }
5646         }
5647     }
5648
5649     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5650
5651     infoPtr->nEditLabelItem = -1;
5652     infoPtr->hwndEdit = 0;
5653
5654     ZeroMemory(&dispInfo, sizeof(dispInfo));
5655     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5656     dispInfo.item.iItem = editedItem;
5657     dispInfo.item.iSubItem = 0;
5658     dispInfo.item.stateMask = ~0;
5659     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item))
5660     {
5661        res = FALSE;
5662        goto cleanup;
5663     }
5664
5665     if (isW)
5666         bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5667     else
5668     {
5669         LPWSTR tmp = textdupTtoW(pszText, FALSE);
5670         bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5671         textfreeT(tmp, FALSE);
5672     }
5673
5674     /* add the text from the edit in */
5675     dispInfo.item.mask |= LVIF_TEXT;
5676     dispInfo.item.pszText = bSame ? NULL : pszText;
5677     dispInfo.item.cchTextMax = bSame ? 0 : textlenT(pszText, isW);
5678
5679     /* Do we need to update the Item Text */
5680     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
5681     {
5682         res = FALSE;
5683         goto cleanup;
5684     }
5685     if (!IsWindow(hwndSelf))
5686     {
5687         res = FALSE;
5688         goto cleanup;
5689     }
5690     if (!pszText) return TRUE;
5691     if (bSame)
5692     {
5693         res = TRUE;
5694         goto cleanup;
5695     }
5696
5697     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5698     {
5699         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5700         ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5701         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5702         {
5703             LISTVIEW_InvalidateItem(infoPtr, editedItem);
5704             res = TRUE;
5705             goto cleanup;
5706         }
5707     }
5708
5709     ZeroMemory(&dispInfo, sizeof(dispInfo));
5710     dispInfo.item.mask = LVIF_TEXT;
5711     dispInfo.item.iItem = editedItem;
5712     dispInfo.item.iSubItem = 0;
5713     dispInfo.item.pszText = pszText;
5714     dispInfo.item.cchTextMax = textlenT(pszText, isW);
5715     res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5716
5717 cleanup:
5718     Free(pszText);
5719
5720     return res;
5721 }
5722
5723 /***
5724  * DESCRIPTION:
5725  * Begin in place editing of specified list view item
5726  *
5727  * PARAMETER(S):
5728  * [I] infoPtr : valid pointer to the listview structure
5729  * [I] nItem : item index
5730  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5731  *
5732  * RETURN:
5733  *   SUCCESS : TRUE
5734  *   FAILURE : FALSE
5735  */
5736 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5737 {
5738     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5739     NMLVDISPINFOW dispInfo;
5740     RECT rect;
5741     SIZE sz;
5742     HWND hwndSelf = infoPtr->hwndSelf;
5743     HDC hdc;
5744     HFONT hOldFont = NULL;
5745     TEXTMETRICW textMetric;
5746
5747     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5748
5749     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5750
5751     /* Is the EditBox still there, if so remove it */
5752     if(infoPtr->hwndEdit != 0)
5753     {
5754         SetFocus(infoPtr->hwndSelf);
5755         infoPtr->hwndEdit = 0;
5756     }
5757
5758     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5759
5760     infoPtr->nEditLabelItem = nItem;
5761
5762     LISTVIEW_SetSelection(infoPtr, nItem);
5763     LISTVIEW_SetItemFocus(infoPtr, nItem);
5764     LISTVIEW_InvalidateItem(infoPtr, nItem);
5765
5766     rect.left = LVIR_LABEL;
5767     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5768     
5769     ZeroMemory(&dispInfo, sizeof(dispInfo));
5770     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5771     dispInfo.item.iItem = nItem;
5772     dispInfo.item.iSubItem = 0;
5773     dispInfo.item.stateMask = ~0;
5774     dispInfo.item.pszText = szDispText;
5775     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5776     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5777
5778     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, isW);
5779     if (!infoPtr->hwndEdit) return 0;
5780     
5781     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5782     {
5783         if (!IsWindow(hwndSelf))
5784             return 0;
5785         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5786         infoPtr->hwndEdit = 0;
5787         return 0;
5788     }
5789
5790     /* Now position and display edit box */
5791     hdc = GetDC(infoPtr->hwndSelf);
5792
5793     /* Select the font to get appropriate metric dimensions */
5794     if(infoPtr->hFont != 0)
5795         hOldFont = SelectObject(hdc, infoPtr->hFont);
5796
5797     /* Get String Length in pixels */
5798     GetTextExtentPoint32W(hdc, dispInfo.item.pszText, lstrlenW(dispInfo.item.pszText), &sz);
5799
5800     /* Add Extra spacing for the next character */
5801     GetTextMetricsW(hdc, &textMetric);
5802     sz.cx += (textMetric.tmMaxCharWidth * 2);
5803
5804     if(infoPtr->hFont != 0)
5805         SelectObject(hdc, hOldFont);
5806
5807     ReleaseDC(infoPtr->hwndSelf, hdc);
5808
5809     MoveWindow(infoPtr->hwndEdit, rect.left - 2, rect.top - 1, sz.cx,
5810                                   rect.bottom - rect.top + 2, FALSE);
5811     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5812     SetFocus(infoPtr->hwndEdit);
5813     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5814     return infoPtr->hwndEdit;
5815 }
5816
5817
5818 /***
5819  * DESCRIPTION:
5820  * Ensures the specified item is visible, scrolling into view if necessary.
5821  *
5822  * PARAMETER(S):
5823  * [I] infoPtr : valid pointer to the listview structure
5824  * [I] nItem : item index
5825  * [I] bPartial : partially or entirely visible
5826  *
5827  * RETURN:
5828  *   SUCCESS : TRUE
5829  *   FAILURE : FALSE
5830  */
5831 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5832 {
5833     INT nScrollPosHeight = 0;
5834     INT nScrollPosWidth = 0;
5835     INT nHorzAdjust = 0;
5836     INT nVertAdjust = 0;
5837     INT nHorzDiff = 0;
5838     INT nVertDiff = 0;
5839     RECT rcItem, rcTemp;
5840
5841     rcItem.left = LVIR_BOUNDS;
5842     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5843
5844     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5845     
5846     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5847     {
5848         /* scroll left/right, but in LV_VIEW_DETAILS mode */
5849         if (infoPtr->uView == LV_VIEW_LIST)
5850             nScrollPosWidth = infoPtr->nItemWidth;
5851         else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
5852             nScrollPosWidth = 1;
5853
5854         if (rcItem.left < infoPtr->rcList.left)
5855         {
5856             nHorzAdjust = -1;
5857             if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5858         }
5859         else
5860         {
5861             nHorzAdjust = 1;
5862             if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5863         }
5864     }
5865
5866     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5867     {
5868         /* scroll up/down, but not in LVS_LIST mode */
5869         if (infoPtr->uView == LV_VIEW_DETAILS)
5870             nScrollPosHeight = infoPtr->nItemHeight;
5871         else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
5872             nScrollPosHeight = 1;
5873
5874         if (rcItem.top < infoPtr->rcList.top)
5875         {
5876             nVertAdjust = -1;
5877             if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5878         }
5879         else
5880         {
5881             nVertAdjust = 1;
5882             if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5883         }
5884     }
5885
5886     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5887
5888     if (nScrollPosWidth)
5889     {
5890         INT diff = nHorzDiff / nScrollPosWidth;
5891         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5892         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5893     }
5894
5895     if (nScrollPosHeight)
5896     {
5897         INT diff = nVertDiff / nScrollPosHeight;
5898         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5899         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5900     }
5901
5902     return TRUE;
5903 }
5904
5905 /***
5906  * DESCRIPTION:
5907  * Searches for an item with specific characteristics.
5908  *
5909  * PARAMETER(S):
5910  * [I] hwnd : window handle
5911  * [I] nStart : base item index
5912  * [I] lpFindInfo : item information to look for
5913  *
5914  * RETURN:
5915  *   SUCCESS : index of item
5916  *   FAILURE : -1
5917  */
5918 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5919                               const LVFINDINFOW *lpFindInfo)
5920 {
5921     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5922     BOOL bWrap = FALSE, bNearest = FALSE;
5923     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5924     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5925     POINT Position, Destination;
5926     LVITEMW lvItem;
5927
5928     /* Search in virtual listviews should be done by application, not by
5929        listview control, so we just send LVN_ODFINDITEMW and return the result */
5930     if (infoPtr->dwStyle & LVS_OWNERDATA)
5931     {
5932         NMLVFINDITEMW nmlv;
5933
5934         nmlv.iStart = nStart;
5935         nmlv.lvfi = *lpFindInfo;
5936         return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5937     }
5938
5939     if (!lpFindInfo || nItem < 0) return -1;
5940     
5941     lvItem.mask = 0;
5942     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
5943         lpFindInfo->flags &  LVFI_SUBSTRING)
5944     {
5945         lvItem.mask |= LVIF_TEXT;
5946         lvItem.pszText = szDispText;
5947         lvItem.cchTextMax = DISP_TEXT_SIZE;
5948     }
5949
5950     if (lpFindInfo->flags & LVFI_WRAP)
5951         bWrap = TRUE;
5952
5953     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
5954         (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
5955     {
5956         POINT Origin;
5957         RECT rcArea;
5958         
5959         LISTVIEW_GetOrigin(infoPtr, &Origin);
5960         Destination.x = lpFindInfo->pt.x - Origin.x;
5961         Destination.y = lpFindInfo->pt.y - Origin.y;
5962         switch(lpFindInfo->vkDirection)
5963         {
5964         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
5965         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
5966         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5967         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
5968         case VK_HOME:  Destination.x = Destination.y = 0; break;
5969         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5970         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5971         case VK_END:
5972             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5973             Destination.x = rcArea.right; 
5974             Destination.y = rcArea.bottom; 
5975             break;
5976         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5977         }
5978         bNearest = TRUE;
5979     }
5980     else Destination.x = Destination.y = 0;
5981
5982     /* if LVFI_PARAM is specified, all other flags are ignored */
5983     if (lpFindInfo->flags & LVFI_PARAM)
5984     {
5985         lvItem.mask |= LVIF_PARAM;
5986         bNearest = FALSE;
5987         lvItem.mask &= ~LVIF_TEXT;
5988     }
5989
5990 again:
5991     for (; nItem < nLast; nItem++)
5992     {
5993         lvItem.iItem = nItem;
5994         lvItem.iSubItem = 0;
5995         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5996
5997         if (lvItem.mask & LVIF_PARAM)
5998         {
5999             if (lpFindInfo->lParam == lvItem.lParam)
6000                 return nItem;
6001             else
6002                 continue;
6003         }
6004
6005         if (lvItem.mask & LVIF_TEXT)
6006         {
6007             if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING))
6008             {
6009                 WCHAR *p = strstrW(lvItem.pszText, lpFindInfo->psz);
6010                 if (!p || p != lvItem.pszText) continue;
6011             }
6012             else
6013             {
6014                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
6015             }
6016         }
6017
6018         if (!bNearest) return nItem;
6019
6020         /* This is very inefficient. To do a good job here,
6021          * we need a sorted array of (x,y) item positions */
6022         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6023
6024         /* compute the distance^2 to the destination */
6025         xdist = Destination.x - Position.x;
6026         ydist = Destination.y - Position.y;
6027         dist = xdist * xdist + ydist * ydist;
6028
6029         /* remember the distance, and item if it's closer */
6030         if (dist < mindist)
6031         {
6032             mindist = dist;
6033             nNearestItem = nItem;
6034         }
6035     }
6036
6037     if (bWrap)
6038     {
6039         nItem = 0;
6040         nLast = min(nStart + 1, infoPtr->nItemCount);
6041         bWrap = FALSE;
6042         goto again;
6043     }
6044
6045     return nNearestItem;
6046 }
6047
6048 /***
6049  * DESCRIPTION:
6050  * Searches for an item with specific characteristics.
6051  *
6052  * PARAMETER(S):
6053  * [I] hwnd : window handle
6054  * [I] nStart : base item index
6055  * [I] lpFindInfo : item information to look for
6056  *
6057  * RETURN:
6058  *   SUCCESS : index of item
6059  *   FAILURE : -1
6060  */
6061 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
6062                               const LVFINDINFOA *lpFindInfo)
6063 {
6064     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
6065                    lpFindInfo->flags &  LVFI_SUBSTRING;
6066     LVFINDINFOW fiw;
6067     INT res;
6068     LPWSTR strW = NULL;
6069
6070     memcpy(&fiw, lpFindInfo, sizeof(fiw));
6071     if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
6072     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
6073     textfreeT(strW, FALSE);
6074     return res;
6075 }
6076
6077 /***
6078  * DESCRIPTION:
6079  * Retrieves the background image of the listview control.
6080  *
6081  * PARAMETER(S):
6082  * [I] infoPtr : valid pointer to the listview structure
6083  * [O] lpBkImage : background image attributes
6084  *
6085  * RETURN:
6086  *   SUCCESS : TRUE
6087  *   FAILURE : FALSE
6088  */
6089 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
6090 /* {   */
6091 /*   FIXME (listview, "empty stub!\n"); */
6092 /*   return FALSE;   */
6093 /* }   */
6094
6095 /***
6096  * DESCRIPTION:
6097  * Retrieves column attributes.
6098  *
6099  * PARAMETER(S):
6100  * [I] infoPtr : valid pointer to the listview structure
6101  * [I] nColumn :  column index
6102  * [IO] lpColumn : column information
6103  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6104  *           otherwise it is in fact a LPLVCOLUMNA
6105  *
6106  * RETURN:
6107  *   SUCCESS : TRUE
6108  *   FAILURE : FALSE
6109  */
6110 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
6111 {
6112     COLUMN_INFO *lpColumnInfo;
6113     HDITEMW hdi;
6114
6115     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6116     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6117
6118     /* initialize memory */
6119     ZeroMemory(&hdi, sizeof(hdi));
6120
6121     if (lpColumn->mask & LVCF_TEXT)
6122     {
6123         hdi.mask |= HDI_TEXT;
6124         hdi.pszText = lpColumn->pszText;
6125         hdi.cchTextMax = lpColumn->cchTextMax;
6126     }
6127
6128     if (lpColumn->mask & LVCF_IMAGE)
6129         hdi.mask |= HDI_IMAGE;
6130
6131     if (lpColumn->mask & LVCF_ORDER)
6132         hdi.mask |= HDI_ORDER;
6133
6134     if (lpColumn->mask & LVCF_SUBITEM)
6135         hdi.mask |= HDI_LPARAM;
6136
6137     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
6138
6139     if (lpColumn->mask & LVCF_FMT)
6140         lpColumn->fmt = lpColumnInfo->fmt;
6141
6142     if (lpColumn->mask & LVCF_WIDTH)
6143         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
6144
6145     if (lpColumn->mask & LVCF_IMAGE)
6146         lpColumn->iImage = hdi.iImage;
6147
6148     if (lpColumn->mask & LVCF_ORDER)
6149         lpColumn->iOrder = hdi.iOrder;
6150
6151     if (lpColumn->mask & LVCF_SUBITEM)
6152         lpColumn->iSubItem = hdi.lParam;
6153
6154     if (lpColumn->mask & LVCF_MINWIDTH)
6155         lpColumn->cxMin = lpColumnInfo->cxMin;
6156
6157     return TRUE;
6158 }
6159
6160
6161 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6162 {
6163     TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
6164
6165     if (!lpiArray)
6166         return FALSE;
6167
6168     return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
6169 }
6170
6171 /***
6172  * DESCRIPTION:
6173  * Retrieves the column width.
6174  *
6175  * PARAMETER(S):
6176  * [I] infoPtr : valid pointer to the listview structure
6177  * [I] int : column index
6178  *
6179  * RETURN:
6180  *   SUCCESS : column width
6181  *   FAILURE : zero
6182  */
6183 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
6184 {
6185     INT nColumnWidth = 0;
6186     HDITEMW hdItem;
6187
6188     TRACE("nColumn=%d\n", nColumn);
6189
6190     /* we have a 'column' in LIST and REPORT mode only */
6191     switch(infoPtr->uView)
6192     {
6193     case LV_VIEW_LIST:
6194         nColumnWidth = infoPtr->nItemWidth;
6195         break;
6196     case LV_VIEW_DETAILS:
6197         /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6198          * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6199          * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6200          *
6201          * TODO: should we do the same in LVM_GETCOLUMN?
6202          */
6203         hdItem.mask = HDI_WIDTH;
6204         if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
6205         {
6206             WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
6207             return 0;
6208         }
6209         nColumnWidth = hdItem.cxy;
6210         break;
6211     }
6212
6213     TRACE("nColumnWidth=%d\n", nColumnWidth);
6214     return nColumnWidth;
6215 }
6216
6217 /***
6218  * DESCRIPTION:
6219  * In list or report display mode, retrieves the number of items that can fit
6220  * vertically in the visible area. In icon or small icon display mode,
6221  * retrieves the total number of visible items.
6222  *
6223  * PARAMETER(S):
6224  * [I] infoPtr : valid pointer to the listview structure
6225  *
6226  * RETURN:
6227  * Number of fully visible items.
6228  */
6229 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
6230 {
6231     switch (infoPtr->uView)
6232     {
6233     case LV_VIEW_ICON:
6234     case LV_VIEW_SMALLICON:
6235         return infoPtr->nItemCount;
6236     case LV_VIEW_DETAILS:
6237         return LISTVIEW_GetCountPerColumn(infoPtr);
6238     case LV_VIEW_LIST:
6239         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
6240     }
6241     assert(FALSE);
6242     return 0;
6243 }
6244
6245 /***
6246  * DESCRIPTION:
6247  * Retrieves an image list handle.
6248  *
6249  * PARAMETER(S):
6250  * [I] infoPtr : valid pointer to the listview structure
6251  * [I] nImageList : image list identifier
6252  *
6253  * RETURN:
6254  *   SUCCESS : image list handle
6255  *   FAILURE : NULL
6256  */
6257 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
6258 {
6259     switch (nImageList)
6260     {
6261     case LVSIL_NORMAL: return infoPtr->himlNormal;
6262     case LVSIL_SMALL:  return infoPtr->himlSmall;
6263     case LVSIL_STATE:  return infoPtr->himlState;
6264     case LVSIL_GROUPHEADER:
6265         FIXME("LVSIL_GROUPHEADER not supported\n");
6266         break;
6267     default:
6268         WARN("got unknown imagelist index - %d\n", nImageList);
6269     }
6270     return NULL;
6271 }
6272
6273 /* LISTVIEW_GetISearchString */
6274
6275 /***
6276  * DESCRIPTION:
6277  * Retrieves item attributes.
6278  *
6279  * PARAMETER(S):
6280  * [I] hwnd : window handle
6281  * [IO] lpLVItem : item info
6282  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6283  *           if FALSE, then lpLVItem is a LPLVITEMA.
6284  *
6285  * NOTE:
6286  *   This is the internal 'GetItem' interface -- it tries to
6287  *   be smart and avoid text copies, if possible, by modifying
6288  *   lpLVItem->pszText to point to the text string. Please note
6289  *   that this is not always possible (e.g. OWNERDATA), so on
6290  *   entry you *must* supply valid values for pszText, and cchTextMax.
6291  *   The only difference to the documented interface is that upon
6292  *   return, you should use *only* the lpLVItem->pszText, rather than
6293  *   the buffer pointer you provided on input. Most code already does
6294  *   that, so it's not a problem.
6295  *   For the two cases when the text must be copied (that is,
6296  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6297  *
6298  * RETURN:
6299  *   SUCCESS : TRUE
6300  *   FAILURE : FALSE
6301  */
6302 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6303 {
6304     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
6305     NMLVDISPINFOW dispInfo;
6306     ITEM_INFO *lpItem;
6307     ITEMHDR* pItemHdr;
6308     HDPA hdpaSubItems;
6309     INT isubitem;
6310
6311     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6312
6313     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6314         return FALSE;
6315
6316     if (lpLVItem->mask == 0) return TRUE;
6317
6318     /* make a local copy */
6319     isubitem = lpLVItem->iSubItem;
6320
6321     /* a quick optimization if all we're asked is the focus state
6322      * these queries are worth optimising since they are common,
6323      * and can be answered in constant time, without the heavy accesses */
6324     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
6325          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
6326     {
6327         lpLVItem->state = 0;
6328         if (infoPtr->nFocusedItem == lpLVItem->iItem)
6329             lpLVItem->state |= LVIS_FOCUSED;
6330         return TRUE;
6331     }
6332
6333     ZeroMemory(&dispInfo, sizeof(dispInfo));
6334
6335     /* if the app stores all the data, handle it separately */
6336     if (infoPtr->dwStyle & LVS_OWNERDATA)
6337     {
6338         dispInfo.item.state = 0;
6339
6340         /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6341         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
6342            ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
6343         {
6344             UINT mask = lpLVItem->mask;
6345
6346             /* NOTE: copy only fields which we _know_ are initialized, some apps
6347              *       depend on the uninitialized fields being 0 */
6348             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
6349             dispInfo.item.iItem = lpLVItem->iItem;
6350             dispInfo.item.iSubItem = isubitem;
6351             if (lpLVItem->mask & LVIF_TEXT)
6352             {
6353                 if (lpLVItem->mask & LVIF_NORECOMPUTE)
6354                     /* reset mask */
6355                     dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
6356                 else
6357                 {
6358                     dispInfo.item.pszText = lpLVItem->pszText;
6359                     dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6360                 }
6361             }
6362             if (lpLVItem->mask & LVIF_STATE)
6363                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
6364             /* could be zeroed on LVIF_NORECOMPUTE case */
6365             if (dispInfo.item.mask != 0)
6366             {
6367                 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6368                 dispInfo.item.stateMask = lpLVItem->stateMask;
6369                 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6370                 {
6371                     /* full size structure expected - _WIN32IE >= 0x560 */
6372                     *lpLVItem = dispInfo.item;
6373                 }
6374                 else if (lpLVItem->mask & LVIF_INDENT)
6375                 {
6376                     /* indent member expected - _WIN32IE >= 0x300 */
6377                     memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
6378                 }
6379                 else
6380                 {
6381                     /* minimal structure expected */
6382                     memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
6383                 }
6384                 lpLVItem->mask = mask;
6385                 TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
6386             }
6387         }
6388         
6389         /* make sure lParam is zeroed out */
6390         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
6391
6392         /* callback marked pointer required here */
6393         if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
6394             lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
6395
6396         /* we store only a little state, so if we're not asked, we're done */
6397         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
6398
6399         /* if focus is handled by us, report it */
6400         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
6401         {
6402             lpLVItem->state &= ~LVIS_FOCUSED;
6403             if (infoPtr->nFocusedItem == lpLVItem->iItem)
6404                 lpLVItem->state |= LVIS_FOCUSED;
6405         }
6406
6407         /* and do the same for selection, if we handle it */
6408         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
6409         {
6410             lpLVItem->state &= ~LVIS_SELECTED;
6411             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6412                 lpLVItem->state |= LVIS_SELECTED;
6413         }
6414         
6415         return TRUE;
6416     }
6417
6418     /* find the item and subitem structures before we proceed */
6419     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
6420     lpItem = DPA_GetPtr(hdpaSubItems, 0);
6421     assert (lpItem);
6422
6423     if (isubitem)
6424     {
6425         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
6426         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
6427         if (!lpSubItem)
6428         {
6429             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
6430             isubitem = 0;
6431         }
6432     }
6433     else
6434         pItemHdr = &lpItem->hdr;
6435
6436     /* Do we need to query the state from the app? */
6437     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
6438     {
6439         dispInfo.item.mask |= LVIF_STATE;
6440         dispInfo.item.stateMask = infoPtr->uCallbackMask;
6441     }
6442   
6443     /* Do we need to enquire about the image? */
6444     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
6445         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
6446     {
6447         dispInfo.item.mask |= LVIF_IMAGE;
6448         dispInfo.item.iImage = I_IMAGECALLBACK;
6449     }
6450
6451     /* Only items support indentation */
6452     if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
6453         (isubitem == 0))
6454     {
6455         dispInfo.item.mask |= LVIF_INDENT;
6456         dispInfo.item.iIndent = I_INDENTCALLBACK;
6457     }
6458
6459     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6460     if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
6461         !is_textW(pItemHdr->pszText))
6462     {
6463         dispInfo.item.mask |= LVIF_TEXT;
6464         dispInfo.item.pszText = lpLVItem->pszText;
6465         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6466         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
6467             *dispInfo.item.pszText = '\0';
6468     }
6469
6470     /* If we don't have all the requested info, query the application */
6471     if (dispInfo.item.mask != 0)
6472     {
6473         dispInfo.item.iItem = lpLVItem->iItem;
6474         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
6475         dispInfo.item.lParam = lpItem->lParam;
6476         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6477         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
6478     }
6479
6480     /* we should not store values for subitems */
6481     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
6482
6483     /* Now, handle the iImage field */
6484     if (dispInfo.item.mask & LVIF_IMAGE)
6485     {
6486         lpLVItem->iImage = dispInfo.item.iImage;
6487         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6488             pItemHdr->iImage = dispInfo.item.iImage;
6489     }
6490     else if (lpLVItem->mask & LVIF_IMAGE)
6491     {
6492         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6493             lpLVItem->iImage = pItemHdr->iImage;
6494         else
6495             lpLVItem->iImage = 0;
6496     }
6497
6498     /* The pszText field */
6499     if (dispInfo.item.mask & LVIF_TEXT)
6500     {
6501         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6502             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6503
6504         lpLVItem->pszText = dispInfo.item.pszText;
6505     }
6506     else if (lpLVItem->mask & LVIF_TEXT)
6507     {
6508         /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6509         if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
6510         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
6511     }
6512
6513     /* Next is the lParam field */
6514     if (dispInfo.item.mask & LVIF_PARAM)
6515     {
6516         lpLVItem->lParam = dispInfo.item.lParam;
6517         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
6518             lpItem->lParam = dispInfo.item.lParam;
6519     }
6520     else if (lpLVItem->mask & LVIF_PARAM)
6521         lpLVItem->lParam = lpItem->lParam;
6522
6523     /* if this is a subitem, we're done */
6524     if (isubitem) return TRUE;
6525
6526     /* ... the state field (this one is different due to uCallbackmask) */
6527     if (lpLVItem->mask & LVIF_STATE)
6528     {
6529         lpLVItem->state = lpItem->state & lpLVItem->stateMask;
6530         if (dispInfo.item.mask & LVIF_STATE)
6531         {
6532             lpLVItem->state &= ~dispInfo.item.stateMask;
6533             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
6534         }
6535         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
6536         {
6537             lpLVItem->state &= ~LVIS_FOCUSED;
6538             if (infoPtr->nFocusedItem == lpLVItem->iItem)
6539                 lpLVItem->state |= LVIS_FOCUSED;
6540         }
6541         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
6542         {
6543             lpLVItem->state &= ~LVIS_SELECTED;
6544             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6545                 lpLVItem->state |= LVIS_SELECTED;
6546         }           
6547     }
6548
6549     /* and last, but not least, the indent field */
6550     if (dispInfo.item.mask & LVIF_INDENT)
6551     {
6552         lpLVItem->iIndent = dispInfo.item.iIndent;
6553         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
6554             lpItem->iIndent = dispInfo.item.iIndent;
6555     }
6556     else if (lpLVItem->mask & LVIF_INDENT)
6557     {
6558         lpLVItem->iIndent = lpItem->iIndent;
6559     }
6560
6561     return TRUE;
6562 }
6563
6564 /***
6565  * DESCRIPTION:
6566  * Retrieves item attributes.
6567  *
6568  * PARAMETER(S):
6569  * [I] hwnd : window handle
6570  * [IO] lpLVItem : item info
6571  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6572  *           if FALSE, then lpLVItem is a LPLVITEMA.
6573  *
6574  * NOTE:
6575  *   This is the external 'GetItem' interface -- it properly copies
6576  *   the text in the provided buffer.
6577  *
6578  * RETURN:
6579  *   SUCCESS : TRUE
6580  *   FAILURE : FALSE
6581  */
6582 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6583 {
6584     LPWSTR pszText;
6585     BOOL bResult;
6586
6587     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6588         return FALSE;
6589
6590     pszText = lpLVItem->pszText;
6591     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
6592     if (bResult && lpLVItem->pszText != pszText)
6593     {
6594         if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6595             textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
6596         else
6597             pszText = LPSTR_TEXTCALLBACKW;
6598     }
6599     lpLVItem->pszText = pszText;
6600
6601     return bResult;
6602 }
6603
6604
6605 /***
6606  * DESCRIPTION:
6607  * Retrieves the position (upper-left) of the listview control item.
6608  * Note that for LVS_ICON style, the upper-left is that of the icon
6609  * and not the bounding box.
6610  *
6611  * PARAMETER(S):
6612  * [I] infoPtr : valid pointer to the listview structure
6613  * [I] nItem : item index
6614  * [O] lpptPosition : coordinate information
6615  *
6616  * RETURN:
6617  *   SUCCESS : TRUE
6618  *   FAILURE : FALSE
6619  */
6620 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
6621 {
6622     POINT Origin;
6623
6624     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6625
6626     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6627
6628     LISTVIEW_GetOrigin(infoPtr, &Origin);
6629     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6630
6631     if (infoPtr->uView == LV_VIEW_ICON)
6632     {
6633         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6634         lpptPosition->y += ICON_TOP_PADDING;
6635     }
6636     lpptPosition->x += Origin.x;
6637     lpptPosition->y += Origin.y;
6638     
6639     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
6640     return TRUE;
6641 }
6642
6643
6644 /***
6645  * DESCRIPTION:
6646  * Retrieves the bounding rectangle for a listview control item.
6647  *
6648  * PARAMETER(S):
6649  * [I] infoPtr : valid pointer to the listview structure
6650  * [I] nItem : item index
6651  * [IO] lprc : bounding rectangle coordinates
6652  *     lprc->left specifies the portion of the item for which the bounding
6653  *     rectangle will be retrieved.
6654  *
6655  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6656  *        including the icon and label.
6657  *         *
6658  *         * For LVS_ICON
6659  *         * Experiment shows that native control returns:
6660  *         *  width = min (48, length of text line)
6661  *         *    .left = position.x - (width - iconsize.cx)/2
6662  *         *    .right = .left + width
6663  *         *  height = #lines of text * ntmHeight + icon height + 8
6664  *         *    .top = position.y - 2
6665  *         *    .bottom = .top + height
6666  *         *  separation between items .y = itemSpacing.cy - height
6667  *         *                           .x = itemSpacing.cx - width
6668  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
6669  *         *
6670  *         * For LVS_ICON
6671  *         * Experiment shows that native control returns:
6672  *         *  width = iconSize.cx + 16
6673  *         *    .left = position.x - (width - iconsize.cx)/2
6674  *         *    .right = .left + width
6675  *         *  height = iconSize.cy + 4
6676  *         *    .top = position.y - 2
6677  *         *    .bottom = .top + height
6678  *         *  separation between items .y = itemSpacing.cy - height
6679  *         *                           .x = itemSpacing.cx - width
6680  *     LVIR_LABEL Returns the bounding rectangle of the item text.
6681  *         *
6682  *         * For LVS_ICON
6683  *         * Experiment shows that native control returns:
6684  *         *  width = text length
6685  *         *    .left = position.x - width/2
6686  *         *    .right = .left + width
6687  *         *  height = ntmH * linecount + 2
6688  *         *    .top = position.y + iconSize.cy + 6
6689  *         *    .bottom = .top + height
6690  *         *  separation between items .y = itemSpacing.cy - height
6691  *         *                           .x = itemSpacing.cx - width
6692  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6693  *      rectangles, but excludes columns in report view.
6694  *
6695  * RETURN:
6696  *   SUCCESS : TRUE
6697  *   FAILURE : FALSE
6698  *
6699  * NOTES
6700  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
6701  *   upon whether the window has the focus currently and on whether the item
6702  *   is the one with the focus.  Ensure that the control's record of which
6703  *   item has the focus agrees with the items' records.
6704  */
6705 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6706 {
6707     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6708     BOOL doLabel = TRUE, oversizedBox = FALSE;
6709     POINT Position, Origin;
6710     LVITEMW lvItem;
6711     LONG mode;
6712
6713     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6714
6715     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6716
6717     LISTVIEW_GetOrigin(infoPtr, &Origin);
6718     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6719
6720     /* Be smart and try to figure out the minimum we have to do */
6721     if (lprc->left == LVIR_ICON) doLabel = FALSE;
6722     if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6723     if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6724         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6725         oversizedBox = TRUE;
6726
6727     /* get what we need from the item before hand, so we make
6728      * only one request. This can speed up things, if data
6729      * is stored on the app side */
6730     lvItem.mask = 0;
6731     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6732     if (doLabel) lvItem.mask |= LVIF_TEXT;
6733     lvItem.iItem = nItem;
6734     lvItem.iSubItem = 0;
6735     lvItem.pszText = szDispText;
6736     lvItem.cchTextMax = DISP_TEXT_SIZE;
6737     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6738     /* we got the state already up, simulate it here, to avoid a reget */
6739     if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6740     {
6741         lvItem.mask |= LVIF_STATE;
6742         lvItem.stateMask = LVIS_FOCUSED;
6743         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6744     }
6745
6746     if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6747         lprc->left = LVIR_BOUNDS;
6748
6749     mode = lprc->left;
6750     switch(lprc->left)
6751     {
6752     case LVIR_ICON:
6753         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6754         break;
6755
6756     case LVIR_LABEL:
6757         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6758         break;
6759
6760     case LVIR_BOUNDS:
6761         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6762         break;
6763
6764     case LVIR_SELECTBOUNDS:
6765         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6766         break;
6767
6768     default:
6769         WARN("Unknown value: %d\n", lprc->left);
6770         return FALSE;
6771     }
6772
6773     if (infoPtr->uView == LV_VIEW_DETAILS)
6774     {
6775         if (mode != LVIR_BOUNDS)
6776             OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left,
6777                              Position.y + Origin.y);
6778         else
6779             OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6780     }
6781     else
6782         OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6783
6784     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6785
6786     return TRUE;
6787 }
6788
6789 /***
6790  * DESCRIPTION:
6791  * Retrieves the spacing between listview control items.
6792  *
6793  * PARAMETER(S):
6794  * [I] infoPtr : valid pointer to the listview structure
6795  * [IO] lprc : rectangle to receive the output
6796  *             on input, lprc->top = nSubItem
6797  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6798  * 
6799  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6800  *       not only those of the first column.
6801  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6802  * 
6803  * RETURN:
6804  *     TRUE: success
6805  *     FALSE: failure
6806  */
6807 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6808 {
6809     POINT Position;
6810     LVITEMW lvItem;
6811     INT nColumn;
6812     
6813     if (!lprc) return FALSE;
6814
6815     nColumn = lprc->top;
6816
6817     TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6818     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6819     if (lprc->top == 0)
6820         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6821
6822     if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6823
6824     /* special case for header items */
6825     if (nItem == -1)
6826     {
6827         if (lprc->left != LVIR_BOUNDS)
6828         {
6829             FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6830             return FALSE;
6831         }
6832
6833         if (infoPtr->hwndHeader)
6834             return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6835         else
6836         {
6837             memset(lprc, 0, sizeof(RECT));
6838             return TRUE;
6839         }
6840     }
6841
6842     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6843
6844     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6845
6846     lvItem.mask = 0;
6847     lvItem.iItem = nItem;
6848     lvItem.iSubItem = nColumn;
6849     
6850     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6851     switch(lprc->left)
6852     {
6853     case LVIR_ICON:
6854         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6855         break;
6856
6857     case LVIR_LABEL:
6858     case LVIR_BOUNDS:
6859         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6860         break;
6861
6862     default:
6863         ERR("Unknown bounds=%d\n", lprc->left);
6864         return FALSE;
6865     }
6866
6867     OffsetRect(lprc, 0, Position.y);
6868     return TRUE;
6869 }
6870
6871 /***
6872  * DESCRIPTION:
6873  * Retrieves the spacing between listview control items.
6874  *
6875  * PARAMETER(S):
6876  * [I] infoPtr : valid pointer to the listview structure
6877  * [I] bSmall : flag for small or large icon
6878  *
6879  * RETURN:
6880  * Horizontal + vertical spacing
6881  */
6882 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6883 {
6884   LONG lResult;
6885
6886   if (!bSmall)
6887   {
6888     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6889   }
6890   else
6891   {
6892     if (infoPtr->uView == LV_VIEW_ICON)
6893       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6894     else
6895       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6896   }
6897   return lResult;
6898 }
6899
6900 /***
6901  * DESCRIPTION:
6902  * Retrieves the state of a listview control item.
6903  *
6904  * PARAMETER(S):
6905  * [I] infoPtr : valid pointer to the listview structure
6906  * [I] nItem : item index
6907  * [I] uMask : state mask
6908  *
6909  * RETURN:
6910  * State specified by the mask.
6911  */
6912 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6913 {
6914     LVITEMW lvItem;
6915
6916     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6917
6918     lvItem.iItem = nItem;
6919     lvItem.iSubItem = 0;
6920     lvItem.mask = LVIF_STATE;
6921     lvItem.stateMask = uMask;
6922     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6923
6924     return lvItem.state & uMask;
6925 }
6926
6927 /***
6928  * DESCRIPTION:
6929  * Retrieves the text of a listview control item or subitem.
6930  *
6931  * PARAMETER(S):
6932  * [I] hwnd : window handle
6933  * [I] nItem : item index
6934  * [IO] lpLVItem : item information
6935  * [I] isW :  TRUE if lpLVItem is Unicode
6936  *
6937  * RETURN:
6938  *   SUCCESS : string length
6939  *   FAILURE : 0
6940  */
6941 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6942 {
6943     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6944
6945     lpLVItem->mask = LVIF_TEXT;
6946     lpLVItem->iItem = nItem;
6947     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6948
6949     return textlenT(lpLVItem->pszText, isW);
6950 }
6951
6952 /***
6953  * DESCRIPTION:
6954  * Searches for an item based on properties + relationships.
6955  *
6956  * PARAMETER(S):
6957  * [I] infoPtr : valid pointer to the listview structure
6958  * [I] nItem : item index
6959  * [I] uFlags : relationship flag
6960  *
6961  * RETURN:
6962  *   SUCCESS : item index
6963  *   FAILURE : -1
6964  */
6965 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6966 {
6967     UINT uMask = 0;
6968     LVFINDINFOW lvFindInfo;
6969     INT nCountPerColumn;
6970     INT nCountPerRow;
6971     INT i;
6972
6973     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6974     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6975
6976     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6977
6978     if (uFlags & LVNI_CUT)
6979       uMask |= LVIS_CUT;
6980
6981     if (uFlags & LVNI_DROPHILITED)
6982       uMask |= LVIS_DROPHILITED;
6983
6984     if (uFlags & LVNI_FOCUSED)
6985       uMask |= LVIS_FOCUSED;
6986
6987     if (uFlags & LVNI_SELECTED)
6988       uMask |= LVIS_SELECTED;
6989
6990     /* if we're asked for the focused item, that's only one, 
6991      * so it's worth optimizing */
6992     if (uFlags & LVNI_FOCUSED)
6993     {
6994         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6995         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6996     }
6997     
6998     if (uFlags & LVNI_ABOVE)
6999     {
7000       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7001       {
7002         while (nItem >= 0)
7003         {
7004           nItem--;
7005           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7006             return nItem;
7007         }
7008       }
7009       else
7010       {
7011         /* Special case for autoarrange - move 'til the top of a list */
7012         if (is_autoarrange(infoPtr))
7013         {
7014           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7015           while (nItem - nCountPerRow >= 0)
7016           {
7017             nItem -= nCountPerRow;
7018             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7019               return nItem;
7020           }
7021           return -1;
7022         }
7023         lvFindInfo.flags = LVFI_NEARESTXY;
7024         lvFindInfo.vkDirection = VK_UP;
7025         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7026         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7027         {
7028           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7029             return nItem;
7030         }
7031       }
7032     }
7033     else if (uFlags & LVNI_BELOW)
7034     {
7035       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7036       {
7037         while (nItem < infoPtr->nItemCount)
7038         {
7039           nItem++;
7040           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7041             return nItem;
7042         }
7043       }
7044       else
7045       {
7046         /* Special case for autoarrange - move 'til the bottom of a list */
7047         if (is_autoarrange(infoPtr))
7048         {
7049           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7050           while (nItem + nCountPerRow < infoPtr->nItemCount )
7051           {
7052             nItem += nCountPerRow;
7053             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7054               return nItem;
7055           }
7056           return -1;
7057         }
7058         lvFindInfo.flags = LVFI_NEARESTXY;
7059         lvFindInfo.vkDirection = VK_DOWN;
7060         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7061         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7062         {
7063           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7064             return nItem;
7065         }
7066       }
7067     }
7068     else if (uFlags & LVNI_TOLEFT)
7069     {
7070       if (infoPtr->uView == LV_VIEW_LIST)
7071       {
7072         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7073         while (nItem - nCountPerColumn >= 0)
7074         {
7075           nItem -= nCountPerColumn;
7076           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7077             return nItem;
7078         }
7079       }
7080       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7081       {
7082         /* Special case for autoarrange - move 'til the beginning of a row */
7083         if (is_autoarrange(infoPtr))
7084         {
7085           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7086           while (nItem % nCountPerRow > 0)
7087           {
7088             nItem --;
7089             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7090               return nItem;
7091           }
7092           return -1;
7093         }
7094         lvFindInfo.flags = LVFI_NEARESTXY;
7095         lvFindInfo.vkDirection = VK_LEFT;
7096         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7097         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7098         {
7099           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7100             return nItem;
7101         }
7102       }
7103     }
7104     else if (uFlags & LVNI_TORIGHT)
7105     {
7106       if (infoPtr->uView == LV_VIEW_LIST)
7107       {
7108         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7109         while (nItem + nCountPerColumn < infoPtr->nItemCount)
7110         {
7111           nItem += nCountPerColumn;
7112           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7113             return nItem;
7114         }
7115       }
7116       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7117       {
7118         /* Special case for autoarrange - move 'til the end of a row */
7119         if (is_autoarrange(infoPtr))
7120         {
7121           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7122           while (nItem % nCountPerRow < nCountPerRow - 1 )
7123           {
7124             nItem ++;
7125             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7126               return nItem;
7127           }
7128           return -1;
7129         }
7130         lvFindInfo.flags = LVFI_NEARESTXY;
7131         lvFindInfo.vkDirection = VK_RIGHT;
7132         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7133         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7134         {
7135           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7136             return nItem;
7137         }
7138       }
7139     }
7140     else
7141     {
7142       nItem++;
7143
7144       /* search by index */
7145       for (i = nItem; i < infoPtr->nItemCount; i++)
7146       {
7147         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
7148           return i;
7149       }
7150     }
7151
7152     return -1;
7153 }
7154
7155 /* LISTVIEW_GetNumberOfWorkAreas */
7156
7157 /***
7158  * DESCRIPTION:
7159  * Retrieves the origin coordinates when in icon or small icon display mode.
7160  *
7161  * PARAMETER(S):
7162  * [I] infoPtr : valid pointer to the listview structure
7163  * [O] lpptOrigin : coordinate information
7164  *
7165  * RETURN:
7166  *   None.
7167  */
7168 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
7169 {
7170     INT nHorzPos = 0, nVertPos = 0;
7171     SCROLLINFO scrollInfo;
7172
7173     scrollInfo.cbSize = sizeof(SCROLLINFO);    
7174     scrollInfo.fMask = SIF_POS;
7175     
7176     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
7177         nHorzPos = scrollInfo.nPos;
7178     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7179         nVertPos = scrollInfo.nPos;
7180
7181     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
7182
7183     lpptOrigin->x = infoPtr->rcList.left;
7184     lpptOrigin->y = infoPtr->rcList.top;
7185     if (infoPtr->uView == LV_VIEW_LIST)
7186         nHorzPos *= infoPtr->nItemWidth;
7187     else if (infoPtr->uView == LV_VIEW_DETAILS)
7188         nVertPos *= infoPtr->nItemHeight;
7189     
7190     lpptOrigin->x -= nHorzPos;
7191     lpptOrigin->y -= nVertPos;
7192
7193     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
7194 }
7195
7196 /***
7197  * DESCRIPTION:
7198  * Retrieves the width of a string.
7199  *
7200  * PARAMETER(S):
7201  * [I] hwnd : window handle
7202  * [I] lpszText : text string to process
7203  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7204  *
7205  * RETURN:
7206  *   SUCCESS : string width (in pixels)
7207  *   FAILURE : zero
7208  */
7209 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
7210 {
7211     SIZE stringSize;
7212     
7213     stringSize.cx = 0;    
7214     if (is_textT(lpszText, isW))
7215     {
7216         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
7217         HDC hdc = GetDC(infoPtr->hwndSelf);
7218         HFONT hOldFont = SelectObject(hdc, hFont);
7219
7220         if (isW)
7221             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
7222         else
7223             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
7224         SelectObject(hdc, hOldFont);
7225         ReleaseDC(infoPtr->hwndSelf, hdc);
7226     }
7227     return stringSize.cx;
7228 }
7229
7230 /***
7231  * DESCRIPTION:
7232  * Determines which listview item is located at the specified position.
7233  *
7234  * PARAMETER(S):
7235  * [I] infoPtr : valid pointer to the listview structure
7236  * [IO] lpht : hit test information
7237  * [I] subitem : fill out iSubItem.
7238  * [I] select : return the index only if the hit selects the item
7239  *
7240  * NOTE:
7241  * (mm 20001022): We must not allow iSubItem to be touched, for
7242  * an app might pass only a structure with space up to iItem!
7243  * (MS Office 97 does that for instance in the file open dialog)
7244  * 
7245  * RETURN:
7246  *   SUCCESS : item index
7247  *   FAILURE : -1
7248  */
7249 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
7250 {
7251     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7252     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
7253     POINT Origin, Position, opt;
7254     LVITEMW lvItem;
7255     ITERATOR i;
7256     INT iItem;
7257     
7258     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
7259     
7260     lpht->flags = 0;
7261     lpht->iItem = -1;
7262     if (subitem) lpht->iSubItem = 0;
7263
7264     LISTVIEW_GetOrigin(infoPtr, &Origin);
7265
7266     /* set whole list relation flags */
7267     if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
7268     {
7269         /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7270         if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
7271             lpht->flags |= LVHT_TOLEFT;
7272
7273         if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7274             opt.y = lpht->pt.y + infoPtr->rcList.top;
7275         else
7276             opt.y = lpht->pt.y;
7277
7278         if (infoPtr->rcList.bottom < opt.y)
7279             lpht->flags |= LVHT_BELOW;
7280     }
7281     else
7282     {
7283         if (infoPtr->rcList.left > lpht->pt.x)
7284             lpht->flags |= LVHT_TOLEFT;
7285         else if (infoPtr->rcList.right < lpht->pt.x)
7286             lpht->flags |= LVHT_TORIGHT;
7287
7288         if (infoPtr->rcList.top > lpht->pt.y)
7289             lpht->flags |= LVHT_ABOVE;
7290         else if (infoPtr->rcList.bottom < lpht->pt.y)
7291             lpht->flags |= LVHT_BELOW;
7292     }
7293
7294     /* even if item is invalid try to find subitem */
7295     if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
7296     {
7297         RECT *pRect;
7298         INT j;
7299
7300         opt.x = lpht->pt.x - Origin.x;
7301
7302         lpht->iSubItem = -1;
7303         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
7304         {
7305             pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
7306
7307             if ((opt.x >= pRect->left) && (opt.x < pRect->right))
7308             {
7309                 lpht->iSubItem = j;
7310                 break;
7311             }
7312         }
7313         TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
7314     }
7315
7316     TRACE("lpht->flags=0x%x\n", lpht->flags);
7317     if (lpht->flags) return -1;
7318
7319     lpht->flags |= LVHT_NOWHERE;
7320
7321     /* first deal with the large items */
7322     rcSearch.left = lpht->pt.x;
7323     rcSearch.top = lpht->pt.y;
7324     rcSearch.right = rcSearch.left + 1;
7325     rcSearch.bottom = rcSearch.top + 1;
7326
7327     iterator_frameditems(&i, infoPtr, &rcSearch);
7328     iterator_next(&i); /* go to first item in the sequence */
7329     iItem = i.nItem;
7330     iterator_destroy(&i);
7331
7332     TRACE("lpht->iItem=%d\n", iItem);
7333     if (iItem == -1) return -1;
7334
7335     lvItem.mask = LVIF_STATE | LVIF_TEXT;
7336     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7337     lvItem.stateMask = LVIS_STATEIMAGEMASK;
7338     if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
7339     lvItem.iItem = iItem;
7340     lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
7341     lvItem.pszText = szDispText;
7342     lvItem.cchTextMax = DISP_TEXT_SIZE;
7343     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
7344     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
7345
7346     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7347     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
7348     opt.x = lpht->pt.x - Position.x - Origin.x;
7349
7350     if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7351         opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
7352     else
7353         opt.y = lpht->pt.y - Position.y - Origin.y;
7354
7355     if (infoPtr->uView == LV_VIEW_DETAILS)
7356     {
7357         rcBounds = rcBox;
7358         if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
7359             opt.x = lpht->pt.x - Origin.x;
7360     }
7361     else
7362     {
7363         UnionRect(&rcBounds, &rcIcon, &rcLabel);
7364         UnionRect(&rcBounds, &rcBounds, &rcState);
7365     }
7366     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
7367     if (!PtInRect(&rcBounds, opt)) return -1;
7368
7369     if (PtInRect(&rcIcon, opt))
7370         lpht->flags |= LVHT_ONITEMICON;
7371     else if (PtInRect(&rcLabel, opt))
7372         lpht->flags |= LVHT_ONITEMLABEL;
7373     else if (infoPtr->himlState && PtInRect(&rcState, opt))
7374         lpht->flags |= LVHT_ONITEMSTATEICON;
7375     /* special case for LVS_EX_FULLROWSELECT */
7376     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
7377       !(lpht->flags & LVHT_ONITEM))
7378     {
7379         lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
7380     }
7381     if (lpht->flags & LVHT_ONITEM)
7382         lpht->flags &= ~LVHT_NOWHERE;
7383     TRACE("lpht->flags=0x%x\n", lpht->flags); 
7384
7385     if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
7386                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
7387                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
7388     {
7389         if (infoPtr->uView == LV_VIEW_DETAILS)
7390         {
7391             /* get main item bounds */
7392             lvItem.iSubItem = 0;
7393             LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7394             UnionRect(&rcBounds, &rcIcon, &rcLabel);
7395             UnionRect(&rcBounds, &rcBounds, &rcState);
7396         }
7397         if (!PtInRect(&rcBounds, opt)) iItem = -1;
7398     }
7399     return lpht->iItem = iItem;
7400 }
7401
7402 /***
7403  * DESCRIPTION:
7404  * Inserts a new item in the listview control.
7405  *
7406  * PARAMETER(S):
7407  * [I] infoPtr : valid pointer to the listview structure
7408  * [I] lpLVItem : item information
7409  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7410  *
7411  * RETURN:
7412  *   SUCCESS : new item index
7413  *   FAILURE : -1
7414  */
7415 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
7416 {
7417     INT nItem;
7418     HDPA hdpaSubItems;
7419     NMLISTVIEW nmlv;
7420     ITEM_INFO *lpItem;
7421     ITEM_ID *lpID;
7422     BOOL is_sorted, has_changed;
7423     LVITEMW item;
7424     HWND hwndSelf = infoPtr->hwndSelf;
7425
7426     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
7427
7428     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
7429
7430     /* make sure it's an item, and not a subitem; cannot insert a subitem */
7431     if (!lpLVItem || lpLVItem->iSubItem) return -1;
7432
7433     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7434
7435     if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
7436     
7437     /* insert item in listview control data structure */
7438     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7439     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7440
7441     /* link with id struct */
7442     if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
7443     lpItem->id = lpID;
7444     lpID->item = hdpaSubItems;
7445     lpID->id = get_next_itemid(infoPtr);
7446     if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7447
7448     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7449                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7450
7451     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7452
7453     /* calculate new item index */
7454     if (is_sorted)
7455     {
7456         HDPA hItem;
7457         ITEM_INFO *item_s;
7458         INT i = 0, cmpv;
7459
7460         while (i < infoPtr->nItemCount)
7461         {
7462             hItem  = DPA_GetPtr( infoPtr->hdpaItems, i);
7463             item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
7464
7465             cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, isW);
7466             if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7467
7468             if (cmpv >= 0) break;
7469             i++;
7470         }
7471         nItem = i;
7472     }
7473     else
7474         nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7475
7476     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7477     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7478     if (nItem == -1) goto fail;
7479     if (infoPtr->nItemCount++ == 0) LISTVIEW_UpdateItemSize(infoPtr);
7480
7481     /* shift indices first so they don't get tangled */
7482     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7483
7484     /* set the item attributes */
7485     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7486     {
7487         /* full size structure expected - _WIN32IE >= 0x560 */
7488         item = *lpLVItem;
7489     }
7490     else if (lpLVItem->mask & LVIF_INDENT)
7491     {
7492         /* indent member expected - _WIN32IE >= 0x300 */
7493         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7494     }
7495     else
7496     {
7497         /* minimal structure expected */
7498         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7499     }
7500     item.iItem = nItem;
7501     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7502     {
7503         item.mask |= LVIF_STATE;
7504         item.stateMask |= LVIS_STATEIMAGEMASK;
7505         item.state &= ~LVIS_STATEIMAGEMASK;
7506         item.state |= INDEXTOSTATEIMAGEMASK(1);
7507     }
7508     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7509
7510     /* make room for the position, if we are in the right mode */
7511     if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7512     {
7513         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7514             goto undo;
7515         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7516         {
7517             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7518             goto undo;
7519         }
7520     }
7521     
7522     /* send LVN_INSERTITEM notification */
7523     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7524     nmlv.iItem = nItem;
7525     nmlv.lParam = lpItem->lParam;
7526     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7527     if (!IsWindow(hwndSelf))
7528         return -1;
7529
7530     /* align items (set position of each item) */
7531     if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7532     {
7533         POINT pt;
7534
7535         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7536             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7537         else
7538             LISTVIEW_NextIconPosTop(infoPtr, &pt);
7539
7540         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7541     }
7542
7543     /* now is the invalidation fun */
7544     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7545     return nItem;
7546
7547 undo:
7548     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7549     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7550     infoPtr->nItemCount--;
7551 fail:
7552     DPA_DeletePtr(hdpaSubItems, 0);
7553     DPA_Destroy (hdpaSubItems);
7554     Free (lpItem);
7555     return -1;
7556 }
7557
7558 /***
7559  * DESCRIPTION:
7560  * Checks item visibility.
7561  *
7562  * PARAMETER(S):
7563  * [I] infoPtr : valid pointer to the listview structure
7564  * [I] nFirst : item index to check for
7565  *
7566  * RETURN:
7567  *   Item visible : TRUE
7568  *   Item invisible or failure : FALSE
7569  */
7570 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7571 {
7572     POINT Origin, Position;
7573     RECT rcItem;
7574     HDC hdc;
7575     BOOL ret;
7576
7577     TRACE("nItem=%d\n", nItem);
7578
7579     if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7580
7581     LISTVIEW_GetOrigin(infoPtr, &Origin);
7582     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7583     rcItem.left = Position.x + Origin.x;
7584     rcItem.top  = Position.y + Origin.y;
7585     rcItem.right  = rcItem.left + infoPtr->nItemWidth;
7586     rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7587
7588     hdc = GetDC(infoPtr->hwndSelf);
7589     if (!hdc) return FALSE;
7590     ret = RectVisible(hdc, &rcItem);
7591     ReleaseDC(infoPtr->hwndSelf, hdc);
7592
7593     return ret;
7594 }
7595
7596 /***
7597  * DESCRIPTION:
7598  * Redraws a range of items.
7599  *
7600  * PARAMETER(S):
7601  * [I] infoPtr : valid pointer to the listview structure
7602  * [I] nFirst : first item
7603  * [I] nLast : last item
7604  *
7605  * RETURN:
7606  *   SUCCESS : TRUE
7607  *   FAILURE : FALSE
7608  */
7609 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
7610 {
7611     INT i;
7612  
7613     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
7614         max(nFirst, nLast) >= infoPtr->nItemCount)
7615         return FALSE;
7616     
7617     for (i = nFirst; i <= nLast; i++)
7618         LISTVIEW_InvalidateItem(infoPtr, i);
7619
7620     return TRUE;
7621 }
7622
7623 /***
7624  * DESCRIPTION:
7625  * Scroll the content of a listview.
7626  *
7627  * PARAMETER(S):
7628  * [I] infoPtr : valid pointer to the listview structure
7629  * [I] dx : horizontal scroll amount in pixels
7630  * [I] dy : vertical scroll amount in pixels
7631  *
7632  * RETURN:
7633  *   SUCCESS : TRUE
7634  *   FAILURE : FALSE
7635  *
7636  * COMMENTS:
7637  *  If the control is in report view (LV_VIEW_DETAILS) the control can
7638  *  be scrolled only in line increments. "dy" will be rounded to the
7639  *  nearest number of pixels that are a whole line. Ex: if line height
7640  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7641  *  is passed, then the scroll will be 0.  (per MSDN 7/2002)
7642  *
7643  *  For:  (per experimentation with native control and CSpy ListView)
7644  *     LV_VIEW_ICON       dy=1 = 1 pixel  (vertical only)
7645  *                        dx ignored
7646  *     LV_VIEW_SMALLICON  dy=1 = 1 pixel  (vertical only)
7647  *                        dx ignored
7648  *     LV_VIEW_LIST       dx=1 = 1 column (horizontal only)
7649  *                           but will only scroll 1 column per message
7650  *                           no matter what the value.
7651  *                        dy must be 0 or FALSE returned.
7652  *     LV_VIEW_DETAILS    dx=1 = 1 pixel
7653  *                        dy=  see above
7654  *
7655  */
7656 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7657 {
7658     switch(infoPtr->uView) {
7659     case LV_VIEW_DETAILS:
7660         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
7661         dy /= infoPtr->nItemHeight;
7662         break;
7663     case LV_VIEW_LIST:
7664         if (dy != 0) return FALSE;
7665         break;
7666     default: /* icon */
7667         dx = 0;
7668         break;
7669     }   
7670
7671     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
7672     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
7673   
7674     return TRUE;
7675 }
7676
7677 /***
7678  * DESCRIPTION:
7679  * Sets the background color.
7680  *
7681  * PARAMETER(S):
7682  * [I] infoPtr : valid pointer to the listview structure
7683  * [I] clrBk : background color
7684  *
7685  * RETURN:
7686  *   SUCCESS : TRUE
7687  *   FAILURE : FALSE
7688  */
7689 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
7690 {
7691     TRACE("(clrBk=%x)\n", clrBk);
7692
7693     if(infoPtr->clrBk != clrBk) {
7694         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7695         infoPtr->clrBk = clrBk;
7696         if (clrBk == CLR_NONE)
7697             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
7698         else
7699         {
7700             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
7701             infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND;
7702         }
7703         LISTVIEW_InvalidateList(infoPtr);
7704     }
7705
7706    return TRUE;
7707 }
7708
7709 /* LISTVIEW_SetBkImage */
7710
7711 /*** Helper for {Insert,Set}ColumnT *only* */
7712 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
7713                                const LVCOLUMNW *lpColumn, BOOL isW)
7714 {
7715     if (lpColumn->mask & LVCF_FMT)
7716     {
7717         /* format member is valid */
7718         lphdi->mask |= HDI_FORMAT;
7719
7720         /* set text alignment (leftmost column must be left-aligned) */
7721         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7722             lphdi->fmt |= HDF_LEFT;
7723         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
7724             lphdi->fmt |= HDF_RIGHT;
7725         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
7726             lphdi->fmt |= HDF_CENTER;
7727
7728         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7729             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
7730
7731         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7732         {
7733             lphdi->fmt |= HDF_IMAGE;
7734             lphdi->iImage = I_IMAGECALLBACK;
7735         }
7736
7737         if (lpColumn->fmt & LVCFMT_FIXED_WIDTH)
7738             lphdi->fmt |= HDF_FIXEDWIDTH;
7739     }
7740
7741     if (lpColumn->mask & LVCF_WIDTH)
7742     {
7743         lphdi->mask |= HDI_WIDTH;
7744         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7745         {
7746             /* make it fill the remainder of the controls width */
7747             RECT rcHeader;
7748             INT item_index;
7749
7750             for(item_index = 0; item_index < (nColumn - 1); item_index++)
7751             {
7752                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7753                 lphdi->cxy += rcHeader.right - rcHeader.left;
7754             }
7755
7756             /* retrieve the layout of the header */
7757             GetClientRect(infoPtr->hwndSelf, &rcHeader);
7758             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7759
7760             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7761         }
7762         else
7763             lphdi->cxy = lpColumn->cx;
7764     }
7765
7766     if (lpColumn->mask & LVCF_TEXT)
7767     {
7768         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7769         lphdi->fmt |= HDF_STRING;
7770         lphdi->pszText = lpColumn->pszText;
7771         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7772     }
7773
7774     if (lpColumn->mask & LVCF_IMAGE)
7775     {
7776         lphdi->mask |= HDI_IMAGE;
7777         lphdi->iImage = lpColumn->iImage;
7778     }
7779
7780     if (lpColumn->mask & LVCF_ORDER)
7781     {
7782         lphdi->mask |= HDI_ORDER;
7783         lphdi->iOrder = lpColumn->iOrder;
7784     }
7785 }
7786
7787
7788 /***
7789  * DESCRIPTION:
7790  * Inserts a new column.
7791  *
7792  * PARAMETER(S):
7793  * [I] infoPtr : valid pointer to the listview structure
7794  * [I] nColumn : column index
7795  * [I] lpColumn : column information
7796  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7797  *
7798  * RETURN:
7799  *   SUCCESS : new column index
7800  *   FAILURE : -1
7801  */
7802 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7803                                   const LVCOLUMNW *lpColumn, BOOL isW)
7804 {
7805     COLUMN_INFO *lpColumnInfo;
7806     INT nNewColumn;
7807     HDITEMW hdi;
7808
7809     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7810
7811     if (!lpColumn || nColumn < 0) return -1;
7812     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7813     
7814     ZeroMemory(&hdi, sizeof(HDITEMW));
7815     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7816
7817     /*
7818      * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7819      * (can be seen in SPY) otherwise column never gets added.
7820      */
7821     if (!(lpColumn->mask & LVCF_WIDTH)) {
7822         hdi.mask |= HDI_WIDTH;
7823         hdi.cxy = 10;
7824     }
7825
7826     /*
7827      * when the iSubItem is available Windows copies it to the header lParam. It seems
7828      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7829      */
7830     if (lpColumn->mask & LVCF_SUBITEM)
7831     {
7832         hdi.mask |= HDI_LPARAM;
7833         hdi.lParam = lpColumn->iSubItem;
7834     }
7835
7836     /* create header if not present */
7837     LISTVIEW_CreateHeader(infoPtr);
7838     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7839          (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7840     {
7841         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7842     }
7843
7844     /* insert item in header control */
7845     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
7846                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7847                               (WPARAM)nColumn, (LPARAM)&hdi);
7848     if (nNewColumn == -1) return -1;
7849     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7850    
7851     /* create our own column info */ 
7852     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7853     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7854
7855     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7856     if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin;
7857     if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7858         goto fail;
7859
7860     /* now we have to actually adjust the data */
7861     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7862     {
7863         SUBITEM_INFO *lpSubItem;
7864         HDPA hdpaSubItems;
7865         INT nItem, i;
7866         
7867         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7868         {
7869             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7870             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7871             {
7872                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7873                 if (lpSubItem->iSubItem >= nNewColumn)
7874                     lpSubItem->iSubItem++;
7875             }
7876         }
7877     }
7878
7879     /* make space for the new column */
7880     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7881     LISTVIEW_UpdateItemSize(infoPtr);
7882     
7883     return nNewColumn;
7884
7885 fail:
7886     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7887     if (lpColumnInfo)
7888     {
7889         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7890         Free(lpColumnInfo);
7891     }
7892     return -1;
7893 }
7894
7895 /***
7896  * DESCRIPTION:
7897  * Sets the attributes of a header item.
7898  *
7899  * PARAMETER(S):
7900  * [I] infoPtr : valid pointer to the listview structure
7901  * [I] nColumn : column index
7902  * [I] lpColumn : column attributes
7903  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7904  *
7905  * RETURN:
7906  *   SUCCESS : TRUE
7907  *   FAILURE : FALSE
7908  */
7909 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7910                                 const LVCOLUMNW *lpColumn, BOOL isW)
7911 {
7912     HDITEMW hdi, hdiget;
7913     BOOL bResult;
7914
7915     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7916     
7917     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7918
7919     ZeroMemory(&hdi, sizeof(HDITEMW));
7920     if (lpColumn->mask & LVCF_FMT)
7921     {
7922         hdi.mask |= HDI_FORMAT;
7923         hdiget.mask = HDI_FORMAT;
7924         if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
7925             hdi.fmt = hdiget.fmt & HDF_STRING;
7926     }
7927     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7928
7929     /* set header item attributes */
7930     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7931     if (!bResult) return FALSE;
7932
7933     if (lpColumn->mask & LVCF_FMT)
7934     {
7935         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7936         INT oldFmt = lpColumnInfo->fmt;
7937         
7938         lpColumnInfo->fmt = lpColumn->fmt;
7939         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7940         {
7941             if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7942         }
7943     }
7944
7945     if (lpColumn->mask & LVCF_MINWIDTH)
7946         LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin;
7947
7948     return TRUE;
7949 }
7950
7951 /***
7952  * DESCRIPTION:
7953  * Sets the column order array
7954  *
7955  * PARAMETERS:
7956  * [I] infoPtr : valid pointer to the listview structure
7957  * [I] iCount : number of elements in column order array
7958  * [I] lpiArray : pointer to column order array
7959  *
7960  * RETURN:
7961  *   SUCCESS : TRUE
7962  *   FAILURE : FALSE
7963  */
7964 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7965 {
7966     TRACE("iCount %d lpiArray %p\n", iCount, lpiArray);
7967
7968     if (!lpiArray || !IsWindow(infoPtr->hwndHeader)) return FALSE;
7969
7970     infoPtr->colRectsDirty = TRUE;
7971
7972     return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray);
7973 }
7974
7975 /***
7976  * DESCRIPTION:
7977  * Sets the width of a column
7978  *
7979  * PARAMETERS:
7980  * [I] infoPtr : valid pointer to the listview structure
7981  * [I] nColumn : column index
7982  * [I] cx : column width
7983  *
7984  * RETURN:
7985  *   SUCCESS : TRUE
7986  *   FAILURE : FALSE
7987  */
7988 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7989 {
7990     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7991     INT max_cx = 0;
7992     HDITEMW hdi;
7993
7994     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7995
7996     /* set column width only if in report or list mode */
7997     if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7998
7999     /* take care of invalid cx values */
8000     if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
8001     else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
8002
8003     /* resize all columns if in LV_VIEW_LIST mode */
8004     if(infoPtr->uView == LV_VIEW_LIST)
8005     {
8006         infoPtr->nItemWidth = cx;
8007         LISTVIEW_InvalidateList(infoPtr);
8008         return TRUE;
8009     }
8010
8011     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8012
8013     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
8014     {
8015         INT nLabelWidth;
8016         LVITEMW lvItem;
8017
8018         lvItem.mask = LVIF_TEXT;        
8019         lvItem.iItem = 0;
8020         lvItem.iSubItem = nColumn;
8021         lvItem.pszText = szDispText;
8022         lvItem.cchTextMax = DISP_TEXT_SIZE;
8023         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8024         {
8025             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
8026             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
8027             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
8028         }
8029         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
8030             max_cx += infoPtr->iconSize.cx;
8031         max_cx += TRAILING_LABEL_PADDING;
8032     }
8033
8034     /* autosize based on listview items width */
8035     if(cx == LVSCW_AUTOSIZE)
8036         cx = max_cx;
8037     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
8038     {
8039         /* if iCol is the last column make it fill the remainder of the controls width */
8040         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
8041         {
8042             RECT rcHeader;
8043             POINT Origin;
8044
8045             LISTVIEW_GetOrigin(infoPtr, &Origin);
8046             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
8047
8048             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
8049         }
8050         else
8051         {
8052             /* Despite what the MS docs say, if this is not the last
8053                column, then MS resizes the column to the width of the
8054                largest text string in the column, including headers
8055                and items. This is different from LVSCW_AUTOSIZE in that
8056                LVSCW_AUTOSIZE ignores the header string length. */
8057             cx = 0;
8058
8059             /* retrieve header text */
8060             hdi.mask = HDI_TEXT;
8061             hdi.cchTextMax = DISP_TEXT_SIZE;
8062             hdi.pszText = szDispText;
8063             if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
8064             {
8065                 HDC hdc = GetDC(infoPtr->hwndSelf);
8066                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
8067                 SIZE size;
8068
8069                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
8070                     cx = size.cx + TRAILING_HEADER_PADDING;
8071                 /* FIXME: Take into account the header image, if one is present */
8072                 SelectObject(hdc, old_font);
8073                 ReleaseDC(infoPtr->hwndSelf, hdc);
8074             }
8075             cx = max (cx, max_cx);
8076         }
8077     }
8078
8079     if (cx < 0) return FALSE;
8080
8081     /* call header to update the column change */
8082     hdi.mask = HDI_WIDTH;
8083     hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin);
8084     TRACE("hdi.cxy=%d\n", hdi.cxy);
8085     return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
8086 }
8087
8088 /***
8089  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
8090  *
8091  */
8092 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
8093 {
8094     HDC hdc_wnd, hdc;
8095     HBITMAP hbm_im, hbm_mask, hbm_orig;
8096     RECT rc;
8097     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
8098     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
8099     HIMAGELIST himl;
8100
8101     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
8102                             ILC_COLOR | ILC_MASK, 2, 2);
8103     hdc_wnd = GetDC(infoPtr->hwndSelf);
8104     hdc = CreateCompatibleDC(hdc_wnd);
8105     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8106     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
8107     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
8108
8109     rc.left = rc.top = 0;
8110     rc.right = GetSystemMetrics(SM_CXSMICON);
8111     rc.bottom = GetSystemMetrics(SM_CYSMICON);
8112
8113     hbm_orig = SelectObject(hdc, hbm_mask);
8114     FillRect(hdc, &rc, hbr_white);
8115     InflateRect(&rc, -2, -2);
8116     FillRect(hdc, &rc, hbr_black);
8117
8118     SelectObject(hdc, hbm_im);
8119     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
8120     SelectObject(hdc, hbm_orig);
8121     ImageList_Add(himl, hbm_im, hbm_mask); 
8122
8123     SelectObject(hdc, hbm_im);
8124     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
8125     SelectObject(hdc, hbm_orig);
8126     ImageList_Add(himl, hbm_im, hbm_mask);
8127
8128     DeleteObject(hbm_mask);
8129     DeleteObject(hbm_im);
8130     DeleteDC(hdc);
8131
8132     return himl;
8133 }
8134
8135 /***
8136  * DESCRIPTION:
8137  * Sets the extended listview style.
8138  *
8139  * PARAMETERS:
8140  * [I] infoPtr : valid pointer to the listview structure
8141  * [I] dwMask : mask
8142  * [I] dwStyle : style
8143  *
8144  * RETURN:
8145  *   SUCCESS : previous style
8146  *   FAILURE : 0
8147  */
8148 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
8149 {
8150     DWORD dwOldExStyle = infoPtr->dwLvExStyle;
8151
8152     /* set new style */
8153     if (dwMask)
8154         infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
8155     else
8156         infoPtr->dwLvExStyle = dwExStyle;
8157
8158     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
8159     {
8160         HIMAGELIST himl = 0;
8161         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8162         {
8163             LVITEMW item;
8164             item.mask = LVIF_STATE;
8165             item.stateMask = LVIS_STATEIMAGEMASK;
8166             item.state = INDEXTOSTATEIMAGEMASK(1);
8167             LISTVIEW_SetItemState(infoPtr, -1, &item);
8168
8169             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
8170             if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8171                 ImageList_Destroy(infoPtr->himlState);
8172         }
8173         himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
8174         /*   checkbox list replaces prevous custom list or... */
8175         if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) &&
8176            !(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) ||
8177             /* ...previous was checkbox list */
8178             (dwOldExStyle & LVS_EX_CHECKBOXES))
8179             ImageList_Destroy(himl);
8180     }
8181
8182     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
8183     {
8184         DWORD dwStyle;
8185
8186         /* if not already created */
8187         LISTVIEW_CreateHeader(infoPtr);
8188
8189         dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
8190         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
8191             dwStyle |= HDS_DRAGDROP;
8192         else
8193             dwStyle &= ~HDS_DRAGDROP;
8194         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
8195     }
8196
8197     /* GRIDLINES adds decoration at top so changes sizes */
8198     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
8199     {
8200         LISTVIEW_UpdateSize(infoPtr);
8201     }
8202
8203     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_TRANSPARENTBKGND)
8204     {
8205         if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
8206             LISTVIEW_SetBkColor(infoPtr, CLR_NONE);
8207     }
8208
8209     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERINALLVIEWS)
8210     {
8211         if (infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
8212             LISTVIEW_CreateHeader(infoPtr);
8213         else
8214             ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8215         LISTVIEW_UpdateSize(infoPtr);
8216         LISTVIEW_UpdateScroll(infoPtr);
8217     }
8218
8219     LISTVIEW_InvalidateList(infoPtr);
8220     return dwOldExStyle;
8221 }
8222
8223 /***
8224  * DESCRIPTION:
8225  * Sets the new hot cursor used during hot tracking and hover selection.
8226  *
8227  * PARAMETER(S):
8228  * [I] infoPtr : valid pointer to the listview structure
8229  * [I] hCursor : the new hot cursor handle
8230  *
8231  * RETURN:
8232  * Returns the previous hot cursor
8233  */
8234 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
8235 {
8236     HCURSOR oldCursor = infoPtr->hHotCursor;
8237     
8238     infoPtr->hHotCursor = hCursor;
8239
8240     return oldCursor;
8241 }
8242
8243
8244 /***
8245  * DESCRIPTION:
8246  * Sets the hot item index.
8247  *
8248  * PARAMETERS:
8249  * [I] infoPtr : valid pointer to the listview structure
8250  * [I] iIndex : index
8251  *
8252  * RETURN:
8253  *   SUCCESS : previous hot item index
8254  *   FAILURE : -1 (no hot item)
8255  */
8256 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
8257 {
8258     INT iOldIndex = infoPtr->nHotItem;
8259     
8260     infoPtr->nHotItem = iIndex;
8261     
8262     return iOldIndex;
8263 }
8264
8265
8266 /***
8267  * DESCRIPTION:
8268  * Sets the amount of time the cursor must hover over an item before it is selected.
8269  *
8270  * PARAMETER(S):
8271  * [I] infoPtr : valid pointer to the listview structure
8272  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8273  *
8274  * RETURN:
8275  * Returns the previous hover time
8276  */
8277 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
8278 {
8279     DWORD oldHoverTime = infoPtr->dwHoverTime;
8280     
8281     infoPtr->dwHoverTime = dwHoverTime;
8282     
8283     return oldHoverTime;
8284 }
8285
8286 /***
8287  * DESCRIPTION:
8288  * Sets spacing for icons of LVS_ICON style.
8289  *
8290  * PARAMETER(S):
8291  * [I] infoPtr : valid pointer to the listview structure
8292  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8293  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8294  *
8295  * RETURN:
8296  *   MAKELONG(oldcx, oldcy)
8297  */
8298 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
8299 {
8300     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
8301
8302     TRACE("requested=(%d,%d)\n", cx, cy);
8303     
8304     /* this is supported only for LVS_ICON style */
8305     if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
8306   
8307     /* set to defaults, if instructed to */
8308     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
8309     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
8310
8311     /* if 0 then compute width
8312      * FIXME: Should scan each item and determine max width of
8313      *        icon or label, then make that the width */
8314     if (cx == 0)
8315         cx = infoPtr->iconSpacing.cx;
8316
8317     /* if 0 then compute height */
8318     if (cy == 0) 
8319         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
8320              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
8321     
8322
8323     infoPtr->iconSpacing.cx = cx;
8324     infoPtr->iconSpacing.cy = cy;
8325
8326     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8327           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
8328           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
8329           infoPtr->ntmHeight);
8330
8331     /* these depend on the iconSpacing */
8332     LISTVIEW_UpdateItemSize(infoPtr);
8333
8334     return oldspacing;
8335 }
8336
8337 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
8338 {
8339     INT cx, cy;
8340     
8341     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
8342     {
8343         size->cx = cx;
8344         size->cy = cy;
8345     }
8346     else
8347     {
8348         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
8349         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
8350     }
8351 }
8352
8353 /***
8354  * DESCRIPTION:
8355  * Sets image lists.
8356  *
8357  * PARAMETER(S):
8358  * [I] infoPtr : valid pointer to the listview structure
8359  * [I] nType : image list type
8360  * [I] himl : image list handle
8361  *
8362  * RETURN:
8363  *   SUCCESS : old image list
8364  *   FAILURE : NULL
8365  */
8366 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
8367 {
8368     INT oldHeight = infoPtr->nItemHeight;
8369     HIMAGELIST himlOld = 0;
8370
8371     TRACE("(nType=%d, himl=%p\n", nType, himl);
8372
8373     switch (nType)
8374     {
8375     case LVSIL_NORMAL:
8376         himlOld = infoPtr->himlNormal;
8377         infoPtr->himlNormal = himl;
8378         if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
8379         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8380     break;
8381
8382     case LVSIL_SMALL:
8383         himlOld = infoPtr->himlSmall;
8384         infoPtr->himlSmall = himl;
8385         if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
8386     break;
8387
8388     case LVSIL_STATE:
8389         himlOld = infoPtr->himlState;
8390         infoPtr->himlState = himl;
8391         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
8392         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
8393     break;
8394
8395     default:
8396         ERR("Unknown icon type=%d\n", nType);
8397         return NULL;
8398     }
8399
8400     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
8401     if (infoPtr->nItemHeight != oldHeight)
8402         LISTVIEW_UpdateScroll(infoPtr);
8403
8404     return himlOld;
8405 }
8406
8407 /***
8408  * DESCRIPTION:
8409  * Preallocates memory (does *not* set the actual count of items !)
8410  *
8411  * PARAMETER(S):
8412  * [I] infoPtr : valid pointer to the listview structure
8413  * [I] nItems : item count (projected number of items to allocate)
8414  * [I] dwFlags : update flags
8415  *
8416  * RETURN:
8417  *   SUCCESS : TRUE
8418  *   FAILURE : FALSE
8419  */
8420 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
8421 {
8422     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
8423
8424     if (infoPtr->dwStyle & LVS_OWNERDATA)
8425     {
8426         INT nOldCount = infoPtr->nItemCount;
8427
8428         if (nItems < nOldCount)
8429         {
8430             RANGE range = { nItems, nOldCount };
8431             ranges_del(infoPtr->selectionRanges, range);
8432             if (infoPtr->nFocusedItem >= nItems)
8433             {
8434                 LISTVIEW_SetItemFocus(infoPtr, -1);
8435                 SetRectEmpty(&infoPtr->rcFocus);
8436             }
8437         }
8438
8439         infoPtr->nItemCount = nItems;
8440         LISTVIEW_UpdateScroll(infoPtr);
8441
8442         /* the flags are valid only in ownerdata report and list modes */
8443         if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
8444
8445         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
8446             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
8447
8448         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
8449             LISTVIEW_InvalidateList(infoPtr);
8450         else
8451         {
8452             INT nFrom, nTo;
8453             POINT Origin;
8454             RECT rcErase;
8455             
8456             LISTVIEW_GetOrigin(infoPtr, &Origin);
8457             nFrom = min(nOldCount, nItems);
8458             nTo = max(nOldCount, nItems);
8459     
8460             if (infoPtr->uView == LV_VIEW_DETAILS)
8461             {
8462                 rcErase.left = 0;
8463                 rcErase.top = nFrom * infoPtr->nItemHeight;
8464                 rcErase.right = infoPtr->nItemWidth;
8465                 rcErase.bottom = nTo * infoPtr->nItemHeight;
8466                 OffsetRect(&rcErase, Origin.x, Origin.y);
8467                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8468                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8469             }
8470             else /* LV_VIEW_LIST */
8471             {
8472                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
8473
8474                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
8475                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
8476                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
8477                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8478                 OffsetRect(&rcErase, Origin.x, Origin.y);
8479                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8480                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8481
8482                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
8483                 rcErase.top = 0;
8484                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
8485                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8486                 OffsetRect(&rcErase, Origin.x, Origin.y);
8487                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8488                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8489             }
8490         }
8491     }
8492     else
8493     {
8494         /* According to MSDN for non-LVS_OWNERDATA this is just
8495          * a performance issue. The control allocates its internal
8496          * data structures for the number of items specified. It
8497          * cuts down on the number of memory allocations. Therefore
8498          * we will just issue a WARN here
8499          */
8500         WARN("for non-ownerdata performance option not implemented.\n");
8501     }
8502
8503     return TRUE;
8504 }
8505
8506 /***
8507  * DESCRIPTION:
8508  * Sets the position of an item.
8509  *
8510  * PARAMETER(S):
8511  * [I] infoPtr : valid pointer to the listview structure
8512  * [I] nItem : item index
8513  * [I] pt : coordinate
8514  *
8515  * RETURN:
8516  *   SUCCESS : TRUE
8517  *   FAILURE : FALSE
8518  */
8519 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT *pt)
8520 {
8521     POINT Origin, Pt;
8522
8523     TRACE("(nItem=%d, pt=%s\n", nItem, wine_dbgstr_point(pt));
8524
8525     if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
8526         !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
8527
8528     Pt = *pt;
8529     LISTVIEW_GetOrigin(infoPtr, &Origin);
8530
8531     /* This point value seems to be an undocumented feature.
8532      * The best guess is that it means either at the origin, 
8533      * or at true beginning of the list. I will assume the origin. */
8534     if ((Pt.x == -1) && (Pt.y == -1))
8535         Pt = Origin;
8536     
8537     if (infoPtr->uView == LV_VIEW_ICON)
8538     {
8539         Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
8540         Pt.y -= ICON_TOP_PADDING;
8541     }
8542     Pt.x -= Origin.x;
8543     Pt.y -= Origin.y;
8544
8545     infoPtr->bAutoarrange = FALSE;
8546
8547     return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE);
8548 }
8549
8550 /***
8551  * DESCRIPTION:
8552  * Sets the state of one or many items.
8553  *
8554  * PARAMETER(S):
8555  * [I] infoPtr : valid pointer to the listview structure
8556  * [I] nItem : item index
8557  * [I] lpLVItem : item or subitem info
8558  *
8559  * RETURN:
8560  *   SUCCESS : TRUE
8561  *   FAILURE : FALSE
8562  */
8563 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
8564 {
8565     BOOL bResult = TRUE;
8566     LVITEMW lvItem;
8567
8568     lvItem.iItem = nItem;
8569     lvItem.iSubItem = 0;
8570     lvItem.mask = LVIF_STATE;
8571     lvItem.state = lpLVItem->state;
8572     lvItem.stateMask = lpLVItem->stateMask;
8573     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
8574
8575     if (nItem == -1)
8576     {
8577         /* select all isn't allowed in LVS_SINGLESEL */
8578         if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
8579             return FALSE;
8580
8581         /* focus all isn't allowed */
8582         if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
8583
8584         /* apply to all items */
8585         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8586             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
8587     }
8588     else
8589         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
8590
8591     return bResult;
8592 }
8593
8594 /***
8595  * DESCRIPTION:
8596  * Sets the text of an item or subitem.
8597  *
8598  * PARAMETER(S):
8599  * [I] hwnd : window handle
8600  * [I] nItem : item index
8601  * [I] lpLVItem : item or subitem info
8602  * [I] isW : TRUE if input is Unicode
8603  *
8604  * RETURN:
8605  *   SUCCESS : TRUE
8606  *   FAILURE : FALSE
8607  */
8608 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
8609 {
8610     LVITEMW lvItem;
8611
8612     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
8613     
8614     lvItem.iItem = nItem;
8615     lvItem.iSubItem = lpLVItem->iSubItem;
8616     lvItem.mask = LVIF_TEXT;
8617     lvItem.pszText = lpLVItem->pszText;
8618     lvItem.cchTextMax = lpLVItem->cchTextMax;
8619     
8620     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
8621
8622     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
8623 }
8624
8625 /***
8626  * DESCRIPTION:
8627  * Set item index that marks the start of a multiple selection.
8628  *
8629  * PARAMETER(S):
8630  * [I] infoPtr : valid pointer to the listview structure
8631  * [I] nIndex : index
8632  *
8633  * RETURN:
8634  * Index number or -1 if there is no selection mark.
8635  */
8636 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
8637 {
8638   INT nOldIndex = infoPtr->nSelectionMark;
8639
8640   TRACE("(nIndex=%d)\n", nIndex);
8641
8642   infoPtr->nSelectionMark = nIndex;
8643
8644   return nOldIndex;
8645 }
8646
8647 /***
8648  * DESCRIPTION:
8649  * Sets the text background color.
8650  *
8651  * PARAMETER(S):
8652  * [I] infoPtr : valid pointer to the listview structure
8653  * [I] clrTextBk : text background color
8654  *
8655  * RETURN:
8656  *   SUCCESS : TRUE
8657  *   FAILURE : FALSE
8658  */
8659 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
8660 {
8661     TRACE("(clrTextBk=%x)\n", clrTextBk);
8662
8663     if (infoPtr->clrTextBk != clrTextBk)
8664     {
8665         infoPtr->clrTextBk = clrTextBk;
8666         LISTVIEW_InvalidateList(infoPtr);
8667     }
8668     
8669   return TRUE;
8670 }
8671
8672 /***
8673  * DESCRIPTION:
8674  * Sets the text foreground color.
8675  *
8676  * PARAMETER(S):
8677  * [I] infoPtr : valid pointer to the listview structure
8678  * [I] clrText : text color
8679  *
8680  * RETURN:
8681  *   SUCCESS : TRUE
8682  *   FAILURE : FALSE
8683  */
8684 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
8685 {
8686     TRACE("(clrText=%x)\n", clrText);
8687
8688     if (infoPtr->clrText != clrText)
8689     {
8690         infoPtr->clrText = clrText;
8691         LISTVIEW_InvalidateList(infoPtr);
8692     }
8693
8694     return TRUE;
8695 }
8696
8697 /***
8698  * DESCRIPTION:
8699  * Sets new ToolTip window to ListView control.
8700  *
8701  * PARAMETER(S):
8702  * [I] infoPtr        : valid pointer to the listview structure
8703  * [I] hwndNewToolTip : handle to new ToolTip
8704  *
8705  * RETURN:
8706  *   old tool tip
8707  */
8708 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
8709 {
8710   HWND hwndOldToolTip = infoPtr->hwndToolTip;
8711   infoPtr->hwndToolTip = hwndNewToolTip;
8712   return hwndOldToolTip;
8713 }
8714
8715 /*
8716  * DESCRIPTION:
8717  *   sets the Unicode character format flag for the control
8718  * PARAMETER(S):
8719  *    [I] infoPtr         :valid pointer to the listview structure
8720  *    [I] fUnicode        :true to switch to UNICODE false to switch to ANSI
8721  *
8722  * RETURN:
8723  *    Old Unicode Format
8724  */
8725 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
8726 {
8727   SHORT rc = infoPtr->notifyFormat;
8728   infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
8729   return rc == NFR_UNICODE;
8730 }
8731
8732 /*
8733  * DESCRIPTION:
8734  *   sets the control view mode
8735  * PARAMETER(S):
8736  *    [I] infoPtr         :valid pointer to the listview structure
8737  *    [I] nView           :new view mode value
8738  *
8739  * RETURN:
8740  *    SUCCESS:  1
8741  *    FAILURE: -1
8742  */
8743 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8744 {
8745   SIZE oldIconSize = infoPtr->iconSize;
8746   HIMAGELIST himl;
8747
8748   if (infoPtr->uView == nView) return 1;
8749
8750   if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8751   if (nView == LV_VIEW_TILE)
8752   {
8753       FIXME("View LV_VIEW_TILE unimplemented\n");
8754       return -1;
8755   }
8756
8757   infoPtr->uView = nView;
8758
8759   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8760   ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8761
8762   ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8763   SetRectEmpty(&infoPtr->rcFocus);
8764
8765   himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8766   set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8767
8768   switch (nView)
8769   {
8770   case LV_VIEW_ICON:
8771       if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8772       {
8773             TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8774                    oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8775             LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8776       }
8777       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8778       break;
8779   case LV_VIEW_SMALLICON:
8780       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8781       break;
8782   case LV_VIEW_DETAILS:
8783   {
8784       HDLAYOUT hl;
8785       WINDOWPOS wp;
8786
8787       LISTVIEW_CreateHeader( infoPtr );
8788
8789       hl.prc = &infoPtr->rcList;
8790       hl.pwpos = &wp;
8791       SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8792       SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8793                    wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8794       break;
8795   }
8796   case LV_VIEW_LIST:
8797       break;
8798   }
8799
8800   LISTVIEW_UpdateItemSize(infoPtr);
8801   LISTVIEW_UpdateSize(infoPtr);
8802   LISTVIEW_UpdateScroll(infoPtr);
8803   LISTVIEW_InvalidateList(infoPtr);
8804
8805   TRACE("nView=%d\n", nView);
8806
8807   return 1;
8808 }
8809
8810 /* LISTVIEW_SetWorkAreas */
8811
8812 /***
8813  * DESCRIPTION:
8814  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8815  *
8816  * PARAMETER(S):
8817  * [I] first : pointer to first ITEM_INFO to compare
8818  * [I] second : pointer to second ITEM_INFO to compare
8819  * [I] lParam : HWND of control
8820  *
8821  * RETURN:
8822  *   if first comes before second : negative
8823  *   if first comes after second : positive
8824  *   if first and second are equivalent : zero
8825  */
8826 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8827 {
8828   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8829   ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8830   ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8831
8832   /* Forward the call to the client defined callback */
8833   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8834 }
8835
8836 /***
8837  * DESCRIPTION:
8838  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8839  *
8840  * PARAMETER(S):
8841  * [I] first : pointer to first ITEM_INFO to compare
8842  * [I] second : pointer to second ITEM_INFO to compare
8843  * [I] lParam : HWND of control
8844  *
8845  * RETURN:
8846  *   if first comes before second : negative
8847  *   if first comes after second : positive
8848  *   if first and second are equivalent : zero
8849  */
8850 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8851 {
8852   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8853   INT first_idx  = DPA_GetPtrIndex( infoPtr->hdpaItems, first  );
8854   INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8855
8856   /* Forward the call to the client defined callback */
8857   return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8858 }
8859
8860 /***
8861  * DESCRIPTION:
8862  * Sorts the listview items.
8863  *
8864  * PARAMETER(S):
8865  * [I] infoPtr : valid pointer to the listview structure
8866  * [I] pfnCompare : application-defined value
8867  * [I] lParamSort : pointer to comparison callback
8868  * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8869  *
8870  * RETURN:
8871  *   SUCCESS : TRUE
8872  *   FAILURE : FALSE
8873  */
8874 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8875                                LPARAM lParamSort, BOOL IsEx)
8876 {
8877     HDPA hdpaSubItems;
8878     ITEM_INFO *lpItem;
8879     LPVOID selectionMarkItem = NULL;
8880     LPVOID focusedItem = NULL;
8881     int i;
8882
8883     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8884
8885     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8886
8887     if (!pfnCompare) return FALSE;
8888     if (!infoPtr->hdpaItems) return FALSE;
8889
8890     /* if there are 0 or 1 items, there is no need to sort */
8891     if (infoPtr->nItemCount < 2) return TRUE;
8892
8893     /* clear selection */
8894     ranges_clear(infoPtr->selectionRanges);
8895
8896     /* save selection mark and focused item */
8897     if (infoPtr->nSelectionMark >= 0)
8898         selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8899     if (infoPtr->nFocusedItem >= 0)
8900         focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8901
8902     infoPtr->pfnCompare = pfnCompare;
8903     infoPtr->lParamSort = lParamSort;
8904     if (IsEx)
8905         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8906     else
8907         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8908
8909     /* restore selection ranges */
8910     for (i=0; i < infoPtr->nItemCount; i++)
8911     {
8912         hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8913         lpItem = DPA_GetPtr(hdpaSubItems, 0);
8914
8915         if (lpItem->state & LVIS_SELECTED)
8916             ranges_additem(infoPtr->selectionRanges, i);
8917     }
8918     /* restore selection mark and focused item */
8919     infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8920     infoPtr->nFocusedItem   = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8921
8922     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8923
8924     /* refresh the display */
8925     if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8926         LISTVIEW_InvalidateList(infoPtr);
8927
8928     return TRUE;
8929 }
8930
8931 /***
8932  * DESCRIPTION:
8933  * Update theme handle after a theme change.
8934  *
8935  * PARAMETER(S):
8936  * [I] infoPtr : valid pointer to the listview structure
8937  *
8938  * RETURN:
8939  *   SUCCESS : 0
8940  *   FAILURE : something else
8941  */
8942 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8943 {
8944     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8945     CloseThemeData(theme);
8946     OpenThemeData(infoPtr->hwndSelf, themeClass);
8947     return 0;
8948 }
8949
8950 /***
8951  * DESCRIPTION:
8952  * Updates an items or rearranges the listview control.
8953  *
8954  * PARAMETER(S):
8955  * [I] infoPtr : valid pointer to the listview structure
8956  * [I] nItem : item index
8957  *
8958  * RETURN:
8959  *   SUCCESS : TRUE
8960  *   FAILURE : FALSE
8961  */
8962 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8963 {
8964     TRACE("(nItem=%d)\n", nItem);
8965
8966     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8967
8968     /* rearrange with default alignment style */
8969     if (is_autoarrange(infoPtr))
8970         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8971     else
8972         LISTVIEW_InvalidateItem(infoPtr, nItem);
8973
8974     return TRUE;
8975 }
8976
8977 /***
8978  * DESCRIPTION:
8979  * Draw the track line at the place defined in the infoPtr structure.
8980  * The line is drawn with a XOR pen so drawing the line for the second time
8981  * in the same place erases the line.
8982  *
8983  * PARAMETER(S):
8984  * [I] infoPtr : valid pointer to the listview structure
8985  *
8986  * RETURN:
8987  *   SUCCESS : TRUE
8988  *   FAILURE : FALSE
8989  */
8990 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8991 {
8992     HPEN hOldPen;
8993     HDC hdc;
8994     INT oldROP;
8995
8996     if (infoPtr->xTrackLine == -1)
8997         return FALSE;
8998
8999     if (!(hdc = GetDC(infoPtr->hwndSelf)))
9000         return FALSE;
9001     hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
9002     oldROP = SetROP2(hdc, R2_XORPEN);
9003     MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
9004     LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
9005     SetROP2(hdc, oldROP);
9006     SelectObject(hdc, hOldPen);
9007     ReleaseDC(infoPtr->hwndSelf, hdc);
9008     return TRUE;
9009 }
9010
9011 /***
9012  * DESCRIPTION:
9013  * Called when an edit control should be displayed. This function is called after
9014  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9015  *
9016  * PARAMETER(S):
9017  * [I] hwnd : Handle to the listview
9018  * [I] uMsg : WM_TIMER (ignored)
9019  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9020  * [I] dwTimer : The elapsed time (ignored)
9021  *
9022  * RETURN:
9023  *   None.
9024  */
9025 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
9026 {
9027     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
9028     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9029
9030     KillTimer(hwnd, idEvent);
9031     editItem->fEnabled = FALSE;
9032     /* check if the item is still selected */
9033     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
9034         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
9035 }
9036
9037 /***
9038  * DESCRIPTION:
9039  * Creates the listview control - the WM_NCCREATE phase.
9040  *
9041  * PARAMETER(S):
9042  * [I] hwnd : window handle
9043  * [I] lpcs : the create parameters
9044  *
9045  * RETURN:
9046  *   Success: TRUE
9047  *   Failure: FALSE
9048  */
9049 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
9050 {
9051   LISTVIEW_INFO *infoPtr;
9052   LOGFONTW logFont;
9053
9054   TRACE("(lpcs=%p)\n", lpcs);
9055
9056   /* initialize info pointer */
9057   infoPtr = Alloc(sizeof(LISTVIEW_INFO));
9058   if (!infoPtr) return FALSE;
9059
9060   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
9061
9062   infoPtr->hwndSelf = hwnd;
9063   infoPtr->dwStyle = lpcs->style;    /* Note: may be changed in WM_CREATE */
9064   map_style_view(infoPtr);
9065   /* determine the type of structures to use */
9066   infoPtr->hwndNotify = lpcs->hwndParent;
9067   /* infoPtr->notifyFormat will be filled in WM_CREATE */
9068
9069   /* initialize color information  */
9070   infoPtr->clrBk = CLR_NONE;
9071   infoPtr->clrText = CLR_DEFAULT;
9072   infoPtr->clrTextBk = CLR_DEFAULT;
9073   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
9074
9075   /* set default values */
9076   infoPtr->nFocusedItem = -1;
9077   infoPtr->nSelectionMark = -1;
9078   infoPtr->nHotItem = -1;
9079   infoPtr->bRedraw = TRUE;
9080   infoPtr->bNoItemMetrics = TRUE;
9081   infoPtr->bDoChangeNotify = TRUE;
9082   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
9083   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
9084   infoPtr->nEditLabelItem = -1;
9085   infoPtr->nLButtonDownItem = -1;
9086   infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */
9087   infoPtr->nMeasureItemHeight = 0;
9088   infoPtr->xTrackLine = -1;  /* no track line */
9089   infoPtr->itemEdit.fEnabled = FALSE;
9090   infoPtr->iVersion = COMCTL32_VERSION;
9091   infoPtr->colRectsDirty = FALSE;
9092
9093   /* get default font (icon title) */
9094   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
9095   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
9096   infoPtr->hFont = infoPtr->hDefaultFont;
9097   LISTVIEW_SaveTextMetrics(infoPtr);
9098
9099   /* allocate memory for the data structure */
9100   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
9101   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
9102   if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
9103   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
9104   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
9105   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
9106   return TRUE;
9107
9108 fail:
9109     DestroyWindow(infoPtr->hwndHeader);
9110     ranges_destroy(infoPtr->selectionRanges);
9111     DPA_Destroy(infoPtr->hdpaItems);
9112     DPA_Destroy(infoPtr->hdpaItemIds);
9113     DPA_Destroy(infoPtr->hdpaPosX);
9114     DPA_Destroy(infoPtr->hdpaPosY);
9115     DPA_Destroy(infoPtr->hdpaColumns);
9116     Free(infoPtr);
9117     return FALSE;
9118 }
9119
9120 /***
9121  * DESCRIPTION:
9122  * Creates the listview control - the WM_CREATE phase. Most of the data is
9123  * already set up in LISTVIEW_NCCreate
9124  *
9125  * PARAMETER(S):
9126  * [I] hwnd : window handle
9127  * [I] lpcs : the create parameters
9128  *
9129  * RETURN:
9130  *   Success: 0
9131  *   Failure: -1
9132  */
9133 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
9134 {
9135   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9136
9137   TRACE("(lpcs=%p)\n", lpcs);
9138
9139   infoPtr->dwStyle = lpcs->style;
9140   map_style_view(infoPtr);
9141
9142   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
9143                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
9144   /* on error defaulting to ANSI notifications */
9145   if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
9146
9147   if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
9148   {
9149     if (LISTVIEW_CreateHeader(infoPtr) < 0)  return -1;
9150   }
9151   else
9152     infoPtr->hwndHeader = 0;
9153
9154   /* init item size to avoid division by 0 */
9155   LISTVIEW_UpdateItemSize (infoPtr);
9156
9157   if (infoPtr->uView == LV_VIEW_DETAILS)
9158   {
9159     if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
9160     {
9161       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9162     }
9163     LISTVIEW_UpdateScroll(infoPtr);
9164     /* send WM_MEASUREITEM notification */
9165     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
9166   }
9167
9168   OpenThemeData(hwnd, themeClass);
9169
9170   /* initialize the icon sizes */
9171   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
9172   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
9173   return 0;
9174 }
9175
9176 /***
9177  * DESCRIPTION:
9178  * Destroys the listview control.
9179  *
9180  * PARAMETER(S):
9181  * [I] infoPtr : valid pointer to the listview structure
9182  *
9183  * RETURN:
9184  *   Success: 0
9185  *   Failure: -1
9186  */
9187 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
9188 {
9189     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
9190     CloseThemeData(theme);
9191     return 0;
9192 }
9193
9194 /***
9195  * DESCRIPTION:
9196  * Enables the listview control.
9197  *
9198  * PARAMETER(S):
9199  * [I] infoPtr : valid pointer to the listview structure
9200  * [I] bEnable : specifies whether to enable or disable the window
9201  *
9202  * RETURN:
9203  *   SUCCESS : TRUE
9204  *   FAILURE : FALSE
9205  */
9206 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
9207 {
9208     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
9209         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9210     return TRUE;
9211 }
9212
9213 /***
9214  * DESCRIPTION:
9215  * Erases the background of the listview control.
9216  *
9217  * PARAMETER(S):
9218  * [I] infoPtr : valid pointer to the listview structure
9219  * [I] hdc : device context handle
9220  *
9221  * RETURN:
9222  *   SUCCESS : TRUE
9223  *   FAILURE : FALSE
9224  */
9225 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
9226 {
9227     RECT rc;
9228
9229     TRACE("(hdc=%p)\n", hdc);
9230
9231     if (!GetClipBox(hdc, &rc)) return FALSE;
9232
9233     if (infoPtr->clrBk == CLR_NONE)
9234     {
9235         if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
9236             return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT,
9237                                 (WPARAM)hdc, PRF_ERASEBKGND);
9238         else
9239             return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
9240     }
9241
9242     /* for double buffered controls we need to do this during refresh */
9243     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
9244
9245     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
9246 }
9247         
9248
9249 /***
9250  * DESCRIPTION:
9251  * Helper function for LISTVIEW_[HV]Scroll *only*.
9252  * Performs vertical/horizontal scrolling by a give amount.
9253  *
9254  * PARAMETER(S):
9255  * [I] infoPtr : valid pointer to the listview structure
9256  * [I] dx : amount of horizontal scroll
9257  * [I] dy : amount of vertical scroll
9258  */
9259 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
9260 {
9261     /* now we can scroll the list */
9262     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
9263                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
9264     /* if we have focus, adjust rect */
9265     OffsetRect(&infoPtr->rcFocus, dx, dy);
9266     UpdateWindow(infoPtr->hwndSelf);
9267 }
9268
9269 /***
9270  * DESCRIPTION:
9271  * Performs vertical scrolling.
9272  *
9273  * PARAMETER(S):
9274  * [I] infoPtr : valid pointer to the listview structure
9275  * [I] nScrollCode : scroll code
9276  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9277  * [I] hScrollWnd  : scrollbar control window handle
9278  *
9279  * RETURN:
9280  * Zero
9281  *
9282  * NOTES:
9283  *   SB_LINEUP/SB_LINEDOWN:
9284  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
9285  *        for LVS_REPORT is 1 line
9286  *        for LVS_LIST cannot occur
9287  *
9288  */
9289 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
9290                                 INT nScrollDiff, HWND hScrollWnd)
9291 {
9292     INT nOldScrollPos, nNewScrollPos;
9293     SCROLLINFO scrollInfo;
9294     BOOL is_an_icon;
9295
9296     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
9297         debugscrollcode(nScrollCode), nScrollDiff);
9298
9299     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9300
9301     scrollInfo.cbSize = sizeof(SCROLLINFO);
9302     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9303
9304     is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9305
9306     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
9307
9308     nOldScrollPos = scrollInfo.nPos;
9309     switch (nScrollCode)
9310     {
9311     case SB_INTERNAL:
9312         break;
9313
9314     case SB_LINEUP:
9315         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9316         break;
9317
9318     case SB_LINEDOWN:
9319         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9320         break;
9321
9322     case SB_PAGEUP:
9323         nScrollDiff = -scrollInfo.nPage;
9324         break;
9325
9326     case SB_PAGEDOWN:
9327         nScrollDiff = scrollInfo.nPage;
9328         break;
9329
9330     case SB_THUMBPOSITION:
9331     case SB_THUMBTRACK:
9332         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9333         break;
9334
9335     default:
9336         nScrollDiff = 0;
9337     }
9338
9339     /* quit right away if pos isn't changing */
9340     if (nScrollDiff == 0) return 0;
9341     
9342     /* calculate new position, and handle overflows */
9343     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9344     if (nScrollDiff > 0) {
9345         if (nNewScrollPos < nOldScrollPos ||
9346             nNewScrollPos > scrollInfo.nMax)
9347             nNewScrollPos = scrollInfo.nMax;
9348     } else {
9349         if (nNewScrollPos > nOldScrollPos ||
9350             nNewScrollPos < scrollInfo.nMin)
9351             nNewScrollPos = scrollInfo.nMin;
9352     }
9353
9354     /* set the new position, and reread in case it changed */
9355     scrollInfo.fMask = SIF_POS;
9356     scrollInfo.nPos = nNewScrollPos;
9357     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
9358     
9359     /* carry on only if it really changed */
9360     if (nNewScrollPos == nOldScrollPos) return 0;
9361     
9362     /* now adjust to client coordinates */
9363     nScrollDiff = nOldScrollPos - nNewScrollPos;
9364     if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
9365    
9366     /* and scroll the window */ 
9367     scroll_list(infoPtr, 0, nScrollDiff);
9368
9369     return 0;
9370 }
9371
9372 /***
9373  * DESCRIPTION:
9374  * Performs horizontal scrolling.
9375  *
9376  * PARAMETER(S):
9377  * [I] infoPtr : valid pointer to the listview structure
9378  * [I] nScrollCode : scroll code
9379  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9380  * [I] hScrollWnd  : scrollbar control window handle
9381  *
9382  * RETURN:
9383  * Zero
9384  *
9385  * NOTES:
9386  *   SB_LINELEFT/SB_LINERIGHT:
9387  *        for LVS_ICON, LVS_SMALLICON  1 pixel
9388  *        for LVS_REPORT is 1 pixel
9389  *        for LVS_LIST  is 1 column --> which is a 1 because the
9390  *                                      scroll is based on columns not pixels
9391  *
9392  */
9393 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9394                                 INT nScrollDiff, HWND hScrollWnd)
9395 {
9396     INT nOldScrollPos, nNewScrollPos;
9397     SCROLLINFO scrollInfo;
9398
9399     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
9400         debugscrollcode(nScrollCode), nScrollDiff);
9401
9402     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9403
9404     scrollInfo.cbSize = sizeof(SCROLLINFO);
9405     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9406
9407     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
9408
9409     nOldScrollPos = scrollInfo.nPos;
9410
9411     switch (nScrollCode)
9412     {
9413     case SB_INTERNAL:
9414         break;
9415
9416     case SB_LINELEFT:
9417         nScrollDiff = -1;
9418         break;
9419
9420     case SB_LINERIGHT:
9421         nScrollDiff = 1;
9422         break;
9423
9424     case SB_PAGELEFT:
9425         nScrollDiff = -scrollInfo.nPage;
9426         break;
9427
9428     case SB_PAGERIGHT:
9429         nScrollDiff = scrollInfo.nPage;
9430         break;
9431
9432     case SB_THUMBPOSITION:
9433     case SB_THUMBTRACK:
9434         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9435         break;
9436
9437     default:
9438         nScrollDiff = 0;
9439     }
9440
9441     /* quit right away if pos isn't changing */
9442     if (nScrollDiff == 0) return 0;
9443     
9444     /* calculate new position, and handle overflows */
9445     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9446     if (nScrollDiff > 0) {
9447         if (nNewScrollPos < nOldScrollPos ||
9448             nNewScrollPos > scrollInfo.nMax)
9449             nNewScrollPos = scrollInfo.nMax;
9450     } else {
9451         if (nNewScrollPos > nOldScrollPos ||
9452             nNewScrollPos < scrollInfo.nMin)
9453             nNewScrollPos = scrollInfo.nMin;
9454     }
9455
9456     /* set the new position, and reread in case it changed */
9457     scrollInfo.fMask = SIF_POS;
9458     scrollInfo.nPos = nNewScrollPos;
9459     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
9460
9461     /* carry on only if it really changed */
9462     if (nNewScrollPos == nOldScrollPos) return 0;
9463
9464     if (infoPtr->hwndHeader) LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
9465
9466     /* now adjust to client coordinates */
9467     nScrollDiff = nOldScrollPos - nNewScrollPos;
9468     if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
9469
9470     /* and scroll the window */
9471     scroll_list(infoPtr, nScrollDiff, 0);
9472
9473     return 0;
9474 }
9475
9476 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
9477 {
9478     INT gcWheelDelta = 0;
9479     INT pulScrollLines = 3;
9480     SCROLLINFO scrollInfo;
9481
9482     TRACE("(wheelDelta=%d)\n", wheelDelta);
9483
9484     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
9485     gcWheelDelta -= wheelDelta;
9486
9487     scrollInfo.cbSize = sizeof(SCROLLINFO);
9488     scrollInfo.fMask = SIF_POS;
9489
9490     switch(infoPtr->uView)
9491     {
9492     case LV_VIEW_ICON:
9493     case LV_VIEW_SMALLICON:
9494        /*
9495         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9496         *  should be fixed in the future.
9497         */
9498         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
9499                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
9500         break;
9501
9502     case LV_VIEW_DETAILS:
9503         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
9504         {
9505             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
9506             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
9507             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
9508         }
9509         break;
9510
9511     case LV_VIEW_LIST:
9512         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
9513         break;
9514     }
9515     return 0;
9516 }
9517
9518 /***
9519  * DESCRIPTION:
9520  * ???
9521  *
9522  * PARAMETER(S):
9523  * [I] infoPtr : valid pointer to the listview structure
9524  * [I] nVirtualKey : virtual key
9525  * [I] lKeyData : key data
9526  *
9527  * RETURN:
9528  * Zero
9529  */
9530 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
9531 {
9532   HWND hwndSelf = infoPtr->hwndSelf;
9533   INT nItem = -1;
9534   NMLVKEYDOWN nmKeyDown;
9535
9536   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
9537
9538   /* send LVN_KEYDOWN notification */
9539   nmKeyDown.wVKey = nVirtualKey;
9540   nmKeyDown.flags = 0;
9541   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
9542   if (!IsWindow(hwndSelf))
9543     return 0;
9544
9545   switch (nVirtualKey)
9546   {
9547   case VK_SPACE:
9548     nItem = infoPtr->nFocusedItem;
9549     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
9550         toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
9551     break;
9552
9553   case VK_RETURN:
9554     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
9555     {
9556         if (!notify(infoPtr, NM_RETURN)) return 0;
9557         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
9558     }
9559     break;
9560
9561   case VK_HOME:
9562     if (infoPtr->nItemCount > 0)
9563       nItem = 0;
9564     break;
9565
9566   case VK_END:
9567     if (infoPtr->nItemCount > 0)
9568       nItem = infoPtr->nItemCount - 1;
9569     break;
9570
9571   case VK_LEFT:
9572     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
9573     break;
9574
9575   case VK_UP:
9576     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
9577     break;
9578
9579   case VK_RIGHT:
9580     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
9581     break;
9582
9583   case VK_DOWN:
9584     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
9585     break;
9586
9587   case VK_PRIOR:
9588     if (infoPtr->uView == LV_VIEW_DETAILS)
9589     {
9590       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9591       if (infoPtr->nFocusedItem == topidx)
9592         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
9593       else
9594         nItem = topidx;
9595     }
9596     else
9597       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
9598                                     * LISTVIEW_GetCountPerRow(infoPtr);
9599     if(nItem < 0) nItem = 0;
9600     break;
9601
9602   case VK_NEXT:
9603     if (infoPtr->uView == LV_VIEW_DETAILS)
9604     {
9605       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9606       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
9607       if (infoPtr->nFocusedItem == topidx + cnt - 1)
9608         nItem = infoPtr->nFocusedItem + cnt - 1;
9609       else
9610         nItem = topidx + cnt - 1;
9611     }
9612     else
9613       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
9614                                     * LISTVIEW_GetCountPerRow(infoPtr);
9615     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
9616     break;
9617   }
9618
9619   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
9620       LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
9621
9622   return 0;
9623 }
9624
9625 /***
9626  * DESCRIPTION:
9627  * Kills the focus.
9628  *
9629  * PARAMETER(S):
9630  * [I] infoPtr : valid pointer to the listview structure
9631  *
9632  * RETURN:
9633  * Zero
9634  */
9635 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
9636 {
9637     TRACE("()\n");
9638
9639     /* if we did not have the focus, there's nothing to do */
9640     if (!infoPtr->bFocus) return 0;
9641    
9642     /* send NM_KILLFOCUS notification */
9643     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
9644
9645     /* if we have a focus rectangle, get rid of it */
9646     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
9647
9648     /* if have a marquee selection, stop it */
9649     if (infoPtr->bMarqueeSelect)
9650     {
9651         /* Remove the marquee rectangle and release our mouse capture */
9652         LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
9653         ReleaseCapture();
9654
9655         SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
9656
9657         infoPtr->bMarqueeSelect = FALSE;
9658         infoPtr->bScrolling = FALSE;
9659         KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
9660     }
9661
9662     /* set window focus flag */
9663     infoPtr->bFocus = FALSE;
9664
9665     /* invalidate the selected items before resetting focus flag */
9666     LISTVIEW_InvalidateSelectedItems(infoPtr);
9667     
9668     return 0;
9669 }
9670
9671 /***
9672  * DESCRIPTION:
9673  * Processes double click messages (left mouse button).
9674  *
9675  * PARAMETER(S):
9676  * [I] infoPtr : valid pointer to the listview structure
9677  * [I] wKey : key flag
9678  * [I] x,y : mouse coordinate
9679  *
9680  * RETURN:
9681  * Zero
9682  */
9683 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9684 {
9685     LVHITTESTINFO htInfo;
9686
9687     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9688     
9689     /* Cancel the item edition if any */
9690     if (infoPtr->itemEdit.fEnabled)
9691     {
9692       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
9693       infoPtr->itemEdit.fEnabled = FALSE;
9694     }
9695
9696     /* send NM_RELEASEDCAPTURE notification */
9697     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9698
9699     htInfo.pt.x = x;
9700     htInfo.pt.y = y;
9701
9702     /* send NM_DBLCLK notification */
9703     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
9704     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
9705
9706     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
9707     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
9708
9709     return 0;
9710 }
9711
9712 /***
9713  * DESCRIPTION:
9714  * Processes mouse down messages (left mouse button).
9715  *
9716  * PARAMETERS:
9717  *   infoPtr  [I ] valid pointer to the listview structure
9718  *   wKey     [I ] key flag
9719  *   x,y      [I ] mouse coordinate
9720  *
9721  * RETURN:
9722  *   Zero
9723  */
9724 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9725 {
9726   LVHITTESTINFO lvHitTestInfo;
9727   static BOOL bGroupSelect = TRUE;
9728   POINT pt = { x, y };
9729   INT nItem;
9730
9731   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9732
9733   /* send NM_RELEASEDCAPTURE notification */
9734   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9735
9736   /* set left button down flag and record the click position */
9737   infoPtr->bLButtonDown = TRUE;
9738   infoPtr->ptClickPos = pt;
9739   infoPtr->bDragging = FALSE;
9740   infoPtr->bMarqueeSelect = FALSE;
9741   infoPtr->bScrolling = FALSE;
9742
9743   lvHitTestInfo.pt.x = x;
9744   lvHitTestInfo.pt.y = y;
9745
9746   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9747   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
9748   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9749   {
9750     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
9751     {
9752         toggle_checkbox_state(infoPtr, nItem);
9753         return 0;
9754     }
9755
9756     if (infoPtr->dwStyle & LVS_SINGLESEL)
9757     {
9758       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9759         infoPtr->nEditLabelItem = nItem;
9760       else
9761         LISTVIEW_SetSelection(infoPtr, nItem);
9762     }
9763     else
9764     {
9765       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9766       {
9767         if (bGroupSelect)
9768         {
9769           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9770           LISTVIEW_SetItemFocus(infoPtr, nItem);
9771           infoPtr->nSelectionMark = nItem;
9772         }
9773         else
9774         {
9775           LVITEMW item;
9776
9777           item.state = LVIS_SELECTED | LVIS_FOCUSED;
9778           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9779
9780           LISTVIEW_SetItemState(infoPtr,nItem,&item);
9781           infoPtr->nSelectionMark = nItem;
9782         }
9783       }
9784       else if (wKey & MK_CONTROL)
9785       {
9786         LVITEMW item;
9787
9788         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9789         
9790         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9791         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9792         LISTVIEW_SetItemState(infoPtr, nItem, &item);
9793         infoPtr->nSelectionMark = nItem;
9794       }
9795       else  if (wKey & MK_SHIFT)
9796       {
9797         LISTVIEW_SetGroupSelection(infoPtr, nItem);
9798       }
9799       else
9800       {
9801         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9802         {
9803           infoPtr->nEditLabelItem = nItem;
9804           infoPtr->nLButtonDownItem = nItem;
9805
9806           LISTVIEW_SetItemFocus(infoPtr, nItem);
9807         }
9808         else
9809           /* set selection (clears other pre-existing selections) */
9810           LISTVIEW_SetSelection(infoPtr, nItem);
9811       }
9812     }
9813
9814     if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9815         if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9816   }
9817   else
9818   {
9819     if (!infoPtr->bFocus)
9820         SetFocus(infoPtr->hwndSelf);
9821
9822     /* remove all selections */
9823     if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9824         LISTVIEW_DeselectAll(infoPtr);
9825     ReleaseCapture();
9826   }
9827   
9828   return 0;
9829 }
9830
9831 /***
9832  * DESCRIPTION:
9833  * Processes mouse up messages (left mouse button).
9834  *
9835  * PARAMETERS:
9836  *   infoPtr [I ] valid pointer to the listview structure
9837  *   wKey    [I ] key flag
9838  *   x,y     [I ] mouse coordinate
9839  *
9840  * RETURN:
9841  *   Zero
9842  */
9843 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9844 {
9845     LVHITTESTINFO lvHitTestInfo;
9846     
9847     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9848
9849     if (!infoPtr->bLButtonDown) return 0;
9850
9851     lvHitTestInfo.pt.x = x;
9852     lvHitTestInfo.pt.y = y;
9853
9854     /* send NM_CLICK notification */
9855     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9856     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9857
9858     /* set left button flag */
9859     infoPtr->bLButtonDown = FALSE;
9860
9861     /* set a single selection, reset others */
9862     if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9863         LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9864     infoPtr->nLButtonDownItem = -1;
9865
9866     if (infoPtr->bDragging || infoPtr->bMarqueeSelect)
9867     {
9868         /* Remove the marquee rectangle and release our mouse capture */
9869         if (infoPtr->bMarqueeSelect)
9870         {
9871             LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
9872             ReleaseCapture();
9873         }
9874
9875         SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
9876         SetRect(&infoPtr->marqueeDrawRect, 0, 0, 0, 0);
9877
9878         infoPtr->bDragging = FALSE;
9879         infoPtr->bMarqueeSelect = FALSE;
9880         infoPtr->bScrolling = FALSE;
9881
9882         KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
9883         return 0;
9884     }
9885
9886     /* if we clicked on a selected item, edit the label */
9887     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9888     {
9889         /* we want to make sure the user doesn't want to do a double click. So we will
9890          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9891          */
9892         infoPtr->itemEdit.fEnabled = TRUE;
9893         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9894         SetTimer(infoPtr->hwndSelf,
9895             (UINT_PTR)&infoPtr->itemEdit,
9896             GetDoubleClickTime(),
9897             LISTVIEW_DelayedEditItem);
9898     }
9899
9900     if (!infoPtr->bFocus)
9901         SetFocus(infoPtr->hwndSelf);
9902
9903     return 0;
9904 }
9905
9906 /***
9907  * DESCRIPTION:
9908  * Destroys the listview control (called after WM_DESTROY).
9909  *
9910  * PARAMETER(S):
9911  * [I] infoPtr : valid pointer to the listview structure
9912  *
9913  * RETURN:
9914  * Zero
9915  */
9916 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9917 {
9918   INT i;
9919
9920   TRACE("()\n");
9921
9922   /* delete all items */
9923   LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9924
9925   /* destroy data structure */
9926   DPA_Destroy(infoPtr->hdpaItems);
9927   DPA_Destroy(infoPtr->hdpaItemIds);
9928   DPA_Destroy(infoPtr->hdpaPosX);
9929   DPA_Destroy(infoPtr->hdpaPosY);
9930   /* columns */
9931   for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++)
9932       Free(DPA_GetPtr(infoPtr->hdpaColumns, i));
9933   DPA_Destroy(infoPtr->hdpaColumns);
9934   ranges_destroy(infoPtr->selectionRanges);
9935
9936   /* destroy image lists */
9937   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9938   {
9939       ImageList_Destroy(infoPtr->himlNormal);
9940       ImageList_Destroy(infoPtr->himlSmall);
9941       ImageList_Destroy(infoPtr->himlState);
9942   }
9943
9944   /* destroy font, bkgnd brush */
9945   infoPtr->hFont = 0;
9946   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9947   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9948
9949   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9950
9951   /* free listview info pointer*/
9952   Free(infoPtr);
9953
9954   return 0;
9955 }
9956
9957 /***
9958  * DESCRIPTION:
9959  * Handles notifications from header.
9960  *
9961  * PARAMETER(S):
9962  * [I] infoPtr : valid pointer to the listview structure
9963  * [I] nCtrlId : control identifier
9964  * [I] lpnmh : notification information
9965  *
9966  * RETURN:
9967  * Zero
9968  */
9969 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9970 {
9971     HWND hwndSelf = infoPtr->hwndSelf;
9972     
9973     TRACE("(lpnmh=%p)\n", lpnmh);
9974
9975     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9976     
9977     switch (lpnmh->hdr.code)
9978     {    
9979         case HDN_TRACKW:
9980         case HDN_TRACKA:
9981         {
9982             COLUMN_INFO *lpColumnInfo;
9983             POINT ptOrigin;
9984             INT x;
9985             
9986             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9987                 break;
9988
9989             /* remove the old line (if any) */
9990             LISTVIEW_DrawTrackLine(infoPtr);
9991             
9992             /* compute & draw the new line */
9993             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9994             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9995             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9996             infoPtr->xTrackLine = x + ptOrigin.x;
9997             LISTVIEW_DrawTrackLine(infoPtr);
9998             break;
9999         }
10000         
10001         case HDN_ENDTRACKA:
10002         case HDN_ENDTRACKW:
10003             /* remove the track line (if any) */
10004             LISTVIEW_DrawTrackLine(infoPtr);
10005             infoPtr->xTrackLine = -1;
10006             break;
10007
10008         case HDN_BEGINDRAG:
10009             notify_forward_header(infoPtr, lpnmh);
10010             return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
10011
10012         case HDN_ENDDRAG:
10013             infoPtr->colRectsDirty = TRUE;
10014             LISTVIEW_InvalidateList(infoPtr);
10015             notify_forward_header(infoPtr, lpnmh);
10016             return FALSE;
10017
10018         case HDN_ITEMCHANGINGW:
10019         case HDN_ITEMCHANGINGA:
10020             return notify_forward_header(infoPtr, lpnmh);
10021             
10022         case HDN_ITEMCHANGEDW:
10023         case HDN_ITEMCHANGEDA:
10024         {
10025             COLUMN_INFO *lpColumnInfo;
10026             HDITEMW hdi;
10027             INT dx, cxy;
10028             
10029             notify_forward_header(infoPtr, lpnmh);
10030             if (!IsWindow(hwndSelf))
10031                 break;
10032
10033             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10034             {
10035                 hdi.mask = HDI_WIDTH;
10036                 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
10037                 cxy = hdi.cxy;
10038             }
10039             else
10040                 cxy = lpnmh->pitem->cxy;
10041             
10042             /* determine how much we change since the last know position */
10043             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10044             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
10045             if (dx != 0)
10046             {
10047                 lpColumnInfo->rcHeader.right += dx;
10048
10049                 hdi.mask = HDI_ORDER;
10050                 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi);
10051
10052                 /* not the rightmost one */
10053                 if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
10054                 {
10055                     INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
10056                                               hdi.iOrder + 1, 0);
10057                     LISTVIEW_ScrollColumns(infoPtr, nIndex, dx);
10058                 }
10059                 else
10060                 {
10061                     /* only needs to update the scrolls */
10062                     infoPtr->nItemWidth += dx;
10063                     LISTVIEW_UpdateScroll(infoPtr);
10064                 }
10065                 LISTVIEW_UpdateItemSize(infoPtr);
10066                 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
10067                 {
10068                     POINT ptOrigin;
10069                     RECT rcCol = lpColumnInfo->rcHeader;
10070                     
10071                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10072                     OffsetRect(&rcCol, ptOrigin.x, 0);
10073                     
10074                     rcCol.top = infoPtr->rcList.top;
10075                     rcCol.bottom = infoPtr->rcList.bottom;
10076
10077                     /* resizing left-aligned columns leaves most of the left side untouched */
10078                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
10079                     {
10080                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
10081                         if (dx > 0)
10082                             nMaxDirty += dx;
10083                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
10084                     }
10085
10086                     /* when shrinking the last column clear the now unused field */
10087                     if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
10088                     {
10089                         RECT right;
10090
10091                         rcCol.right -= dx;
10092
10093                         /* deal with right from rightmost column area */
10094                         right.left = rcCol.right;
10095                         right.top  = rcCol.top;
10096                         right.bottom = rcCol.bottom;
10097                         right.right = infoPtr->rcList.right;
10098
10099                         LISTVIEW_InvalidateRect(infoPtr, &right);
10100                     }
10101
10102                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
10103                 }
10104             }
10105         }
10106         break;
10107
10108         case HDN_ITEMCLICKW:
10109         case HDN_ITEMCLICKA:
10110         {
10111             /* Handle sorting by Header Column */
10112             NMLISTVIEW nmlv;
10113
10114             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
10115             nmlv.iItem = -1;
10116             nmlv.iSubItem = lpnmh->iItem;
10117             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
10118             notify_forward_header(infoPtr, lpnmh);
10119         }
10120         break;
10121
10122         case HDN_DIVIDERDBLCLICKW:
10123         case HDN_DIVIDERDBLCLICKA:
10124             /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10125                       we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10126                       split needed for that */
10127             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
10128             notify_forward_header(infoPtr, lpnmh);
10129             break;
10130     }
10131
10132     return 0;
10133 }
10134
10135 /***
10136  * DESCRIPTION:
10137  * Paint non-client area of control.
10138  *
10139  * PARAMETER(S):
10140  * [I] infoPtr : valid pointer to the listview structureof the sender
10141  * [I] region : update region
10142  *
10143  * RETURN:
10144  *  TRUE  - frame was painted
10145  *  FALSE - call default window proc
10146  */
10147 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
10148 {
10149     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
10150     HDC dc;
10151     RECT r;
10152     HRGN cliprgn;
10153     int cxEdge = GetSystemMetrics (SM_CXEDGE),
10154         cyEdge = GetSystemMetrics (SM_CYEDGE);
10155
10156     if (!theme)
10157        return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0);
10158
10159     GetWindowRect(infoPtr->hwndSelf, &r);
10160
10161     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
10162         r.right - cxEdge, r.bottom - cyEdge);
10163     if (region != (HRGN)1)
10164         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
10165     OffsetRect(&r, -r.left, -r.top);
10166
10167     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
10168     OffsetRect(&r, -r.left, -r.top);
10169
10170     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
10171         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
10172     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
10173     ReleaseDC(infoPtr->hwndSelf, dc);
10174
10175     /* Call default proc to get the scrollbars etc. painted */
10176     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
10177
10178     return FALSE;
10179 }
10180
10181 /***
10182  * DESCRIPTION:
10183  * Determines the type of structure to use.
10184  *
10185  * PARAMETER(S):
10186  * [I] infoPtr : valid pointer to the listview structureof the sender
10187  * [I] hwndFrom : listview window handle
10188  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10189  *
10190  * RETURN:
10191  * Zero
10192  */
10193 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
10194 {
10195     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
10196
10197     if (nCommand == NF_REQUERY)
10198         infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
10199
10200     return infoPtr->notifyFormat;
10201 }
10202
10203 /***
10204  * DESCRIPTION:
10205  * Paints/Repaints the listview control. Internal use.
10206  *
10207  * PARAMETER(S):
10208  * [I] infoPtr : valid pointer to the listview structure
10209  * [I] hdc : device context handle
10210  *
10211  * RETURN:
10212  * Zero
10213  */
10214 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
10215 {
10216     TRACE("(hdc=%p)\n", hdc);
10217
10218     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
10219     {
10220         infoPtr->bNoItemMetrics = FALSE;
10221         LISTVIEW_UpdateItemSize(infoPtr);
10222         if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
10223             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10224         LISTVIEW_UpdateScroll(infoPtr);
10225     }
10226
10227     if (infoPtr->hwndHeader)  UpdateWindow(infoPtr->hwndHeader);
10228
10229     if (hdc) 
10230         LISTVIEW_Refresh(infoPtr, hdc, NULL);
10231     else
10232     {
10233         PAINTSTRUCT ps;
10234
10235         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
10236         if (!hdc) return 1;
10237         LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
10238         EndPaint(infoPtr->hwndSelf, &ps);
10239     }
10240
10241     return 0;
10242 }
10243
10244 /***
10245  * DESCRIPTION:
10246  * Paints/Repaints the listview control, WM_PAINT handler.
10247  *
10248  * PARAMETER(S):
10249  * [I] infoPtr : valid pointer to the listview structure
10250  * [I] hdc : device context handle
10251  *
10252  * RETURN:
10253  * Zero
10254  */
10255 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc)
10256 {
10257     TRACE("(hdc=%p)\n", hdc);
10258
10259     if (!is_redrawing(infoPtr))
10260         return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0);
10261
10262     return LISTVIEW_Paint(infoPtr, hdc);
10263 }
10264
10265 /***
10266  * DESCRIPTION:
10267  * Paints/Repaints the listview control.
10268  *
10269  * PARAMETER(S):
10270  * [I] infoPtr : valid pointer to the listview structure
10271  * [I] hdc : device context handle
10272  * [I] options : drawing options
10273  *
10274  * RETURN:
10275  * Zero
10276  */
10277 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
10278 {
10279     FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
10280
10281     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
10282         return 0;
10283
10284     if (options & PRF_ERASEBKGND)
10285         LISTVIEW_EraseBkgnd(infoPtr, hdc);
10286
10287     if (options & PRF_CLIENT)
10288         LISTVIEW_Paint(infoPtr, hdc);
10289
10290     return 0;
10291 }
10292
10293
10294 /***
10295  * DESCRIPTION:
10296  * Processes double click messages (right mouse button).
10297  *
10298  * PARAMETER(S):
10299  * [I] infoPtr : valid pointer to the listview structure
10300  * [I] wKey : key flag
10301  * [I] x,y : mouse coordinate
10302  *
10303  * RETURN:
10304  * Zero
10305  */
10306 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10307 {
10308     LVHITTESTINFO lvHitTestInfo;
10309     
10310     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10311
10312     /* send NM_RELEASEDCAPTURE notification */
10313     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10314
10315     /* send NM_RDBLCLK notification */
10316     lvHitTestInfo.pt.x = x;
10317     lvHitTestInfo.pt.y = y;
10318     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10319     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
10320
10321     return 0;
10322 }
10323
10324 /***
10325  * DESCRIPTION:
10326  * Processes mouse down messages (right mouse button).
10327  *
10328  * PARAMETER(S):
10329  * [I] infoPtr : valid pointer to the listview structure
10330  * [I] wKey : key flag
10331  * [I] x,y : mouse coordinate
10332  *
10333  * RETURN:
10334  * Zero
10335  */
10336 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10337 {
10338     LVHITTESTINFO lvHitTestInfo;
10339     INT nItem;
10340
10341     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10342
10343     /* send NM_RELEASEDCAPTURE notification */
10344     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10345
10346     /* make sure the listview control window has the focus */
10347     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
10348
10349     /* set right button down flag */
10350     infoPtr->bRButtonDown = TRUE;
10351
10352     /* determine the index of the selected item */
10353     lvHitTestInfo.pt.x = x;
10354     lvHitTestInfo.pt.y = y;
10355     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
10356   
10357     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
10358     {
10359         LISTVIEW_SetItemFocus(infoPtr, nItem);
10360         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
10361             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10362             LISTVIEW_SetSelection(infoPtr, nItem);
10363     }
10364     else
10365     {
10366         LISTVIEW_DeselectAll(infoPtr);
10367     }
10368
10369     return 0;
10370 }
10371
10372 /***
10373  * DESCRIPTION:
10374  * Processes mouse up messages (right mouse button).
10375  *
10376  * PARAMETER(S):
10377  * [I] infoPtr : valid pointer to the listview structure
10378  * [I] wKey : key flag
10379  * [I] x,y : mouse coordinate
10380  *
10381  * RETURN:
10382  * Zero
10383  */
10384 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10385 {
10386     LVHITTESTINFO lvHitTestInfo;
10387     POINT pt;
10388
10389     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10390
10391     if (!infoPtr->bRButtonDown) return 0;
10392  
10393     /* set button flag */
10394     infoPtr->bRButtonDown = FALSE;
10395
10396     /* Send NM_RCLICK notification */
10397     lvHitTestInfo.pt.x = x;
10398     lvHitTestInfo.pt.y = y;
10399     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10400     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
10401
10402     /* Change to screen coordinate for WM_CONTEXTMENU */
10403     pt = lvHitTestInfo.pt;
10404     ClientToScreen(infoPtr->hwndSelf, &pt);
10405
10406     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
10407     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
10408                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
10409
10410     return 0;
10411 }
10412
10413
10414 /***
10415  * DESCRIPTION:
10416  * Sets the cursor.
10417  *
10418  * PARAMETER(S):
10419  * [I] infoPtr : valid pointer to the listview structure
10420  * [I] hwnd : window handle of window containing the cursor
10421  * [I] nHittest : hit-test code
10422  * [I] wMouseMsg : ideintifier of the mouse message
10423  *
10424  * RETURN:
10425  * TRUE if cursor is set
10426  * FALSE otherwise
10427  */
10428 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10429 {
10430     LVHITTESTINFO lvHitTestInfo;
10431
10432     if (!LISTVIEW_IsHotTracking(infoPtr)) goto forward;
10433
10434     if (!infoPtr->hHotCursor) goto forward;
10435
10436     GetCursorPos(&lvHitTestInfo.pt);
10437     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward;
10438
10439     SetCursor(infoPtr->hHotCursor);
10440
10441     return TRUE;
10442
10443 forward:
10444
10445     return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam);
10446 }
10447
10448 /***
10449  * DESCRIPTION:
10450  * Sets the focus.
10451  *
10452  * PARAMETER(S):
10453  * [I] infoPtr : valid pointer to the listview structure
10454  * [I] hwndLoseFocus : handle of previously focused window
10455  *
10456  * RETURN:
10457  * Zero
10458  */
10459 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
10460 {
10461     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
10462
10463     /* if we have the focus already, there's nothing to do */
10464     if (infoPtr->bFocus) return 0;
10465    
10466     /* send NM_SETFOCUS notification */
10467     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
10468
10469     /* set window focus flag */
10470     infoPtr->bFocus = TRUE;
10471
10472     /* put the focus rect back on */
10473     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
10474
10475     /* redraw all visible selected items */
10476     LISTVIEW_InvalidateSelectedItems(infoPtr);
10477
10478     return 0;
10479 }
10480
10481 /***
10482  * DESCRIPTION:
10483  * Sets the font.
10484  *
10485  * PARAMETER(S):
10486  * [I] infoPtr : valid pointer to the listview structure
10487  * [I] fRedraw : font handle
10488  * [I] fRedraw : redraw flag
10489  *
10490  * RETURN:
10491  * Zero
10492  */
10493 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
10494 {
10495     HFONT oldFont = infoPtr->hFont;
10496
10497     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
10498
10499     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
10500     if (infoPtr->hFont == oldFont) return 0;
10501     
10502     LISTVIEW_SaveTextMetrics(infoPtr);
10503
10504     if (infoPtr->uView == LV_VIEW_DETAILS)
10505     {
10506         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
10507         LISTVIEW_UpdateSize(infoPtr);
10508         LISTVIEW_UpdateScroll(infoPtr);
10509     }
10510
10511     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
10512
10513     return 0;
10514 }
10515
10516 /***
10517  * DESCRIPTION:
10518  * Message handling for WM_SETREDRAW.
10519  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10520  *
10521  * PARAMETER(S):
10522  * [I] infoPtr : valid pointer to the listview structure
10523  * [I] bRedraw: state of redraw flag
10524  *
10525  * RETURN:
10526  * DefWinProc return value
10527  */
10528 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
10529 {
10530     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
10531
10532     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
10533     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
10534
10535     infoPtr->bRedraw = bRedraw;
10536
10537     if(!bRedraw) return 0;
10538     
10539     if (is_autoarrange(infoPtr))
10540         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10541     LISTVIEW_UpdateScroll(infoPtr);
10542
10543     /* despite what the WM_SETREDRAW docs says, apps expect us
10544      * to invalidate the listview here... stupid! */
10545     LISTVIEW_InvalidateList(infoPtr);
10546
10547     return 0;
10548 }
10549
10550 /***
10551  * DESCRIPTION:
10552  * Resizes the listview control. This function processes WM_SIZE
10553  * messages.  At this time, the width and height are not used.
10554  *
10555  * PARAMETER(S):
10556  * [I] infoPtr : valid pointer to the listview structure
10557  * [I] Width : new width
10558  * [I] Height : new height
10559  *
10560  * RETURN:
10561  * Zero
10562  */
10563 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
10564 {
10565     RECT rcOld = infoPtr->rcList;
10566
10567     TRACE("(width=%d, height=%d)\n", Width, Height);
10568
10569     LISTVIEW_UpdateSize(infoPtr);
10570     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
10571   
10572     /* do not bother with display related stuff if we're not redrawing */ 
10573     if (!is_redrawing(infoPtr)) return 0;
10574     
10575     if (is_autoarrange(infoPtr)) 
10576         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10577
10578     LISTVIEW_UpdateScroll(infoPtr);
10579
10580     /* refresh all only for lists whose height changed significantly */
10581     if ((infoPtr->uView == LV_VIEW_LIST) &&
10582         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
10583         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
10584         LISTVIEW_InvalidateList(infoPtr);
10585
10586   return 0;
10587 }
10588
10589 /***
10590  * DESCRIPTION:
10591  * Sets the size information.
10592  *
10593  * PARAMETER(S):
10594  * [I] infoPtr : valid pointer to the listview structure
10595  *
10596  * RETURN:
10597  *  None
10598  */
10599 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
10600 {
10601     TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
10602     
10603     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
10604
10605     if (infoPtr->uView == LV_VIEW_LIST)
10606     {
10607         /* Apparently the "LIST" style is supposed to have the same
10608          * number of items in a column even if there is no scroll bar.
10609          * Since if a scroll bar already exists then the bottom is already
10610          * reduced, only reduce if the scroll bar does not currently exist.
10611          * The "2" is there to mimic the native control. I think it may be
10612          * related to either padding or edges.  (GLA 7/2002)
10613          */
10614         if (!(infoPtr->dwStyle & WS_HSCROLL))
10615             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
10616         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
10617     }
10618
10619     /* if control created invisible header isn't created */
10620     if (infoPtr->hwndHeader)
10621     {
10622         HDLAYOUT hl;
10623         WINDOWPOS wp;
10624
10625         hl.prc = &infoPtr->rcList;
10626         hl.pwpos = &wp;
10627         SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10628         TRACE("  wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
10629
10630         if (LISTVIEW_IsHeaderEnabled(infoPtr))
10631             wp.flags |= SWP_SHOWWINDOW;
10632         else
10633         {
10634             wp.flags |= SWP_HIDEWINDOW;
10635             wp.cy = 0;
10636         }
10637
10638         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
10639         TRACE("  after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
10640
10641         infoPtr->rcList.top = max(wp.cy, 0);
10642     }
10643     /* extra padding for grid */
10644     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
10645         infoPtr->rcList.top += 2;
10646
10647     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
10648 }
10649
10650 /***
10651  * DESCRIPTION:
10652  * Processes WM_STYLECHANGED messages.
10653  *
10654  * PARAMETER(S):
10655  * [I] infoPtr : valid pointer to the listview structure
10656  * [I] wStyleType : window style type (normal or extended)
10657  * [I] lpss : window style information
10658  *
10659  * RETURN:
10660  * Zero
10661  */
10662 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10663                                  const STYLESTRUCT *lpss)
10664 {
10665     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
10666     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
10667     UINT style;
10668
10669     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10670           wStyleType, lpss->styleOld, lpss->styleNew);
10671
10672     if (wStyleType != GWL_STYLE) return 0;
10673
10674     infoPtr->dwStyle = lpss->styleNew;
10675     map_style_view(infoPtr);
10676
10677     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
10678         ((lpss->styleNew & WS_HSCROLL) == 0))
10679        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
10680
10681     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
10682         ((lpss->styleNew & WS_VSCROLL) == 0))
10683        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
10684
10685     if (uNewView != uOldView)
10686     {
10687         SIZE oldIconSize = infoPtr->iconSize;
10688         HIMAGELIST himl;
10689     
10690         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
10691         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
10692
10693         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
10694         SetRectEmpty(&infoPtr->rcFocus);
10695
10696         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
10697         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
10698     
10699         if (uNewView == LVS_ICON)
10700         {
10701             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
10702             {
10703                 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
10704                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
10705                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
10706             }
10707         }
10708         else if (uNewView == LVS_REPORT)
10709         {
10710             HDLAYOUT hl;
10711             WINDOWPOS wp;
10712
10713             LISTVIEW_CreateHeader( infoPtr );
10714
10715             hl.prc = &infoPtr->rcList;
10716             hl.pwpos = &wp;
10717             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10718             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
10719                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10720                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10721         }
10722
10723         LISTVIEW_UpdateItemSize(infoPtr);
10724     }
10725
10726     if (uNewView == LVS_REPORT || infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
10727     {
10728         if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
10729         {
10730             if (lpss->styleNew & LVS_NOCOLUMNHEADER)
10731             {
10732                 /* Turn off the header control */
10733                 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
10734                 TRACE("Hide header control, was 0x%08x\n", style);
10735                 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
10736             } else {
10737                 /* Turn on the header control */
10738                 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
10739                 {
10740                     TRACE("Show header control, was 0x%08x\n", style);
10741                     SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
10742                 }
10743             }
10744         }
10745     }
10746
10747     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
10748          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
10749          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10750
10751     /* update the size of the client area */
10752     LISTVIEW_UpdateSize(infoPtr);
10753
10754     /* add scrollbars if needed */
10755     LISTVIEW_UpdateScroll(infoPtr);
10756
10757     /* invalidate client area + erase background */
10758     LISTVIEW_InvalidateList(infoPtr);
10759
10760     return 0;
10761 }
10762
10763 /***
10764  * DESCRIPTION:
10765  * Processes WM_STYLECHANGING messages.
10766  *
10767  * PARAMETER(S):
10768  * [I] infoPtr : valid pointer to the listview structure
10769  * [I] wStyleType : window style type (normal or extended)
10770  * [I0] lpss : window style information
10771  *
10772  * RETURN:
10773  * Zero
10774  */
10775 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10776                                   STYLESTRUCT *lpss)
10777 {
10778     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10779           wStyleType, lpss->styleOld, lpss->styleNew);
10780
10781     /* don't forward LVS_OWNERDATA only if not already set to */
10782     if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
10783     {
10784         if (lpss->styleOld & LVS_OWNERDATA)
10785             lpss->styleNew |= LVS_OWNERDATA;
10786         else
10787             lpss->styleNew &= ~LVS_OWNERDATA;
10788     }
10789
10790     return 0;
10791 }
10792
10793 /***
10794  * DESCRIPTION:
10795  * Processes WM_SHOWWINDOW messages.
10796  *
10797  * PARAMETER(S):
10798  * [I] infoPtr : valid pointer to the listview structure
10799  * [I] bShown  : window is being shown (FALSE when hidden)
10800  * [I] iStatus : window show status
10801  *
10802  * RETURN:
10803  * Zero
10804  */
10805 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus)
10806 {
10807   /* header delayed creation */
10808   if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
10809   {
10810     LISTVIEW_CreateHeader(infoPtr);
10811
10812     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
10813       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
10814   }
10815
10816   return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus);
10817 }
10818
10819 /***
10820  * DESCRIPTION:
10821  * Processes CCM_GETVERSION messages.
10822  *
10823  * PARAMETER(S):
10824  * [I] infoPtr : valid pointer to the listview structure
10825  *
10826  * RETURN:
10827  * Current version
10828  */
10829 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
10830 {
10831   return infoPtr->iVersion;
10832 }
10833
10834 /***
10835  * DESCRIPTION:
10836  * Processes CCM_SETVERSION messages.
10837  *
10838  * PARAMETER(S):
10839  * [I] infoPtr  : valid pointer to the listview structure
10840  * [I] iVersion : version to be set
10841  *
10842  * RETURN:
10843  * -1 when requested version is greater than DLL version;
10844  * previous version otherwise
10845  */
10846 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10847 {
10848   INT iOldVersion = infoPtr->iVersion;
10849
10850   if (iVersion > COMCTL32_VERSION)
10851     return -1;
10852
10853   infoPtr->iVersion = iVersion;
10854
10855   TRACE("new version %d\n", iVersion);
10856
10857   return iOldVersion;
10858 }
10859
10860 /***
10861  * DESCRIPTION:
10862  * Window procedure of the listview control.
10863  *
10864  */
10865 static LRESULT WINAPI
10866 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10867 {
10868   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10869
10870   TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10871
10872   if (!infoPtr && (uMsg != WM_NCCREATE))
10873     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10874
10875   switch (uMsg)
10876   {
10877   case LVM_APPROXIMATEVIEWRECT:
10878     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10879                                         LOWORD(lParam), HIWORD(lParam));
10880   case LVM_ARRANGE:
10881     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10882
10883   case LVM_CANCELEDITLABEL:
10884     return LISTVIEW_CancelEditLabel(infoPtr);
10885
10886   case LVM_CREATEDRAGIMAGE:
10887     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10888
10889   case LVM_DELETEALLITEMS:
10890     return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10891
10892   case LVM_DELETECOLUMN:
10893     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10894
10895   case LVM_DELETEITEM:
10896     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10897
10898   case LVM_EDITLABELA:
10899   case LVM_EDITLABELW:
10900     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam,
10901                                         uMsg == LVM_EDITLABELW);
10902   /* case LVM_ENABLEGROUPVIEW: */
10903
10904   case LVM_ENSUREVISIBLE:
10905     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10906
10907   case LVM_FINDITEMW:
10908     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10909
10910   case LVM_FINDITEMA:
10911     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10912
10913   case LVM_GETBKCOLOR:
10914     return infoPtr->clrBk;
10915
10916   /* case LVM_GETBKIMAGE: */
10917
10918   case LVM_GETCALLBACKMASK:
10919     return infoPtr->uCallbackMask;
10920
10921   case LVM_GETCOLUMNA:
10922   case LVM_GETCOLUMNW:
10923     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
10924                                uMsg == LVM_GETCOLUMNW);
10925
10926   case LVM_GETCOLUMNORDERARRAY:
10927     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10928
10929   case LVM_GETCOLUMNWIDTH:
10930     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10931
10932   case LVM_GETCOUNTPERPAGE:
10933     return LISTVIEW_GetCountPerPage(infoPtr);
10934
10935   case LVM_GETEDITCONTROL:
10936     return (LRESULT)infoPtr->hwndEdit;
10937
10938   case LVM_GETEXTENDEDLISTVIEWSTYLE:
10939     return infoPtr->dwLvExStyle;
10940
10941   /* case LVM_GETGROUPINFO: */
10942
10943   /* case LVM_GETGROUPMETRICS: */
10944
10945   case LVM_GETHEADER:
10946     return (LRESULT)infoPtr->hwndHeader;
10947
10948   case LVM_GETHOTCURSOR:
10949     return (LRESULT)infoPtr->hHotCursor;
10950
10951   case LVM_GETHOTITEM:
10952     return infoPtr->nHotItem;
10953
10954   case LVM_GETHOVERTIME:
10955     return infoPtr->dwHoverTime;
10956
10957   case LVM_GETIMAGELIST:
10958     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10959
10960   /* case LVM_GETINSERTMARK: */
10961
10962   /* case LVM_GETINSERTMARKCOLOR: */
10963
10964   /* case LVM_GETINSERTMARKRECT: */
10965
10966   case LVM_GETISEARCHSTRINGA:
10967   case LVM_GETISEARCHSTRINGW:
10968     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10969     return FALSE;
10970
10971   case LVM_GETITEMA:
10972   case LVM_GETITEMW:
10973     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW);
10974
10975   case LVM_GETITEMCOUNT:
10976     return infoPtr->nItemCount;
10977
10978   case LVM_GETITEMPOSITION:
10979     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10980
10981   case LVM_GETITEMRECT:
10982     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10983
10984   case LVM_GETITEMSPACING:
10985     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10986
10987   case LVM_GETITEMSTATE:
10988     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10989
10990   case LVM_GETITEMTEXTA:
10991   case LVM_GETITEMTEXTW:
10992     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
10993                                  uMsg == LVM_GETITEMTEXTW);
10994
10995   case LVM_GETNEXTITEM:
10996     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10997
10998   case LVM_GETNUMBEROFWORKAREAS:
10999     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11000     return 1;
11001
11002   case LVM_GETORIGIN:
11003     if (!lParam) return FALSE;
11004     if (infoPtr->uView == LV_VIEW_DETAILS ||
11005         infoPtr->uView == LV_VIEW_LIST) return FALSE;
11006     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
11007     return TRUE;
11008
11009   /* case LVM_GETOUTLINECOLOR: */
11010
11011   /* case LVM_GETSELECTEDCOLUMN: */
11012
11013   case LVM_GETSELECTEDCOUNT:
11014     return LISTVIEW_GetSelectedCount(infoPtr);
11015
11016   case LVM_GETSELECTIONMARK:
11017     return infoPtr->nSelectionMark;
11018
11019   case LVM_GETSTRINGWIDTHA:
11020   case LVM_GETSTRINGWIDTHW:
11021     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam,
11022                                     uMsg == LVM_GETSTRINGWIDTHW);
11023
11024   case LVM_GETSUBITEMRECT:
11025     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
11026
11027   case LVM_GETTEXTBKCOLOR:
11028     return infoPtr->clrTextBk;
11029
11030   case LVM_GETTEXTCOLOR:
11031     return infoPtr->clrText;
11032
11033   /* case LVM_GETTILEINFO: */
11034
11035   /* case LVM_GETTILEVIEWINFO: */
11036
11037   case LVM_GETTOOLTIPS:
11038     if( !infoPtr->hwndToolTip )
11039         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
11040     return (LRESULT)infoPtr->hwndToolTip;
11041
11042   case LVM_GETTOPINDEX:
11043     return LISTVIEW_GetTopIndex(infoPtr);
11044
11045   case LVM_GETUNICODEFORMAT:
11046     return (infoPtr->notifyFormat == NFR_UNICODE);
11047
11048   case LVM_GETVIEW:
11049     return infoPtr->uView;
11050
11051   case LVM_GETVIEWRECT:
11052     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
11053
11054   case LVM_GETWORKAREAS:
11055     FIXME("LVM_GETWORKAREAS: unimplemented\n");
11056     return FALSE;
11057
11058   /* case LVM_HASGROUP: */
11059
11060   case LVM_HITTEST:
11061     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
11062
11063   case LVM_INSERTCOLUMNA:
11064   case LVM_INSERTCOLUMNW:
11065     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11066                                   uMsg == LVM_INSERTCOLUMNW);
11067
11068   /* case LVM_INSERTGROUP: */
11069
11070   /* case LVM_INSERTGROUPSORTED: */
11071
11072   case LVM_INSERTITEMA:
11073   case LVM_INSERTITEMW:
11074     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW);
11075
11076   /* case LVM_INSERTMARKHITTEST: */
11077
11078   /* case LVM_ISGROUPVIEWENABLED: */
11079
11080   case LVM_ISITEMVISIBLE:
11081     return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
11082
11083   case LVM_MAPIDTOINDEX:
11084     return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
11085
11086   case LVM_MAPINDEXTOID:
11087     return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
11088
11089   /* case LVM_MOVEGROUP: */
11090
11091   /* case LVM_MOVEITEMTOGROUP: */
11092
11093   case LVM_REDRAWITEMS:
11094     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
11095
11096   /* case LVM_REMOVEALLGROUPS: */
11097
11098   /* case LVM_REMOVEGROUP: */
11099
11100   case LVM_SCROLL:
11101     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
11102
11103   case LVM_SETBKCOLOR:
11104     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
11105
11106   /* case LVM_SETBKIMAGE: */
11107
11108   case LVM_SETCALLBACKMASK:
11109     infoPtr->uCallbackMask = (UINT)wParam;
11110     return TRUE;
11111
11112   case LVM_SETCOLUMNA:
11113   case LVM_SETCOLUMNW:
11114     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11115                                uMsg == LVM_SETCOLUMNW);
11116
11117   case LVM_SETCOLUMNORDERARRAY:
11118     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11119
11120   case LVM_SETCOLUMNWIDTH:
11121     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
11122
11123   case LVM_SETEXTENDEDLISTVIEWSTYLE:
11124     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
11125
11126   /* case LVM_SETGROUPINFO: */
11127
11128   /* case LVM_SETGROUPMETRICS: */
11129
11130   case LVM_SETHOTCURSOR:
11131     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
11132
11133   case LVM_SETHOTITEM:
11134     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
11135
11136   case LVM_SETHOVERTIME:
11137     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam);
11138
11139   case LVM_SETICONSPACING:
11140     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
11141
11142   case LVM_SETIMAGELIST:
11143     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
11144
11145   /* case LVM_SETINFOTIP: */
11146
11147   /* case LVM_SETINSERTMARK: */
11148
11149   /* case LVM_SETINSERTMARKCOLOR: */
11150
11151   case LVM_SETITEMA:
11152   case LVM_SETITEMW:
11153     {
11154         if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
11155         return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
11156     }
11157
11158   case LVM_SETITEMCOUNT:
11159     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
11160
11161   case LVM_SETITEMPOSITION:
11162     {
11163         POINT pt;
11164         pt.x = (short)LOWORD(lParam);
11165         pt.y = (short)HIWORD(lParam);
11166         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt);
11167     }
11168
11169   case LVM_SETITEMPOSITION32:
11170     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
11171
11172   case LVM_SETITEMSTATE:
11173     if (lParam == 0) return FALSE;
11174     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
11175
11176   case LVM_SETITEMTEXTA:
11177   case LVM_SETITEMTEXTW:
11178     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11179                                  uMsg == LVM_SETITEMTEXTW);
11180
11181   /* case LVM_SETOUTLINECOLOR: */
11182
11183   /* case LVM_SETSELECTEDCOLUMN: */
11184
11185   case LVM_SETSELECTIONMARK:
11186     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
11187
11188   case LVM_SETTEXTBKCOLOR:
11189     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
11190
11191   case LVM_SETTEXTCOLOR:
11192     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
11193
11194   /* case LVM_SETTILEINFO: */
11195
11196   /* case LVM_SETTILEVIEWINFO: */
11197
11198   /* case LVM_SETTILEWIDTH: */
11199
11200   case LVM_SETTOOLTIPS:
11201     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
11202
11203   case LVM_SETUNICODEFORMAT:
11204     return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
11205
11206   case LVM_SETVIEW:
11207     return LISTVIEW_SetView(infoPtr, wParam);
11208
11209   /* case LVM_SETWORKAREAS: */
11210
11211   /* case LVM_SORTGROUPS: */
11212
11213   case LVM_SORTITEMS:
11214   case LVM_SORTITEMSEX:
11215     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam,
11216                               uMsg == LVM_SORTITEMSEX);
11217   case LVM_SUBITEMHITTEST:
11218     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
11219
11220   case LVM_UPDATE:
11221     return LISTVIEW_Update(infoPtr, (INT)wParam);
11222
11223   case CCM_GETVERSION:
11224     return LISTVIEW_GetVersion(infoPtr);
11225
11226   case CCM_SETVERSION:
11227     return LISTVIEW_SetVersion(infoPtr, wParam);
11228
11229   case WM_CHAR:
11230     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
11231
11232   case WM_COMMAND:
11233     return LISTVIEW_Command(infoPtr, wParam, lParam);
11234
11235   case WM_NCCREATE:
11236     return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
11237
11238   case WM_CREATE:
11239     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
11240
11241   case WM_DESTROY:
11242     return LISTVIEW_Destroy(infoPtr);
11243
11244   case WM_ENABLE:
11245     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
11246
11247   case WM_ERASEBKGND:
11248     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
11249
11250   case WM_GETDLGCODE:
11251     return DLGC_WANTCHARS | DLGC_WANTARROWS;
11252
11253   case WM_GETFONT:
11254     return (LRESULT)infoPtr->hFont;
11255
11256   case WM_HSCROLL:
11257     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
11258
11259   case WM_KEYDOWN:
11260     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
11261
11262   case WM_KILLFOCUS:
11263     return LISTVIEW_KillFocus(infoPtr);
11264
11265   case WM_LBUTTONDBLCLK:
11266     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11267
11268   case WM_LBUTTONDOWN:
11269     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11270
11271   case WM_LBUTTONUP:
11272     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11273
11274   case WM_MOUSEMOVE:
11275     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11276
11277   case WM_MOUSEHOVER:
11278     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11279
11280   case WM_NCDESTROY:
11281     return LISTVIEW_NCDestroy(infoPtr);
11282
11283   case WM_NCPAINT:
11284     return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam);
11285
11286   case WM_NOTIFY:
11287     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
11288         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
11289     else return 0;
11290
11291   case WM_NOTIFYFORMAT:
11292     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
11293
11294   case WM_PRINTCLIENT:
11295     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
11296
11297   case WM_PAINT:
11298     return LISTVIEW_WMPaint(infoPtr, (HDC)wParam);
11299
11300   case WM_RBUTTONDBLCLK:
11301     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11302
11303   case WM_RBUTTONDOWN:
11304     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11305
11306   case WM_RBUTTONUP:
11307     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11308
11309   case WM_SETCURSOR:
11310     return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
11311
11312   case WM_SETFOCUS:
11313     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
11314
11315   case WM_SETFONT:
11316     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
11317
11318   case WM_SETREDRAW:
11319     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
11320
11321   case WM_SHOWWINDOW:
11322     return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
11323
11324   case WM_SIZE:
11325     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
11326
11327   case WM_STYLECHANGED:
11328     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11329
11330   case WM_STYLECHANGING:
11331     return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11332
11333   case WM_SYSCOLORCHANGE:
11334     COMCTL32_RefreshSysColors();
11335     return 0;
11336
11337 /*      case WM_TIMER: */
11338   case WM_THEMECHANGED:
11339     return LISTVIEW_ThemeChanged(infoPtr);
11340
11341   case WM_VSCROLL:
11342     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
11343
11344   case WM_MOUSEWHEEL:
11345       if (wParam & (MK_SHIFT | MK_CONTROL))
11346           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11347       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
11348
11349   case WM_WINDOWPOSCHANGED:
11350       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
11351       {
11352       SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
11353                    SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
11354
11355       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
11356       {
11357           if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
11358       }
11359
11360           LISTVIEW_UpdateSize(infoPtr);
11361           LISTVIEW_UpdateScroll(infoPtr);
11362       }
11363       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11364
11365 /*      case WM_WININICHANGE: */
11366
11367   default:
11368     if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
11369       ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
11370
11371     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11372   }
11373
11374 }
11375
11376 /***
11377  * DESCRIPTION:
11378  * Registers the window class.
11379  *
11380  * PARAMETER(S):
11381  * None
11382  *
11383  * RETURN:
11384  * None
11385  */
11386 void LISTVIEW_Register(void)
11387 {
11388     WNDCLASSW wndClass;
11389
11390     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
11391     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
11392     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
11393     wndClass.cbClsExtra = 0;
11394     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
11395     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
11396     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
11397     wndClass.lpszClassName = WC_LISTVIEWW;
11398     RegisterClassW(&wndClass);
11399 }
11400
11401 /***
11402  * DESCRIPTION:
11403  * Unregisters the window class.
11404  *
11405  * PARAMETER(S):
11406  * None
11407  *
11408  * RETURN:
11409  * None
11410  */
11411 void LISTVIEW_Unregister(void)
11412 {
11413     UnregisterClassW(WC_LISTVIEWW, NULL);
11414 }
11415
11416 /***
11417  * DESCRIPTION:
11418  * Handle any WM_COMMAND messages
11419  *
11420  * PARAMETER(S):
11421  * [I] infoPtr : valid pointer to the listview structure
11422  * [I] wParam : the first message parameter
11423  * [I] lParam : the second message parameter
11424  *
11425  * RETURN:
11426  *   Zero.
11427  */
11428 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
11429 {
11430     switch (HIWORD(wParam))
11431     {
11432         case EN_UPDATE:
11433         {
11434             /*
11435              * Adjust the edit window size
11436              */
11437             WCHAR buffer[1024];
11438             HDC           hdc = GetDC(infoPtr->hwndEdit);
11439             HFONT         hFont, hOldFont = 0;
11440             RECT          rect;
11441             SIZE          sz;
11442
11443             if (!infoPtr->hwndEdit || !hdc) return 0;
11444             GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
11445             GetWindowRect(infoPtr->hwndEdit, &rect);
11446
11447             /* Select font to get the right dimension of the string */
11448             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
11449             if(hFont != 0)
11450             {
11451                 hOldFont = SelectObject(hdc, hFont);
11452             }
11453
11454             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
11455             {
11456                 TEXTMETRICW textMetric;
11457
11458                 /* Add Extra spacing for the next character */
11459                 GetTextMetricsW(hdc, &textMetric);
11460                 sz.cx += (textMetric.tmMaxCharWidth * 2);
11461
11462                 SetWindowPos (
11463                     infoPtr->hwndEdit,
11464                     HWND_TOP,
11465                     0,
11466                     0,
11467                     sz.cx,
11468                     rect.bottom - rect.top,
11469                     SWP_DRAWFRAME|SWP_NOMOVE);
11470             }
11471             if(hFont != 0)
11472                 SelectObject(hdc, hOldFont);
11473
11474             ReleaseDC(infoPtr->hwndEdit, hdc);
11475
11476             break;
11477         }
11478         case EN_KILLFOCUS:
11479         {
11480             LISTVIEW_CancelEditLabel(infoPtr);
11481             break;
11482         }
11483
11484         default:
11485           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
11486     }
11487
11488     return 0;
11489 }
11490
11491
11492 /***
11493  * DESCRIPTION:
11494  * Subclassed edit control windproc function
11495  *
11496  * PARAMETER(S):
11497  * [I] hwnd : the edit window handle
11498  * [I] uMsg : the message that is to be processed
11499  * [I] wParam : first message parameter
11500  * [I] lParam : second message parameter
11501  * [I] isW : TRUE if input is Unicode
11502  *
11503  * RETURN:
11504  *   Zero.
11505  */
11506 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
11507 {
11508     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
11509     BOOL save = TRUE;
11510
11511     TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
11512           hwnd, uMsg, wParam, lParam, isW);
11513
11514     switch (uMsg)
11515     {
11516         case WM_GETDLGCODE:
11517           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
11518
11519         case WM_DESTROY:
11520         {
11521             WNDPROC editProc = infoPtr->EditWndProc;
11522             infoPtr->EditWndProc = 0;
11523             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
11524             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
11525         }
11526
11527         case WM_KEYDOWN:
11528             if (VK_ESCAPE == (INT)wParam)
11529             {
11530                 save = FALSE;
11531                 break;
11532             }
11533             else if (VK_RETURN == (INT)wParam)
11534                 break;
11535
11536         default:
11537             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
11538     }
11539
11540     /* kill the edit */
11541     if (infoPtr->hwndEdit)
11542         LISTVIEW_EndEditLabelT(infoPtr, save, isW);
11543
11544     SendMessageW(hwnd, WM_CLOSE, 0, 0);
11545     return 0;
11546 }
11547
11548 /***
11549  * DESCRIPTION:
11550  * Subclassed edit control Unicode windproc function
11551  *
11552  * PARAMETER(S):
11553  * [I] hwnd : the edit window handle
11554  * [I] uMsg : the message that is to be processed
11555  * [I] wParam : first message parameter
11556  * [I] lParam : second message parameter
11557  *
11558  * RETURN:
11559  */
11560 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11561 {
11562     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
11563 }
11564
11565 /***
11566  * DESCRIPTION:
11567  * Subclassed edit control ANSI windproc function
11568  *
11569  * PARAMETER(S):
11570  * [I] hwnd : the edit window handle
11571  * [I] uMsg : the message that is to be processed
11572  * [I] wParam : first message parameter
11573  * [I] lParam : second message parameter
11574  *
11575  * RETURN:
11576  */
11577 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11578 {
11579     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
11580 }
11581
11582 /***
11583  * DESCRIPTION:
11584  * Creates a subclassed edit control
11585  *
11586  * PARAMETER(S):
11587  * [I] infoPtr : valid pointer to the listview structure
11588  * [I] text : initial text for the edit
11589  * [I] style : the window style
11590  * [I] isW : TRUE if input is Unicode
11591  *
11592  * RETURN:
11593  */
11594 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
11595 {
11596     HWND hedit;
11597     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
11598
11599     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
11600
11601     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
11602
11603     /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
11604     if (isW)
11605         hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11606     else
11607         hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11608
11609     if (!hedit) return 0;
11610
11611     infoPtr->EditWndProc = (WNDPROC)
11612         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
11613                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
11614
11615     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
11616
11617     return hedit;
11618 }