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