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