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