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